@lanonasis/cli 3.9.4 → 3.9.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/README.md +46 -15
- package/dist/commands/config.js +11 -0
- package/dist/commands/memory.js +276 -6
- package/dist/index-simple.js +29 -4
- package/dist/index.js +32 -4
- package/dist/mcp/schemas/tool-schemas.d.ts +24 -24
- package/dist/utils/api.d.ts +6 -0
- package/dist/utils/api.js +264 -14
- package/dist/utils/config.d.ts +11 -0
- package/dist/utils/config.js +298 -33
- package/dist/ux/implementations/TextInputHandlerImpl.d.ts +1 -0
- package/dist/ux/implementations/TextInputHandlerImpl.js +8 -3
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -53,6 +53,18 @@ program
|
|
|
53
53
|
process.env.MEMORY_API_URL = opts.apiUrl;
|
|
54
54
|
}
|
|
55
55
|
process.env.CLI_OUTPUT_FORMAT = opts.output;
|
|
56
|
+
const forceApiFromEnv = process.env.LANONASIS_FORCE_API === 'true' ||
|
|
57
|
+
process.env.CLI_FORCE_API === 'true' ||
|
|
58
|
+
process.env.ONASIS_FORCE_API === 'true';
|
|
59
|
+
const forceApiFromConfig = cliConfig.get('forceApi') === true ||
|
|
60
|
+
cliConfig.get('connectionTransport') === 'api';
|
|
61
|
+
const forceDirectApi = forceApiFromEnv || forceApiFromConfig || opts.mcp === false;
|
|
62
|
+
if (process.env.CLI_VERBOSE === 'true') {
|
|
63
|
+
console.log(colors.muted(`transport flags: env=${forceApiFromEnv} config=${forceApiFromConfig} no_mcp=${opts.mcp === false}`));
|
|
64
|
+
}
|
|
65
|
+
if (forceDirectApi) {
|
|
66
|
+
process.env.LANONASIS_FORCE_API = 'true';
|
|
67
|
+
}
|
|
56
68
|
const skipOnboarding = actionCommand.name() === 'init' ||
|
|
57
69
|
actionCommand.name() === 'auth' ||
|
|
58
70
|
actionCommand.parent?.name?.() === 'auth';
|
|
@@ -76,7 +88,9 @@ program
|
|
|
76
88
|
actionCommand.parent?.name?.() === 'mcp' ||
|
|
77
89
|
actionCommand.name() === 'mcp-server' ||
|
|
78
90
|
actionCommand.parent?.name?.() === 'mcp-server';
|
|
79
|
-
|
|
91
|
+
const isConfigFlow = actionCommand.name() === 'config' ||
|
|
92
|
+
actionCommand.parent?.name?.() === 'config';
|
|
93
|
+
if (!forceDirectApi && !isMcpFlow && !isConfigFlow && !['init', 'auth', 'login', 'health', 'status'].includes(actionCommand.name())) {
|
|
80
94
|
try {
|
|
81
95
|
const client = getMCPClient();
|
|
82
96
|
if (!client.isConnectedToServer()) {
|
|
@@ -94,6 +108,9 @@ program
|
|
|
94
108
|
}
|
|
95
109
|
}
|
|
96
110
|
}
|
|
111
|
+
else if (forceDirectApi && process.env.CLI_VERBOSE === 'true') {
|
|
112
|
+
console.log(colors.muted('MCP auto-connect skipped (force direct API enabled)'));
|
|
113
|
+
}
|
|
97
114
|
});
|
|
98
115
|
// Enhanced global error handler
|
|
99
116
|
process.on('uncaughtException', (error) => {
|
|
@@ -399,11 +416,10 @@ const topicCmd = program
|
|
|
399
416
|
.description('Topic management commands');
|
|
400
417
|
requireAuth(topicCmd);
|
|
401
418
|
topicCommands(topicCmd);
|
|
402
|
-
// Configuration commands (
|
|
419
|
+
// Configuration commands (no auth required)
|
|
403
420
|
const configCmd = program
|
|
404
421
|
.command('config')
|
|
405
422
|
.description('Configuration management');
|
|
406
|
-
requireAuth(configCmd);
|
|
407
423
|
configCommands(configCmd);
|
|
408
424
|
// Organization commands (require auth)
|
|
409
425
|
const orgCmd = program
|
|
@@ -614,18 +630,30 @@ program
|
|
|
614
630
|
.description('Show overall system status')
|
|
615
631
|
.action(async () => {
|
|
616
632
|
await cliConfig.init();
|
|
617
|
-
const
|
|
633
|
+
const verification = await cliConfig.verifyCurrentCredentialsWithServer().catch((error) => ({
|
|
634
|
+
valid: false,
|
|
635
|
+
method: 'none',
|
|
636
|
+
endpoint: undefined,
|
|
637
|
+
reason: error instanceof Error ? error.message : String(error)
|
|
638
|
+
}));
|
|
639
|
+
const isAuth = verification.valid;
|
|
618
640
|
const apiUrl = cliConfig.getApiUrl();
|
|
619
641
|
console.log(chalk.blue.bold('MaaS CLI Status'));
|
|
620
642
|
console.log(`API URL: ${apiUrl}`);
|
|
621
643
|
console.log(`Authenticated: ${isAuth ? chalk.green('Yes') : chalk.red('No')}`);
|
|
644
|
+
if (process.env.CLI_VERBOSE === 'true' && verification.endpoint) {
|
|
645
|
+
console.log(`Verified via: ${verification.endpoint}`);
|
|
646
|
+
}
|
|
622
647
|
if (isAuth) {
|
|
623
648
|
const user = await cliConfig.getCurrentUser();
|
|
624
649
|
if (user) {
|
|
625
650
|
console.log(`User: ${user.email}`);
|
|
626
651
|
console.log(`Plan: ${user.plan}`);
|
|
627
652
|
}
|
|
653
|
+
return;
|
|
628
654
|
}
|
|
655
|
+
console.log(chalk.yellow(`Auth check: ${verification.reason || 'Credential validation failed'}`));
|
|
656
|
+
console.log(chalk.yellow('Please run:'), chalk.white('lanonasis auth login'));
|
|
629
657
|
});
|
|
630
658
|
// Health command using the healthCheck function
|
|
631
659
|
program
|
|
@@ -14,16 +14,16 @@ export declare const MemoryCreateSchema: z.ZodObject<{
|
|
|
14
14
|
title?: string;
|
|
15
15
|
content?: string;
|
|
16
16
|
tags?: string[];
|
|
17
|
+
memory_type?: "context" | "reference" | "note";
|
|
17
18
|
topic_id?: string;
|
|
18
19
|
metadata?: Record<string, any>;
|
|
19
|
-
memory_type?: "context" | "reference" | "note";
|
|
20
20
|
}, {
|
|
21
21
|
title?: string;
|
|
22
22
|
content?: string;
|
|
23
23
|
tags?: string[];
|
|
24
|
+
memory_type?: "context" | "reference" | "note";
|
|
24
25
|
topic_id?: string;
|
|
25
26
|
metadata?: Record<string, any>;
|
|
26
|
-
memory_type?: "context" | "reference" | "note";
|
|
27
27
|
}>;
|
|
28
28
|
export declare const MemorySearchSchema: z.ZodObject<{
|
|
29
29
|
query: z.ZodString;
|
|
@@ -36,16 +36,16 @@ export declare const MemorySearchSchema: z.ZodObject<{
|
|
|
36
36
|
query?: string;
|
|
37
37
|
tags?: string[];
|
|
38
38
|
limit?: number;
|
|
39
|
+
memory_type?: "context" | "reference" | "note";
|
|
39
40
|
topic_id?: string;
|
|
40
41
|
threshold?: number;
|
|
41
|
-
memory_type?: "context" | "reference" | "note";
|
|
42
42
|
}, {
|
|
43
43
|
query?: string;
|
|
44
44
|
tags?: string[];
|
|
45
45
|
limit?: number;
|
|
46
|
+
memory_type?: "context" | "reference" | "note";
|
|
46
47
|
topic_id?: string;
|
|
47
48
|
threshold?: number;
|
|
48
|
-
memory_type?: "context" | "reference" | "note";
|
|
49
49
|
}>;
|
|
50
50
|
export declare const MemoryUpdateSchema: z.ZodObject<{
|
|
51
51
|
memory_id: z.ZodString;
|
|
@@ -58,16 +58,16 @@ export declare const MemoryUpdateSchema: z.ZodObject<{
|
|
|
58
58
|
title?: string;
|
|
59
59
|
content?: string;
|
|
60
60
|
tags?: string[];
|
|
61
|
+
memory_type?: "context" | "reference" | "note";
|
|
61
62
|
memory_id?: string;
|
|
62
63
|
metadata?: Record<string, any>;
|
|
63
|
-
memory_type?: "context" | "reference" | "note";
|
|
64
64
|
}, {
|
|
65
65
|
title?: string;
|
|
66
66
|
content?: string;
|
|
67
67
|
tags?: string[];
|
|
68
|
+
memory_type?: "context" | "reference" | "note";
|
|
68
69
|
memory_id?: string;
|
|
69
70
|
metadata?: Record<string, any>;
|
|
70
|
-
memory_type?: "context" | "reference" | "note";
|
|
71
71
|
}>;
|
|
72
72
|
export declare const MemoryDeleteSchema: z.ZodObject<{
|
|
73
73
|
memory_id: z.ZodString;
|
|
@@ -90,17 +90,17 @@ export declare const MemoryListSchema: z.ZodObject<{
|
|
|
90
90
|
}, "strip", z.ZodTypeAny, {
|
|
91
91
|
tags?: string[];
|
|
92
92
|
limit?: number;
|
|
93
|
-
topic_id?: string;
|
|
94
|
-
memory_type?: "context" | "reference" | "note";
|
|
95
93
|
offset?: number;
|
|
94
|
+
memory_type?: "context" | "reference" | "note";
|
|
95
|
+
topic_id?: string;
|
|
96
96
|
sort_by?: "title" | "created_at" | "updated_at";
|
|
97
97
|
order?: "desc" | "asc";
|
|
98
98
|
}, {
|
|
99
99
|
tags?: string[];
|
|
100
100
|
limit?: number;
|
|
101
|
-
topic_id?: string;
|
|
102
|
-
memory_type?: "context" | "reference" | "note";
|
|
103
101
|
offset?: number;
|
|
102
|
+
memory_type?: "context" | "reference" | "note";
|
|
103
|
+
topic_id?: string;
|
|
104
104
|
sort_by?: "title" | "created_at" | "updated_at";
|
|
105
105
|
order?: "desc" | "asc";
|
|
106
106
|
}>;
|
|
@@ -201,14 +201,14 @@ export declare const SystemConfigSchema: z.ZodObject<{
|
|
|
201
201
|
scope: z.ZodDefault<z.ZodEnum<["user", "global"]>>;
|
|
202
202
|
}, "strip", z.ZodTypeAny, {
|
|
203
203
|
value?: any;
|
|
204
|
-
key?: string;
|
|
205
204
|
action?: "get" | "set" | "reset";
|
|
206
205
|
scope?: "user" | "global";
|
|
206
|
+
key?: string;
|
|
207
207
|
}, {
|
|
208
208
|
value?: any;
|
|
209
|
-
key?: string;
|
|
210
209
|
action?: "get" | "set" | "reset";
|
|
211
210
|
scope?: "user" | "global";
|
|
211
|
+
key?: string;
|
|
212
212
|
}>;
|
|
213
213
|
export declare const BulkOperationSchema: z.ZodObject<{
|
|
214
214
|
operation: z.ZodEnum<["create", "update", "delete"]>;
|
|
@@ -386,16 +386,16 @@ export declare const MCPSchemas: {
|
|
|
386
386
|
title?: string;
|
|
387
387
|
content?: string;
|
|
388
388
|
tags?: string[];
|
|
389
|
+
memory_type?: "context" | "reference" | "note";
|
|
389
390
|
topic_id?: string;
|
|
390
391
|
metadata?: Record<string, any>;
|
|
391
|
-
memory_type?: "context" | "reference" | "note";
|
|
392
392
|
}, {
|
|
393
393
|
title?: string;
|
|
394
394
|
content?: string;
|
|
395
395
|
tags?: string[];
|
|
396
|
+
memory_type?: "context" | "reference" | "note";
|
|
396
397
|
topic_id?: string;
|
|
397
398
|
metadata?: Record<string, any>;
|
|
398
|
-
memory_type?: "context" | "reference" | "note";
|
|
399
399
|
}>;
|
|
400
400
|
search: z.ZodObject<{
|
|
401
401
|
query: z.ZodString;
|
|
@@ -408,16 +408,16 @@ export declare const MCPSchemas: {
|
|
|
408
408
|
query?: string;
|
|
409
409
|
tags?: string[];
|
|
410
410
|
limit?: number;
|
|
411
|
+
memory_type?: "context" | "reference" | "note";
|
|
411
412
|
topic_id?: string;
|
|
412
413
|
threshold?: number;
|
|
413
|
-
memory_type?: "context" | "reference" | "note";
|
|
414
414
|
}, {
|
|
415
415
|
query?: string;
|
|
416
416
|
tags?: string[];
|
|
417
417
|
limit?: number;
|
|
418
|
+
memory_type?: "context" | "reference" | "note";
|
|
418
419
|
topic_id?: string;
|
|
419
420
|
threshold?: number;
|
|
420
|
-
memory_type?: "context" | "reference" | "note";
|
|
421
421
|
}>;
|
|
422
422
|
update: z.ZodObject<{
|
|
423
423
|
memory_id: z.ZodString;
|
|
@@ -430,16 +430,16 @@ export declare const MCPSchemas: {
|
|
|
430
430
|
title?: string;
|
|
431
431
|
content?: string;
|
|
432
432
|
tags?: string[];
|
|
433
|
+
memory_type?: "context" | "reference" | "note";
|
|
433
434
|
memory_id?: string;
|
|
434
435
|
metadata?: Record<string, any>;
|
|
435
|
-
memory_type?: "context" | "reference" | "note";
|
|
436
436
|
}, {
|
|
437
437
|
title?: string;
|
|
438
438
|
content?: string;
|
|
439
439
|
tags?: string[];
|
|
440
|
+
memory_type?: "context" | "reference" | "note";
|
|
440
441
|
memory_id?: string;
|
|
441
442
|
metadata?: Record<string, any>;
|
|
442
|
-
memory_type?: "context" | "reference" | "note";
|
|
443
443
|
}>;
|
|
444
444
|
delete: z.ZodObject<{
|
|
445
445
|
memory_id: z.ZodString;
|
|
@@ -462,17 +462,17 @@ export declare const MCPSchemas: {
|
|
|
462
462
|
}, "strip", z.ZodTypeAny, {
|
|
463
463
|
tags?: string[];
|
|
464
464
|
limit?: number;
|
|
465
|
-
topic_id?: string;
|
|
466
|
-
memory_type?: "context" | "reference" | "note";
|
|
467
465
|
offset?: number;
|
|
466
|
+
memory_type?: "context" | "reference" | "note";
|
|
467
|
+
topic_id?: string;
|
|
468
468
|
sort_by?: "title" | "created_at" | "updated_at";
|
|
469
469
|
order?: "desc" | "asc";
|
|
470
470
|
}, {
|
|
471
471
|
tags?: string[];
|
|
472
472
|
limit?: number;
|
|
473
|
-
topic_id?: string;
|
|
474
|
-
memory_type?: "context" | "reference" | "note";
|
|
475
473
|
offset?: number;
|
|
474
|
+
memory_type?: "context" | "reference" | "note";
|
|
475
|
+
topic_id?: string;
|
|
476
476
|
sort_by?: "title" | "created_at" | "updated_at";
|
|
477
477
|
order?: "desc" | "asc";
|
|
478
478
|
}>;
|
|
@@ -579,14 +579,14 @@ export declare const MCPSchemas: {
|
|
|
579
579
|
scope: z.ZodDefault<z.ZodEnum<["user", "global"]>>;
|
|
580
580
|
}, "strip", z.ZodTypeAny, {
|
|
581
581
|
value?: any;
|
|
582
|
-
key?: string;
|
|
583
582
|
action?: "get" | "set" | "reset";
|
|
584
583
|
scope?: "user" | "global";
|
|
584
|
+
key?: string;
|
|
585
585
|
}, {
|
|
586
586
|
value?: any;
|
|
587
|
-
key?: string;
|
|
588
587
|
action?: "get" | "set" | "reset";
|
|
589
588
|
scope?: "user" | "global";
|
|
589
|
+
key?: string;
|
|
590
590
|
}>;
|
|
591
591
|
};
|
|
592
592
|
operations: {
|
package/dist/utils/api.d.ts
CHANGED
|
@@ -54,11 +54,15 @@ export interface UpdateMemoryRequest {
|
|
|
54
54
|
metadata?: Record<string, unknown>;
|
|
55
55
|
}
|
|
56
56
|
export interface GetMemoriesParams {
|
|
57
|
+
page?: number;
|
|
57
58
|
limit?: number;
|
|
58
59
|
offset?: number;
|
|
59
60
|
memory_type?: MemoryType;
|
|
60
61
|
tags?: string[] | string;
|
|
61
62
|
topic_id?: string;
|
|
63
|
+
user_id?: string;
|
|
64
|
+
sort?: 'created_at' | 'updated_at' | 'last_accessed' | 'access_count' | 'title';
|
|
65
|
+
order?: 'asc' | 'desc';
|
|
62
66
|
sort_by?: 'created_at' | 'updated_at' | 'last_accessed' | 'access_count';
|
|
63
67
|
sort_order?: 'asc' | 'desc';
|
|
64
68
|
}
|
|
@@ -148,6 +152,8 @@ export interface ApiErrorResponse {
|
|
|
148
152
|
export declare class APIClient {
|
|
149
153
|
private client;
|
|
150
154
|
private config;
|
|
155
|
+
private normalizeMemoryEntry;
|
|
156
|
+
private shouldUseLegacyMemoryRpcFallback;
|
|
151
157
|
constructor();
|
|
152
158
|
login(email: string, password: string): Promise<AuthResponse>;
|
|
153
159
|
register(email: string, password: string, organizationName?: string): Promise<AuthResponse>;
|
package/dist/utils/api.js
CHANGED
|
@@ -5,6 +5,41 @@ import { CLIConfig } from './config.js';
|
|
|
5
5
|
export class APIClient {
|
|
6
6
|
client;
|
|
7
7
|
config;
|
|
8
|
+
normalizeMemoryEntry(payload) {
|
|
9
|
+
// API responses are inconsistent across gateways:
|
|
10
|
+
// - Some return the memory entry directly
|
|
11
|
+
// - Some wrap it in `{ data: <memory>, message?: string }`
|
|
12
|
+
if (payload && typeof payload === 'object') {
|
|
13
|
+
const obj = payload;
|
|
14
|
+
const directId = obj.id;
|
|
15
|
+
if (typeof directId === 'string' && directId.length > 0) {
|
|
16
|
+
return payload;
|
|
17
|
+
}
|
|
18
|
+
const data = obj.data;
|
|
19
|
+
if (data && typeof data === 'object') {
|
|
20
|
+
const dataObj = data;
|
|
21
|
+
if (typeof dataObj.id === 'string' && dataObj.id.length > 0) {
|
|
22
|
+
return data;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return payload;
|
|
27
|
+
}
|
|
28
|
+
shouldUseLegacyMemoryRpcFallback(error) {
|
|
29
|
+
const status = error?.response?.status;
|
|
30
|
+
const errorData = error?.response?.data;
|
|
31
|
+
const message = `${errorData?.error || ''} ${errorData?.message || ''}`.toLowerCase();
|
|
32
|
+
if (status === 405) {
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
if (status === 400 && message.includes('memory id is required')) {
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
if (status === 400 && message.includes('method not allowed')) {
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
8
43
|
constructor() {
|
|
9
44
|
this.config = new CLIConfig();
|
|
10
45
|
this.client = axios.create({
|
|
@@ -13,6 +48,8 @@ export class APIClient {
|
|
|
13
48
|
// Setup request interceptor to add auth token and headers
|
|
14
49
|
this.client.interceptors.request.use(async (config) => {
|
|
15
50
|
await this.config.init();
|
|
51
|
+
// Keep OAuth sessions alive automatically (prevents intermittent "auth required" cutouts).
|
|
52
|
+
await this.config.refreshTokenIfNeeded();
|
|
16
53
|
// Service Discovery
|
|
17
54
|
await this.config.discoverServices();
|
|
18
55
|
// Use appropriate base URL based on endpoint and auth method
|
|
@@ -20,17 +57,28 @@ export class APIClient {
|
|
|
20
57
|
const discoveredServices = this.config.get('discoveredServices');
|
|
21
58
|
const authMethod = this.config.get('authMethod');
|
|
22
59
|
const vendorKey = await this.config.getVendorKeyAsync();
|
|
60
|
+
const token = this.config.getToken();
|
|
61
|
+
const isMemoryEndpoint = typeof config.url === 'string' && config.url.startsWith('/api/v1/memories');
|
|
62
|
+
const forceApiFromEnv = process.env.LANONASIS_FORCE_API === 'true'
|
|
63
|
+
|| process.env.CLI_FORCE_API === 'true'
|
|
64
|
+
|| process.env.ONASIS_FORCE_API === 'true';
|
|
65
|
+
const forceApiFromConfig = this.config.get('forceApi') === true
|
|
66
|
+
|| this.config.get('connectionTransport') === 'api';
|
|
67
|
+
// Memory CRUD/search endpoints should always use the API gateway path.
|
|
68
|
+
const forceDirectApi = forceApiFromEnv || forceApiFromConfig || isMemoryEndpoint;
|
|
69
|
+
const prefersTokenAuth = Boolean(token) && (authMethod === 'jwt' || authMethod === 'oauth' || authMethod === 'oauth2');
|
|
70
|
+
const useVendorKeyAuth = Boolean(vendorKey) && !prefersTokenAuth;
|
|
23
71
|
// Determine the correct API base URL:
|
|
24
72
|
// - Auth endpoints -> auth.lanonasis.com
|
|
25
73
|
// - JWT auth (no vendor key) -> mcp.lanonasis.com (supports JWT tokens)
|
|
26
74
|
// - Vendor key auth -> api.lanonasis.com (requires vendor key)
|
|
27
75
|
let apiBaseUrl;
|
|
28
|
-
const useMcpServer = !
|
|
76
|
+
const useMcpServer = !forceDirectApi && prefersTokenAuth && !useVendorKeyAuth;
|
|
29
77
|
if (isAuthEndpoint) {
|
|
30
78
|
apiBaseUrl = discoveredServices?.auth_base || 'https://auth.lanonasis.com';
|
|
31
79
|
}
|
|
32
|
-
else if (
|
|
33
|
-
//
|
|
80
|
+
else if (forceDirectApi) {
|
|
81
|
+
// Force direct REST API mode to bypass MCP routing for troubleshooting.
|
|
34
82
|
apiBaseUrl = this.config.getApiUrl();
|
|
35
83
|
}
|
|
36
84
|
else if (useMcpServer) {
|
|
@@ -51,8 +99,22 @@ export class APIClient {
|
|
|
51
99
|
config.headers['X-Project-Scope'] = 'lanonasis-maas';
|
|
52
100
|
}
|
|
53
101
|
// Enhanced Authentication Support
|
|
54
|
-
|
|
55
|
-
|
|
102
|
+
// Even in forced direct-API mode, prefer bearer token auth when available.
|
|
103
|
+
// This avoids accidentally sending an OAuth access token as X-API-Key (we store it
|
|
104
|
+
// in secure storage for MCP/WebSocket usage), which can cause 401s.
|
|
105
|
+
const preferVendorKeyInDirectApiMode = forceDirectApi && Boolean(vendorKey) && !prefersTokenAuth;
|
|
106
|
+
if (preferVendorKeyInDirectApiMode) {
|
|
107
|
+
// Vendor key authentication (validated server-side)
|
|
108
|
+
// Send raw key - server handles hashing for comparison
|
|
109
|
+
config.headers['X-API-Key'] = vendorKey;
|
|
110
|
+
config.headers['X-Auth-Method'] = 'vendor_key';
|
|
111
|
+
}
|
|
112
|
+
else if (prefersTokenAuth) {
|
|
113
|
+
// JWT/OAuth token authentication takes precedence when both are present.
|
|
114
|
+
config.headers.Authorization = `Bearer ${token}`;
|
|
115
|
+
config.headers['X-Auth-Method'] = 'jwt';
|
|
116
|
+
}
|
|
117
|
+
else if (vendorKey) {
|
|
56
118
|
// Vendor key authentication (validated server-side)
|
|
57
119
|
// Send raw key - server handles hashing for comparison
|
|
58
120
|
config.headers['X-API-Key'] = vendorKey;
|
|
@@ -69,7 +131,10 @@ export class APIClient {
|
|
|
69
131
|
// Add project scope for Golden Contract compliance
|
|
70
132
|
config.headers['X-Project-Scope'] = 'lanonasis-maas';
|
|
71
133
|
if (process.env.CLI_VERBOSE === 'true') {
|
|
134
|
+
const transportMode = forceDirectApi ? 'api-forced' : (useMcpServer ? 'mcp-http' : 'api');
|
|
135
|
+
config.headers['X-Transport-Mode'] = transportMode;
|
|
72
136
|
console.log(chalk.dim(`→ ${config.method?.toUpperCase()} ${config.url} [${requestId}]`));
|
|
137
|
+
console.log(chalk.dim(` transport=${transportMode} baseURL=${config.baseURL}`));
|
|
73
138
|
}
|
|
74
139
|
return config;
|
|
75
140
|
});
|
|
@@ -84,7 +149,7 @@ export class APIClient {
|
|
|
84
149
|
const { status, data } = error.response;
|
|
85
150
|
if (status === 401) {
|
|
86
151
|
console.error(chalk.red('✖ Authentication failed'));
|
|
87
|
-
console.log(chalk.yellow('Please run:'), chalk.white('
|
|
152
|
+
console.log(chalk.yellow('Please run:'), chalk.white('lanonasis auth login'));
|
|
88
153
|
process.exit(1);
|
|
89
154
|
}
|
|
90
155
|
if (status === 403) {
|
|
@@ -127,22 +192,207 @@ export class APIClient {
|
|
|
127
192
|
// All memory endpoints use /api/v1/memories path (plural, per REST conventions)
|
|
128
193
|
async createMemory(data) {
|
|
129
194
|
const response = await this.client.post('/api/v1/memories', data);
|
|
130
|
-
return response.data;
|
|
195
|
+
return this.normalizeMemoryEntry(response.data);
|
|
131
196
|
}
|
|
132
197
|
async getMemories(params = {}) {
|
|
133
|
-
|
|
134
|
-
|
|
198
|
+
try {
|
|
199
|
+
const response = await this.client.get('/api/v1/memories', { params });
|
|
200
|
+
return response.data;
|
|
201
|
+
}
|
|
202
|
+
catch (error) {
|
|
203
|
+
// Backward-compatible fallback: newer API contracts may reject GET list.
|
|
204
|
+
if (error?.response?.status === 405) {
|
|
205
|
+
const limit = Number(params.limit || 20);
|
|
206
|
+
const page = Number(params.page || 1);
|
|
207
|
+
const offset = Number(params.offset ?? Math.max(0, (page - 1) * limit));
|
|
208
|
+
// Preferred fallback: POST list endpoint (avoids triggering vector search for plain listings).
|
|
209
|
+
const listPayload = {
|
|
210
|
+
limit,
|
|
211
|
+
offset
|
|
212
|
+
};
|
|
213
|
+
if (params.memory_type) {
|
|
214
|
+
listPayload.memory_type = params.memory_type;
|
|
215
|
+
}
|
|
216
|
+
if (params.tags) {
|
|
217
|
+
listPayload.tags = Array.isArray(params.tags)
|
|
218
|
+
? params.tags
|
|
219
|
+
: String(params.tags).split(',').map((tag) => tag.trim()).filter(Boolean);
|
|
220
|
+
}
|
|
221
|
+
if (params.topic_id) {
|
|
222
|
+
listPayload.topic_id = params.topic_id;
|
|
223
|
+
}
|
|
224
|
+
if (params.user_id) {
|
|
225
|
+
listPayload.user_id = params.user_id;
|
|
226
|
+
}
|
|
227
|
+
if (params.sort || params.sort_by) {
|
|
228
|
+
listPayload.sort_by = params.sort_by || params.sort;
|
|
229
|
+
}
|
|
230
|
+
if (params.order || params.sort_order) {
|
|
231
|
+
listPayload.sort_order = params.sort_order || params.order;
|
|
232
|
+
}
|
|
233
|
+
for (const endpoint of ['/api/v1/memories/list', '/api/v1/memory/list']) {
|
|
234
|
+
try {
|
|
235
|
+
const listResponse = await this.client.post(endpoint, listPayload);
|
|
236
|
+
const payload = listResponse.data || {};
|
|
237
|
+
const resultsArray = Array.isArray(payload.data)
|
|
238
|
+
? payload.data
|
|
239
|
+
: Array.isArray(payload.memories)
|
|
240
|
+
? payload.memories
|
|
241
|
+
: Array.isArray(payload.results)
|
|
242
|
+
? payload.results
|
|
243
|
+
: [];
|
|
244
|
+
const memories = resultsArray.map((entry) => this.normalizeMemoryEntry(entry));
|
|
245
|
+
const pagination = (payload.pagination && typeof payload.pagination === 'object')
|
|
246
|
+
? payload.pagination
|
|
247
|
+
: {};
|
|
248
|
+
const total = Number.isFinite(Number(pagination.total))
|
|
249
|
+
? Number(pagination.total)
|
|
250
|
+
: Number.isFinite(Number(payload.total))
|
|
251
|
+
? Number(payload.total)
|
|
252
|
+
: memories.length;
|
|
253
|
+
const pages = Number.isFinite(Number(pagination.total_pages))
|
|
254
|
+
? Number(pagination.total_pages)
|
|
255
|
+
: Number.isFinite(Number(pagination.pages))
|
|
256
|
+
? Number(pagination.pages)
|
|
257
|
+
: Math.max(1, Math.ceil(total / limit));
|
|
258
|
+
const currentPage = Number.isFinite(Number(pagination.page))
|
|
259
|
+
? Number(pagination.page)
|
|
260
|
+
: Math.max(1, Math.floor(offset / limit) + 1);
|
|
261
|
+
const hasMore = typeof pagination.has_more === 'boolean'
|
|
262
|
+
? pagination.has_more
|
|
263
|
+
: typeof pagination.has_next === 'boolean'
|
|
264
|
+
? pagination.has_next
|
|
265
|
+
: (offset + memories.length) < total;
|
|
266
|
+
return {
|
|
267
|
+
...payload,
|
|
268
|
+
data: memories,
|
|
269
|
+
memories,
|
|
270
|
+
pagination: {
|
|
271
|
+
total,
|
|
272
|
+
limit,
|
|
273
|
+
offset,
|
|
274
|
+
has_more: hasMore,
|
|
275
|
+
page: currentPage,
|
|
276
|
+
pages
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
catch (listError) {
|
|
281
|
+
if (listError?.response?.status === 404 || listError?.response?.status === 405) {
|
|
282
|
+
continue;
|
|
283
|
+
}
|
|
284
|
+
throw listError;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
// Secondary fallback: search endpoint for legacy contracts that expose only search.
|
|
288
|
+
const searchPayload = {
|
|
289
|
+
query: '*',
|
|
290
|
+
limit,
|
|
291
|
+
threshold: 0
|
|
292
|
+
};
|
|
293
|
+
if (params.memory_type) {
|
|
294
|
+
searchPayload.memory_types = [params.memory_type];
|
|
295
|
+
}
|
|
296
|
+
if (params.tags) {
|
|
297
|
+
searchPayload.tags = Array.isArray(params.tags)
|
|
298
|
+
? params.tags
|
|
299
|
+
: String(params.tags).split(',').map((tag) => tag.trim()).filter(Boolean);
|
|
300
|
+
}
|
|
301
|
+
if (params.topic_id) {
|
|
302
|
+
searchPayload.topic_id = params.topic_id;
|
|
303
|
+
}
|
|
304
|
+
if (offset > 0) {
|
|
305
|
+
searchPayload.offset = offset;
|
|
306
|
+
}
|
|
307
|
+
const fallback = await this.client.post('/api/v1/memories/search', searchPayload);
|
|
308
|
+
const payload = fallback.data || {};
|
|
309
|
+
const resultsArray = Array.isArray(payload.data)
|
|
310
|
+
? payload.data
|
|
311
|
+
: Array.isArray(payload.results)
|
|
312
|
+
? payload.results
|
|
313
|
+
: [];
|
|
314
|
+
const memories = resultsArray
|
|
315
|
+
.map((entry) => {
|
|
316
|
+
// Some gateways/search endpoints wrap results as `{ data: <memory> }`.
|
|
317
|
+
if (entry && typeof entry === 'object') {
|
|
318
|
+
const obj = entry;
|
|
319
|
+
const data = obj.data;
|
|
320
|
+
if (data && typeof data === 'object') {
|
|
321
|
+
const dataObj = data;
|
|
322
|
+
if (typeof dataObj.id === 'string' && dataObj.id.length > 0) {
|
|
323
|
+
return data;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return entry;
|
|
328
|
+
})
|
|
329
|
+
.map((entry) => this.normalizeMemoryEntry(entry));
|
|
330
|
+
const total = Number.isFinite(payload.total) ? Number(payload.total) : memories.length;
|
|
331
|
+
const pages = Math.max(1, Math.ceil(total / limit));
|
|
332
|
+
const currentPage = Math.max(1, Math.floor(offset / limit) + 1);
|
|
333
|
+
return {
|
|
334
|
+
...payload,
|
|
335
|
+
data: memories,
|
|
336
|
+
memories,
|
|
337
|
+
pagination: {
|
|
338
|
+
total,
|
|
339
|
+
limit,
|
|
340
|
+
offset,
|
|
341
|
+
has_more: (offset + memories.length) < total,
|
|
342
|
+
page: currentPage,
|
|
343
|
+
pages
|
|
344
|
+
}
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
throw error;
|
|
348
|
+
}
|
|
135
349
|
}
|
|
136
350
|
async getMemory(id) {
|
|
137
|
-
|
|
138
|
-
|
|
351
|
+
try {
|
|
352
|
+
const response = await this.client.get(`/api/v1/memories/${id}`);
|
|
353
|
+
return this.normalizeMemoryEntry(response.data);
|
|
354
|
+
}
|
|
355
|
+
catch (error) {
|
|
356
|
+
if (this.shouldUseLegacyMemoryRpcFallback(error)) {
|
|
357
|
+
const fallback = await this.client.post('/api/v1/memory/get', { id });
|
|
358
|
+
const payload = fallback.data && typeof fallback.data === 'object'
|
|
359
|
+
? fallback.data.data ?? fallback.data
|
|
360
|
+
: fallback.data;
|
|
361
|
+
return this.normalizeMemoryEntry(payload);
|
|
362
|
+
}
|
|
363
|
+
throw error;
|
|
364
|
+
}
|
|
139
365
|
}
|
|
140
366
|
async updateMemory(id, data) {
|
|
141
|
-
|
|
142
|
-
|
|
367
|
+
try {
|
|
368
|
+
const response = await this.client.put(`/api/v1/memories/${id}`, data);
|
|
369
|
+
return this.normalizeMemoryEntry(response.data);
|
|
370
|
+
}
|
|
371
|
+
catch (error) {
|
|
372
|
+
if (this.shouldUseLegacyMemoryRpcFallback(error)) {
|
|
373
|
+
const fallback = await this.client.post('/api/v1/memory/update', {
|
|
374
|
+
id,
|
|
375
|
+
...data
|
|
376
|
+
});
|
|
377
|
+
const payload = fallback.data && typeof fallback.data === 'object'
|
|
378
|
+
? fallback.data.data ?? fallback.data
|
|
379
|
+
: fallback.data;
|
|
380
|
+
return this.normalizeMemoryEntry(payload);
|
|
381
|
+
}
|
|
382
|
+
throw error;
|
|
383
|
+
}
|
|
143
384
|
}
|
|
144
385
|
async deleteMemory(id) {
|
|
145
|
-
|
|
386
|
+
try {
|
|
387
|
+
await this.client.delete(`/api/v1/memories/${id}`);
|
|
388
|
+
}
|
|
389
|
+
catch (error) {
|
|
390
|
+
if (this.shouldUseLegacyMemoryRpcFallback(error)) {
|
|
391
|
+
await this.client.post('/api/v1/memory/delete', { id });
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
throw error;
|
|
395
|
+
}
|
|
146
396
|
}
|
|
147
397
|
async searchMemories(query, options = {}) {
|
|
148
398
|
const response = await this.client.post('/api/v1/memories/search', {
|
package/dist/utils/config.d.ts
CHANGED
|
@@ -34,6 +34,12 @@ interface CLIConfigData {
|
|
|
34
34
|
lastAuthFailure?: string | undefined;
|
|
35
35
|
[key: string]: unknown;
|
|
36
36
|
}
|
|
37
|
+
export type RemoteAuthVerification = {
|
|
38
|
+
valid: boolean;
|
|
39
|
+
method: 'token' | 'vendor_key' | 'none';
|
|
40
|
+
endpoint?: string;
|
|
41
|
+
reason?: string;
|
|
42
|
+
};
|
|
37
43
|
export declare class CLIConfig {
|
|
38
44
|
private configDir;
|
|
39
45
|
private configPath;
|
|
@@ -70,6 +76,11 @@ export declare class CLIConfig {
|
|
|
70
76
|
private resolveFallbackEndpoints;
|
|
71
77
|
private logFallbackUsage;
|
|
72
78
|
private pingAuthHealth;
|
|
79
|
+
private getAuthVerificationEndpoints;
|
|
80
|
+
private extractAuthErrorMessage;
|
|
81
|
+
private verifyTokenWithAuthGateway;
|
|
82
|
+
private verifyVendorKeyWithAuthGateway;
|
|
83
|
+
verifyCurrentCredentialsWithServer(): Promise<RemoteAuthVerification>;
|
|
73
84
|
setManualEndpoints(endpoints: Partial<CLIConfigData['discoveredServices']>): Promise<void>;
|
|
74
85
|
hasManualEndpointOverrides(): boolean;
|
|
75
86
|
clearManualEndpointOverrides(): Promise<void>;
|