@lanonasis/memory-client 2.0.0 → 2.1.0
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/dist/core/index.d.ts +578 -51
- package/dist/core/index.js +657 -120
- package/dist/core/index.js.map +1 -1
- package/dist/core/tsconfig.tsbuildinfo +1 -0
- package/dist/index.d.ts +635 -51
- package/dist/index.esm.js +1338 -121
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +1359 -121
- package/dist/index.js.map +1 -1
- package/dist/node/index.d.ts +31 -3
- package/dist/node/index.js +65 -28
- package/dist/node/index.js.map +1 -1
- package/dist/node/tsconfig.tsbuildinfo +1 -0
- package/dist/presets/index.js.map +1 -1
- package/dist/presets/tsconfig.tsbuildinfo +1 -0
- package/dist/react/index.d.ts +33 -11
- package/dist/react/index.js +9 -24
- package/dist/react/index.js.map +1 -1
- package/dist/react/tsconfig.tsbuildinfo +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/vue/index.d.ts +127 -22
- package/dist/vue/index.js +9 -24
- package/dist/vue/index.js.map +1 -1
- package/dist/vue/tsconfig.tsbuildinfo +1 -0
- package/package.json +11 -8
package/dist/index.js
CHANGED
|
@@ -1,6 +1,224 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var zod = require('zod');
|
|
4
|
+
var child_process = require('child_process');
|
|
5
|
+
var util = require('util');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Memory types supported by the service
|
|
9
|
+
*/
|
|
10
|
+
const MEMORY_TYPES = ['context', 'project', 'knowledge', 'reference', 'personal', 'workflow'];
|
|
11
|
+
/**
|
|
12
|
+
* Memory status values
|
|
13
|
+
*/
|
|
14
|
+
const MEMORY_STATUSES = ['active', 'archived', 'draft', 'deleted'];
|
|
15
|
+
/**
|
|
16
|
+
* Validation schemas using Zod
|
|
17
|
+
*/
|
|
18
|
+
const createMemorySchema = zod.z.object({
|
|
19
|
+
title: zod.z.string().min(1).max(500),
|
|
20
|
+
content: zod.z.string().min(1).max(50000),
|
|
21
|
+
summary: zod.z.string().max(1000).optional(),
|
|
22
|
+
memory_type: zod.z.enum(MEMORY_TYPES).default('context'),
|
|
23
|
+
topic_id: zod.z.string().uuid().optional(),
|
|
24
|
+
project_ref: zod.z.string().max(100).optional(),
|
|
25
|
+
tags: zod.z.array(zod.z.string().min(1).max(50)).max(20).default([]),
|
|
26
|
+
metadata: zod.z.record(zod.z.string(), zod.z.unknown()).optional()
|
|
27
|
+
});
|
|
28
|
+
const updateMemorySchema = zod.z.object({
|
|
29
|
+
title: zod.z.string().min(1).max(500).optional(),
|
|
30
|
+
content: zod.z.string().min(1).max(50000).optional(),
|
|
31
|
+
summary: zod.z.string().max(1000).optional(),
|
|
32
|
+
memory_type: zod.z.enum(MEMORY_TYPES).optional(),
|
|
33
|
+
status: zod.z.enum(MEMORY_STATUSES).optional(),
|
|
34
|
+
topic_id: zod.z.string().uuid().nullable().optional(),
|
|
35
|
+
project_ref: zod.z.string().max(100).nullable().optional(),
|
|
36
|
+
tags: zod.z.array(zod.z.string().min(1).max(50)).max(20).optional(),
|
|
37
|
+
metadata: zod.z.record(zod.z.string(), zod.z.unknown()).optional()
|
|
38
|
+
});
|
|
39
|
+
const searchMemorySchema = zod.z.object({
|
|
40
|
+
query: zod.z.string().min(1).max(1000),
|
|
41
|
+
memory_types: zod.z.array(zod.z.enum(MEMORY_TYPES)).optional(),
|
|
42
|
+
tags: zod.z.array(zod.z.string()).optional(),
|
|
43
|
+
topic_id: zod.z.string().uuid().optional(),
|
|
44
|
+
project_ref: zod.z.string().optional(),
|
|
45
|
+
status: zod.z.enum(MEMORY_STATUSES).default('active'),
|
|
46
|
+
limit: zod.z.number().int().min(1).max(100).default(20),
|
|
47
|
+
threshold: zod.z.number().min(0).max(1).default(0.7)
|
|
48
|
+
});
|
|
49
|
+
const createTopicSchema = zod.z.object({
|
|
50
|
+
name: zod.z.string().min(1).max(100),
|
|
51
|
+
description: zod.z.string().max(500).optional(),
|
|
52
|
+
color: zod.z.string().regex(/^#[0-9A-Fa-f]{6}$/).optional(),
|
|
53
|
+
icon: zod.z.string().max(50).optional(),
|
|
54
|
+
parent_topic_id: zod.z.string().uuid().optional()
|
|
55
|
+
});
|
|
56
|
+
// ========================================
|
|
57
|
+
// Intelligence Feature Types (v2.0)
|
|
58
|
+
// ========================================
|
|
59
|
+
/**
|
|
60
|
+
* Chunking strategies for content preprocessing
|
|
61
|
+
*/
|
|
62
|
+
const CHUNKING_STRATEGIES = ['semantic', 'fixed-size', 'paragraph', 'sentence', 'code-block'];
|
|
63
|
+
/**
|
|
64
|
+
* Content types detected or specified
|
|
65
|
+
*/
|
|
66
|
+
const CONTENT_TYPES = ['text', 'code', 'markdown', 'json', 'yaml'];
|
|
67
|
+
// ========================================
|
|
68
|
+
// Enhanced Search Types
|
|
69
|
+
// ========================================
|
|
70
|
+
/**
|
|
71
|
+
* Search modes for memory queries
|
|
72
|
+
*/
|
|
73
|
+
const SEARCH_MODES = ['vector', 'text', 'hybrid'];
|
|
74
|
+
// ========================================
|
|
75
|
+
// Validation Schemas for Intelligence
|
|
76
|
+
// ========================================
|
|
77
|
+
const preprocessingOptionsSchema = zod.z.object({
|
|
78
|
+
chunking: zod.z.object({
|
|
79
|
+
strategy: zod.z.enum(CHUNKING_STRATEGIES).optional(),
|
|
80
|
+
maxChunkSize: zod.z.number().int().min(100).max(10000).optional(),
|
|
81
|
+
overlap: zod.z.number().int().min(0).max(500).optional()
|
|
82
|
+
}).optional(),
|
|
83
|
+
cleanContent: zod.z.boolean().optional(),
|
|
84
|
+
extractMetadata: zod.z.boolean().optional()
|
|
85
|
+
}).optional();
|
|
86
|
+
const enhancedSearchSchema = zod.z.object({
|
|
87
|
+
query: zod.z.string().min(1).max(1000),
|
|
88
|
+
type: zod.z.enum(MEMORY_TYPES).optional(),
|
|
89
|
+
threshold: zod.z.number().min(0).max(1).default(0.7),
|
|
90
|
+
limit: zod.z.number().int().min(1).max(100).default(20),
|
|
91
|
+
search_mode: zod.z.enum(SEARCH_MODES).default('hybrid'),
|
|
92
|
+
filters: zod.z.object({
|
|
93
|
+
tags: zod.z.array(zod.z.string()).optional(),
|
|
94
|
+
project_id: zod.z.string().uuid().optional(),
|
|
95
|
+
topic_id: zod.z.string().uuid().optional(),
|
|
96
|
+
date_range: zod.z.object({
|
|
97
|
+
from: zod.z.string().optional(),
|
|
98
|
+
to: zod.z.string().optional()
|
|
99
|
+
}).optional()
|
|
100
|
+
}).optional(),
|
|
101
|
+
include_chunks: zod.z.boolean().default(false)
|
|
102
|
+
});
|
|
103
|
+
const analyticsDateRangeSchema = zod.z.object({
|
|
104
|
+
from: zod.z.string().optional(),
|
|
105
|
+
to: zod.z.string().optional(),
|
|
106
|
+
group_by: zod.z.enum(['day', 'week', 'month']).default('day')
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Core Utilities for Memory Client
|
|
111
|
+
* Browser-safe, no Node.js dependencies
|
|
112
|
+
*/
|
|
113
|
+
/**
|
|
114
|
+
* Safely parse JSON with detailed error reporting
|
|
115
|
+
* Prevents scattered try/catch blocks throughout the codebase
|
|
116
|
+
*/
|
|
117
|
+
function safeJsonParse(input) {
|
|
118
|
+
try {
|
|
119
|
+
const data = JSON.parse(input);
|
|
120
|
+
return { success: true, data };
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
const message = error instanceof Error
|
|
124
|
+
? error.message
|
|
125
|
+
: 'Unknown JSON parse error';
|
|
126
|
+
return { success: false, error: `Invalid JSON: ${message}` };
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* HTTP status code to error code mapping
|
|
131
|
+
*/
|
|
132
|
+
function httpStatusToErrorCode(status) {
|
|
133
|
+
switch (status) {
|
|
134
|
+
case 400:
|
|
135
|
+
return 'VALIDATION_ERROR';
|
|
136
|
+
case 401:
|
|
137
|
+
return 'AUTH_ERROR';
|
|
138
|
+
case 403:
|
|
139
|
+
return 'FORBIDDEN';
|
|
140
|
+
case 404:
|
|
141
|
+
return 'NOT_FOUND';
|
|
142
|
+
case 408:
|
|
143
|
+
return 'TIMEOUT_ERROR';
|
|
144
|
+
case 409:
|
|
145
|
+
return 'CONFLICT';
|
|
146
|
+
case 429:
|
|
147
|
+
return 'RATE_LIMIT_ERROR';
|
|
148
|
+
case 500:
|
|
149
|
+
case 502:
|
|
150
|
+
case 503:
|
|
151
|
+
case 504:
|
|
152
|
+
return 'SERVER_ERROR';
|
|
153
|
+
default:
|
|
154
|
+
return 'API_ERROR';
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Create a standardized error response from various error sources
|
|
159
|
+
*/
|
|
160
|
+
function createErrorResponse(message, code = 'API_ERROR', statusCode, details) {
|
|
161
|
+
return {
|
|
162
|
+
code,
|
|
163
|
+
message,
|
|
164
|
+
statusCode,
|
|
165
|
+
details,
|
|
166
|
+
timestamp: new Date().toISOString()
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Create an error response from an HTTP response
|
|
171
|
+
*/
|
|
172
|
+
function createErrorFromResponse(status, statusText, body) {
|
|
173
|
+
const code = httpStatusToErrorCode(status);
|
|
174
|
+
// Try to extract message from response body
|
|
175
|
+
let message = `HTTP ${status}: ${statusText}`;
|
|
176
|
+
let details = undefined;
|
|
177
|
+
if (body && typeof body === 'object') {
|
|
178
|
+
const bodyObj = body;
|
|
179
|
+
if (typeof bodyObj.error === 'string') {
|
|
180
|
+
message = bodyObj.error;
|
|
181
|
+
}
|
|
182
|
+
else if (typeof bodyObj.message === 'string') {
|
|
183
|
+
message = bodyObj.message;
|
|
184
|
+
}
|
|
185
|
+
if (bodyObj.details) {
|
|
186
|
+
details = bodyObj.details;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return createErrorResponse(message, code, status, details);
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Sleep utility for retry logic
|
|
193
|
+
*/
|
|
194
|
+
function sleep(ms) {
|
|
195
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Calculate retry delay with exponential backoff and jitter
|
|
199
|
+
*/
|
|
200
|
+
function calculateRetryDelay(attempt, baseDelay = 1000, backoff = 'exponential', maxDelay = 30000) {
|
|
201
|
+
let delay;
|
|
202
|
+
if (backoff === 'exponential') {
|
|
203
|
+
delay = baseDelay * Math.pow(2, attempt);
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
delay = baseDelay * (attempt + 1);
|
|
207
|
+
}
|
|
208
|
+
// Add jitter (±20%) to prevent thundering herd
|
|
209
|
+
const jitter = delay * 0.2 * (Math.random() * 2 - 1);
|
|
210
|
+
delay = Math.min(delay + jitter, maxDelay);
|
|
211
|
+
return Math.round(delay);
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Check if an error is retryable based on status code
|
|
215
|
+
*/
|
|
216
|
+
function isRetryableError(statusCode) {
|
|
217
|
+
if (!statusCode)
|
|
218
|
+
return true; // Network errors are retryable
|
|
219
|
+
// Retry on server errors and rate limits
|
|
220
|
+
return statusCode >= 500 || statusCode === 429 || statusCode === 408;
|
|
221
|
+
}
|
|
4
222
|
|
|
5
223
|
/**
|
|
6
224
|
* Core Memory Client - Pure Browser-Safe Implementation
|
|
@@ -10,6 +228,18 @@ var zod = require('zod');
|
|
|
10
228
|
*
|
|
11
229
|
* Bundle size: ~15KB gzipped
|
|
12
230
|
*/
|
|
231
|
+
/**
|
|
232
|
+
* Helper to check if response has error
|
|
233
|
+
*/
|
|
234
|
+
function hasError(response) {
|
|
235
|
+
return response.error !== undefined;
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Helper to check if response has data
|
|
239
|
+
*/
|
|
240
|
+
function hasData(response) {
|
|
241
|
+
return response.data !== undefined;
|
|
242
|
+
}
|
|
13
243
|
/**
|
|
14
244
|
* Core Memory Client class for interacting with the Memory as a Service API
|
|
15
245
|
*
|
|
@@ -25,6 +255,7 @@ class CoreMemoryClient {
|
|
|
25
255
|
this.baseHeaders = {
|
|
26
256
|
'Content-Type': 'application/json',
|
|
27
257
|
'User-Agent': '@lanonasis/memory-client/2.0.0',
|
|
258
|
+
'X-Project-Scope': 'lanonasis-maas', // Required by backend auth middleware
|
|
28
259
|
...config.headers
|
|
29
260
|
};
|
|
30
261
|
// Set authentication headers
|
|
@@ -61,10 +292,13 @@ class CoreMemoryClient {
|
|
|
61
292
|
return body;
|
|
62
293
|
}
|
|
63
294
|
/**
|
|
64
|
-
* Make an HTTP request to the API
|
|
295
|
+
* Make an HTTP request to the API with retry support
|
|
65
296
|
*/
|
|
66
297
|
async request(endpoint, options = {}) {
|
|
67
298
|
const startTime = Date.now();
|
|
299
|
+
const maxRetries = this.config.retry?.maxRetries ?? 3;
|
|
300
|
+
const baseDelay = this.config.retry?.retryDelay ?? 1000;
|
|
301
|
+
const backoff = this.config.retry?.backoff ?? 'exponential';
|
|
68
302
|
// Call onRequest hook if provided
|
|
69
303
|
if (this.config.onRequest) {
|
|
70
304
|
try {
|
|
@@ -79,85 +313,123 @@ class CoreMemoryClient {
|
|
|
79
313
|
? this.config.apiUrl.replace('/api', '')
|
|
80
314
|
: this.config.apiUrl;
|
|
81
315
|
const url = `${baseUrl}/api/v1${endpoint}`;
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
data
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
316
|
+
let lastError;
|
|
317
|
+
let attempt = 0;
|
|
318
|
+
while (attempt <= maxRetries) {
|
|
319
|
+
try {
|
|
320
|
+
const controller = new AbortController();
|
|
321
|
+
const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
|
|
322
|
+
const response = await fetch(url, {
|
|
323
|
+
headers: { ...this.baseHeaders, ...options.headers },
|
|
324
|
+
signal: controller.signal,
|
|
325
|
+
...options,
|
|
326
|
+
});
|
|
327
|
+
clearTimeout(timeoutId);
|
|
328
|
+
let data;
|
|
329
|
+
const contentType = response.headers.get('content-type');
|
|
330
|
+
if (contentType && contentType.includes('application/json')) {
|
|
331
|
+
data = await response.json();
|
|
332
|
+
}
|
|
333
|
+
else {
|
|
334
|
+
data = await response.text();
|
|
335
|
+
}
|
|
336
|
+
if (!response.ok) {
|
|
337
|
+
const error = createErrorFromResponse(response.status, response.statusText, data);
|
|
338
|
+
// Only retry on retryable errors (5xx, 429, 408)
|
|
339
|
+
if (isRetryableError(response.status) && attempt < maxRetries) {
|
|
340
|
+
lastError = error;
|
|
341
|
+
const delay = calculateRetryDelay(attempt, baseDelay, backoff);
|
|
342
|
+
await sleep(delay);
|
|
343
|
+
attempt++;
|
|
344
|
+
continue;
|
|
345
|
+
}
|
|
346
|
+
// Call onError hook if provided
|
|
347
|
+
if (this.config.onError) {
|
|
348
|
+
try {
|
|
349
|
+
this.config.onError(error);
|
|
350
|
+
}
|
|
351
|
+
catch (hookError) {
|
|
352
|
+
console.warn('onError hook error:', hookError);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
return { error, meta: { duration: Date.now() - startTime, retries: attempt } };
|
|
356
|
+
}
|
|
357
|
+
// Call onResponse hook if provided
|
|
358
|
+
if (this.config.onResponse) {
|
|
107
359
|
try {
|
|
108
|
-
|
|
360
|
+
const duration = Date.now() - startTime;
|
|
361
|
+
this.config.onResponse(endpoint, duration);
|
|
109
362
|
}
|
|
110
|
-
catch (
|
|
111
|
-
console.warn('
|
|
363
|
+
catch (error) {
|
|
364
|
+
console.warn('onResponse hook error:', error);
|
|
112
365
|
}
|
|
113
366
|
}
|
|
114
|
-
return {
|
|
367
|
+
return { data, meta: { duration: Date.now() - startTime, retries: attempt } };
|
|
115
368
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
369
|
+
catch (error) {
|
|
370
|
+
if (error instanceof Error && error.name === 'AbortError') {
|
|
371
|
+
const timeoutError = createErrorResponse('Request timeout', 'TIMEOUT_ERROR', 408);
|
|
372
|
+
// Retry on timeout
|
|
373
|
+
if (attempt < maxRetries) {
|
|
374
|
+
lastError = timeoutError;
|
|
375
|
+
const delay = calculateRetryDelay(attempt, baseDelay, backoff);
|
|
376
|
+
await sleep(delay);
|
|
377
|
+
attempt++;
|
|
378
|
+
continue;
|
|
379
|
+
}
|
|
380
|
+
if (this.config.onError) {
|
|
381
|
+
try {
|
|
382
|
+
this.config.onError(timeoutError);
|
|
383
|
+
}
|
|
384
|
+
catch (hookError) {
|
|
385
|
+
console.warn('onError hook error:', hookError);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
return { error: timeoutError, meta: { duration: Date.now() - startTime, retries: attempt } };
|
|
121
389
|
}
|
|
122
|
-
|
|
123
|
-
|
|
390
|
+
const networkError = createErrorResponse(error instanceof Error ? error.message : 'Network error', 'NETWORK_ERROR');
|
|
391
|
+
// Retry on network errors
|
|
392
|
+
if (attempt < maxRetries) {
|
|
393
|
+
lastError = networkError;
|
|
394
|
+
const delay = calculateRetryDelay(attempt, baseDelay, backoff);
|
|
395
|
+
await sleep(delay);
|
|
396
|
+
attempt++;
|
|
397
|
+
continue;
|
|
124
398
|
}
|
|
125
|
-
}
|
|
126
|
-
return { data };
|
|
127
|
-
}
|
|
128
|
-
catch (error) {
|
|
129
|
-
if (error instanceof Error && error.name === 'AbortError') {
|
|
130
|
-
const timeoutError = {
|
|
131
|
-
message: 'Request timeout',
|
|
132
|
-
code: 'TIMEOUT_ERROR',
|
|
133
|
-
statusCode: 408
|
|
134
|
-
};
|
|
135
399
|
if (this.config.onError) {
|
|
136
400
|
try {
|
|
137
|
-
this.config.onError(
|
|
401
|
+
this.config.onError(networkError);
|
|
138
402
|
}
|
|
139
403
|
catch (hookError) {
|
|
140
404
|
console.warn('onError hook error:', hookError);
|
|
141
405
|
}
|
|
142
406
|
}
|
|
143
|
-
return { error:
|
|
144
|
-
}
|
|
145
|
-
const networkError = {
|
|
146
|
-
message: error instanceof Error ? error.message : 'Network error',
|
|
147
|
-
code: 'NETWORK_ERROR'
|
|
148
|
-
};
|
|
149
|
-
if (this.config.onError) {
|
|
150
|
-
try {
|
|
151
|
-
this.config.onError(networkError);
|
|
152
|
-
}
|
|
153
|
-
catch (hookError) {
|
|
154
|
-
console.warn('onError hook error:', hookError);
|
|
155
|
-
}
|
|
407
|
+
return { error: networkError, meta: { duration: Date.now() - startTime, retries: attempt } };
|
|
156
408
|
}
|
|
409
|
+
}
|
|
410
|
+
// Should never reach here, but handle it gracefully
|
|
411
|
+
return {
|
|
412
|
+
error: lastError ?? createErrorResponse('Max retries exceeded', 'API_ERROR'),
|
|
413
|
+
meta: { duration: Date.now() - startTime, retries: attempt }
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* Validate input using Zod schema and return validation error if invalid
|
|
418
|
+
*/
|
|
419
|
+
validateInput(schema, data) {
|
|
420
|
+
const result = schema.safeParse(data);
|
|
421
|
+
if (!result.success) {
|
|
422
|
+
// Extract error details from Zod error
|
|
423
|
+
const zodError = result.error;
|
|
424
|
+
const details = zodError?.issues?.map(issue => ({
|
|
425
|
+
field: issue.path.map(String).join('.'),
|
|
426
|
+
message: issue.message
|
|
427
|
+
})) ?? [];
|
|
157
428
|
return {
|
|
158
|
-
error:
|
|
429
|
+
error: createErrorResponse('Validation failed', 'VALIDATION_ERROR', 400, details)
|
|
159
430
|
};
|
|
160
431
|
}
|
|
432
|
+
return null;
|
|
161
433
|
}
|
|
162
434
|
/**
|
|
163
435
|
* Test the API connection and authentication
|
|
@@ -167,9 +439,14 @@ class CoreMemoryClient {
|
|
|
167
439
|
}
|
|
168
440
|
// Memory Operations
|
|
169
441
|
/**
|
|
170
|
-
* Create a new memory
|
|
442
|
+
* Create a new memory with validation
|
|
171
443
|
*/
|
|
172
444
|
async createMemory(memory) {
|
|
445
|
+
// Validate input before making request
|
|
446
|
+
const validationError = this.validateInput(createMemorySchema, memory);
|
|
447
|
+
if (validationError) {
|
|
448
|
+
return { error: validationError.error };
|
|
449
|
+
}
|
|
173
450
|
const enrichedMemory = this.enrichWithOrgContext(memory);
|
|
174
451
|
return this.request('/memory', {
|
|
175
452
|
method: 'POST',
|
|
@@ -183,9 +460,14 @@ class CoreMemoryClient {
|
|
|
183
460
|
return this.request(`/memory/${encodeURIComponent(id)}`);
|
|
184
461
|
}
|
|
185
462
|
/**
|
|
186
|
-
* Update an existing memory
|
|
463
|
+
* Update an existing memory with validation
|
|
187
464
|
*/
|
|
188
465
|
async updateMemory(id, updates) {
|
|
466
|
+
// Validate input before making request
|
|
467
|
+
const validationError = this.validateInput(updateMemorySchema, updates);
|
|
468
|
+
if (validationError) {
|
|
469
|
+
return { error: validationError.error };
|
|
470
|
+
}
|
|
189
471
|
return this.request(`/memory/${encodeURIComponent(id)}`, {
|
|
190
472
|
method: 'PUT',
|
|
191
473
|
body: JSON.stringify(updates)
|
|
@@ -219,9 +501,15 @@ class CoreMemoryClient {
|
|
|
219
501
|
return this.request(endpoint);
|
|
220
502
|
}
|
|
221
503
|
/**
|
|
222
|
-
* Search memories using semantic search
|
|
504
|
+
* Search memories using semantic search with validation
|
|
223
505
|
*/
|
|
224
506
|
async searchMemories(request) {
|
|
507
|
+
// Validate input before making request
|
|
508
|
+
const validationError = this.validateInput(searchMemorySchema, request);
|
|
509
|
+
if (validationError) {
|
|
510
|
+
// Return error response (data will be undefined, only error is set)
|
|
511
|
+
return { error: validationError.error };
|
|
512
|
+
}
|
|
225
513
|
const enrichedRequest = this.enrichWithOrgContext(request);
|
|
226
514
|
return this.request('/memory/search', {
|
|
227
515
|
method: 'POST',
|
|
@@ -240,9 +528,14 @@ class CoreMemoryClient {
|
|
|
240
528
|
}
|
|
241
529
|
// Topic Operations
|
|
242
530
|
/**
|
|
243
|
-
* Create a new topic
|
|
531
|
+
* Create a new topic with validation
|
|
244
532
|
*/
|
|
245
533
|
async createTopic(topic) {
|
|
534
|
+
// Validate input before making request
|
|
535
|
+
const validationError = this.validateInput(createTopicSchema, topic);
|
|
536
|
+
if (validationError) {
|
|
537
|
+
return { error: validationError.error };
|
|
538
|
+
}
|
|
246
539
|
const enrichedTopic = this.enrichWithOrgContext(topic);
|
|
247
540
|
return this.request('/topics', {
|
|
248
541
|
method: 'POST',
|
|
@@ -284,6 +577,182 @@ class CoreMemoryClient {
|
|
|
284
577
|
async getMemoryStats() {
|
|
285
578
|
return this.request('/memory/stats');
|
|
286
579
|
}
|
|
580
|
+
// ========================================
|
|
581
|
+
// Intelligence Features (v2.0)
|
|
582
|
+
// ========================================
|
|
583
|
+
/**
|
|
584
|
+
* Create a memory with preprocessing options (chunking, intelligence extraction)
|
|
585
|
+
*
|
|
586
|
+
* @example
|
|
587
|
+
* ```typescript
|
|
588
|
+
* const result = await client.createMemoryWithPreprocessing({
|
|
589
|
+
* title: 'Auth System Docs',
|
|
590
|
+
* content: 'Long content...',
|
|
591
|
+
* memory_type: 'knowledge',
|
|
592
|
+
* preprocessing: {
|
|
593
|
+
* chunking: { strategy: 'semantic', maxChunkSize: 1000 },
|
|
594
|
+
* extractMetadata: true
|
|
595
|
+
* }
|
|
596
|
+
* });
|
|
597
|
+
* ```
|
|
598
|
+
*/
|
|
599
|
+
async createMemoryWithPreprocessing(memory) {
|
|
600
|
+
// Validate base memory fields
|
|
601
|
+
const validationError = this.validateInput(createMemorySchema, memory);
|
|
602
|
+
if (validationError) {
|
|
603
|
+
return { error: validationError.error };
|
|
604
|
+
}
|
|
605
|
+
const enrichedMemory = this.enrichWithOrgContext(memory);
|
|
606
|
+
return this.request('/memory', {
|
|
607
|
+
method: 'POST',
|
|
608
|
+
body: JSON.stringify(enrichedMemory)
|
|
609
|
+
});
|
|
610
|
+
}
|
|
611
|
+
/**
|
|
612
|
+
* Update a memory with re-chunking and embedding regeneration
|
|
613
|
+
*
|
|
614
|
+
* @example
|
|
615
|
+
* ```typescript
|
|
616
|
+
* const result = await client.updateMemoryWithPreprocessing('mem_123', {
|
|
617
|
+
* content: 'Updated content...',
|
|
618
|
+
* rechunk: true,
|
|
619
|
+
* regenerate_embedding: true
|
|
620
|
+
* });
|
|
621
|
+
* ```
|
|
622
|
+
*/
|
|
623
|
+
async updateMemoryWithPreprocessing(id, updates) {
|
|
624
|
+
const validationError = this.validateInput(updateMemorySchema, updates);
|
|
625
|
+
if (validationError) {
|
|
626
|
+
return { error: validationError.error };
|
|
627
|
+
}
|
|
628
|
+
return this.request(`/memory/${encodeURIComponent(id)}`, {
|
|
629
|
+
method: 'PUT',
|
|
630
|
+
body: JSON.stringify(updates)
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
/**
|
|
634
|
+
* Enhanced semantic search with hybrid mode (vector + text)
|
|
635
|
+
*
|
|
636
|
+
* @example
|
|
637
|
+
* ```typescript
|
|
638
|
+
* const result = await client.enhancedSearch({
|
|
639
|
+
* query: 'authentication flow',
|
|
640
|
+
* search_mode: 'hybrid',
|
|
641
|
+
* filters: { tags: ['auth'], project_id: 'proj_123' },
|
|
642
|
+
* include_chunks: true
|
|
643
|
+
* });
|
|
644
|
+
* ```
|
|
645
|
+
*/
|
|
646
|
+
async enhancedSearch(request) {
|
|
647
|
+
const validationError = this.validateInput(enhancedSearchSchema, request);
|
|
648
|
+
if (validationError) {
|
|
649
|
+
return { error: validationError.error };
|
|
650
|
+
}
|
|
651
|
+
const enrichedRequest = this.enrichWithOrgContext(request);
|
|
652
|
+
return this.request('/memory/search', {
|
|
653
|
+
method: 'POST',
|
|
654
|
+
body: JSON.stringify(enrichedRequest)
|
|
655
|
+
});
|
|
656
|
+
}
|
|
657
|
+
// ========================================
|
|
658
|
+
// Analytics Operations
|
|
659
|
+
// ========================================
|
|
660
|
+
/**
|
|
661
|
+
* Get search analytics data
|
|
662
|
+
*
|
|
663
|
+
* @example
|
|
664
|
+
* ```typescript
|
|
665
|
+
* const analytics = await client.getSearchAnalytics({
|
|
666
|
+
* from: '2025-01-01',
|
|
667
|
+
* to: '2025-12-31',
|
|
668
|
+
* group_by: 'day'
|
|
669
|
+
* });
|
|
670
|
+
* ```
|
|
671
|
+
*/
|
|
672
|
+
async getSearchAnalytics(options = {}) {
|
|
673
|
+
const validationError = this.validateInput(analyticsDateRangeSchema, options);
|
|
674
|
+
if (validationError) {
|
|
675
|
+
return { error: validationError.error };
|
|
676
|
+
}
|
|
677
|
+
const params = new URLSearchParams();
|
|
678
|
+
if (options.from)
|
|
679
|
+
params.append('from', options.from);
|
|
680
|
+
if (options.to)
|
|
681
|
+
params.append('to', options.to);
|
|
682
|
+
if (options.group_by)
|
|
683
|
+
params.append('group_by', options.group_by);
|
|
684
|
+
const queryString = params.toString();
|
|
685
|
+
const endpoint = queryString ? `/analytics/search?${queryString}` : '/analytics/search';
|
|
686
|
+
return this.request(endpoint);
|
|
687
|
+
}
|
|
688
|
+
/**
|
|
689
|
+
* Get memory access patterns
|
|
690
|
+
*
|
|
691
|
+
* @example
|
|
692
|
+
* ```typescript
|
|
693
|
+
* const patterns = await client.getAccessPatterns({
|
|
694
|
+
* from: '2025-01-01',
|
|
695
|
+
* to: '2025-12-31'
|
|
696
|
+
* });
|
|
697
|
+
* console.log(patterns.data?.most_accessed);
|
|
698
|
+
* ```
|
|
699
|
+
*/
|
|
700
|
+
async getAccessPatterns(options = {}) {
|
|
701
|
+
const params = new URLSearchParams();
|
|
702
|
+
if (options.from)
|
|
703
|
+
params.append('from', options.from);
|
|
704
|
+
if (options.to)
|
|
705
|
+
params.append('to', options.to);
|
|
706
|
+
const queryString = params.toString();
|
|
707
|
+
const endpoint = queryString ? `/analytics/access?${queryString}` : '/analytics/access';
|
|
708
|
+
return this.request(endpoint);
|
|
709
|
+
}
|
|
710
|
+
/**
|
|
711
|
+
* Get extended memory statistics with storage and activity metrics
|
|
712
|
+
*
|
|
713
|
+
* @example
|
|
714
|
+
* ```typescript
|
|
715
|
+
* const stats = await client.getExtendedStats();
|
|
716
|
+
* console.log(`Total chunks: ${stats.data?.storage.total_chunks}`);
|
|
717
|
+
* console.log(`Created today: ${stats.data?.activity.created_today}`);
|
|
718
|
+
* ```
|
|
719
|
+
*/
|
|
720
|
+
async getExtendedStats() {
|
|
721
|
+
return this.request('/analytics/stats');
|
|
722
|
+
}
|
|
723
|
+
/**
|
|
724
|
+
* Get topic with its memories
|
|
725
|
+
*
|
|
726
|
+
* @example
|
|
727
|
+
* ```typescript
|
|
728
|
+
* const topic = await client.getTopicWithMemories('topic_123');
|
|
729
|
+
* console.log(topic.data?.memories);
|
|
730
|
+
* ```
|
|
731
|
+
*/
|
|
732
|
+
async getTopicWithMemories(topicId, options = {}) {
|
|
733
|
+
const params = new URLSearchParams();
|
|
734
|
+
if (options.limit)
|
|
735
|
+
params.append('limit', String(options.limit));
|
|
736
|
+
if (options.offset)
|
|
737
|
+
params.append('offset', String(options.offset));
|
|
738
|
+
const queryString = params.toString();
|
|
739
|
+
const endpoint = queryString
|
|
740
|
+
? `/topics/${encodeURIComponent(topicId)}/memories?${queryString}`
|
|
741
|
+
: `/topics/${encodeURIComponent(topicId)}/memories`;
|
|
742
|
+
return this.request(endpoint);
|
|
743
|
+
}
|
|
744
|
+
/**
|
|
745
|
+
* Get topics in hierarchical structure
|
|
746
|
+
*
|
|
747
|
+
* @example
|
|
748
|
+
* ```typescript
|
|
749
|
+
* const topics = await client.getTopicsHierarchy();
|
|
750
|
+
* // Returns nested topic tree with children
|
|
751
|
+
* ```
|
|
752
|
+
*/
|
|
753
|
+
async getTopicsHierarchy() {
|
|
754
|
+
return this.request('/topics?include_hierarchy=true');
|
|
755
|
+
}
|
|
287
756
|
// Utility Methods
|
|
288
757
|
/**
|
|
289
758
|
* Update authentication token
|
|
@@ -332,64 +801,40 @@ function createMemoryClient(config) {
|
|
|
332
801
|
}
|
|
333
802
|
|
|
334
803
|
/**
|
|
335
|
-
*
|
|
336
|
-
|
|
337
|
-
const MEMORY_TYPES = ['context', 'project', 'knowledge', 'reference', 'personal', 'workflow'];
|
|
338
|
-
/**
|
|
339
|
-
* Memory status values
|
|
804
|
+
* Error handling for Memory Client
|
|
805
|
+
* Browser-safe, no Node.js dependencies
|
|
340
806
|
*/
|
|
341
|
-
const MEMORY_STATUSES = ['active', 'archived', 'draft', 'deleted'];
|
|
342
807
|
/**
|
|
343
|
-
*
|
|
808
|
+
* Standardized error codes for programmatic error handling
|
|
344
809
|
*/
|
|
345
|
-
const
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
content: zod.z.string().min(1).max(50000).optional(),
|
|
358
|
-
summary: zod.z.string().max(1000).optional(),
|
|
359
|
-
memory_type: zod.z.enum(MEMORY_TYPES).optional(),
|
|
360
|
-
status: zod.z.enum(MEMORY_STATUSES).optional(),
|
|
361
|
-
topic_id: zod.z.string().uuid().nullable().optional(),
|
|
362
|
-
project_ref: zod.z.string().max(100).nullable().optional(),
|
|
363
|
-
tags: zod.z.array(zod.z.string().min(1).max(50)).max(20).optional(),
|
|
364
|
-
metadata: zod.z.record(zod.z.string(), zod.z.unknown()).optional()
|
|
365
|
-
});
|
|
366
|
-
const searchMemorySchema = zod.z.object({
|
|
367
|
-
query: zod.z.string().min(1).max(1000),
|
|
368
|
-
memory_types: zod.z.array(zod.z.enum(MEMORY_TYPES)).optional(),
|
|
369
|
-
tags: zod.z.array(zod.z.string()).optional(),
|
|
370
|
-
topic_id: zod.z.string().uuid().optional(),
|
|
371
|
-
project_ref: zod.z.string().optional(),
|
|
372
|
-
status: zod.z.enum(MEMORY_STATUSES).default('active'),
|
|
373
|
-
limit: zod.z.number().int().min(1).max(100).default(20),
|
|
374
|
-
threshold: zod.z.number().min(0).max(1).default(0.7)
|
|
375
|
-
});
|
|
376
|
-
const createTopicSchema = zod.z.object({
|
|
377
|
-
name: zod.z.string().min(1).max(100),
|
|
378
|
-
description: zod.z.string().max(500).optional(),
|
|
379
|
-
color: zod.z.string().regex(/^#[0-9A-Fa-f]{6}$/).optional(),
|
|
380
|
-
icon: zod.z.string().max(50).optional(),
|
|
381
|
-
parent_topic_id: zod.z.string().uuid().optional()
|
|
382
|
-
});
|
|
383
|
-
|
|
810
|
+
const ERROR_CODES = [
|
|
811
|
+
'API_ERROR',
|
|
812
|
+
'AUTH_ERROR',
|
|
813
|
+
'VALIDATION_ERROR',
|
|
814
|
+
'TIMEOUT_ERROR',
|
|
815
|
+
'RATE_LIMIT_ERROR',
|
|
816
|
+
'NOT_FOUND',
|
|
817
|
+
'NETWORK_ERROR',
|
|
818
|
+
'FORBIDDEN',
|
|
819
|
+
'CONFLICT',
|
|
820
|
+
'SERVER_ERROR'
|
|
821
|
+
];
|
|
384
822
|
/**
|
|
385
|
-
*
|
|
386
|
-
* Browser-safe, no Node.js dependencies
|
|
823
|
+
* Type guard to check if an object is an ApiErrorResponse
|
|
387
824
|
*/
|
|
825
|
+
function isApiErrorResponse(value) {
|
|
826
|
+
return (typeof value === 'object' &&
|
|
827
|
+
value !== null &&
|
|
828
|
+
'code' in value &&
|
|
829
|
+
'message' in value &&
|
|
830
|
+
typeof value.code === 'string' &&
|
|
831
|
+
typeof value.message === 'string');
|
|
832
|
+
}
|
|
388
833
|
/**
|
|
389
834
|
* Base error class for Memory Client errors
|
|
390
835
|
*/
|
|
391
836
|
class MemoryClientError extends Error {
|
|
392
|
-
constructor(message, code, statusCode, details) {
|
|
837
|
+
constructor(message, code = 'API_ERROR', statusCode, details) {
|
|
393
838
|
super(message);
|
|
394
839
|
this.code = code;
|
|
395
840
|
this.statusCode = statusCode;
|
|
@@ -400,6 +845,18 @@ class MemoryClientError extends Error {
|
|
|
400
845
|
Error.captureStackTrace(this, MemoryClientError);
|
|
401
846
|
}
|
|
402
847
|
}
|
|
848
|
+
/**
|
|
849
|
+
* Convert to ApiErrorResponse for consistent API responses
|
|
850
|
+
*/
|
|
851
|
+
toResponse() {
|
|
852
|
+
return {
|
|
853
|
+
code: this.code,
|
|
854
|
+
message: this.message,
|
|
855
|
+
statusCode: this.statusCode,
|
|
856
|
+
details: this.details,
|
|
857
|
+
timestamp: new Date().toISOString()
|
|
858
|
+
};
|
|
859
|
+
}
|
|
403
860
|
}
|
|
404
861
|
/**
|
|
405
862
|
* Network/API error
|
|
@@ -409,6 +866,26 @@ class ApiError extends MemoryClientError {
|
|
|
409
866
|
super(message, 'API_ERROR', statusCode, details);
|
|
410
867
|
this.name = 'ApiError';
|
|
411
868
|
}
|
|
869
|
+
/**
|
|
870
|
+
* Create from an HTTP response
|
|
871
|
+
*/
|
|
872
|
+
static fromResponse(status, statusText, body) {
|
|
873
|
+
let message = `HTTP ${status}: ${statusText}`;
|
|
874
|
+
let details = undefined;
|
|
875
|
+
if (body && typeof body === 'object') {
|
|
876
|
+
const bodyObj = body;
|
|
877
|
+
if (typeof bodyObj.error === 'string') {
|
|
878
|
+
message = bodyObj.error;
|
|
879
|
+
}
|
|
880
|
+
else if (typeof bodyObj.message === 'string') {
|
|
881
|
+
message = bodyObj.message;
|
|
882
|
+
}
|
|
883
|
+
if (bodyObj.details) {
|
|
884
|
+
details = bodyObj.details;
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
return new ApiError(message, status, details);
|
|
888
|
+
}
|
|
412
889
|
}
|
|
413
890
|
/**
|
|
414
891
|
* Authentication error
|
|
@@ -420,12 +897,30 @@ class AuthenticationError extends MemoryClientError {
|
|
|
420
897
|
}
|
|
421
898
|
}
|
|
422
899
|
/**
|
|
423
|
-
* Validation error
|
|
900
|
+
* Validation error with field-level details
|
|
424
901
|
*/
|
|
425
902
|
class ValidationError extends MemoryClientError {
|
|
426
903
|
constructor(message, details) {
|
|
427
904
|
super(message, 'VALIDATION_ERROR', 400, details);
|
|
428
905
|
this.name = 'ValidationError';
|
|
906
|
+
// Parse validation details into field errors
|
|
907
|
+
this.validationErrors = [];
|
|
908
|
+
if (Array.isArray(details)) {
|
|
909
|
+
this.validationErrors = details.filter((item) => typeof item === 'object' &&
|
|
910
|
+
item !== null &&
|
|
911
|
+
typeof item.field === 'string' &&
|
|
912
|
+
typeof item.message === 'string');
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
/**
|
|
916
|
+
* Create from Zod error
|
|
917
|
+
*/
|
|
918
|
+
static fromZodError(error) {
|
|
919
|
+
const details = error.issues.map(issue => ({
|
|
920
|
+
field: issue.path.join('.'),
|
|
921
|
+
message: issue.message
|
|
922
|
+
}));
|
|
923
|
+
return new ValidationError('Validation failed', details);
|
|
429
924
|
}
|
|
430
925
|
}
|
|
431
926
|
/**
|
|
@@ -438,12 +933,13 @@ class TimeoutError extends MemoryClientError {
|
|
|
438
933
|
}
|
|
439
934
|
}
|
|
440
935
|
/**
|
|
441
|
-
* Rate limit error
|
|
936
|
+
* Rate limit error with retry-after info
|
|
442
937
|
*/
|
|
443
938
|
class RateLimitError extends MemoryClientError {
|
|
444
|
-
constructor(message = 'Rate limit exceeded') {
|
|
445
|
-
super(message, 'RATE_LIMIT_ERROR', 429);
|
|
939
|
+
constructor(message = 'Rate limit exceeded', retryAfter) {
|
|
940
|
+
super(message, 'RATE_LIMIT_ERROR', 429, { retryAfter });
|
|
446
941
|
this.name = 'RateLimitError';
|
|
942
|
+
this.retryAfter = retryAfter;
|
|
447
943
|
}
|
|
448
944
|
}
|
|
449
945
|
/**
|
|
@@ -453,6 +949,49 @@ class NotFoundError extends MemoryClientError {
|
|
|
453
949
|
constructor(resource) {
|
|
454
950
|
super(`${resource} not found`, 'NOT_FOUND', 404);
|
|
455
951
|
this.name = 'NotFoundError';
|
|
952
|
+
this.resource = resource;
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
/**
|
|
956
|
+
* Network error (no response received)
|
|
957
|
+
*/
|
|
958
|
+
class NetworkError extends MemoryClientError {
|
|
959
|
+
constructor(message = 'Network error') {
|
|
960
|
+
super(message, 'NETWORK_ERROR');
|
|
961
|
+
this.name = 'NetworkError';
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
/**
|
|
965
|
+
* Server error (5xx responses)
|
|
966
|
+
*/
|
|
967
|
+
class ServerError extends MemoryClientError {
|
|
968
|
+
constructor(message, statusCode = 500) {
|
|
969
|
+
super(message, 'SERVER_ERROR', statusCode);
|
|
970
|
+
this.name = 'ServerError';
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
/**
|
|
974
|
+
* Create appropriate error class from status code
|
|
975
|
+
*/
|
|
976
|
+
function createErrorFromStatus(status, message, details) {
|
|
977
|
+
switch (status) {
|
|
978
|
+
case 400:
|
|
979
|
+
return new ValidationError(message, details);
|
|
980
|
+
case 401:
|
|
981
|
+
return new AuthenticationError(message);
|
|
982
|
+
case 404:
|
|
983
|
+
return new NotFoundError(message);
|
|
984
|
+
case 408:
|
|
985
|
+
return new TimeoutError(message);
|
|
986
|
+
case 429:
|
|
987
|
+
return new RateLimitError(message);
|
|
988
|
+
case 500:
|
|
989
|
+
case 502:
|
|
990
|
+
case 503:
|
|
991
|
+
case 504:
|
|
992
|
+
return new ServerError(message, status);
|
|
993
|
+
default:
|
|
994
|
+
return new ApiError(message, status, details);
|
|
456
995
|
}
|
|
457
996
|
}
|
|
458
997
|
|
|
@@ -488,7 +1027,7 @@ class NotFoundError extends MemoryClientError {
|
|
|
488
1027
|
* ```
|
|
489
1028
|
*/
|
|
490
1029
|
// ========================================
|
|
491
|
-
//
|
|
1030
|
+
// Internal Imports (for use in this file)
|
|
492
1031
|
// ========================================
|
|
493
1032
|
// ========================================
|
|
494
1033
|
// Constants
|
|
@@ -500,6 +1039,66 @@ const CLIENT_NAME = '@lanonasis/memory-client';
|
|
|
500
1039
|
// ========================================
|
|
501
1040
|
const isBrowser = typeof window !== 'undefined';
|
|
502
1041
|
const isNode = typeof globalThis !== 'undefined' && 'process' in globalThis && globalThis.process?.versions?.node;
|
|
1042
|
+
const isEdge = !isBrowser && !isNode;
|
|
1043
|
+
/**
|
|
1044
|
+
* Get the current runtime environment
|
|
1045
|
+
*/
|
|
1046
|
+
function getEnvironment() {
|
|
1047
|
+
if (isBrowser)
|
|
1048
|
+
return 'browser';
|
|
1049
|
+
if (isNode)
|
|
1050
|
+
return 'node';
|
|
1051
|
+
return 'edge';
|
|
1052
|
+
}
|
|
1053
|
+
/**
|
|
1054
|
+
* Auto-detecting client factory - "Drop In and Sleep" architecture
|
|
1055
|
+
*
|
|
1056
|
+
* Automatically detects the runtime environment and returns the appropriate client:
|
|
1057
|
+
* - Browser/Edge: Returns CoreMemoryClient (lightweight, browser-safe)
|
|
1058
|
+
* - Node.js: Returns EnhancedMemoryClient (with CLI/MCP support)
|
|
1059
|
+
*
|
|
1060
|
+
* @example
|
|
1061
|
+
* ```typescript
|
|
1062
|
+
* import { createClient } from '@lanonasis/memory-client';
|
|
1063
|
+
*
|
|
1064
|
+
* // Works in any environment!
|
|
1065
|
+
* const client = await createClient({
|
|
1066
|
+
* apiUrl: 'https://api.lanonasis.com',
|
|
1067
|
+
* apiKey: 'your-key'
|
|
1068
|
+
* });
|
|
1069
|
+
*
|
|
1070
|
+
* const memories = await client.listMemories();
|
|
1071
|
+
* ```
|
|
1072
|
+
*/
|
|
1073
|
+
async function createClient(config) {
|
|
1074
|
+
const environment = getEnvironment();
|
|
1075
|
+
if (environment === 'node') {
|
|
1076
|
+
try {
|
|
1077
|
+
// Dynamic import for Node.js client to avoid bundling in browser
|
|
1078
|
+
const { createNodeMemoryClient } = await Promise.resolve().then(function () { return index; });
|
|
1079
|
+
return await createNodeMemoryClient({
|
|
1080
|
+
...config,
|
|
1081
|
+
preferCLI: config.preferCLI ?? true,
|
|
1082
|
+
enableMCP: config.enableMCP ?? true
|
|
1083
|
+
});
|
|
1084
|
+
}
|
|
1085
|
+
catch {
|
|
1086
|
+
// Fallback to core client if Node module fails to load
|
|
1087
|
+
console.warn('Failed to load Node.js client, falling back to core client');
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
// Browser, Edge, or fallback
|
|
1091
|
+
const clientConfig = {
|
|
1092
|
+
apiUrl: config.apiUrl,
|
|
1093
|
+
apiKey: config.apiKey,
|
|
1094
|
+
authToken: config.authToken,
|
|
1095
|
+
organizationId: config.organizationId,
|
|
1096
|
+
timeout: config.timeout ?? (environment === 'edge' ? 5000 : 30000),
|
|
1097
|
+
headers: config.headers,
|
|
1098
|
+
retry: config.retry
|
|
1099
|
+
};
|
|
1100
|
+
return createMemoryClient(clientConfig);
|
|
1101
|
+
}
|
|
503
1102
|
// ========================================
|
|
504
1103
|
// Default Configurations
|
|
505
1104
|
// ========================================
|
|
@@ -613,25 +1212,664 @@ const defaultConfigs = {
|
|
|
613
1212
|
* - Examples: https://github.com/lanonasis/examples
|
|
614
1213
|
*/
|
|
615
1214
|
|
|
616
|
-
|
|
1215
|
+
/**
|
|
1216
|
+
* CLI Integration Module for Memory Client SDK
|
|
1217
|
+
*
|
|
1218
|
+
* Provides intelligent CLI detection and MCP channel utilization
|
|
1219
|
+
* when @lanonasis/cli v1.5.2+ is available in the environment
|
|
1220
|
+
*
|
|
1221
|
+
* IMPORTANT: This file imports Node.js modules and should only be used in Node.js environments
|
|
1222
|
+
*/
|
|
1223
|
+
const execAsync = util.promisify(child_process.exec);
|
|
1224
|
+
/**
|
|
1225
|
+
* CLI Detection and Integration Service
|
|
1226
|
+
*/
|
|
1227
|
+
class CLIIntegration {
|
|
1228
|
+
constructor() {
|
|
1229
|
+
this.cliInfo = null;
|
|
1230
|
+
this.detectionPromise = null;
|
|
1231
|
+
}
|
|
1232
|
+
/**
|
|
1233
|
+
* Detect if CLI is available and get its capabilities
|
|
1234
|
+
*/
|
|
1235
|
+
async detectCLI() {
|
|
1236
|
+
// Return cached result if already detected
|
|
1237
|
+
if (this.cliInfo) {
|
|
1238
|
+
return this.cliInfo;
|
|
1239
|
+
}
|
|
1240
|
+
// Return existing promise if detection is in progress
|
|
1241
|
+
if (this.detectionPromise) {
|
|
1242
|
+
return this.detectionPromise;
|
|
1243
|
+
}
|
|
1244
|
+
// Start new detection
|
|
1245
|
+
this.detectionPromise = this.performDetection();
|
|
1246
|
+
this.cliInfo = await this.detectionPromise;
|
|
1247
|
+
return this.cliInfo;
|
|
1248
|
+
}
|
|
1249
|
+
async performDetection() {
|
|
1250
|
+
try {
|
|
1251
|
+
// Check if onasis/lanonasis CLI is available
|
|
1252
|
+
let versionOutput = '';
|
|
1253
|
+
try {
|
|
1254
|
+
const { stdout } = await execAsync('onasis --version 2>/dev/null', { timeout: 5000 });
|
|
1255
|
+
versionOutput = stdout;
|
|
1256
|
+
}
|
|
1257
|
+
catch {
|
|
1258
|
+
// Try lanonasis if onasis fails
|
|
1259
|
+
const { stdout } = await execAsync('lanonasis --version 2>/dev/null', { timeout: 5000 });
|
|
1260
|
+
versionOutput = stdout;
|
|
1261
|
+
}
|
|
1262
|
+
const version = versionOutput.trim();
|
|
1263
|
+
// Verify it's v1.5.2 or higher for Golden Contract support
|
|
1264
|
+
const versionMatch = version.match(/(\d+)\.(\d+)\.(\d+)/);
|
|
1265
|
+
if (!versionMatch) {
|
|
1266
|
+
return { available: false };
|
|
1267
|
+
}
|
|
1268
|
+
const [, major, minor, patch] = versionMatch.map(Number);
|
|
1269
|
+
const isCompatible = major > 1 || (major === 1 && minor > 5) || (major === 1 && minor === 5 && patch >= 2);
|
|
1270
|
+
if (!isCompatible) {
|
|
1271
|
+
return {
|
|
1272
|
+
available: true,
|
|
1273
|
+
version,
|
|
1274
|
+
mcpAvailable: false,
|
|
1275
|
+
authenticated: false
|
|
1276
|
+
};
|
|
1277
|
+
}
|
|
1278
|
+
// Check MCP availability
|
|
1279
|
+
let mcpAvailable = false;
|
|
1280
|
+
try {
|
|
1281
|
+
await execAsync('onasis mcp status --output json 2>/dev/null || lanonasis mcp status --output json 2>/dev/null', {
|
|
1282
|
+
timeout: 3000
|
|
1283
|
+
});
|
|
1284
|
+
mcpAvailable = true;
|
|
1285
|
+
}
|
|
1286
|
+
catch {
|
|
1287
|
+
// MCP not available or not configured
|
|
1288
|
+
}
|
|
1289
|
+
// Check authentication status
|
|
1290
|
+
let authenticated = false;
|
|
1291
|
+
try {
|
|
1292
|
+
const { stdout: authOutput } = await execAsync('onasis auth status --output json 2>/dev/null || lanonasis auth status --output json 2>/dev/null', {
|
|
1293
|
+
timeout: 3000
|
|
1294
|
+
});
|
|
1295
|
+
const parseResult = safeJsonParse(authOutput);
|
|
1296
|
+
if (parseResult.success) {
|
|
1297
|
+
authenticated = parseResult.data.authenticated === true;
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
catch {
|
|
1301
|
+
// Authentication check failed
|
|
1302
|
+
}
|
|
1303
|
+
return {
|
|
1304
|
+
available: true,
|
|
1305
|
+
version,
|
|
1306
|
+
mcpAvailable,
|
|
1307
|
+
authenticated
|
|
1308
|
+
};
|
|
1309
|
+
}
|
|
1310
|
+
catch {
|
|
1311
|
+
return { available: false };
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
/**
|
|
1315
|
+
* Execute CLI command and return parsed JSON result
|
|
1316
|
+
*/
|
|
1317
|
+
async executeCLICommand(command, options = {}) {
|
|
1318
|
+
const cliInfo = await this.detectCLI();
|
|
1319
|
+
if (!cliInfo.available) {
|
|
1320
|
+
return { error: createErrorResponse('CLI not available', 'API_ERROR') };
|
|
1321
|
+
}
|
|
1322
|
+
if (!cliInfo.authenticated) {
|
|
1323
|
+
return { error: createErrorResponse('CLI not authenticated. Run: onasis login', 'AUTH_ERROR', 401) };
|
|
1324
|
+
}
|
|
1325
|
+
try {
|
|
1326
|
+
const timeout = options.timeout || 30000;
|
|
1327
|
+
const outputFormat = options.outputFormat || 'json';
|
|
1328
|
+
const verbose = options.verbose ? '--verbose' : '';
|
|
1329
|
+
// Determine which CLI command to use (prefer onasis for Golden Contract)
|
|
1330
|
+
const cliCmd = await this.getPreferredCLICommand();
|
|
1331
|
+
const fullCommand = `${cliCmd} ${command} --output ${outputFormat} ${verbose}`.trim();
|
|
1332
|
+
const { stdout, stderr } = await execAsync(fullCommand, {
|
|
1333
|
+
timeout,
|
|
1334
|
+
maxBuffer: 1024 * 1024 // 1MB buffer
|
|
1335
|
+
});
|
|
1336
|
+
if (stderr && stderr.trim()) {
|
|
1337
|
+
console.warn('CLI warning:', stderr);
|
|
1338
|
+
}
|
|
1339
|
+
if (outputFormat === 'json') {
|
|
1340
|
+
const parseResult = safeJsonParse(stdout);
|
|
1341
|
+
if (parseResult.success) {
|
|
1342
|
+
return { data: parseResult.data };
|
|
1343
|
+
}
|
|
1344
|
+
return { error: createErrorResponse(parseResult.error, 'VALIDATION_ERROR', 400) };
|
|
1345
|
+
}
|
|
1346
|
+
return { data: stdout };
|
|
1347
|
+
}
|
|
1348
|
+
catch (error) {
|
|
1349
|
+
if (error instanceof Error && error.message.includes('timeout')) {
|
|
1350
|
+
return { error: createErrorResponse('CLI command timeout', 'TIMEOUT_ERROR', 408) };
|
|
1351
|
+
}
|
|
1352
|
+
return {
|
|
1353
|
+
error: createErrorResponse(error instanceof Error ? error.message : 'CLI command failed', 'API_ERROR')
|
|
1354
|
+
};
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
/**
|
|
1358
|
+
* Get preferred CLI command (onasis for Golden Contract, fallback to lanonasis)
|
|
1359
|
+
*/
|
|
1360
|
+
async getPreferredCLICommand() {
|
|
1361
|
+
try {
|
|
1362
|
+
child_process.execSync('which onasis', { stdio: 'ignore', timeout: 1000 });
|
|
1363
|
+
return 'onasis';
|
|
1364
|
+
}
|
|
1365
|
+
catch {
|
|
1366
|
+
return 'lanonasis';
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
/**
|
|
1370
|
+
* Memory operations via CLI
|
|
1371
|
+
*/
|
|
1372
|
+
async createMemoryViaCLI(title, content, options = {}) {
|
|
1373
|
+
const { memoryType = 'context', tags = [], topicId } = options;
|
|
1374
|
+
let command = `memory create --title "${title}" --content "${content}" --memory-type ${memoryType}`;
|
|
1375
|
+
if (tags.length > 0) {
|
|
1376
|
+
command += ` --tags "${tags.join(',')}"`;
|
|
1377
|
+
}
|
|
1378
|
+
if (topicId) {
|
|
1379
|
+
command += ` --topic-id "${topicId}"`;
|
|
1380
|
+
}
|
|
1381
|
+
return this.executeCLICommand(command);
|
|
1382
|
+
}
|
|
1383
|
+
async listMemoriesViaCLI(options = {}) {
|
|
1384
|
+
let command = 'memory list';
|
|
1385
|
+
if (options.limit) {
|
|
1386
|
+
command += ` --limit ${options.limit}`;
|
|
1387
|
+
}
|
|
1388
|
+
if (options.memoryType) {
|
|
1389
|
+
command += ` --memory-type ${options.memoryType}`;
|
|
1390
|
+
}
|
|
1391
|
+
if (options.tags && options.tags.length > 0) {
|
|
1392
|
+
command += ` --tags "${options.tags.join(',')}"`;
|
|
1393
|
+
}
|
|
1394
|
+
if (options.sortBy) {
|
|
1395
|
+
command += ` --sort-by ${options.sortBy}`;
|
|
1396
|
+
}
|
|
1397
|
+
return this.executeCLICommand(command);
|
|
1398
|
+
}
|
|
1399
|
+
async searchMemoriesViaCLI(query, options = {}) {
|
|
1400
|
+
let command = `memory search "${query}"`;
|
|
1401
|
+
if (options.limit) {
|
|
1402
|
+
command += ` --limit ${options.limit}`;
|
|
1403
|
+
}
|
|
1404
|
+
if (options.memoryTypes && options.memoryTypes.length > 0) {
|
|
1405
|
+
command += ` --memory-types "${options.memoryTypes.join(',')}"`;
|
|
1406
|
+
}
|
|
1407
|
+
return this.executeCLICommand(command);
|
|
1408
|
+
}
|
|
1409
|
+
/**
|
|
1410
|
+
* Health check via CLI
|
|
1411
|
+
*/
|
|
1412
|
+
async healthCheckViaCLI() {
|
|
1413
|
+
return this.executeCLICommand('health');
|
|
1414
|
+
}
|
|
1415
|
+
/**
|
|
1416
|
+
* MCP-specific operations
|
|
1417
|
+
*/
|
|
1418
|
+
async getMCPStatus() {
|
|
1419
|
+
const cliInfo = await this.detectCLI();
|
|
1420
|
+
if (!cliInfo.mcpAvailable) {
|
|
1421
|
+
return { error: createErrorResponse('MCP not available via CLI', 'API_ERROR') };
|
|
1422
|
+
}
|
|
1423
|
+
return this.executeCLICommand('mcp status');
|
|
1424
|
+
}
|
|
1425
|
+
async listMCPTools() {
|
|
1426
|
+
const cliInfo = await this.detectCLI();
|
|
1427
|
+
if (!cliInfo.mcpAvailable) {
|
|
1428
|
+
return { error: createErrorResponse('MCP not available via CLI', 'API_ERROR') };
|
|
1429
|
+
}
|
|
1430
|
+
return this.executeCLICommand('mcp tools');
|
|
1431
|
+
}
|
|
1432
|
+
/**
|
|
1433
|
+
* Authentication operations
|
|
1434
|
+
*/
|
|
1435
|
+
async getAuthStatus() {
|
|
1436
|
+
return this.executeCLICommand('auth status');
|
|
1437
|
+
}
|
|
1438
|
+
/**
|
|
1439
|
+
* Check if specific CLI features are available
|
|
1440
|
+
*/
|
|
1441
|
+
async getCapabilities() {
|
|
1442
|
+
const cliInfo = await this.detectCLI();
|
|
1443
|
+
return {
|
|
1444
|
+
cliAvailable: cliInfo.available,
|
|
1445
|
+
version: cliInfo.version,
|
|
1446
|
+
mcpSupport: cliInfo.mcpAvailable || false,
|
|
1447
|
+
authenticated: cliInfo.authenticated || false,
|
|
1448
|
+
goldenContract: cliInfo.available && this.isGoldenContractCompliant(cliInfo.version)
|
|
1449
|
+
};
|
|
1450
|
+
}
|
|
1451
|
+
isGoldenContractCompliant(version) {
|
|
1452
|
+
if (!version)
|
|
1453
|
+
return false;
|
|
1454
|
+
const versionMatch = version.match(/(\d+)\.(\d+)\.(\d+)/);
|
|
1455
|
+
if (!versionMatch)
|
|
1456
|
+
return false;
|
|
1457
|
+
const [, major, minor, patch] = versionMatch.map(Number);
|
|
1458
|
+
return major > 1 || (major === 1 && minor > 5) || (major === 1 && minor === 5 && patch >= 2);
|
|
1459
|
+
}
|
|
1460
|
+
/**
|
|
1461
|
+
* Force refresh CLI detection
|
|
1462
|
+
*/
|
|
1463
|
+
async refresh() {
|
|
1464
|
+
this.cliInfo = null;
|
|
1465
|
+
this.detectionPromise = null;
|
|
1466
|
+
return this.detectCLI();
|
|
1467
|
+
}
|
|
1468
|
+
/**
|
|
1469
|
+
* Get cached CLI info without re-detection
|
|
1470
|
+
*/
|
|
1471
|
+
getCachedInfo() {
|
|
1472
|
+
return this.cliInfo;
|
|
1473
|
+
}
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1476
|
+
/**
|
|
1477
|
+
* Enhanced Memory Client with CLI Integration
|
|
1478
|
+
*
|
|
1479
|
+
* Intelligently routes requests through CLI v1.5.2+ when available,
|
|
1480
|
+
* with fallback to direct API for maximum compatibility and performance
|
|
1481
|
+
*
|
|
1482
|
+
* IMPORTANT: This file uses Node.js-specific features (process.env) and should only be used in Node.js environments
|
|
1483
|
+
*/
|
|
1484
|
+
/**
|
|
1485
|
+
* Enhanced Memory Client with intelligent CLI/API routing
|
|
1486
|
+
*/
|
|
1487
|
+
class EnhancedMemoryClient {
|
|
1488
|
+
createDefaultCapabilities() {
|
|
1489
|
+
return {
|
|
1490
|
+
cliAvailable: false,
|
|
1491
|
+
mcpSupport: false,
|
|
1492
|
+
authenticated: false,
|
|
1493
|
+
goldenContract: false
|
|
1494
|
+
};
|
|
1495
|
+
}
|
|
1496
|
+
constructor(config) {
|
|
1497
|
+
this.capabilities = null;
|
|
1498
|
+
// Merge config with defaults, ensuring all required fields are present
|
|
1499
|
+
// Spread config first, then apply defaults only for undefined values
|
|
1500
|
+
const mergedConfig = {
|
|
1501
|
+
...config,
|
|
1502
|
+
preferCLI: config.preferCLI ?? true,
|
|
1503
|
+
enableMCP: config.enableMCP ?? true,
|
|
1504
|
+
cliDetectionTimeout: config.cliDetectionTimeout ?? 5000,
|
|
1505
|
+
fallbackToAPI: config.fallbackToAPI ?? true,
|
|
1506
|
+
minCLIVersion: config.minCLIVersion ?? '1.5.2',
|
|
1507
|
+
verbose: config.verbose ?? false,
|
|
1508
|
+
timeout: config.timeout ?? 30000,
|
|
1509
|
+
apiUrl: config.apiUrl || 'https://api.lanonasis.com',
|
|
1510
|
+
apiKey: config.apiKey || process.env.LANONASIS_API_KEY || '',
|
|
1511
|
+
authToken: config.authToken || '',
|
|
1512
|
+
headers: config.headers || {}
|
|
1513
|
+
};
|
|
1514
|
+
this.config = mergedConfig;
|
|
1515
|
+
this.directClient = new CoreMemoryClient(config);
|
|
1516
|
+
this.cliIntegration = new CLIIntegration();
|
|
1517
|
+
}
|
|
1518
|
+
/**
|
|
1519
|
+
* Initialize the client and detect capabilities
|
|
1520
|
+
*/
|
|
1521
|
+
async initialize() {
|
|
1522
|
+
try {
|
|
1523
|
+
const detectionPromise = this.cliIntegration.getCapabilities();
|
|
1524
|
+
const capabilities = this.config.cliDetectionTimeout > 0
|
|
1525
|
+
? await Promise.race([
|
|
1526
|
+
detectionPromise,
|
|
1527
|
+
new Promise((resolve) => {
|
|
1528
|
+
setTimeout(() => resolve(null), this.config.cliDetectionTimeout);
|
|
1529
|
+
})
|
|
1530
|
+
])
|
|
1531
|
+
: await detectionPromise;
|
|
1532
|
+
if (capabilities) {
|
|
1533
|
+
this.capabilities = capabilities;
|
|
1534
|
+
if (this.config.verbose && capabilities.cliAvailable && !capabilities.authenticated) {
|
|
1535
|
+
const suggestedCommand = capabilities.goldenContract ? 'onasis login' : 'lanonasis login';
|
|
1536
|
+
console.warn(`CLI detected but not authenticated. Run '${suggestedCommand}' to enable enhanced SDK features.`);
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
else {
|
|
1540
|
+
this.capabilities = this.createDefaultCapabilities();
|
|
1541
|
+
if (this.config.verbose) {
|
|
1542
|
+
console.warn(`CLI detection timed out after ${this.config.cliDetectionTimeout}ms. Falling back to API mode.`);
|
|
1543
|
+
}
|
|
1544
|
+
}
|
|
1545
|
+
}
|
|
1546
|
+
catch (error) {
|
|
1547
|
+
if (this.config.verbose) {
|
|
1548
|
+
console.warn('CLI detection failed:', error);
|
|
1549
|
+
}
|
|
1550
|
+
this.capabilities = this.createDefaultCapabilities();
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1553
|
+
/**
|
|
1554
|
+
* Get current capabilities
|
|
1555
|
+
*/
|
|
1556
|
+
async getCapabilities() {
|
|
1557
|
+
if (!this.capabilities) {
|
|
1558
|
+
await this.initialize();
|
|
1559
|
+
}
|
|
1560
|
+
if (!this.capabilities) {
|
|
1561
|
+
this.capabilities = this.createDefaultCapabilities();
|
|
1562
|
+
}
|
|
1563
|
+
return this.capabilities;
|
|
1564
|
+
}
|
|
1565
|
+
/**
|
|
1566
|
+
* Determine if operation should use CLI
|
|
1567
|
+
*/
|
|
1568
|
+
async shouldUseCLI() {
|
|
1569
|
+
const capabilities = await this.getCapabilities();
|
|
1570
|
+
return (this.config.preferCLI &&
|
|
1571
|
+
capabilities.cliAvailable &&
|
|
1572
|
+
capabilities.authenticated &&
|
|
1573
|
+
capabilities.goldenContract);
|
|
1574
|
+
}
|
|
1575
|
+
/**
|
|
1576
|
+
* Execute operation with intelligent routing
|
|
1577
|
+
*/
|
|
1578
|
+
async executeOperation(operation, cliOperation, apiOperation) {
|
|
1579
|
+
const useCLI = await this.shouldUseCLI();
|
|
1580
|
+
const capabilities = await this.getCapabilities();
|
|
1581
|
+
if (useCLI) {
|
|
1582
|
+
try {
|
|
1583
|
+
const result = await cliOperation();
|
|
1584
|
+
if (result.error && this.config.fallbackToAPI) {
|
|
1585
|
+
console.warn(`CLI ${operation} failed, falling back to API:`, result.error);
|
|
1586
|
+
const apiResult = await apiOperation();
|
|
1587
|
+
return {
|
|
1588
|
+
...apiResult,
|
|
1589
|
+
source: 'api',
|
|
1590
|
+
mcpUsed: false
|
|
1591
|
+
};
|
|
1592
|
+
}
|
|
1593
|
+
return {
|
|
1594
|
+
...result,
|
|
1595
|
+
source: 'cli',
|
|
1596
|
+
mcpUsed: capabilities.mcpSupport
|
|
1597
|
+
};
|
|
1598
|
+
}
|
|
1599
|
+
catch (error) {
|
|
1600
|
+
if (this.config.fallbackToAPI) {
|
|
1601
|
+
console.warn(`CLI ${operation} error, falling back to API:`, error);
|
|
1602
|
+
const apiResult = await apiOperation();
|
|
1603
|
+
return {
|
|
1604
|
+
...apiResult,
|
|
1605
|
+
source: 'api',
|
|
1606
|
+
mcpUsed: false
|
|
1607
|
+
};
|
|
1608
|
+
}
|
|
1609
|
+
return {
|
|
1610
|
+
error: createErrorResponse(error instanceof Error ? error.message : `CLI ${operation} failed`, 'API_ERROR'),
|
|
1611
|
+
source: 'cli',
|
|
1612
|
+
mcpUsed: false
|
|
1613
|
+
};
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
else {
|
|
1617
|
+
const result = await apiOperation();
|
|
1618
|
+
return {
|
|
1619
|
+
...result,
|
|
1620
|
+
source: 'api',
|
|
1621
|
+
mcpUsed: false
|
|
1622
|
+
};
|
|
1623
|
+
}
|
|
1624
|
+
}
|
|
1625
|
+
// Enhanced API Methods
|
|
1626
|
+
/**
|
|
1627
|
+
* Health check with intelligent routing
|
|
1628
|
+
*/
|
|
1629
|
+
async healthCheck() {
|
|
1630
|
+
return this.executeOperation('health check', () => this.cliIntegration.healthCheckViaCLI(), () => this.directClient.healthCheck());
|
|
1631
|
+
}
|
|
1632
|
+
/**
|
|
1633
|
+
* Create memory with CLI/API routing
|
|
1634
|
+
*/
|
|
1635
|
+
async createMemory(memory) {
|
|
1636
|
+
return this.executeOperation('create memory', () => this.cliIntegration.createMemoryViaCLI(memory.title, memory.content, {
|
|
1637
|
+
memoryType: memory.memory_type,
|
|
1638
|
+
tags: memory.tags,
|
|
1639
|
+
topicId: memory.topic_id
|
|
1640
|
+
}), () => this.directClient.createMemory(memory));
|
|
1641
|
+
}
|
|
1642
|
+
/**
|
|
1643
|
+
* List memories with intelligent routing
|
|
1644
|
+
*/
|
|
1645
|
+
async listMemories(options = {}) {
|
|
1646
|
+
return this.executeOperation('list memories', () => this.cliIntegration.listMemoriesViaCLI({
|
|
1647
|
+
limit: options.limit,
|
|
1648
|
+
memoryType: options.memory_type,
|
|
1649
|
+
tags: options.tags,
|
|
1650
|
+
sortBy: options.sort
|
|
1651
|
+
}), () => this.directClient.listMemories(options));
|
|
1652
|
+
}
|
|
1653
|
+
/**
|
|
1654
|
+
* Search memories with MCP enhancement when available
|
|
1655
|
+
*/
|
|
1656
|
+
async searchMemories(request) {
|
|
1657
|
+
return this.executeOperation('search memories', () => this.cliIntegration.searchMemoriesViaCLI(request.query, {
|
|
1658
|
+
limit: request.limit,
|
|
1659
|
+
memoryTypes: request.memory_types
|
|
1660
|
+
}), () => this.directClient.searchMemories(request));
|
|
1661
|
+
}
|
|
1662
|
+
/**
|
|
1663
|
+
* Get memory by ID (API only for now)
|
|
1664
|
+
*/
|
|
1665
|
+
async getMemory(id) {
|
|
1666
|
+
// CLI doesn't have get by ID yet, use API
|
|
1667
|
+
const result = await this.directClient.getMemory(id);
|
|
1668
|
+
return {
|
|
1669
|
+
...result,
|
|
1670
|
+
source: 'api',
|
|
1671
|
+
mcpUsed: false
|
|
1672
|
+
};
|
|
1673
|
+
}
|
|
1674
|
+
/**
|
|
1675
|
+
* Update memory (API only for now)
|
|
1676
|
+
*/
|
|
1677
|
+
async updateMemory(id, updates) {
|
|
1678
|
+
// CLI doesn't have update yet, use API
|
|
1679
|
+
const result = await this.directClient.updateMemory(id, updates);
|
|
1680
|
+
return {
|
|
1681
|
+
...result,
|
|
1682
|
+
source: 'api',
|
|
1683
|
+
mcpUsed: false
|
|
1684
|
+
};
|
|
1685
|
+
}
|
|
1686
|
+
/**
|
|
1687
|
+
* Delete memory (API only for now)
|
|
1688
|
+
*/
|
|
1689
|
+
async deleteMemory(id) {
|
|
1690
|
+
// CLI doesn't have delete yet, use API
|
|
1691
|
+
const result = await this.directClient.deleteMemory(id);
|
|
1692
|
+
return {
|
|
1693
|
+
...result,
|
|
1694
|
+
source: 'api',
|
|
1695
|
+
mcpUsed: false
|
|
1696
|
+
};
|
|
1697
|
+
}
|
|
1698
|
+
// Topic Operations (API only for now)
|
|
1699
|
+
async createTopic(topic) {
|
|
1700
|
+
const result = await this.directClient.createTopic(topic);
|
|
1701
|
+
return { ...result, source: 'api', mcpUsed: false };
|
|
1702
|
+
}
|
|
1703
|
+
async getTopics() {
|
|
1704
|
+
const result = await this.directClient.getTopics();
|
|
1705
|
+
return { ...result, source: 'api', mcpUsed: false };
|
|
1706
|
+
}
|
|
1707
|
+
async getTopic(id) {
|
|
1708
|
+
const result = await this.directClient.getTopic(id);
|
|
1709
|
+
return { ...result, source: 'api', mcpUsed: false };
|
|
1710
|
+
}
|
|
1711
|
+
async updateTopic(id, updates) {
|
|
1712
|
+
const result = await this.directClient.updateTopic(id, updates);
|
|
1713
|
+
return { ...result, source: 'api', mcpUsed: false };
|
|
1714
|
+
}
|
|
1715
|
+
async deleteTopic(id) {
|
|
1716
|
+
const result = await this.directClient.deleteTopic(id);
|
|
1717
|
+
return { ...result, source: 'api', mcpUsed: false };
|
|
1718
|
+
}
|
|
1719
|
+
/**
|
|
1720
|
+
* Get memory statistics
|
|
1721
|
+
*/
|
|
1722
|
+
async getMemoryStats() {
|
|
1723
|
+
const result = await this.directClient.getMemoryStats();
|
|
1724
|
+
return { ...result, source: 'api', mcpUsed: false };
|
|
1725
|
+
}
|
|
1726
|
+
// Utility Methods
|
|
1727
|
+
/**
|
|
1728
|
+
* Force CLI re-detection
|
|
1729
|
+
*/
|
|
1730
|
+
async refreshCLIDetection() {
|
|
1731
|
+
this.capabilities = null;
|
|
1732
|
+
await this.cliIntegration.refresh();
|
|
1733
|
+
await this.initialize();
|
|
1734
|
+
}
|
|
1735
|
+
/**
|
|
1736
|
+
* Get authentication status from CLI
|
|
1737
|
+
*/
|
|
1738
|
+
async getAuthStatus() {
|
|
1739
|
+
try {
|
|
1740
|
+
const result = await this.cliIntegration.getAuthStatus();
|
|
1741
|
+
return { ...result, source: 'cli', mcpUsed: false };
|
|
1742
|
+
}
|
|
1743
|
+
catch (error) {
|
|
1744
|
+
return {
|
|
1745
|
+
error: createErrorResponse(error instanceof Error ? error.message : 'Auth status check failed', 'API_ERROR'),
|
|
1746
|
+
source: 'cli',
|
|
1747
|
+
mcpUsed: false
|
|
1748
|
+
};
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
/**
|
|
1752
|
+
* Get MCP status when available
|
|
1753
|
+
*/
|
|
1754
|
+
async getMCPStatus() {
|
|
1755
|
+
const capabilities = await this.getCapabilities();
|
|
1756
|
+
if (!capabilities.mcpSupport) {
|
|
1757
|
+
return {
|
|
1758
|
+
error: createErrorResponse('MCP not available', 'API_ERROR'),
|
|
1759
|
+
source: 'cli',
|
|
1760
|
+
mcpUsed: false
|
|
1761
|
+
};
|
|
1762
|
+
}
|
|
1763
|
+
try {
|
|
1764
|
+
const result = await this.cliIntegration.getMCPStatus();
|
|
1765
|
+
return { ...result, source: 'cli', mcpUsed: true };
|
|
1766
|
+
}
|
|
1767
|
+
catch (error) {
|
|
1768
|
+
return {
|
|
1769
|
+
error: createErrorResponse(error instanceof Error ? error.message : 'MCP status check failed', 'API_ERROR'),
|
|
1770
|
+
source: 'cli',
|
|
1771
|
+
mcpUsed: false
|
|
1772
|
+
};
|
|
1773
|
+
}
|
|
1774
|
+
}
|
|
1775
|
+
/**
|
|
1776
|
+
* Update authentication for both CLI and API client
|
|
1777
|
+
*/
|
|
1778
|
+
setAuthToken(token) {
|
|
1779
|
+
this.directClient.setAuthToken(token);
|
|
1780
|
+
}
|
|
1781
|
+
setApiKey(apiKey) {
|
|
1782
|
+
this.directClient.setApiKey(apiKey);
|
|
1783
|
+
}
|
|
1784
|
+
clearAuth() {
|
|
1785
|
+
this.directClient.clearAuth();
|
|
1786
|
+
}
|
|
1787
|
+
/**
|
|
1788
|
+
* Update configuration
|
|
1789
|
+
*/
|
|
1790
|
+
updateConfig(updates) {
|
|
1791
|
+
this.config = { ...this.config, ...updates };
|
|
1792
|
+
this.directClient.updateConfig(updates);
|
|
1793
|
+
}
|
|
1794
|
+
/**
|
|
1795
|
+
* Get configuration summary
|
|
1796
|
+
*/
|
|
1797
|
+
getConfigSummary() {
|
|
1798
|
+
return {
|
|
1799
|
+
apiUrl: this.config.apiUrl,
|
|
1800
|
+
preferCLI: this.config.preferCLI,
|
|
1801
|
+
enableMCP: this.config.enableMCP,
|
|
1802
|
+
capabilities: this.capabilities || undefined
|
|
1803
|
+
};
|
|
1804
|
+
}
|
|
1805
|
+
}
|
|
1806
|
+
/**
|
|
1807
|
+
* Factory function to create an enhanced memory client
|
|
1808
|
+
*/
|
|
1809
|
+
async function createNodeMemoryClient(config) {
|
|
1810
|
+
const client = new EnhancedMemoryClient(config);
|
|
1811
|
+
await client.initialize();
|
|
1812
|
+
return client;
|
|
1813
|
+
}
|
|
1814
|
+
|
|
1815
|
+
/**
|
|
1816
|
+
* @lanonasis/memory-client/node
|
|
1817
|
+
*
|
|
1818
|
+
* Node.js-enhanced client with CLI integration
|
|
1819
|
+
* ONLY import this in Node.js environments
|
|
1820
|
+
*
|
|
1821
|
+
* Provides intelligent CLI detection and MCP channel utilization
|
|
1822
|
+
* when @lanonasis/cli v1.5.2+ is available
|
|
1823
|
+
*/
|
|
1824
|
+
// Enhanced client with CLI support
|
|
1825
|
+
|
|
1826
|
+
var index = /*#__PURE__*/Object.freeze({
|
|
1827
|
+
__proto__: null,
|
|
1828
|
+
CLIIntegration: CLIIntegration,
|
|
1829
|
+
EnhancedMemoryClient: EnhancedMemoryClient,
|
|
1830
|
+
createNodeMemoryClient: createNodeMemoryClient
|
|
1831
|
+
});
|
|
1832
|
+
|
|
1833
|
+
exports.ApiError = ApiError;
|
|
617
1834
|
exports.AuthenticationError = AuthenticationError;
|
|
1835
|
+
exports.CHUNKING_STRATEGIES = CHUNKING_STRATEGIES;
|
|
618
1836
|
exports.CLIENT_NAME = CLIENT_NAME;
|
|
1837
|
+
exports.CONTENT_TYPES = CONTENT_TYPES;
|
|
619
1838
|
exports.CoreMemoryClient = CoreMemoryClient;
|
|
1839
|
+
exports.ERROR_CODES = ERROR_CODES;
|
|
620
1840
|
exports.MEMORY_STATUSES = MEMORY_STATUSES;
|
|
621
1841
|
exports.MEMORY_TYPES = MEMORY_TYPES;
|
|
622
1842
|
exports.MemoryClient = CoreMemoryClient;
|
|
623
1843
|
exports.MemoryClientError = MemoryClientError;
|
|
1844
|
+
exports.NetworkError = NetworkError;
|
|
624
1845
|
exports.NotFoundError = NotFoundError;
|
|
625
1846
|
exports.RateLimitError = RateLimitError;
|
|
1847
|
+
exports.SEARCH_MODES = SEARCH_MODES;
|
|
1848
|
+
exports.ServerError = ServerError;
|
|
626
1849
|
exports.TimeoutError = TimeoutError;
|
|
627
1850
|
exports.VERSION = VERSION;
|
|
628
1851
|
exports.ValidationError = ValidationError;
|
|
1852
|
+
exports.analyticsDateRangeSchema = analyticsDateRangeSchema;
|
|
1853
|
+
exports.calculateRetryDelay = calculateRetryDelay;
|
|
1854
|
+
exports.createClient = createClient;
|
|
1855
|
+
exports.createErrorFromStatus = createErrorFromStatus;
|
|
1856
|
+
exports.createErrorResponse = createErrorResponse;
|
|
629
1857
|
exports.createMemoryClient = createMemoryClient;
|
|
630
1858
|
exports.createMemorySchema = createMemorySchema;
|
|
631
1859
|
exports.createTopicSchema = createTopicSchema;
|
|
632
1860
|
exports.defaultConfigs = defaultConfigs;
|
|
1861
|
+
exports.enhancedSearchSchema = enhancedSearchSchema;
|
|
1862
|
+
exports.getEnvironment = getEnvironment;
|
|
1863
|
+
exports.hasData = hasData;
|
|
1864
|
+
exports.hasError = hasError;
|
|
1865
|
+
exports.httpStatusToErrorCode = httpStatusToErrorCode;
|
|
1866
|
+
exports.isApiErrorResponse = isApiErrorResponse;
|
|
633
1867
|
exports.isBrowser = isBrowser;
|
|
1868
|
+
exports.isEdge = isEdge;
|
|
634
1869
|
exports.isNode = isNode;
|
|
1870
|
+
exports.isRetryableError = isRetryableError;
|
|
1871
|
+
exports.preprocessingOptionsSchema = preprocessingOptionsSchema;
|
|
1872
|
+
exports.safeJsonParse = safeJsonParse;
|
|
635
1873
|
exports.searchMemorySchema = searchMemorySchema;
|
|
636
1874
|
exports.updateMemorySchema = updateMemorySchema;
|
|
637
1875
|
//# sourceMappingURL=index.js.map
|