@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/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Changelog - @lanonasis/cli
|
|
2
2
|
|
|
3
|
+
## [3.9.6] - 2026-02-21
|
|
4
|
+
|
|
5
|
+
### š Bug Fixes
|
|
6
|
+
|
|
7
|
+
- **Reliable Memory Auth Routing**: Memory CRUD and search operations now consistently route through the API gateway (`https://api.lanonasis.com`) to avoid MCP endpoint contract mismatches.
|
|
8
|
+
- **Legacy Endpoint Compatibility**: Added fallback support for deployments that still expose RPC-style memory routes (`/api/v1/memory/*`) when REST routes return `400/405`.
|
|
9
|
+
- **Auth Status Accuracy**: `status` now validates live auth state against the auth verify endpoint before reporting authenticated session state.
|
|
10
|
+
- **OAuth Session Stability**: Requests proactively refresh OAuth/JWT sessions to reduce intermittent `memory login required` errors during long-running CLI usage.
|
|
11
|
+
- **Response Normalization**: Memory get/list/search handlers normalize wrapped gateway responses (`{ data: ... }`) for consistent CLI behavior across environments.
|
|
12
|
+
|
|
13
|
+
### š Documentation
|
|
14
|
+
|
|
15
|
+
- Clarified auth flow behavior for vendor keys and bearer tokens.
|
|
16
|
+
- Added release notes for endpoint override guidance and memory transport behavior.
|
|
17
|
+
|
|
3
18
|
## [3.9.3] - 2026-02-02
|
|
4
19
|
|
|
5
20
|
### ⨠Features
|
package/README.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
# @lanonasis/cli v3.9.
|
|
1
|
+
# @lanonasis/cli v3.9.6 - Auth Routing & Memory Reliability
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/@lanonasis/cli)
|
|
4
4
|
[](https://www.npmjs.com/package/@lanonasis/cli)
|
|
5
5
|
[](https://opensource.org/licenses/MIT)
|
|
6
6
|
[](https://api.lanonasis.com/.well-known/onasis.json)
|
|
7
7
|
|
|
8
|
-
š **NEW IN v3.9.
|
|
8
|
+
š **NEW IN v3.9.6**: Fixed memory auth routing to the API gateway, added compatibility fallbacks for legacy memory endpoints, improved auth status verification, and stabilized OAuth/JWT refresh behavior during CLI memory operations.
|
|
9
9
|
|
|
10
10
|
## š Quick Start
|
|
11
11
|
|
|
@@ -129,23 +129,19 @@ maas memory list
|
|
|
129
129
|
|
|
130
130
|
## š Security & Authentication
|
|
131
131
|
|
|
132
|
-
### Enterprise-Grade
|
|
132
|
+
### Enterprise-Grade API Key Handling
|
|
133
133
|
|
|
134
|
-
|
|
134
|
+
The CLI uses secure local storage and sends credentials in the expected wire format:
|
|
135
135
|
|
|
136
|
-
- ā
**
|
|
137
|
-
- ā
**
|
|
138
|
-
- ā
**
|
|
139
|
-
- ā
**
|
|
140
|
-
|
|
141
|
-
```typescript
|
|
142
|
-
// Hash utilities are built-in and automatic
|
|
143
|
-
// Your vendor keys are automatically secured
|
|
144
|
-
onasis login --vendor-key pk_xxxxx.sk_xxxxx // ā
Automatically hashed
|
|
145
|
-
```
|
|
136
|
+
- ā
**Encrypted At Rest**: Vendor keys are stored in encrypted local storage (keytar when available, encrypted file fallback otherwise).
|
|
137
|
+
- ā
**Correct On-Wire Format**: Vendor key auth sends the raw vendor key in `X-API-Key` over HTTPS.
|
|
138
|
+
- ā
**Single Server-Side Hash Validation**: The API validates keys with server-side hashing and does not require client-side hashing.
|
|
139
|
+
- ā
**Token-First Sessions**: OAuth/JWT sessions use `Authorization: Bearer <token>` and refresh automatically before expiry.
|
|
146
140
|
|
|
147
141
|
### Authentication Methods
|
|
148
142
|
|
|
143
|
+
> Transport note: for memory commands, keep `manualEndpointOverrides=false` so requests route through `https://api.lanonasis.com`.
|
|
144
|
+
|
|
149
145
|
### 1. Vendor Key Authentication (Recommended)
|
|
150
146
|
|
|
151
147
|
Best for API integrations and automation. Copy the vendor key value exactly as shown in your LanOnasis dashboard (keys may vary in format):
|
|
@@ -242,6 +238,12 @@ onasis memory list --type context --limit 20
|
|
|
242
238
|
onasis memory create -t "Project Notes" -c "Important information"
|
|
243
239
|
onasis memory create -t "Reference" --type reference --tags "docs,api"
|
|
244
240
|
|
|
241
|
+
# Create memory via JSON payload
|
|
242
|
+
onasis memory create --json '{"title":"Design decisions","type":"project","content":"Summary...","tags":["architecture","design"]}'
|
|
243
|
+
|
|
244
|
+
# Create memory from a file
|
|
245
|
+
onasis memory create -t "Session notes" --content-file ./notes.md
|
|
246
|
+
|
|
245
247
|
# Create memories (interactive)
|
|
246
248
|
onasis memory create -i # Interactive mode with inline editor
|
|
247
249
|
onasis memory create # Prompts for missing fields
|
|
@@ -258,6 +260,34 @@ onasis memory delete <id> # Delete memory
|
|
|
258
260
|
onasis memory stats # Memory statistics
|
|
259
261
|
```
|
|
260
262
|
|
|
263
|
+
#### `onasis memory save-session`
|
|
264
|
+
|
|
265
|
+
Save the current CLI session context (CWD + git branch/status + changed files) as a memory entry so you can persist what you worked on and pick it up later.
|
|
266
|
+
|
|
267
|
+
- `--test-summary`: Stores a human-readable test result summary (e.g., `Vitest: 53 passed, 1 skipped`) in the saved session memory.
|
|
268
|
+
- `--title`: Sets the memory title (default: `Session summary`).
|
|
269
|
+
- `--type`: Sets the memory type (default: `project`).
|
|
270
|
+
- `--tags`: Comma-separated tags for session metadata (default: `session,cli`).
|
|
271
|
+
|
|
272
|
+
**Examples**
|
|
273
|
+
```bash
|
|
274
|
+
onasis memory save-session --test-summary "Vitest: 53 passed, 1 skipped"
|
|
275
|
+
onasis memory save-session --title "API client fixes" --type project --tags "session,cli,testing"
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
See **Session management** below for related commands.
|
|
279
|
+
|
|
280
|
+
#### Session management
|
|
281
|
+
|
|
282
|
+
Sessions are stored as memory entries (tagged `session,cli` by default). Related commands:
|
|
283
|
+
|
|
284
|
+
```bash
|
|
285
|
+
onasis memory save-session
|
|
286
|
+
onasis memory list-sessions
|
|
287
|
+
onasis memory load-session <id>
|
|
288
|
+
onasis memory delete-session <id>
|
|
289
|
+
```
|
|
290
|
+
|
|
261
291
|
**Create/Update Options:**
|
|
262
292
|
| Short | Long | Description |
|
|
263
293
|
|-------|------|-------------|
|
|
@@ -266,6 +296,8 @@ onasis memory stats # Memory statistics
|
|
|
266
296
|
| `-i` | `--interactive` | Interactive mode |
|
|
267
297
|
| | `--type` | Memory type (context, project, knowledge, etc.) |
|
|
268
298
|
| | `--tags` | Comma-separated tags |
|
|
299
|
+
| | `--json` | JSON payload (title, content, type/memory_type, tags, topic_id) |
|
|
300
|
+
| | `--content-file` | Read content from a file |
|
|
269
301
|
|
|
270
302
|
### Topic Management
|
|
271
303
|
|
|
@@ -637,4 +669,3 @@ The CLI will show the authorization URL - copy and paste it into your browser ma
|
|
|
637
669
|
|
|
638
670
|
**Token refresh failed:**
|
|
639
671
|
Run `onasis auth login` to re-authenticate.
|
|
640
|
-
|
package/dist/commands/config.js
CHANGED
|
@@ -11,11 +11,13 @@ export function configCommands(program) {
|
|
|
11
11
|
.action(async (key, value) => {
|
|
12
12
|
const config = new CLIConfig();
|
|
13
13
|
await config.init();
|
|
14
|
+
let shouldSave = true;
|
|
14
15
|
// Handle special cases
|
|
15
16
|
switch (key) {
|
|
16
17
|
case 'api-url':
|
|
17
18
|
await config.setApiUrl(value);
|
|
18
19
|
console.log(chalk.green('ā API URL updated:'), value);
|
|
20
|
+
shouldSave = false; // setApiUrl already persists
|
|
19
21
|
break;
|
|
20
22
|
case 'ai-integration':
|
|
21
23
|
if (value === 'claude-mcp') {
|
|
@@ -42,11 +44,19 @@ export function configCommands(program) {
|
|
|
42
44
|
config.set('mcpServerUrl', value);
|
|
43
45
|
console.log(chalk.green('ā MCP server URL updated:'), value);
|
|
44
46
|
break;
|
|
47
|
+
case 'force-api':
|
|
48
|
+
config.set('forceApi', value === 'true');
|
|
49
|
+
config.set('connectionTransport', value === 'true' ? 'api' : 'auto');
|
|
50
|
+
console.log(chalk.green('ā Force direct API mode:'), value === 'true' ? 'enabled' : 'disabled');
|
|
51
|
+
break;
|
|
45
52
|
default:
|
|
46
53
|
// Generic config set
|
|
47
54
|
config.set(key, value);
|
|
48
55
|
console.log(chalk.green(`ā ${key} set to:`), value);
|
|
49
56
|
}
|
|
57
|
+
if (shouldSave) {
|
|
58
|
+
await config.save();
|
|
59
|
+
}
|
|
50
60
|
});
|
|
51
61
|
// Generic config get command
|
|
52
62
|
program
|
|
@@ -101,6 +111,7 @@ export function configCommands(program) {
|
|
|
101
111
|
{ key: 'mcp-use-remote', description: 'Use remote MCP server', current: config.get('mcpUseRemote') || false },
|
|
102
112
|
{ key: 'mcp-server-path', description: 'Local MCP server path', current: config.get('mcpServerPath') || 'default' },
|
|
103
113
|
{ key: 'mcp-server-url', description: 'Remote MCP server URL', current: config.get('mcpServerUrl') || 'https://mcp.lanonasis.com' },
|
|
114
|
+
{ key: 'force-api', description: 'Force direct API transport', current: config.get('forceApi') || false },
|
|
104
115
|
{ key: 'mcpEnabled', description: 'MCP integration enabled', current: config.get('mcpEnabled') || false }
|
|
105
116
|
];
|
|
106
117
|
configOptions.forEach(opt => {
|
package/dist/commands/memory.js
CHANGED
|
@@ -8,6 +8,30 @@ import { apiClient } from '../utils/api.js';
|
|
|
8
8
|
import { formatBytes, truncateText } from '../utils/formatting.js';
|
|
9
9
|
import { CLIConfig } from '../utils/config.js';
|
|
10
10
|
import { createTextInputHandler } from '../ux/index.js';
|
|
11
|
+
import * as fs from 'fs/promises';
|
|
12
|
+
import { exec as execCb } from 'node:child_process';
|
|
13
|
+
import { promisify } from 'node:util';
|
|
14
|
+
const exec = promisify(execCb);
|
|
15
|
+
const MEMORY_TYPE_CHOICES = [
|
|
16
|
+
'context',
|
|
17
|
+
'project',
|
|
18
|
+
'knowledge',
|
|
19
|
+
'reference',
|
|
20
|
+
'personal',
|
|
21
|
+
'workflow',
|
|
22
|
+
];
|
|
23
|
+
const coerceMemoryType = (value) => {
|
|
24
|
+
if (typeof value !== 'string')
|
|
25
|
+
return undefined;
|
|
26
|
+
const normalized = value.trim().toLowerCase();
|
|
27
|
+
// Backward compatibility for older docs/examples.
|
|
28
|
+
if (normalized === 'conversation')
|
|
29
|
+
return 'context';
|
|
30
|
+
if (MEMORY_TYPE_CHOICES.includes(normalized)) {
|
|
31
|
+
return normalized;
|
|
32
|
+
}
|
|
33
|
+
return undefined;
|
|
34
|
+
};
|
|
11
35
|
const resolveInputMode = async () => {
|
|
12
36
|
const config = new CLIConfig();
|
|
13
37
|
await config.init();
|
|
@@ -41,13 +65,53 @@ export function memoryCommands(program) {
|
|
|
41
65
|
.description('Create a new memory entry')
|
|
42
66
|
.option('-t, --title <title>', 'memory title')
|
|
43
67
|
.option('-c, --content <content>', 'memory content')
|
|
44
|
-
.option('--type <type>',
|
|
68
|
+
.option('--type <type>', `memory type (${MEMORY_TYPE_CHOICES.join(', ')})`)
|
|
45
69
|
.option('--tags <tags>', 'comma-separated tags')
|
|
46
70
|
.option('--topic-id <id>', 'topic ID')
|
|
47
71
|
.option('-i, --interactive', 'interactive mode')
|
|
72
|
+
.option('--json <json>', 'JSON payload (title, content, type/memory_type, tags[], topic_id)')
|
|
73
|
+
.option('--content-file <path>', 'Read memory content from a file (overrides --content)')
|
|
48
74
|
.action(async (options) => {
|
|
49
75
|
try {
|
|
50
|
-
let { title, content, type, tags, topicId, interactive } = options;
|
|
76
|
+
let { title, content, type, tags, topicId, interactive, json, contentFile } = options;
|
|
77
|
+
// 1) JSON payload (optional)
|
|
78
|
+
if (json) {
|
|
79
|
+
let parsed;
|
|
80
|
+
try {
|
|
81
|
+
parsed = JSON.parse(json);
|
|
82
|
+
}
|
|
83
|
+
catch (err) {
|
|
84
|
+
const msg = err instanceof Error ? err.message : 'Invalid JSON';
|
|
85
|
+
throw new Error(`Invalid --json payload: ${msg}`);
|
|
86
|
+
}
|
|
87
|
+
if (!title && typeof parsed.title === 'string')
|
|
88
|
+
title = parsed.title;
|
|
89
|
+
if (!content && typeof parsed.content === 'string')
|
|
90
|
+
content = parsed.content;
|
|
91
|
+
const parsedType = parsed.memory_type ?? parsed.type;
|
|
92
|
+
if (!type && parsedType !== undefined) {
|
|
93
|
+
const coerced = coerceMemoryType(parsedType);
|
|
94
|
+
if (!coerced) {
|
|
95
|
+
throw new Error(`Invalid memory type in --json payload. Expected one of: ${MEMORY_TYPE_CHOICES.join(', ')}`);
|
|
96
|
+
}
|
|
97
|
+
type = coerced;
|
|
98
|
+
}
|
|
99
|
+
if (!tags) {
|
|
100
|
+
if (Array.isArray(parsed.tags)) {
|
|
101
|
+
tags = parsed.tags.map((t) => String(t)).join(',');
|
|
102
|
+
}
|
|
103
|
+
else if (typeof parsed.tags === 'string') {
|
|
104
|
+
tags = parsed.tags;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
const parsedTopic = parsed.topic_id ?? parsed.topicId;
|
|
108
|
+
if (!topicId && typeof parsedTopic === 'string')
|
|
109
|
+
topicId = parsedTopic;
|
|
110
|
+
}
|
|
111
|
+
// 2) Content file (optional)
|
|
112
|
+
if (contentFile) {
|
|
113
|
+
content = await fs.readFile(contentFile, 'utf-8');
|
|
114
|
+
}
|
|
51
115
|
if (interactive || (!title || !content)) {
|
|
52
116
|
const inputMode = await resolveInputMode();
|
|
53
117
|
const answers = await inquirer.prompt([
|
|
@@ -62,7 +126,7 @@ export function memoryCommands(program) {
|
|
|
62
126
|
type: 'list',
|
|
63
127
|
name: 'type',
|
|
64
128
|
message: 'Memory type:',
|
|
65
|
-
choices: [
|
|
129
|
+
choices: [...MEMORY_TYPE_CHOICES],
|
|
66
130
|
default: type || 'context',
|
|
67
131
|
},
|
|
68
132
|
{
|
|
@@ -86,11 +150,12 @@ export function memoryCommands(program) {
|
|
|
86
150
|
if (!content || content.trim().length === 0) {
|
|
87
151
|
throw new Error('Content is required');
|
|
88
152
|
}
|
|
153
|
+
const resolvedType = type ?? 'context';
|
|
89
154
|
const spinner = ora('Creating memory...').start();
|
|
90
155
|
const memoryData = {
|
|
91
156
|
title,
|
|
92
157
|
content,
|
|
93
|
-
memory_type:
|
|
158
|
+
memory_type: resolvedType
|
|
94
159
|
};
|
|
95
160
|
if (tags) {
|
|
96
161
|
memoryData.tags = tags.split(',').map((tag) => tag.trim()).filter(Boolean);
|
|
@@ -115,6 +180,211 @@ export function memoryCommands(program) {
|
|
|
115
180
|
process.exit(1);
|
|
116
181
|
}
|
|
117
182
|
});
|
|
183
|
+
// Save current working session context as a memory
|
|
184
|
+
program
|
|
185
|
+
.command('save-session')
|
|
186
|
+
.description('Save current session context (git branch/status + optional test summary) as a memory')
|
|
187
|
+
.option('-t, --title <title>', 'memory title', 'Session summary')
|
|
188
|
+
.option('--type <type>', `memory type (${MEMORY_TYPE_CHOICES.join(', ')})`, 'project')
|
|
189
|
+
.option('--tags <tags>', 'comma-separated tags', 'session,cli')
|
|
190
|
+
.option('--test-summary <text>', 'Optional test summary to include')
|
|
191
|
+
.action(async (options) => {
|
|
192
|
+
try {
|
|
193
|
+
const spinner = ora('Collecting session info...').start();
|
|
194
|
+
const cwd = process.cwd();
|
|
195
|
+
const runGit = async (cmd) => {
|
|
196
|
+
try {
|
|
197
|
+
const { stdout } = await exec(cmd, { cwd });
|
|
198
|
+
return String(stdout || '').trim();
|
|
199
|
+
}
|
|
200
|
+
catch {
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
const branch = await runGit('git rev-parse --abbrev-ref HEAD');
|
|
205
|
+
const status = await runGit('git status --porcelain');
|
|
206
|
+
const changedFiles = status
|
|
207
|
+
? status
|
|
208
|
+
.split('\n')
|
|
209
|
+
.map((line) => line.trim())
|
|
210
|
+
.filter(Boolean)
|
|
211
|
+
.map((line) => line.replace(/^.. /, ''))
|
|
212
|
+
: [];
|
|
213
|
+
const lines = [];
|
|
214
|
+
lines.push(`# Session Summary`);
|
|
215
|
+
lines.push('');
|
|
216
|
+
lines.push(`- Date: ${new Date().toISOString()}`);
|
|
217
|
+
lines.push(`- CWD: ${cwd}`);
|
|
218
|
+
if (branch)
|
|
219
|
+
lines.push(`- Git branch: ${branch}`);
|
|
220
|
+
lines.push('');
|
|
221
|
+
lines.push('## Changes');
|
|
222
|
+
if (changedFiles.length === 0) {
|
|
223
|
+
lines.push('No uncommitted changes detected (or git not available).');
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
lines.push(changedFiles.map((f) => `- ${f}`).join('\n'));
|
|
227
|
+
}
|
|
228
|
+
lines.push('');
|
|
229
|
+
if (options.testSummary) {
|
|
230
|
+
lines.push('## Test Summary');
|
|
231
|
+
lines.push(options.testSummary);
|
|
232
|
+
lines.push('');
|
|
233
|
+
}
|
|
234
|
+
spinner.text = 'Saving session memory...';
|
|
235
|
+
const resolvedType = coerceMemoryType(options.type) ?? 'project';
|
|
236
|
+
const memoryData = {
|
|
237
|
+
title: options.title || 'Session summary',
|
|
238
|
+
content: lines.join('\n'),
|
|
239
|
+
memory_type: resolvedType
|
|
240
|
+
};
|
|
241
|
+
if (options.tags) {
|
|
242
|
+
memoryData.tags = options.tags.split(',').map((t) => t.trim()).filter(Boolean);
|
|
243
|
+
}
|
|
244
|
+
const memory = await apiClient.createMemory(memoryData);
|
|
245
|
+
spinner.succeed('Session saved');
|
|
246
|
+
console.log();
|
|
247
|
+
console.log(chalk.green('ā Memory created:'));
|
|
248
|
+
console.log(` ID: ${chalk.cyan(memory.id)}`);
|
|
249
|
+
console.log(` Title: ${memory.title}`);
|
|
250
|
+
console.log(` Type: ${memory.memory_type}`);
|
|
251
|
+
}
|
|
252
|
+
catch (error) {
|
|
253
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
254
|
+
console.error(chalk.red('ā Failed to save session:'), errorMessage);
|
|
255
|
+
process.exit(1);
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
// Session management helpers (sessions are stored as memory entries tagged `session,cli` by default)
|
|
259
|
+
program
|
|
260
|
+
.command('list-sessions')
|
|
261
|
+
.description('List saved CLI sessions (memories tagged session,cli by default)')
|
|
262
|
+
.option('-p, --page <page>', 'page number', '1')
|
|
263
|
+
.option('-l, --limit <limit>', 'number of entries per page', '20')
|
|
264
|
+
.option('--type <type>', 'filter by memory type', 'project')
|
|
265
|
+
.option('--tags <tags>', 'filter by tags (comma-separated)', 'session,cli')
|
|
266
|
+
.option('--sort <field>', 'sort by field (created_at, updated_at, title, last_accessed)', 'created_at')
|
|
267
|
+
.option('--order <order>', 'sort order (asc, desc)', 'desc')
|
|
268
|
+
.action(async (options) => {
|
|
269
|
+
try {
|
|
270
|
+
const spinner = ora('Fetching sessions...').start();
|
|
271
|
+
const params = {
|
|
272
|
+
page: parseInt(options.page || '1'),
|
|
273
|
+
limit: parseInt(options.limit || '20'),
|
|
274
|
+
sort: options.sort || 'created_at',
|
|
275
|
+
order: options.order || 'desc'
|
|
276
|
+
};
|
|
277
|
+
if (options.type)
|
|
278
|
+
params.memory_type = options.type;
|
|
279
|
+
if (options.tags)
|
|
280
|
+
params.tags = options.tags;
|
|
281
|
+
const result = await apiClient.getMemories(params);
|
|
282
|
+
spinner.stop();
|
|
283
|
+
const memories = result.memories || result.data || [];
|
|
284
|
+
if (memories.length === 0) {
|
|
285
|
+
console.log(chalk.yellow('No sessions found'));
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
console.log(chalk.blue.bold(`\nš Sessions (${result.pagination.total} total)`));
|
|
289
|
+
console.log(chalk.gray(`Page ${result.pagination.page || 1} of ${result.pagination.pages || Math.ceil(result.pagination.total / result.pagination.limit)}`));
|
|
290
|
+
console.log();
|
|
291
|
+
const outputFormat = process.env.CLI_OUTPUT_FORMAT || 'table';
|
|
292
|
+
if (outputFormat === 'json') {
|
|
293
|
+
console.log(JSON.stringify(result, null, 2));
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
const tableData = memories.map((memory) => [
|
|
297
|
+
truncateText(memory.title, 30),
|
|
298
|
+
memory.memory_type,
|
|
299
|
+
memory.tags.slice(0, 3).join(', '),
|
|
300
|
+
format(new Date(memory.created_at), 'MMM dd, yyyy'),
|
|
301
|
+
memory.access_count
|
|
302
|
+
]);
|
|
303
|
+
const tableConfig = {
|
|
304
|
+
header: ['Title', 'Type', 'Tags', 'Created', 'Access'],
|
|
305
|
+
columnDefault: {
|
|
306
|
+
width: 20,
|
|
307
|
+
wrapWord: true
|
|
308
|
+
},
|
|
309
|
+
columns: [
|
|
310
|
+
{ width: 30 },
|
|
311
|
+
{ width: 12 },
|
|
312
|
+
{ width: 20 },
|
|
313
|
+
{ width: 12 },
|
|
314
|
+
{ width: 8 }
|
|
315
|
+
]
|
|
316
|
+
};
|
|
317
|
+
console.log(table([tableConfig.header, ...tableData], {
|
|
318
|
+
columnDefault: tableConfig.columnDefault,
|
|
319
|
+
columns: tableConfig.columns
|
|
320
|
+
}));
|
|
321
|
+
if (result.pagination.pages > 1) {
|
|
322
|
+
console.log(chalk.gray(`\nUse --page ${result.pagination.page + 1} for next page`));
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
catch (error) {
|
|
326
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
327
|
+
console.error(chalk.red('ā Failed to list sessions:'), errorMessage);
|
|
328
|
+
process.exit(1);
|
|
329
|
+
}
|
|
330
|
+
});
|
|
331
|
+
program
|
|
332
|
+
.command('load-session')
|
|
333
|
+
.description('Load a saved session by memory ID (prints the saved session context)')
|
|
334
|
+
.argument('<id>', 'session memory ID')
|
|
335
|
+
.action(async (id) => {
|
|
336
|
+
try {
|
|
337
|
+
const spinner = ora('Loading session...').start();
|
|
338
|
+
const memory = await apiClient.getMemory(id);
|
|
339
|
+
spinner.stop();
|
|
340
|
+
const outputFormat = process.env.CLI_OUTPUT_FORMAT || 'text';
|
|
341
|
+
if (outputFormat === 'json') {
|
|
342
|
+
console.log(JSON.stringify(memory, null, 2));
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
console.log(chalk.blue.bold('\nš Session'));
|
|
346
|
+
console.log(chalk.gray(`${memory.title} (${memory.id})`));
|
|
347
|
+
console.log();
|
|
348
|
+
console.log(memory.content);
|
|
349
|
+
}
|
|
350
|
+
catch (error) {
|
|
351
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
352
|
+
console.error(chalk.red('ā Failed to load session:'), errorMessage);
|
|
353
|
+
process.exit(1);
|
|
354
|
+
}
|
|
355
|
+
});
|
|
356
|
+
program
|
|
357
|
+
.command('delete-session')
|
|
358
|
+
.description('Delete a saved session by memory ID')
|
|
359
|
+
.argument('<id>', 'session memory ID')
|
|
360
|
+
.option('-f, --force', 'skip confirmation')
|
|
361
|
+
.action(async (id, options) => {
|
|
362
|
+
try {
|
|
363
|
+
if (!options.force) {
|
|
364
|
+
const memory = await apiClient.getMemory(id);
|
|
365
|
+
const answer = await inquirer.prompt([
|
|
366
|
+
{
|
|
367
|
+
type: 'confirm',
|
|
368
|
+
name: 'confirm',
|
|
369
|
+
message: `Are you sure you want to delete session "${memory.title}"?`,
|
|
370
|
+
default: false
|
|
371
|
+
}
|
|
372
|
+
]);
|
|
373
|
+
if (!answer.confirm) {
|
|
374
|
+
console.log(chalk.yellow('Deletion cancelled'));
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
const spinner = ora('Deleting session...').start();
|
|
379
|
+
await apiClient.deleteMemory(id);
|
|
380
|
+
spinner.succeed('Session deleted successfully');
|
|
381
|
+
}
|
|
382
|
+
catch (error) {
|
|
383
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
384
|
+
console.error(chalk.red('ā Failed to delete session:'), errorMessage);
|
|
385
|
+
process.exit(1);
|
|
386
|
+
}
|
|
387
|
+
});
|
|
118
388
|
// List memories
|
|
119
389
|
program
|
|
120
390
|
.command('list')
|
|
@@ -294,7 +564,7 @@ export function memoryCommands(program) {
|
|
|
294
564
|
.argument('<id>', 'memory ID')
|
|
295
565
|
.option('-t, --title <title>', 'new title')
|
|
296
566
|
.option('-c, --content <content>', 'new content')
|
|
297
|
-
.option('--type <type>',
|
|
567
|
+
.option('--type <type>', `new memory type (${MEMORY_TYPE_CHOICES.join(', ')})`)
|
|
298
568
|
.option('--tags <tags>', 'new tags (comma-separated)')
|
|
299
569
|
.option('-i, --interactive', 'interactive mode')
|
|
300
570
|
.action(async (id, options) => {
|
|
@@ -317,7 +587,7 @@ export function memoryCommands(program) {
|
|
|
317
587
|
type: 'list',
|
|
318
588
|
name: 'type',
|
|
319
589
|
message: 'Memory type:',
|
|
320
|
-
choices: [
|
|
590
|
+
choices: [...MEMORY_TYPE_CHOICES],
|
|
321
591
|
default: currentMemory.memory_type,
|
|
322
592
|
},
|
|
323
593
|
{
|
package/dist/index-simple.js
CHANGED
|
@@ -64,12 +64,23 @@ program
|
|
|
64
64
|
process.env.MEMORY_API_URL = opts.apiUrl;
|
|
65
65
|
}
|
|
66
66
|
process.env.CLI_OUTPUT_FORMAT = opts.output;
|
|
67
|
+
const forceApiFromEnv = process.env.LANONASIS_FORCE_API === 'true' ||
|
|
68
|
+
process.env.CLI_FORCE_API === 'true' ||
|
|
69
|
+
process.env.ONASIS_FORCE_API === 'true';
|
|
70
|
+
const forceApiFromConfig = cliConfig.get('forceApi') === true ||
|
|
71
|
+
cliConfig.get('connectionTransport') === 'api';
|
|
72
|
+
const forceDirectApi = forceApiFromEnv || forceApiFromConfig || opts.mcp === false;
|
|
73
|
+
if (forceDirectApi) {
|
|
74
|
+
process.env.LANONASIS_FORCE_API = 'true';
|
|
75
|
+
}
|
|
67
76
|
// Auto-initialize MCP unless disabled
|
|
68
77
|
const isMcpFlow = actionCommand.name() === 'mcp' ||
|
|
69
78
|
actionCommand.parent?.name?.() === 'mcp' ||
|
|
70
79
|
actionCommand.name() === 'mcp-server' ||
|
|
71
80
|
actionCommand.parent?.name?.() === 'mcp-server';
|
|
72
|
-
|
|
81
|
+
const isConfigFlow = actionCommand.name() === 'config' ||
|
|
82
|
+
actionCommand.parent?.name?.() === 'config';
|
|
83
|
+
if (!forceDirectApi && !isMcpFlow && !isConfigFlow && !['init', 'auth', 'login', 'health', 'status'].includes(actionCommand.name())) {
|
|
73
84
|
try {
|
|
74
85
|
const client = getMCPClient();
|
|
75
86
|
if (!client.isConnectedToServer()) {
|
|
@@ -87,6 +98,9 @@ program
|
|
|
87
98
|
}
|
|
88
99
|
}
|
|
89
100
|
}
|
|
101
|
+
else if (forceDirectApi && process.env.CLI_VERBOSE === 'true') {
|
|
102
|
+
console.log(colors.muted('MCP auto-connect skipped (force direct API enabled)'));
|
|
103
|
+
}
|
|
90
104
|
});
|
|
91
105
|
// Enhanced global error handler
|
|
92
106
|
process.on('uncaughtException', (error) => {
|
|
@@ -324,11 +338,10 @@ const topicCmd = program
|
|
|
324
338
|
.description('Topic management commands');
|
|
325
339
|
requireAuth(topicCmd);
|
|
326
340
|
topicCommands(topicCmd);
|
|
327
|
-
// Configuration commands (
|
|
341
|
+
// Configuration commands (no auth required)
|
|
328
342
|
const configCmd = program
|
|
329
343
|
.command('config')
|
|
330
344
|
.description('Configuration management');
|
|
331
|
-
requireAuth(configCmd);
|
|
332
345
|
configCommands(configCmd);
|
|
333
346
|
// Organization commands (require auth)
|
|
334
347
|
const orgCmd = program
|
|
@@ -540,18 +553,30 @@ program
|
|
|
540
553
|
.action(async () => {
|
|
541
554
|
// Initialize config first
|
|
542
555
|
await cliConfig.init();
|
|
543
|
-
const
|
|
556
|
+
const verification = await cliConfig.verifyCurrentCredentialsWithServer().catch((error) => ({
|
|
557
|
+
valid: false,
|
|
558
|
+
method: 'none',
|
|
559
|
+
endpoint: undefined,
|
|
560
|
+
reason: error instanceof Error ? error.message : String(error)
|
|
561
|
+
}));
|
|
562
|
+
const isAuth = verification.valid;
|
|
544
563
|
const apiUrl = cliConfig.getApiUrl();
|
|
545
564
|
console.log(chalk.blue.bold('MaaS CLI Status'));
|
|
546
565
|
console.log(`API URL: ${apiUrl}`);
|
|
547
566
|
console.log(`Authenticated: ${isAuth ? chalk.green('Yes') : chalk.red('No')}`);
|
|
567
|
+
if (process.env.CLI_VERBOSE === 'true' && verification.endpoint) {
|
|
568
|
+
console.log(`Verified via: ${verification.endpoint}`);
|
|
569
|
+
}
|
|
548
570
|
if (isAuth) {
|
|
549
571
|
const user = await cliConfig.getCurrentUser();
|
|
550
572
|
if (user) {
|
|
551
573
|
console.log(`User: ${user.email}`);
|
|
552
574
|
console.log(`Plan: ${user.plan}`);
|
|
553
575
|
}
|
|
576
|
+
return;
|
|
554
577
|
}
|
|
578
|
+
console.log(chalk.yellow(`Auth check: ${verification.reason || 'Credential validation failed'}`));
|
|
579
|
+
console.log(chalk.yellow('Please run:'), chalk.white('lanonasis auth login'));
|
|
555
580
|
});
|
|
556
581
|
// Health command using the healthCheck function
|
|
557
582
|
program
|