supaclaw 1.0.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/LICENSE +21 -0
- package/README.md +871 -0
- package/SCHEMA.md +215 -0
- package/dist/clawdbot-integration.d.ts +171 -0
- package/dist/clawdbot-integration.js +339 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +1815 -0
- package/dist/context-manager.d.ts +143 -0
- package/dist/context-manager.js +360 -0
- package/dist/error-handling.d.ts +100 -0
- package/dist/error-handling.js +301 -0
- package/dist/index.d.ts +735 -0
- package/dist/index.js +2256 -0
- package/dist/parsers.d.ts +115 -0
- package/dist/parsers.js +406 -0
- package/migrations/001_initial.sql +153 -0
- package/migrations/002_vector_search.sql +219 -0
- package/migrations/003_entity_relationships.sql +143 -0
- package/package.json +66 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,1815 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
36
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
+
const commander_1 = require("commander");
|
|
38
|
+
const fs_1 = require("fs");
|
|
39
|
+
const path_1 = require("path");
|
|
40
|
+
const supabase_js_1 = require("@supabase/supabase-js");
|
|
41
|
+
const readline_1 = require("readline");
|
|
42
|
+
const CONFIG_FILE = '.openclaw-memory.json';
|
|
43
|
+
// ============ CLI HELPERS ============
|
|
44
|
+
function loadConfig() {
|
|
45
|
+
const configPath = (0, path_1.join)(process.cwd(), CONFIG_FILE);
|
|
46
|
+
if (!(0, fs_1.existsSync)(configPath)) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
try {
|
|
50
|
+
return JSON.parse((0, fs_1.readFileSync)(configPath, 'utf-8'));
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
console.error(`ā Failed to read ${CONFIG_FILE}:`, err);
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function saveConfig(config) {
|
|
58
|
+
const configPath = (0, path_1.join)(process.cwd(), CONFIG_FILE);
|
|
59
|
+
(0, fs_1.writeFileSync)(configPath, JSON.stringify(config, null, 2));
|
|
60
|
+
console.log(`ā
Saved config to ${CONFIG_FILE}`);
|
|
61
|
+
}
|
|
62
|
+
async function prompt(question) {
|
|
63
|
+
const rl = (0, readline_1.createInterface)({
|
|
64
|
+
input: process.stdin,
|
|
65
|
+
output: process.stdout
|
|
66
|
+
});
|
|
67
|
+
return new Promise((resolve) => {
|
|
68
|
+
rl.question(question, (answer) => {
|
|
69
|
+
rl.close();
|
|
70
|
+
resolve(answer.trim());
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
function getSupabaseClient(config) {
|
|
75
|
+
return (0, supabase_js_1.createClient)(config.supabaseUrl, config.supabaseKey);
|
|
76
|
+
}
|
|
77
|
+
// ============ COMMANDS ============
|
|
78
|
+
async function cmdInit() {
|
|
79
|
+
console.log('š OpenClaw Memory - Setup\n');
|
|
80
|
+
// Check if config already exists
|
|
81
|
+
if ((0, fs_1.existsSync)((0, path_1.join)(process.cwd(), CONFIG_FILE))) {
|
|
82
|
+
const overwrite = await prompt('ā ļø Config already exists. Overwrite? (y/N): ');
|
|
83
|
+
if (overwrite.toLowerCase() !== 'y') {
|
|
84
|
+
console.log('Aborted.');
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
console.log('Enter your Supabase project details:\n');
|
|
89
|
+
const supabaseUrl = await prompt('Supabase URL: ');
|
|
90
|
+
const supabaseKey = await prompt('Supabase anon/service key: ');
|
|
91
|
+
const agentId = await prompt('Agent ID (e.g., "hans-assistant"): ');
|
|
92
|
+
if (!supabaseUrl || !supabaseKey || !agentId) {
|
|
93
|
+
console.error('ā All fields are required.');
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
// Validate URL
|
|
97
|
+
try {
|
|
98
|
+
new URL(supabaseUrl);
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
console.error('ā Invalid Supabase URL');
|
|
102
|
+
process.exit(1);
|
|
103
|
+
}
|
|
104
|
+
const config = {
|
|
105
|
+
supabaseUrl,
|
|
106
|
+
supabaseKey,
|
|
107
|
+
agentId
|
|
108
|
+
};
|
|
109
|
+
saveConfig(config);
|
|
110
|
+
console.log('\nā
Configuration saved!');
|
|
111
|
+
console.log('\nNext steps:');
|
|
112
|
+
console.log(' 1. Run migrations: npx openclaw-memory migrate');
|
|
113
|
+
console.log(' 2. Test connection: npx openclaw-memory test');
|
|
114
|
+
console.log(' 3. Check status: npx openclaw-memory status');
|
|
115
|
+
}
|
|
116
|
+
async function cmdMigrate() {
|
|
117
|
+
const config = loadConfig();
|
|
118
|
+
if (!config) {
|
|
119
|
+
console.error('ā No config found. Run `openclaw-memory init` first.');
|
|
120
|
+
process.exit(1);
|
|
121
|
+
}
|
|
122
|
+
console.log('š Database Migration\n');
|
|
123
|
+
// Read migration file
|
|
124
|
+
const migrationPath = (0, path_1.join)(__dirname, '../migrations/001_initial.sql');
|
|
125
|
+
if (!(0, fs_1.existsSync)(migrationPath)) {
|
|
126
|
+
console.error('ā Migration file not found:', migrationPath);
|
|
127
|
+
process.exit(1);
|
|
128
|
+
}
|
|
129
|
+
const sql = (0, fs_1.readFileSync)(migrationPath, 'utf-8');
|
|
130
|
+
console.log('To set up your database:\n');
|
|
131
|
+
console.log('1. Go to your Supabase dashboard');
|
|
132
|
+
console.log(` ${config.supabaseUrl.replace('/rest/v1', '')}`);
|
|
133
|
+
console.log('2. Navigate to: SQL Editor ā New Query');
|
|
134
|
+
console.log('3. Copy the SQL from:');
|
|
135
|
+
console.log(` ${migrationPath}`);
|
|
136
|
+
console.log('4. Paste and run the query\n');
|
|
137
|
+
console.log('Or run this SQL directly:\n');
|
|
138
|
+
console.log('ā'.repeat(60));
|
|
139
|
+
console.log(sql);
|
|
140
|
+
console.log('ā'.repeat(60));
|
|
141
|
+
console.log('\nā
After running the migration, verify with:');
|
|
142
|
+
console.log(' npx openclaw-memory test');
|
|
143
|
+
console.log(' npx openclaw-memory status');
|
|
144
|
+
}
|
|
145
|
+
async function cmdTest() {
|
|
146
|
+
const config = loadConfig();
|
|
147
|
+
if (!config) {
|
|
148
|
+
console.error('ā No config found. Run `openclaw-memory init` first.');
|
|
149
|
+
process.exit(1);
|
|
150
|
+
}
|
|
151
|
+
console.log('š Testing Supabase Connection...\n');
|
|
152
|
+
const supabase = getSupabaseClient(config);
|
|
153
|
+
try {
|
|
154
|
+
// Test basic connectivity
|
|
155
|
+
const { error: sessionsError } = await supabase
|
|
156
|
+
.from('sessions')
|
|
157
|
+
.select('id')
|
|
158
|
+
.limit(1);
|
|
159
|
+
if (sessionsError) {
|
|
160
|
+
if (sessionsError.code === '42P01') {
|
|
161
|
+
console.error('ā Tables not found. Run migrations first:');
|
|
162
|
+
console.error(' npx openclaw-memory migrate');
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
console.error('ā Connection failed:', sessionsError.message);
|
|
166
|
+
}
|
|
167
|
+
process.exit(1);
|
|
168
|
+
}
|
|
169
|
+
console.log('ā
Connection successful!');
|
|
170
|
+
console.log(` Agent ID: ${config.agentId}`);
|
|
171
|
+
console.log(` Database: ${config.supabaseUrl}\n`);
|
|
172
|
+
// Test each table
|
|
173
|
+
const tables = ['sessions', 'messages', 'memories', 'entities', 'tasks', 'learnings'];
|
|
174
|
+
let allTablesExist = true;
|
|
175
|
+
for (const table of tables) {
|
|
176
|
+
const { error } = await supabase
|
|
177
|
+
.from(table)
|
|
178
|
+
.select('id')
|
|
179
|
+
.limit(1);
|
|
180
|
+
if (error) {
|
|
181
|
+
console.log(`ā Table "${table}" not found`);
|
|
182
|
+
allTablesExist = false;
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
console.log(`ā
Table "${table}" accessible`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
if (!allTablesExist) {
|
|
189
|
+
console.log('\nā ļø Some tables are missing. Run migrations:');
|
|
190
|
+
console.log(' npx openclaw-memory migrate');
|
|
191
|
+
process.exit(1);
|
|
192
|
+
}
|
|
193
|
+
console.log('\nš All systems operational!');
|
|
194
|
+
}
|
|
195
|
+
catch (err) {
|
|
196
|
+
console.error('ā Unexpected error:', err);
|
|
197
|
+
process.exit(1);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
async function cmdStatus() {
|
|
201
|
+
const config = loadConfig();
|
|
202
|
+
if (!config) {
|
|
203
|
+
console.error('ā No config found. Run `openclaw-memory init` first.');
|
|
204
|
+
process.exit(1);
|
|
205
|
+
}
|
|
206
|
+
console.log('š OpenClaw Memory - Status\n');
|
|
207
|
+
const supabase = getSupabaseClient(config);
|
|
208
|
+
try {
|
|
209
|
+
const tables = ['sessions', 'messages', 'memories', 'entities', 'tasks', 'learnings'];
|
|
210
|
+
const stats = {};
|
|
211
|
+
for (const table of tables) {
|
|
212
|
+
const { count, error } = await supabase
|
|
213
|
+
.from(table)
|
|
214
|
+
.select('*', { count: 'exact', head: true })
|
|
215
|
+
.eq('agent_id', config.agentId);
|
|
216
|
+
if (error) {
|
|
217
|
+
if (error.code === '42P01') {
|
|
218
|
+
stats[table] = -1;
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
console.error(`ā Error checking ${table}:`, error.message);
|
|
222
|
+
stats[table] = -1;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
stats[table] = count || 0;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
console.log(`Agent ID: ${config.agentId}`);
|
|
230
|
+
console.log(`Supabase: ${config.supabaseUrl}\n`);
|
|
231
|
+
console.log('Database Statistics:');
|
|
232
|
+
for (const [table, count] of Object.entries(stats)) {
|
|
233
|
+
if (count === -1) {
|
|
234
|
+
console.log(` ${table.padEnd(12)} ā ļø not found`);
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
console.log(` ${table.padEnd(12)} ${count.toString().padStart(6)} records`);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
// Check for active sessions
|
|
241
|
+
const { data: activeSessions } = await supabase
|
|
242
|
+
.from('sessions')
|
|
243
|
+
.select('id, started_at, channel')
|
|
244
|
+
.eq('agent_id', config.agentId)
|
|
245
|
+
.is('ended_at', null)
|
|
246
|
+
.order('started_at', { ascending: false })
|
|
247
|
+
.limit(5);
|
|
248
|
+
if (activeSessions && activeSessions.length > 0) {
|
|
249
|
+
console.log(`\nš¬ Active Sessions: ${activeSessions.length}`);
|
|
250
|
+
activeSessions.forEach(s => {
|
|
251
|
+
const date = new Date(s.started_at).toLocaleString();
|
|
252
|
+
console.log(` - ${s.id.slice(0, 8)}... (${s.channel || 'unknown'}) started ${date}`);
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
else {
|
|
256
|
+
console.log('\nš¬ No active sessions');
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
catch (err) {
|
|
260
|
+
console.error('ā Failed to fetch status:', err);
|
|
261
|
+
process.exit(1);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
async function cmdSearch(query, options) {
|
|
265
|
+
const config = loadConfig();
|
|
266
|
+
if (!config) {
|
|
267
|
+
console.error('ā No config found. Run `openclaw-memory init` first.');
|
|
268
|
+
process.exit(1);
|
|
269
|
+
}
|
|
270
|
+
if (!query || query.trim().length === 0) {
|
|
271
|
+
console.error('ā Search query is required');
|
|
272
|
+
process.exit(1);
|
|
273
|
+
}
|
|
274
|
+
const limit = options.limit || 10;
|
|
275
|
+
const mode = options.mode || 'keyword';
|
|
276
|
+
const modeEmoji = {
|
|
277
|
+
keyword: 'š',
|
|
278
|
+
semantic: 'š§ ',
|
|
279
|
+
hybrid: 'ā”'
|
|
280
|
+
};
|
|
281
|
+
console.log(`${modeEmoji[mode]} Searching memories (${mode} mode): "${query}"\n`);
|
|
282
|
+
const supabase = getSupabaseClient(config);
|
|
283
|
+
try {
|
|
284
|
+
let data = [];
|
|
285
|
+
if (mode === 'keyword') {
|
|
286
|
+
// Traditional keyword search
|
|
287
|
+
const { data: results, error } = await supabase
|
|
288
|
+
.from('memories')
|
|
289
|
+
.select('*')
|
|
290
|
+
.eq('agent_id', config.agentId)
|
|
291
|
+
.or(`content.ilike.%${query}%,category.ilike.%${query}%`)
|
|
292
|
+
.order('importance', { ascending: false })
|
|
293
|
+
.limit(limit);
|
|
294
|
+
if (error)
|
|
295
|
+
throw error;
|
|
296
|
+
data = results || [];
|
|
297
|
+
}
|
|
298
|
+
else if (mode === 'semantic') {
|
|
299
|
+
// Vector similarity search (requires OpenAI API key)
|
|
300
|
+
const openaiKey = process.env.OPENAI_API_KEY;
|
|
301
|
+
if (!openaiKey) {
|
|
302
|
+
console.error('ā OPENAI_API_KEY environment variable required for semantic search');
|
|
303
|
+
process.exit(1);
|
|
304
|
+
}
|
|
305
|
+
const OpenAI = (await Promise.resolve().then(() => __importStar(require('openai')))).default;
|
|
306
|
+
const openai = new OpenAI({ apiKey: openaiKey });
|
|
307
|
+
const embeddingResponse = await openai.embeddings.create({
|
|
308
|
+
model: 'text-embedding-3-small',
|
|
309
|
+
input: query,
|
|
310
|
+
});
|
|
311
|
+
const queryEmbedding = embeddingResponse.data[0].embedding;
|
|
312
|
+
const { data: results, error } = await supabase.rpc('match_memories', {
|
|
313
|
+
query_embedding: queryEmbedding,
|
|
314
|
+
match_threshold: options.minSimilarity || 0.7,
|
|
315
|
+
match_count: limit,
|
|
316
|
+
p_agent_id: config.agentId
|
|
317
|
+
});
|
|
318
|
+
if (error)
|
|
319
|
+
throw error;
|
|
320
|
+
data = results || [];
|
|
321
|
+
}
|
|
322
|
+
else if (mode === 'hybrid') {
|
|
323
|
+
// Hybrid search: vector + keyword
|
|
324
|
+
const openaiKey = process.env.OPENAI_API_KEY;
|
|
325
|
+
if (!openaiKey) {
|
|
326
|
+
console.error('ā OPENAI_API_KEY environment variable required for hybrid search');
|
|
327
|
+
process.exit(1);
|
|
328
|
+
}
|
|
329
|
+
const OpenAI = (await Promise.resolve().then(() => __importStar(require('openai')))).default;
|
|
330
|
+
const openai = new OpenAI({ apiKey: openaiKey });
|
|
331
|
+
const embeddingResponse = await openai.embeddings.create({
|
|
332
|
+
model: 'text-embedding-3-small',
|
|
333
|
+
input: query,
|
|
334
|
+
});
|
|
335
|
+
const queryEmbedding = embeddingResponse.data[0].embedding;
|
|
336
|
+
const { data: results, error } = await supabase.rpc('hybrid_search_memories', {
|
|
337
|
+
query_embedding: queryEmbedding,
|
|
338
|
+
query_text: query,
|
|
339
|
+
vector_weight: 0.7,
|
|
340
|
+
keyword_weight: 0.3,
|
|
341
|
+
match_count: limit,
|
|
342
|
+
p_agent_id: config.agentId
|
|
343
|
+
});
|
|
344
|
+
if (error)
|
|
345
|
+
throw error;
|
|
346
|
+
data = results || [];
|
|
347
|
+
}
|
|
348
|
+
if (!data || data.length === 0) {
|
|
349
|
+
console.log('No memories found.');
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
console.log(`Found ${data.length} memories:\n`);
|
|
353
|
+
data.forEach((mem, idx) => {
|
|
354
|
+
const scoreLabel = mem.similarity ? `similarity: ${mem.similarity.toFixed(3)}` :
|
|
355
|
+
mem.score ? `score: ${mem.score.toFixed(3)}` :
|
|
356
|
+
`importance: ${mem.importance}`;
|
|
357
|
+
console.log(`${idx + 1}. [${mem.category || 'none'}] (${scoreLabel})`);
|
|
358
|
+
console.log(` ${mem.content}`);
|
|
359
|
+
if (mem.metadata && Object.keys(mem.metadata).length > 0) {
|
|
360
|
+
console.log(` Metadata: ${JSON.stringify(mem.metadata)}`);
|
|
361
|
+
}
|
|
362
|
+
console.log(` Created: ${new Date(mem.created_at).toLocaleString()}\n`);
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
catch (err) {
|
|
366
|
+
console.error('ā Search error:', err);
|
|
367
|
+
process.exit(1);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
async function cmdSessions(options) {
|
|
371
|
+
const config = loadConfig();
|
|
372
|
+
if (!config) {
|
|
373
|
+
console.error('ā No config found. Run `openclaw-memory init` first.');
|
|
374
|
+
process.exit(1);
|
|
375
|
+
}
|
|
376
|
+
console.log('š Sessions\n');
|
|
377
|
+
const supabase = getSupabaseClient(config);
|
|
378
|
+
const limit = options.limit || 20;
|
|
379
|
+
try {
|
|
380
|
+
let query = supabase
|
|
381
|
+
.from('sessions')
|
|
382
|
+
.select('*')
|
|
383
|
+
.eq('agent_id', config.agentId);
|
|
384
|
+
if (options.active) {
|
|
385
|
+
query = query.is('ended_at', null);
|
|
386
|
+
}
|
|
387
|
+
const { data, error } = await query
|
|
388
|
+
.order('started_at', { ascending: false })
|
|
389
|
+
.limit(limit);
|
|
390
|
+
if (error) {
|
|
391
|
+
console.error('ā Failed to fetch sessions:', error.message);
|
|
392
|
+
process.exit(1);
|
|
393
|
+
}
|
|
394
|
+
if (!data || data.length === 0) {
|
|
395
|
+
console.log('No sessions found.');
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
console.log(`Found ${data.length} sessions:\n`);
|
|
399
|
+
data.forEach((session, idx) => {
|
|
400
|
+
const started = new Date(session.started_at).toLocaleString();
|
|
401
|
+
const ended = session.ended_at ? new Date(session.ended_at).toLocaleString() : 'active';
|
|
402
|
+
const status = session.ended_at ? 'ā' : 'ā';
|
|
403
|
+
console.log(`${status} ${session.id.slice(0, 8)}...`);
|
|
404
|
+
console.log(` User: ${session.user_id || 'unknown'}`);
|
|
405
|
+
console.log(` Channel: ${session.channel || 'unknown'}`);
|
|
406
|
+
console.log(` Started: ${started}`);
|
|
407
|
+
console.log(` Ended: ${ended}`);
|
|
408
|
+
if (session.summary) {
|
|
409
|
+
console.log(` Summary: ${session.summary.slice(0, 100)}${session.summary.length > 100 ? '...' : ''}`);
|
|
410
|
+
}
|
|
411
|
+
console.log('');
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
catch (err) {
|
|
415
|
+
console.error('ā Error:', err);
|
|
416
|
+
process.exit(1);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
async function cmdExport(outputPath) {
|
|
420
|
+
const config = loadConfig();
|
|
421
|
+
if (!config) {
|
|
422
|
+
console.error('ā No config found. Run `openclaw-memory init` first.');
|
|
423
|
+
process.exit(1);
|
|
424
|
+
}
|
|
425
|
+
console.log('š¤ Exporting memories to markdown...\n');
|
|
426
|
+
const supabase = getSupabaseClient(config);
|
|
427
|
+
try {
|
|
428
|
+
// Fetch all memories
|
|
429
|
+
const { data, error } = await supabase
|
|
430
|
+
.from('memories')
|
|
431
|
+
.select('*')
|
|
432
|
+
.eq('agent_id', config.agentId)
|
|
433
|
+
.order('created_at', { ascending: false });
|
|
434
|
+
if (error) {
|
|
435
|
+
console.error('ā Export failed:', error.message);
|
|
436
|
+
process.exit(1);
|
|
437
|
+
}
|
|
438
|
+
if (!data || data.length === 0) {
|
|
439
|
+
console.log('No memories to export.');
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
// Build markdown
|
|
443
|
+
let markdown = `# OpenClaw Memory Export\n\n`;
|
|
444
|
+
markdown += `**Agent:** ${config.agentId}\n`;
|
|
445
|
+
markdown += `**Exported:** ${new Date().toISOString()}\n`;
|
|
446
|
+
markdown += `**Total Memories:** ${data.length}\n\n`;
|
|
447
|
+
markdown += `---\n\n`;
|
|
448
|
+
// Group by category
|
|
449
|
+
const byCategory = {};
|
|
450
|
+
data.forEach(mem => {
|
|
451
|
+
const cat = mem.category || 'uncategorized';
|
|
452
|
+
if (!byCategory[cat])
|
|
453
|
+
byCategory[cat] = [];
|
|
454
|
+
byCategory[cat].push(mem);
|
|
455
|
+
});
|
|
456
|
+
for (const [category, memories] of Object.entries(byCategory)) {
|
|
457
|
+
markdown += `## ${category.charAt(0).toUpperCase() + category.slice(1)}\n\n`;
|
|
458
|
+
memories.forEach(mem => {
|
|
459
|
+
markdown += `### ${new Date(mem.created_at).toISOString().split('T')[0]}\n\n`;
|
|
460
|
+
markdown += `**Importance:** ${mem.importance}\n\n`;
|
|
461
|
+
markdown += `${mem.content}\n\n`;
|
|
462
|
+
if (mem.metadata) {
|
|
463
|
+
markdown += `*Metadata:* ${JSON.stringify(mem.metadata)}\n\n`;
|
|
464
|
+
}
|
|
465
|
+
markdown += `---\n\n`;
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
// Write to file
|
|
469
|
+
const resolvedPath = outputPath || 'openclaw-memory-export.md';
|
|
470
|
+
(0, fs_1.writeFileSync)(resolvedPath, markdown, 'utf-8');
|
|
471
|
+
console.log(`ā
Exported ${data.length} memories to: ${resolvedPath}`);
|
|
472
|
+
}
|
|
473
|
+
catch (err) {
|
|
474
|
+
console.error('ā Export error:', err);
|
|
475
|
+
process.exit(1);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
async function cmdImport(inputPath) {
|
|
479
|
+
const config = loadConfig();
|
|
480
|
+
if (!config) {
|
|
481
|
+
console.error('ā No config found. Run `openclaw-memory init` first.');
|
|
482
|
+
process.exit(1);
|
|
483
|
+
}
|
|
484
|
+
console.log(`š„ Importing memories from: ${inputPath}\n`);
|
|
485
|
+
if (!(0, fs_1.existsSync)(inputPath)) {
|
|
486
|
+
console.error('ā File not found:', inputPath);
|
|
487
|
+
process.exit(1);
|
|
488
|
+
}
|
|
489
|
+
try {
|
|
490
|
+
const content = (0, fs_1.readFileSync)(inputPath, 'utf-8');
|
|
491
|
+
// Simple parser: extract lines that look like memories
|
|
492
|
+
// Format: "- Memory text" or "* Memory text"
|
|
493
|
+
const lines = content.split('\n');
|
|
494
|
+
const memories = [];
|
|
495
|
+
let currentCategory = 'imported';
|
|
496
|
+
lines.forEach(line => {
|
|
497
|
+
line = line.trim();
|
|
498
|
+
// Detect category headers
|
|
499
|
+
if (line.startsWith('## ')) {
|
|
500
|
+
currentCategory = line.slice(3).toLowerCase().trim();
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
// Detect memory lines
|
|
504
|
+
if (line.startsWith('- ') || line.startsWith('* ')) {
|
|
505
|
+
const content = line.slice(2).trim();
|
|
506
|
+
if (content.length > 0) {
|
|
507
|
+
memories.push({
|
|
508
|
+
content,
|
|
509
|
+
category: currentCategory,
|
|
510
|
+
importance: 0.5 // default
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
});
|
|
515
|
+
if (memories.length === 0) {
|
|
516
|
+
console.log('ā ļø No memories found in file. Expected format:');
|
|
517
|
+
console.log(' ## Category Name');
|
|
518
|
+
console.log(' - Memory item one');
|
|
519
|
+
console.log(' - Memory item two');
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
console.log(`Found ${memories.length} memories to import.\n`);
|
|
523
|
+
const supabase = getSupabaseClient(config);
|
|
524
|
+
let imported = 0;
|
|
525
|
+
for (const mem of memories) {
|
|
526
|
+
const { error } = await supabase
|
|
527
|
+
.from('memories')
|
|
528
|
+
.insert({
|
|
529
|
+
agent_id: config.agentId,
|
|
530
|
+
content: mem.content,
|
|
531
|
+
category: mem.category,
|
|
532
|
+
importance: mem.importance
|
|
533
|
+
});
|
|
534
|
+
if (error) {
|
|
535
|
+
console.error(`ā Failed to import: ${mem.content.slice(0, 50)}...`);
|
|
536
|
+
console.error(` Error: ${error.message}`);
|
|
537
|
+
}
|
|
538
|
+
else {
|
|
539
|
+
imported++;
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
console.log(`\nā
Successfully imported ${imported}/${memories.length} memories`);
|
|
543
|
+
}
|
|
544
|
+
catch (err) {
|
|
545
|
+
console.error('ā Import error:', err);
|
|
546
|
+
process.exit(1);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
async function cmdDecay(options) {
|
|
550
|
+
const config = loadConfig();
|
|
551
|
+
if (!config) {
|
|
552
|
+
console.error('ā No config found. Run `openclaw-memory init` first.');
|
|
553
|
+
process.exit(1);
|
|
554
|
+
}
|
|
555
|
+
try {
|
|
556
|
+
const { OpenClawMemory } = await Promise.resolve().then(() => __importStar(require('./index')));
|
|
557
|
+
const memory = new OpenClawMemory({
|
|
558
|
+
supabaseUrl: config.supabaseUrl,
|
|
559
|
+
supabaseKey: config.supabaseKey,
|
|
560
|
+
agentId: config.agentId
|
|
561
|
+
});
|
|
562
|
+
console.log(`š Applying importance decay to memories older than ${options.olderThanDays} days...`);
|
|
563
|
+
const result = await memory.decayMemoryImportance(options);
|
|
564
|
+
console.log(`\nā
Decay complete:`);
|
|
565
|
+
console.log(` - Updated: ${result.updated} memories`);
|
|
566
|
+
console.log(` - Average decay: ${(result.avgDecay * 100).toFixed(2)}%`);
|
|
567
|
+
}
|
|
568
|
+
catch (err) {
|
|
569
|
+
console.error('ā Decay error:', err);
|
|
570
|
+
process.exit(1);
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
async function cmdConsolidate(options) {
|
|
574
|
+
const config = loadConfig();
|
|
575
|
+
if (!config) {
|
|
576
|
+
console.error('ā No config found. Run `openclaw-memory init` first.');
|
|
577
|
+
process.exit(1);
|
|
578
|
+
}
|
|
579
|
+
try {
|
|
580
|
+
const { OpenClawMemory } = await Promise.resolve().then(() => __importStar(require('./index')));
|
|
581
|
+
const memory = new OpenClawMemory({
|
|
582
|
+
supabaseUrl: config.supabaseUrl,
|
|
583
|
+
supabaseKey: config.supabaseKey,
|
|
584
|
+
agentId: config.agentId
|
|
585
|
+
});
|
|
586
|
+
console.log(`š Consolidating similar memories (threshold: ${options.similarityThreshold})...`);
|
|
587
|
+
const result = await memory.consolidateMemories(options);
|
|
588
|
+
console.log(`\nā
Consolidation complete:`);
|
|
589
|
+
console.log(` - Merged: ${result.merged} memories`);
|
|
590
|
+
console.log(` - Kept: ${result.kept} unique memories`);
|
|
591
|
+
}
|
|
592
|
+
catch (err) {
|
|
593
|
+
console.error('ā Consolidation error:', err);
|
|
594
|
+
process.exit(1);
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
async function cmdTag(memoryId, tags) {
|
|
598
|
+
const config = loadConfig();
|
|
599
|
+
if (!config) {
|
|
600
|
+
console.error('ā No config found. Run `openclaw-memory init` first.');
|
|
601
|
+
process.exit(1);
|
|
602
|
+
}
|
|
603
|
+
try {
|
|
604
|
+
const { OpenClawMemory } = await Promise.resolve().then(() => __importStar(require('./index')));
|
|
605
|
+
const memory = new OpenClawMemory({
|
|
606
|
+
supabaseUrl: config.supabaseUrl,
|
|
607
|
+
supabaseKey: config.supabaseKey,
|
|
608
|
+
agentId: config.agentId
|
|
609
|
+
});
|
|
610
|
+
const result = await memory.tagMemory(memoryId, tags);
|
|
611
|
+
const allTags = result.metadata?.tags || [];
|
|
612
|
+
console.log(`ā
Tagged memory ${memoryId}`);
|
|
613
|
+
console.log(` Tags: ${allTags.join(', ')}`);
|
|
614
|
+
}
|
|
615
|
+
catch (err) {
|
|
616
|
+
console.error('ā Tag error:', err);
|
|
617
|
+
process.exit(1);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
async function cmdUntag(memoryId, tags) {
|
|
621
|
+
const config = loadConfig();
|
|
622
|
+
if (!config) {
|
|
623
|
+
console.error('ā No config found. Run `openclaw-memory init` first.');
|
|
624
|
+
process.exit(1);
|
|
625
|
+
}
|
|
626
|
+
try {
|
|
627
|
+
const { OpenClawMemory } = await Promise.resolve().then(() => __importStar(require('./index')));
|
|
628
|
+
const memory = new OpenClawMemory({
|
|
629
|
+
supabaseUrl: config.supabaseUrl,
|
|
630
|
+
supabaseKey: config.supabaseKey,
|
|
631
|
+
agentId: config.agentId
|
|
632
|
+
});
|
|
633
|
+
const result = await memory.untagMemory(memoryId, tags);
|
|
634
|
+
const allTags = result.metadata?.tags || [];
|
|
635
|
+
console.log(`ā
Removed tags from memory ${memoryId}`);
|
|
636
|
+
console.log(` Remaining tags: ${allTags.length > 0 ? allTags.join(', ') : 'none'}`);
|
|
637
|
+
}
|
|
638
|
+
catch (err) {
|
|
639
|
+
console.error('ā Untag error:', err);
|
|
640
|
+
process.exit(1);
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
async function cmdSearchTags(tags, options) {
|
|
644
|
+
const config = loadConfig();
|
|
645
|
+
if (!config) {
|
|
646
|
+
console.error('ā No config found. Run `openclaw-memory init` first.');
|
|
647
|
+
process.exit(1);
|
|
648
|
+
}
|
|
649
|
+
try {
|
|
650
|
+
const { OpenClawMemory } = await Promise.resolve().then(() => __importStar(require('./index')));
|
|
651
|
+
const memory = new OpenClawMemory({
|
|
652
|
+
supabaseUrl: config.supabaseUrl,
|
|
653
|
+
supabaseKey: config.supabaseKey,
|
|
654
|
+
agentId: config.agentId
|
|
655
|
+
});
|
|
656
|
+
console.log(`š Searching for memories with tags: ${tags.join(', ')} (${options.matchAll ? 'ALL' : 'ANY'})`);
|
|
657
|
+
const results = await memory.searchMemoriesByTags(tags, options);
|
|
658
|
+
if (results.length === 0) {
|
|
659
|
+
console.log(' No matching memories found.');
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
console.log(`\nFound ${results.length} memories:\n`);
|
|
663
|
+
results.forEach((mem, i) => {
|
|
664
|
+
const memTags = mem.metadata?.tags || [];
|
|
665
|
+
console.log(`${i + 1}. [${mem.category || 'uncategorized'}] ${mem.content.slice(0, 80)}...`);
|
|
666
|
+
console.log(` Tags: ${memTags.join(', ')}`);
|
|
667
|
+
console.log(` Importance: ${mem.importance}`);
|
|
668
|
+
console.log();
|
|
669
|
+
});
|
|
670
|
+
}
|
|
671
|
+
catch (err) {
|
|
672
|
+
console.error('ā Search error:', err);
|
|
673
|
+
process.exit(1);
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
async function cmdCleanup(options) {
|
|
677
|
+
const config = loadConfig();
|
|
678
|
+
if (!config) {
|
|
679
|
+
console.error('ā No config found. Run `openclaw-memory init` first.');
|
|
680
|
+
process.exit(1);
|
|
681
|
+
}
|
|
682
|
+
try {
|
|
683
|
+
const { OpenClawMemory } = await Promise.resolve().then(() => __importStar(require('./index')));
|
|
684
|
+
const memory = new OpenClawMemory({
|
|
685
|
+
supabaseUrl: config.supabaseUrl,
|
|
686
|
+
supabaseKey: config.supabaseKey,
|
|
687
|
+
agentId: config.agentId
|
|
688
|
+
});
|
|
689
|
+
const actionWord = options.action === 'delete' ? 'Deleting' : 'Archiving';
|
|
690
|
+
console.log(`š ${actionWord} sessions older than ${options.olderThanDays} days...`);
|
|
691
|
+
const result = await memory.cleanupOldSessions(options);
|
|
692
|
+
const count = options.action === 'delete' ? result.deleted : result.archived;
|
|
693
|
+
console.log(`\nā
Cleanup complete: ${actionWord.toLowerCase()} ${count} sessions`);
|
|
694
|
+
}
|
|
695
|
+
catch (err) {
|
|
696
|
+
console.error('ā Cleanup error:', err);
|
|
697
|
+
process.exit(1);
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
async function cmdCleanupStats() {
|
|
701
|
+
const config = loadConfig();
|
|
702
|
+
if (!config) {
|
|
703
|
+
console.error('ā No config found. Run `openclaw-memory init` first.');
|
|
704
|
+
process.exit(1);
|
|
705
|
+
}
|
|
706
|
+
try {
|
|
707
|
+
const { OpenClawMemory } = await Promise.resolve().then(() => __importStar(require('./index')));
|
|
708
|
+
const memory = new OpenClawMemory({
|
|
709
|
+
supabaseUrl: config.supabaseUrl,
|
|
710
|
+
supabaseKey: config.supabaseKey,
|
|
711
|
+
agentId: config.agentId
|
|
712
|
+
});
|
|
713
|
+
console.log('š Gathering cleanup statistics...\n');
|
|
714
|
+
const stats = await memory.getCleanupStats();
|
|
715
|
+
console.log('Sessions:');
|
|
716
|
+
console.log(` Total: ${stats.totalSessions}`);
|
|
717
|
+
console.log(` Archived: ${stats.archivedSessions}`);
|
|
718
|
+
console.log(` Old (>90 days): ${stats.oldSessions}`);
|
|
719
|
+
console.log();
|
|
720
|
+
console.log('Messages:');
|
|
721
|
+
console.log(` Total: ${stats.totalMessages}`);
|
|
722
|
+
console.log(` Orphaned: ${stats.orphanedMessages}`);
|
|
723
|
+
}
|
|
724
|
+
catch (err) {
|
|
725
|
+
console.error('ā Stats error:', err);
|
|
726
|
+
process.exit(1);
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
// ============ MAIN ============
|
|
730
|
+
const program = new commander_1.Command();
|
|
731
|
+
program
|
|
732
|
+
.name('openclaw-memory')
|
|
733
|
+
.description('Persistent memory for AI agents using Supabase')
|
|
734
|
+
.version('0.1.0');
|
|
735
|
+
program
|
|
736
|
+
.command('init')
|
|
737
|
+
.description('Initialize configuration (creates .openclaw-memory.json)')
|
|
738
|
+
.action(cmdInit);
|
|
739
|
+
program
|
|
740
|
+
.command('migrate')
|
|
741
|
+
.description('Display database migration SQL')
|
|
742
|
+
.action(cmdMigrate);
|
|
743
|
+
program
|
|
744
|
+
.command('test')
|
|
745
|
+
.description('Test Supabase connection and verify tables')
|
|
746
|
+
.action(cmdTest);
|
|
747
|
+
program
|
|
748
|
+
.command('status')
|
|
749
|
+
.description('Show database statistics and active sessions')
|
|
750
|
+
.action(cmdStatus);
|
|
751
|
+
program
|
|
752
|
+
.command('search <query>')
|
|
753
|
+
.description('Search memories using keyword, semantic, or hybrid search')
|
|
754
|
+
.option('-l, --limit <number>', 'Maximum results', '10')
|
|
755
|
+
.option('-m, --mode <type>', 'Search mode: keyword, semantic, or hybrid', 'keyword')
|
|
756
|
+
.option('-s, --min-similarity <number>', 'Minimum similarity score (0-1) for semantic/hybrid search', '0.7')
|
|
757
|
+
.action((query, options) => {
|
|
758
|
+
const mode = ['keyword', 'semantic', 'hybrid'].includes(options.mode) ? options.mode : 'keyword';
|
|
759
|
+
cmdSearch(query, {
|
|
760
|
+
limit: parseInt(options.limit),
|
|
761
|
+
mode: mode,
|
|
762
|
+
minSimilarity: parseFloat(options.minSimilarity)
|
|
763
|
+
});
|
|
764
|
+
});
|
|
765
|
+
program
|
|
766
|
+
.command('sessions')
|
|
767
|
+
.description('List recent sessions')
|
|
768
|
+
.option('-l, --limit <number>', 'Maximum sessions', '20')
|
|
769
|
+
.option('-a, --active', 'Show only active sessions')
|
|
770
|
+
.action((options) => {
|
|
771
|
+
cmdSessions({
|
|
772
|
+
limit: parseInt(options.limit),
|
|
773
|
+
active: options.active
|
|
774
|
+
});
|
|
775
|
+
});
|
|
776
|
+
program
|
|
777
|
+
.command('export [path]')
|
|
778
|
+
.description('Export memories to markdown file')
|
|
779
|
+
.action((path) => {
|
|
780
|
+
cmdExport(path || 'openclaw-memory-export.md');
|
|
781
|
+
});
|
|
782
|
+
program
|
|
783
|
+
.command('import <path>')
|
|
784
|
+
.description('Import memories from markdown file')
|
|
785
|
+
.action(cmdImport);
|
|
786
|
+
program
|
|
787
|
+
.command('decay')
|
|
788
|
+
.description('Apply importance decay to old memories')
|
|
789
|
+
.option('-d, --days <number>', 'Only decay memories older than X days', '7')
|
|
790
|
+
.option('-r, --rate <number>', 'Decay rate (0-1)', '0.1')
|
|
791
|
+
.option('--min <number>', 'Minimum importance threshold', '0.1')
|
|
792
|
+
.action((options) => {
|
|
793
|
+
cmdDecay({
|
|
794
|
+
olderThanDays: parseInt(options.days),
|
|
795
|
+
decayRate: parseFloat(options.rate),
|
|
796
|
+
minImportance: parseFloat(options.min)
|
|
797
|
+
});
|
|
798
|
+
});
|
|
799
|
+
program
|
|
800
|
+
.command('consolidate')
|
|
801
|
+
.description('Merge similar memories')
|
|
802
|
+
.option('-t, --threshold <number>', 'Similarity threshold (0-1)', '0.9')
|
|
803
|
+
.option('-c, --category <name>', 'Filter by category')
|
|
804
|
+
.option('-l, --limit <number>', 'Max memories to check', '100')
|
|
805
|
+
.action((options) => {
|
|
806
|
+
cmdConsolidate({
|
|
807
|
+
similarityThreshold: parseFloat(options.threshold),
|
|
808
|
+
category: options.category,
|
|
809
|
+
limit: parseInt(options.limit)
|
|
810
|
+
});
|
|
811
|
+
});
|
|
812
|
+
program
|
|
813
|
+
.command('tag <memoryId> <tags...>')
|
|
814
|
+
.description('Add tags to a memory')
|
|
815
|
+
.action(cmdTag);
|
|
816
|
+
program
|
|
817
|
+
.command('untag <memoryId> <tags...>')
|
|
818
|
+
.description('Remove tags from a memory')
|
|
819
|
+
.action(cmdUntag);
|
|
820
|
+
program
|
|
821
|
+
.command('search-tags <tags...>')
|
|
822
|
+
.description('Search memories by tags')
|
|
823
|
+
.option('-a, --all', 'Match ALL tags (default: match ANY)')
|
|
824
|
+
.option('-l, --limit <number>', 'Maximum results', '50')
|
|
825
|
+
.action((tags, options) => {
|
|
826
|
+
cmdSearchTags(tags, {
|
|
827
|
+
matchAll: options.all,
|
|
828
|
+
limit: parseInt(options.limit)
|
|
829
|
+
});
|
|
830
|
+
});
|
|
831
|
+
program
|
|
832
|
+
.command('cleanup')
|
|
833
|
+
.description('Archive or delete old sessions')
|
|
834
|
+
.option('-d, --days <number>', 'Archive sessions older than X days', '90')
|
|
835
|
+
.option('--delete', 'Delete instead of archive')
|
|
836
|
+
.option('--keep-summaries', 'Keep sessions with summaries', true)
|
|
837
|
+
.action((options) => {
|
|
838
|
+
cmdCleanup({
|
|
839
|
+
olderThanDays: parseInt(options.days),
|
|
840
|
+
action: options.delete ? 'delete' : 'archive',
|
|
841
|
+
keepSummaries: options.keepSummaries
|
|
842
|
+
});
|
|
843
|
+
});
|
|
844
|
+
program
|
|
845
|
+
.command('cleanup-stats')
|
|
846
|
+
.description('Show cleanup statistics')
|
|
847
|
+
.action(cmdCleanupStats);
|
|
848
|
+
program
|
|
849
|
+
.command('entities')
|
|
850
|
+
.description('List entities')
|
|
851
|
+
.option('-t, --type <type>', 'Filter by entity type')
|
|
852
|
+
.option('-l, --limit <number>', 'Maximum results', '50')
|
|
853
|
+
.action(async (options) => {
|
|
854
|
+
const config = loadConfig();
|
|
855
|
+
if (!config) {
|
|
856
|
+
console.error('ā No config found. Run `openclaw-memory init` first.');
|
|
857
|
+
process.exit(1);
|
|
858
|
+
}
|
|
859
|
+
try {
|
|
860
|
+
const { OpenClawMemory } = await Promise.resolve().then(() => __importStar(require('./index')));
|
|
861
|
+
const memory = new OpenClawMemory({
|
|
862
|
+
supabaseUrl: config.supabaseUrl,
|
|
863
|
+
supabaseKey: config.supabaseKey,
|
|
864
|
+
agentId: config.agentId
|
|
865
|
+
});
|
|
866
|
+
const entities = await memory.searchEntities({
|
|
867
|
+
entityType: options.type,
|
|
868
|
+
limit: parseInt(options.limit)
|
|
869
|
+
});
|
|
870
|
+
if (entities.length === 0) {
|
|
871
|
+
console.log('No entities found.');
|
|
872
|
+
return;
|
|
873
|
+
}
|
|
874
|
+
console.log(`\nš¦ Found ${entities.length} entities:\n`);
|
|
875
|
+
entities.forEach((entity, i) => {
|
|
876
|
+
console.log(`${i + 1}. ${entity.name} [${entity.entity_type}]`);
|
|
877
|
+
console.log(` ID: ${entity.id}`);
|
|
878
|
+
if (entity.description) {
|
|
879
|
+
console.log(` Description: ${entity.description}`);
|
|
880
|
+
}
|
|
881
|
+
if (entity.aliases && entity.aliases.length > 0) {
|
|
882
|
+
console.log(` Aliases: ${entity.aliases.join(', ')}`);
|
|
883
|
+
}
|
|
884
|
+
console.log(` Mentions: ${entity.mention_count}`);
|
|
885
|
+
console.log(` Last seen: ${new Date(entity.last_seen_at).toLocaleString()}`);
|
|
886
|
+
console.log();
|
|
887
|
+
});
|
|
888
|
+
}
|
|
889
|
+
catch (err) {
|
|
890
|
+
console.error('ā Error:', err);
|
|
891
|
+
process.exit(1);
|
|
892
|
+
}
|
|
893
|
+
});
|
|
894
|
+
program
|
|
895
|
+
.command('entity-graph <entityId>')
|
|
896
|
+
.description('Show entity relationship graph')
|
|
897
|
+
.option('-d, --depth <number>', 'Maximum depth', '2')
|
|
898
|
+
.option('-c, --min-confidence <number>', 'Minimum confidence', '0.5')
|
|
899
|
+
.action(async (entityId, options) => {
|
|
900
|
+
const config = loadConfig();
|
|
901
|
+
if (!config) {
|
|
902
|
+
console.error('ā No config found. Run `openclaw-memory init` first.');
|
|
903
|
+
process.exit(1);
|
|
904
|
+
}
|
|
905
|
+
try {
|
|
906
|
+
const { OpenClawMemory } = await Promise.resolve().then(() => __importStar(require('./index')));
|
|
907
|
+
const memory = new OpenClawMemory({
|
|
908
|
+
supabaseUrl: config.supabaseUrl,
|
|
909
|
+
supabaseKey: config.supabaseKey,
|
|
910
|
+
agentId: config.agentId
|
|
911
|
+
});
|
|
912
|
+
// Get direct relationships
|
|
913
|
+
const relationships = await memory.getEntityRelationships(entityId);
|
|
914
|
+
console.log(`\nšøļø Entity Relationship Graph\n`);
|
|
915
|
+
if (relationships.length === 0) {
|
|
916
|
+
console.log('No relationships found.');
|
|
917
|
+
return;
|
|
918
|
+
}
|
|
919
|
+
// Group by direction
|
|
920
|
+
const outgoing = relationships.filter(r => r.direction === 'outgoing');
|
|
921
|
+
const incoming = relationships.filter(r => r.direction === 'incoming');
|
|
922
|
+
if (outgoing.length > 0) {
|
|
923
|
+
console.log('Outgoing relationships:');
|
|
924
|
+
outgoing.forEach(r => {
|
|
925
|
+
console.log(` ā ${r.relatedEntity.name} [${r.relationship.relationship_type}]`);
|
|
926
|
+
console.log(` Confidence: ${r.relationship.confidence.toFixed(2)}, Mentions: ${r.relationship.mention_count}`);
|
|
927
|
+
});
|
|
928
|
+
console.log();
|
|
929
|
+
}
|
|
930
|
+
if (incoming.length > 0) {
|
|
931
|
+
console.log('Incoming relationships:');
|
|
932
|
+
incoming.forEach(r => {
|
|
933
|
+
console.log(` ā ${r.relatedEntity.name} [${r.relationship.relationship_type}]`);
|
|
934
|
+
console.log(` Confidence: ${r.relationship.confidence.toFixed(2)}, Mentions: ${r.relationship.mention_count}`);
|
|
935
|
+
});
|
|
936
|
+
console.log();
|
|
937
|
+
}
|
|
938
|
+
// Get related entities (graph traversal)
|
|
939
|
+
const related = await memory.findRelatedEntities(entityId, {
|
|
940
|
+
maxDepth: parseInt(options.depth),
|
|
941
|
+
minConfidence: parseFloat(options.minConfidence)
|
|
942
|
+
});
|
|
943
|
+
if (related.length > 0) {
|
|
944
|
+
console.log(`Related entities (within ${options.depth} hops):`);
|
|
945
|
+
related.forEach(r => {
|
|
946
|
+
const path = r.relationshipPath.join(' ā ');
|
|
947
|
+
console.log(` ${r.entityName} [${r.entityType}]`);
|
|
948
|
+
console.log(` Path: ${path}`);
|
|
949
|
+
console.log(` Confidence: ${r.totalConfidence.toFixed(3)}, Depth: ${r.depth}`);
|
|
950
|
+
});
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
catch (err) {
|
|
954
|
+
console.error('ā Error:', err);
|
|
955
|
+
process.exit(1);
|
|
956
|
+
}
|
|
957
|
+
});
|
|
958
|
+
program
|
|
959
|
+
.command('entity-stats')
|
|
960
|
+
.description('Show entity network statistics')
|
|
961
|
+
.action(async () => {
|
|
962
|
+
const config = loadConfig();
|
|
963
|
+
if (!config) {
|
|
964
|
+
console.error('ā No config found. Run `openclaw-memory init` first.');
|
|
965
|
+
process.exit(1);
|
|
966
|
+
}
|
|
967
|
+
try {
|
|
968
|
+
const { OpenClawMemory } = await Promise.resolve().then(() => __importStar(require('./index')));
|
|
969
|
+
const memory = new OpenClawMemory({
|
|
970
|
+
supabaseUrl: config.supabaseUrl,
|
|
971
|
+
supabaseKey: config.supabaseKey,
|
|
972
|
+
agentId: config.agentId
|
|
973
|
+
});
|
|
974
|
+
const stats = await memory.getEntityNetworkStats();
|
|
975
|
+
console.log('\nš Entity Network Statistics\n');
|
|
976
|
+
console.log(`Total Entities: ${stats.totalEntities}`);
|
|
977
|
+
console.log(`Total Relationships: ${stats.totalRelationships}`);
|
|
978
|
+
console.log(`Avg Connections per Entity: ${stats.avgConnectionsPerEntity.toFixed(2)}`);
|
|
979
|
+
if (stats.mostConnectedEntity) {
|
|
980
|
+
console.log(`\nMost Connected Entity:`);
|
|
981
|
+
console.log(` Name: ${stats.mostConnectedEntity.name}`);
|
|
982
|
+
console.log(` Connections: ${stats.mostConnectedEntity.connectionCount}`);
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
catch (err) {
|
|
986
|
+
console.error('ā Error:', err);
|
|
987
|
+
process.exit(1);
|
|
988
|
+
}
|
|
989
|
+
});
|
|
990
|
+
program
|
|
991
|
+
.command('extract-entities <text>')
|
|
992
|
+
.description('Extract entities and relationships from text')
|
|
993
|
+
.option('-k, --openai-key <key>', 'OpenAI API key (or set OPENAI_API_KEY env var)')
|
|
994
|
+
.action(async (text, options) => {
|
|
995
|
+
const config = loadConfig();
|
|
996
|
+
if (!config) {
|
|
997
|
+
console.error('ā No config found. Run `openclaw-memory init` first.');
|
|
998
|
+
process.exit(1);
|
|
999
|
+
}
|
|
1000
|
+
const apiKey = options.openaiKey || process.env.OPENAI_API_KEY;
|
|
1001
|
+
if (!apiKey) {
|
|
1002
|
+
console.error('ā OpenAI API key required. Provide via --openai-key or OPENAI_API_KEY env var.');
|
|
1003
|
+
process.exit(1);
|
|
1004
|
+
}
|
|
1005
|
+
try {
|
|
1006
|
+
const { OpenClawMemory } = await Promise.resolve().then(() => __importStar(require('./index')));
|
|
1007
|
+
const memory = new OpenClawMemory({
|
|
1008
|
+
supabaseUrl: config.supabaseUrl,
|
|
1009
|
+
supabaseKey: config.supabaseKey,
|
|
1010
|
+
agentId: config.agentId,
|
|
1011
|
+
openaiApiKey: apiKey
|
|
1012
|
+
});
|
|
1013
|
+
console.log('š§ Extracting entities and relationships...\n');
|
|
1014
|
+
const result = await memory.extractEntitiesWithRelationships(text);
|
|
1015
|
+
console.log(`ā
Extracted:`);
|
|
1016
|
+
console.log(` Entities: ${result.entities.length}`);
|
|
1017
|
+
console.log(` Relationships: ${result.relationships.length}\n`);
|
|
1018
|
+
if (result.entities.length > 0) {
|
|
1019
|
+
console.log('Entities:');
|
|
1020
|
+
result.entities.forEach(e => {
|
|
1021
|
+
console.log(` - ${e.name} [${e.entity_type}]`);
|
|
1022
|
+
if (e.description) {
|
|
1023
|
+
console.log(` ${e.description}`);
|
|
1024
|
+
}
|
|
1025
|
+
});
|
|
1026
|
+
console.log();
|
|
1027
|
+
}
|
|
1028
|
+
if (result.relationships.length > 0) {
|
|
1029
|
+
console.log('Relationships:');
|
|
1030
|
+
result.relationships.forEach(r => {
|
|
1031
|
+
const source = result.entities.find(e => e.id === r.source_entity_id)?.name;
|
|
1032
|
+
const target = result.entities.find(e => e.id === r.target_entity_id)?.name;
|
|
1033
|
+
console.log(` - ${source} ā ${r.relationship_type} ā ${target}`);
|
|
1034
|
+
console.log(` Confidence: ${r.confidence.toFixed(2)}`);
|
|
1035
|
+
});
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
catch (err) {
|
|
1039
|
+
console.error('ā Error:', err);
|
|
1040
|
+
process.exit(1);
|
|
1041
|
+
}
|
|
1042
|
+
});
|
|
1043
|
+
// ============ TASK DEPENDENCIES ============
|
|
1044
|
+
program
|
|
1045
|
+
.command('task-deps <taskId>')
|
|
1046
|
+
.description('Show task dependencies')
|
|
1047
|
+
.action(async (taskId) => {
|
|
1048
|
+
const config = loadConfig();
|
|
1049
|
+
if (!config) {
|
|
1050
|
+
console.error('ā No config found. Run `openclaw-memory init` first.');
|
|
1051
|
+
process.exit(1);
|
|
1052
|
+
}
|
|
1053
|
+
try {
|
|
1054
|
+
const { OpenClawMemory } = await Promise.resolve().then(() => __importStar(require('./index')));
|
|
1055
|
+
const memory = new OpenClawMemory({
|
|
1056
|
+
supabaseUrl: config.supabaseUrl,
|
|
1057
|
+
supabaseKey: config.supabaseKey,
|
|
1058
|
+
agentId: config.agentId
|
|
1059
|
+
});
|
|
1060
|
+
const dependencies = await memory.getTaskDependencies(taskId);
|
|
1061
|
+
const blocked = await memory.isTaskBlocked(taskId);
|
|
1062
|
+
console.log(`\nš Task Dependencies for ${taskId}:`);
|
|
1063
|
+
console.log(`Status: ${blocked ? 'š« BLOCKED' : 'ā
Ready'}\n`);
|
|
1064
|
+
if (dependencies.length === 0) {
|
|
1065
|
+
console.log('No dependencies');
|
|
1066
|
+
}
|
|
1067
|
+
else {
|
|
1068
|
+
dependencies.forEach(dep => {
|
|
1069
|
+
const statusIcon = dep.status === 'done' ? 'ā
' : 'ā³';
|
|
1070
|
+
console.log(`${statusIcon} [${dep.status.toUpperCase()}] ${dep.title}`);
|
|
1071
|
+
});
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
catch (err) {
|
|
1075
|
+
console.error('ā Error:', err);
|
|
1076
|
+
process.exit(1);
|
|
1077
|
+
}
|
|
1078
|
+
});
|
|
1079
|
+
program
|
|
1080
|
+
.command('task-add-dep <taskId> <dependsOnTaskId>')
|
|
1081
|
+
.description('Add a task dependency')
|
|
1082
|
+
.action(async (taskId, dependsOnTaskId) => {
|
|
1083
|
+
const config = loadConfig();
|
|
1084
|
+
if (!config) {
|
|
1085
|
+
console.error('ā No config found. Run `openclaw-memory init` first.');
|
|
1086
|
+
process.exit(1);
|
|
1087
|
+
}
|
|
1088
|
+
try {
|
|
1089
|
+
const { OpenClawMemory } = await Promise.resolve().then(() => __importStar(require('./index')));
|
|
1090
|
+
const memory = new OpenClawMemory({
|
|
1091
|
+
supabaseUrl: config.supabaseUrl,
|
|
1092
|
+
supabaseKey: config.supabaseKey,
|
|
1093
|
+
agentId: config.agentId
|
|
1094
|
+
});
|
|
1095
|
+
await memory.addTaskDependency(taskId, dependsOnTaskId);
|
|
1096
|
+
console.log(`ā
Dependency added: ${taskId} depends on ${dependsOnTaskId}`);
|
|
1097
|
+
}
|
|
1098
|
+
catch (err) {
|
|
1099
|
+
console.error('ā Error:', err);
|
|
1100
|
+
process.exit(1);
|
|
1101
|
+
}
|
|
1102
|
+
});
|
|
1103
|
+
program
|
|
1104
|
+
.command('task-ready')
|
|
1105
|
+
.description('List tasks ready to start (no blocking dependencies)')
|
|
1106
|
+
.option('-u, --user <userId>', 'Filter by user ID')
|
|
1107
|
+
.action(async (options) => {
|
|
1108
|
+
const config = loadConfig();
|
|
1109
|
+
if (!config) {
|
|
1110
|
+
console.error('ā No config found. Run `openclaw-memory init` first.');
|
|
1111
|
+
process.exit(1);
|
|
1112
|
+
}
|
|
1113
|
+
try {
|
|
1114
|
+
const { OpenClawMemory } = await Promise.resolve().then(() => __importStar(require('./index')));
|
|
1115
|
+
const memory = new OpenClawMemory({
|
|
1116
|
+
supabaseUrl: config.supabaseUrl,
|
|
1117
|
+
supabaseKey: config.supabaseKey,
|
|
1118
|
+
agentId: config.agentId
|
|
1119
|
+
});
|
|
1120
|
+
const tasks = await memory.getReadyTasks({ userId: options.user });
|
|
1121
|
+
console.log(`\nā
Ready Tasks (${tasks.length}):\n`);
|
|
1122
|
+
tasks.forEach(task => {
|
|
1123
|
+
console.log(`[P${task.priority}] ${task.title}`);
|
|
1124
|
+
if (task.description) {
|
|
1125
|
+
console.log(` ${task.description}`);
|
|
1126
|
+
}
|
|
1127
|
+
if (task.due_at) {
|
|
1128
|
+
console.log(` Due: ${new Date(task.due_at).toLocaleString()}`);
|
|
1129
|
+
}
|
|
1130
|
+
console.log();
|
|
1131
|
+
});
|
|
1132
|
+
}
|
|
1133
|
+
catch (err) {
|
|
1134
|
+
console.error('ā Error:', err);
|
|
1135
|
+
process.exit(1);
|
|
1136
|
+
}
|
|
1137
|
+
});
|
|
1138
|
+
// ============ TASK TEMPLATES ============
|
|
1139
|
+
program
|
|
1140
|
+
.command('task-template <name>')
|
|
1141
|
+
.description('Create a task template')
|
|
1142
|
+
.option('-f, --file <path>', 'JSON file with template definition')
|
|
1143
|
+
.action(async (name, options) => {
|
|
1144
|
+
const config = loadConfig();
|
|
1145
|
+
if (!config) {
|
|
1146
|
+
console.error('ā No config found. Run `openclaw-memory init` first.');
|
|
1147
|
+
process.exit(1);
|
|
1148
|
+
}
|
|
1149
|
+
if (!options.file) {
|
|
1150
|
+
console.error('ā --file required. Provide a JSON file with template definition.');
|
|
1151
|
+
console.log('\nExample JSON:');
|
|
1152
|
+
console.log(JSON.stringify({
|
|
1153
|
+
name: 'Example Template',
|
|
1154
|
+
description: 'Template description',
|
|
1155
|
+
tasks: [
|
|
1156
|
+
{ title: 'Task 1', description: 'First task', priority: 5 },
|
|
1157
|
+
{ title: 'Task 2', description: 'Depends on Task 1', priority: 3, dependencies: [0] }
|
|
1158
|
+
]
|
|
1159
|
+
}, null, 2));
|
|
1160
|
+
process.exit(1);
|
|
1161
|
+
}
|
|
1162
|
+
try {
|
|
1163
|
+
const fs = await Promise.resolve().then(() => __importStar(require('fs/promises')));
|
|
1164
|
+
const templateData = JSON.parse(await fs.readFile(options.file, 'utf-8'));
|
|
1165
|
+
const { OpenClawMemory } = await Promise.resolve().then(() => __importStar(require('./index')));
|
|
1166
|
+
const memory = new OpenClawMemory({
|
|
1167
|
+
supabaseUrl: config.supabaseUrl,
|
|
1168
|
+
supabaseKey: config.supabaseKey,
|
|
1169
|
+
agentId: config.agentId
|
|
1170
|
+
});
|
|
1171
|
+
const result = await memory.createTaskTemplate(templateData);
|
|
1172
|
+
console.log(`ā
Template created: ${result.id}`);
|
|
1173
|
+
}
|
|
1174
|
+
catch (err) {
|
|
1175
|
+
console.error('ā Error:', err);
|
|
1176
|
+
process.exit(1);
|
|
1177
|
+
}
|
|
1178
|
+
});
|
|
1179
|
+
program
|
|
1180
|
+
.command('task-templates')
|
|
1181
|
+
.description('List all task templates')
|
|
1182
|
+
.action(async () => {
|
|
1183
|
+
const config = loadConfig();
|
|
1184
|
+
if (!config) {
|
|
1185
|
+
console.error('ā No config found. Run `openclaw-memory init` first.');
|
|
1186
|
+
process.exit(1);
|
|
1187
|
+
}
|
|
1188
|
+
try {
|
|
1189
|
+
const { OpenClawMemory } = await Promise.resolve().then(() => __importStar(require('./index')));
|
|
1190
|
+
const memory = new OpenClawMemory({
|
|
1191
|
+
supabaseUrl: config.supabaseUrl,
|
|
1192
|
+
supabaseKey: config.supabaseKey,
|
|
1193
|
+
agentId: config.agentId
|
|
1194
|
+
});
|
|
1195
|
+
const templates = await memory.getTaskTemplates();
|
|
1196
|
+
console.log(`\nš Task Templates (${templates.length}):\n`);
|
|
1197
|
+
templates.forEach(t => {
|
|
1198
|
+
console.log(`š ${t.name} [ID: ${t.id}]`);
|
|
1199
|
+
if (t.description) {
|
|
1200
|
+
console.log(` ${t.description}`);
|
|
1201
|
+
}
|
|
1202
|
+
console.log(` Tasks: ${t.tasks.length}`);
|
|
1203
|
+
console.log();
|
|
1204
|
+
});
|
|
1205
|
+
}
|
|
1206
|
+
catch (err) {
|
|
1207
|
+
console.error('ā Error:', err);
|
|
1208
|
+
process.exit(1);
|
|
1209
|
+
}
|
|
1210
|
+
});
|
|
1211
|
+
program
|
|
1212
|
+
.command('task-apply-template <templateId>')
|
|
1213
|
+
.description('Apply a task template (create all tasks)')
|
|
1214
|
+
.option('-u, --user <userId>', 'User ID for created tasks')
|
|
1215
|
+
.option('-s, --start <date>', 'Start date (ISO format)')
|
|
1216
|
+
.action(async (templateId, options) => {
|
|
1217
|
+
const config = loadConfig();
|
|
1218
|
+
if (!config) {
|
|
1219
|
+
console.error('ā No config found. Run `openclaw-memory init` first.');
|
|
1220
|
+
process.exit(1);
|
|
1221
|
+
}
|
|
1222
|
+
try {
|
|
1223
|
+
const { OpenClawMemory } = await Promise.resolve().then(() => __importStar(require('./index')));
|
|
1224
|
+
const memory = new OpenClawMemory({
|
|
1225
|
+
supabaseUrl: config.supabaseUrl,
|
|
1226
|
+
supabaseKey: config.supabaseKey,
|
|
1227
|
+
agentId: config.agentId
|
|
1228
|
+
});
|
|
1229
|
+
const tasks = await memory.applyTaskTemplate(templateId, {
|
|
1230
|
+
userId: options.user,
|
|
1231
|
+
startDate: options.start
|
|
1232
|
+
});
|
|
1233
|
+
console.log(`ā
Created ${tasks.length} tasks from template:`);
|
|
1234
|
+
tasks.forEach(t => {
|
|
1235
|
+
console.log(` - ${t.title} [${t.id}]`);
|
|
1236
|
+
});
|
|
1237
|
+
}
|
|
1238
|
+
catch (err) {
|
|
1239
|
+
console.error('ā Error:', err);
|
|
1240
|
+
process.exit(1);
|
|
1241
|
+
}
|
|
1242
|
+
});
|
|
1243
|
+
// ============ TASK REMINDERS ============
|
|
1244
|
+
program
|
|
1245
|
+
.command('task-reminders')
|
|
1246
|
+
.description('Show tasks needing reminders')
|
|
1247
|
+
.option('-u, --user <userId>', 'Filter by user ID')
|
|
1248
|
+
.option('-h, --hours <hours>', 'Hours ahead to check (default: 24)', '24')
|
|
1249
|
+
.action(async (options) => {
|
|
1250
|
+
const config = loadConfig();
|
|
1251
|
+
if (!config) {
|
|
1252
|
+
console.error('ā No config found. Run `openclaw-memory init` first.');
|
|
1253
|
+
process.exit(1);
|
|
1254
|
+
}
|
|
1255
|
+
try {
|
|
1256
|
+
const { OpenClawMemory } = await Promise.resolve().then(() => __importStar(require('./index')));
|
|
1257
|
+
const memory = new OpenClawMemory({
|
|
1258
|
+
supabaseUrl: config.supabaseUrl,
|
|
1259
|
+
supabaseKey: config.supabaseKey,
|
|
1260
|
+
agentId: config.agentId
|
|
1261
|
+
});
|
|
1262
|
+
const tasks = await memory.getTasksNeedingReminders({
|
|
1263
|
+
userId: options.user,
|
|
1264
|
+
hoursAhead: parseInt(options.hours)
|
|
1265
|
+
});
|
|
1266
|
+
console.log(`\nā° Tasks Needing Reminders (${tasks.length}):\n`);
|
|
1267
|
+
tasks.forEach(task => {
|
|
1268
|
+
console.log(memory.formatTaskReminder(task, task.timeUntilDue));
|
|
1269
|
+
console.log();
|
|
1270
|
+
});
|
|
1271
|
+
}
|
|
1272
|
+
catch (err) {
|
|
1273
|
+
console.error('ā Error:', err);
|
|
1274
|
+
process.exit(1);
|
|
1275
|
+
}
|
|
1276
|
+
});
|
|
1277
|
+
// ============ LEARNING PATTERNS ============
|
|
1278
|
+
program
|
|
1279
|
+
.command('learning-patterns')
|
|
1280
|
+
.description('Detect patterns in learnings')
|
|
1281
|
+
.action(async () => {
|
|
1282
|
+
const config = loadConfig();
|
|
1283
|
+
if (!config) {
|
|
1284
|
+
console.error('ā No config found. Run `openclaw-memory init` first.');
|
|
1285
|
+
process.exit(1);
|
|
1286
|
+
}
|
|
1287
|
+
try {
|
|
1288
|
+
const { OpenClawMemory } = await Promise.resolve().then(() => __importStar(require('./index')));
|
|
1289
|
+
const memory = new OpenClawMemory({
|
|
1290
|
+
supabaseUrl: config.supabaseUrl,
|
|
1291
|
+
supabaseKey: config.supabaseKey,
|
|
1292
|
+
agentId: config.agentId
|
|
1293
|
+
});
|
|
1294
|
+
const patterns = await memory.detectLearningPatterns();
|
|
1295
|
+
console.log('\nš Learning Patterns:\n');
|
|
1296
|
+
console.log('Common Categories:');
|
|
1297
|
+
patterns.commonCategories.forEach(c => {
|
|
1298
|
+
console.log(` - ${c.category}: ${c.count}`);
|
|
1299
|
+
});
|
|
1300
|
+
console.log('\nCommon Triggers:');
|
|
1301
|
+
patterns.commonTriggers.slice(0, 5).forEach(t => {
|
|
1302
|
+
console.log(` - "${t.pattern}": ${t.count} occurrences`);
|
|
1303
|
+
});
|
|
1304
|
+
console.log('\nRecent Trends:');
|
|
1305
|
+
patterns.recentTrends.forEach(t => {
|
|
1306
|
+
const icon = t.severity === 'critical' ? 'š“' : t.severity === 'warning' ? 'š”' : 'š¢';
|
|
1307
|
+
console.log(` ${icon} Week ${t.week}: ${t.count} learnings`);
|
|
1308
|
+
});
|
|
1309
|
+
console.log('\nTop Applied Lessons:');
|
|
1310
|
+
patterns.topLessons.forEach(l => {
|
|
1311
|
+
console.log(` - "${l.lesson.substring(0, 60)}..." (${l.applied} times)`);
|
|
1312
|
+
});
|
|
1313
|
+
}
|
|
1314
|
+
catch (err) {
|
|
1315
|
+
console.error('ā Error:', err);
|
|
1316
|
+
process.exit(1);
|
|
1317
|
+
}
|
|
1318
|
+
});
|
|
1319
|
+
program
|
|
1320
|
+
.command('learning-recommend <context>')
|
|
1321
|
+
.description('Get learning recommendations for current context')
|
|
1322
|
+
.option('-l, --limit <number>', 'Max recommendations', '5')
|
|
1323
|
+
.action(async (context, options) => {
|
|
1324
|
+
const config = loadConfig();
|
|
1325
|
+
if (!config) {
|
|
1326
|
+
console.error('ā No config found. Run `openclaw-memory init` first.');
|
|
1327
|
+
process.exit(1);
|
|
1328
|
+
}
|
|
1329
|
+
try {
|
|
1330
|
+
const { OpenClawMemory } = await Promise.resolve().then(() => __importStar(require('./index')));
|
|
1331
|
+
const memory = new OpenClawMemory({
|
|
1332
|
+
supabaseUrl: config.supabaseUrl,
|
|
1333
|
+
supabaseKey: config.supabaseKey,
|
|
1334
|
+
agentId: config.agentId
|
|
1335
|
+
});
|
|
1336
|
+
const recommendations = await memory.getLearningRecommendations(context, parseInt(options.limit));
|
|
1337
|
+
console.log(`\nš” Learning Recommendations for "${context}":\n`);
|
|
1338
|
+
recommendations.forEach(l => {
|
|
1339
|
+
console.log(`[${l.severity.toUpperCase()}] ${l.category}`);
|
|
1340
|
+
console.log(`Trigger: ${l.trigger}`);
|
|
1341
|
+
console.log(`Lesson: ${l.lesson}`);
|
|
1342
|
+
if (l.action) {
|
|
1343
|
+
console.log(`Action: ${l.action}`);
|
|
1344
|
+
}
|
|
1345
|
+
console.log(`Applied: ${l.applied_count} times\n`);
|
|
1346
|
+
});
|
|
1347
|
+
}
|
|
1348
|
+
catch (err) {
|
|
1349
|
+
console.error('ā Error:', err);
|
|
1350
|
+
process.exit(1);
|
|
1351
|
+
}
|
|
1352
|
+
});
|
|
1353
|
+
// ============ LEARNING SIMILARITY ============
|
|
1354
|
+
program
|
|
1355
|
+
.command('learning-similar <learningId>')
|
|
1356
|
+
.description('Find similar learnings using embeddings')
|
|
1357
|
+
.option('-k, --openai-key <key>', 'OpenAI API key (or set OPENAI_API_KEY env var)')
|
|
1358
|
+
.option('-l, --limit <number>', 'Max results', '5')
|
|
1359
|
+
.option('-t, --threshold <number>', 'Similarity threshold (0-1)', '0.7')
|
|
1360
|
+
.action(async (learningId, options) => {
|
|
1361
|
+
const config = loadConfig();
|
|
1362
|
+
if (!config) {
|
|
1363
|
+
console.error('ā No config found. Run `openclaw-memory init` first.');
|
|
1364
|
+
process.exit(1);
|
|
1365
|
+
}
|
|
1366
|
+
const apiKey = options.openaiKey || process.env.OPENAI_API_KEY;
|
|
1367
|
+
if (!apiKey) {
|
|
1368
|
+
console.error('ā OpenAI API key required. Provide via --openai-key or OPENAI_API_KEY env var.');
|
|
1369
|
+
process.exit(1);
|
|
1370
|
+
}
|
|
1371
|
+
try {
|
|
1372
|
+
const { OpenClawMemory } = await Promise.resolve().then(() => __importStar(require('./index')));
|
|
1373
|
+
const memory = new OpenClawMemory({
|
|
1374
|
+
supabaseUrl: config.supabaseUrl,
|
|
1375
|
+
supabaseKey: config.supabaseKey,
|
|
1376
|
+
agentId: config.agentId,
|
|
1377
|
+
openaiApiKey: apiKey
|
|
1378
|
+
});
|
|
1379
|
+
const similar = await memory.findSimilarLearnings(learningId, {
|
|
1380
|
+
limit: parseInt(options.limit),
|
|
1381
|
+
threshold: parseFloat(options.threshold)
|
|
1382
|
+
});
|
|
1383
|
+
console.log(`\nš Similar Learnings to ${learningId}:\n`);
|
|
1384
|
+
similar.forEach(l => {
|
|
1385
|
+
console.log(`[Similarity: ${(l.similarity * 100).toFixed(1)}%] ${l.category}`);
|
|
1386
|
+
console.log(`Trigger: ${l.trigger}`);
|
|
1387
|
+
console.log(`Lesson: ${l.lesson}`);
|
|
1388
|
+
console.log(`ID: ${l.id}\n`);
|
|
1389
|
+
});
|
|
1390
|
+
}
|
|
1391
|
+
catch (err) {
|
|
1392
|
+
console.error('ā Error:', err);
|
|
1393
|
+
process.exit(1);
|
|
1394
|
+
}
|
|
1395
|
+
});
|
|
1396
|
+
// ============ LEARNING EXPORT ============
|
|
1397
|
+
program
|
|
1398
|
+
.command('learning-export')
|
|
1399
|
+
.description('Export learnings to markdown report')
|
|
1400
|
+
.option('-c, --category <category>', 'Filter by category')
|
|
1401
|
+
.option('-s, --severity <severity>', 'Filter by severity')
|
|
1402
|
+
.option('-d, --since <date>', 'Only include learnings since date (ISO format)')
|
|
1403
|
+
.option('-o, --output <path>', 'Output file path', 'learnings-report.md')
|
|
1404
|
+
.action(async (options) => {
|
|
1405
|
+
const config = loadConfig();
|
|
1406
|
+
if (!config) {
|
|
1407
|
+
console.error('ā No config found. Run `openclaw-memory init` first.');
|
|
1408
|
+
process.exit(1);
|
|
1409
|
+
}
|
|
1410
|
+
try {
|
|
1411
|
+
const { OpenClawMemory } = await Promise.resolve().then(() => __importStar(require('./index')));
|
|
1412
|
+
const memory = new OpenClawMemory({
|
|
1413
|
+
supabaseUrl: config.supabaseUrl,
|
|
1414
|
+
supabaseKey: config.supabaseKey,
|
|
1415
|
+
agentId: config.agentId
|
|
1416
|
+
});
|
|
1417
|
+
const report = await memory.exportLearningsReport({
|
|
1418
|
+
category: options.category,
|
|
1419
|
+
severity: options.severity,
|
|
1420
|
+
since: options.since
|
|
1421
|
+
});
|
|
1422
|
+
const fs = await Promise.resolve().then(() => __importStar(require('fs/promises')));
|
|
1423
|
+
await fs.writeFile(options.output, report, 'utf-8');
|
|
1424
|
+
console.log(`ā
Learning report exported to ${options.output}`);
|
|
1425
|
+
}
|
|
1426
|
+
catch (err) {
|
|
1427
|
+
console.error('ā Error:', err);
|
|
1428
|
+
process.exit(1);
|
|
1429
|
+
}
|
|
1430
|
+
});
|
|
1431
|
+
program
|
|
1432
|
+
.command('learning-export-json')
|
|
1433
|
+
.description('Export learnings to JSON')
|
|
1434
|
+
.option('-c, --category <category>', 'Filter by category')
|
|
1435
|
+
.option('-s, --severity <severity>', 'Filter by severity')
|
|
1436
|
+
.option('-d, --since <date>', 'Only include learnings since date (ISO format)')
|
|
1437
|
+
.option('-o, --output <path>', 'Output file path', 'learnings-export.json')
|
|
1438
|
+
.action(async (options) => {
|
|
1439
|
+
const config = loadConfig();
|
|
1440
|
+
if (!config) {
|
|
1441
|
+
console.error('ā No config found. Run `openclaw-memory init` first.');
|
|
1442
|
+
process.exit(1);
|
|
1443
|
+
}
|
|
1444
|
+
try {
|
|
1445
|
+
const { OpenClawMemory } = await Promise.resolve().then(() => __importStar(require('./index')));
|
|
1446
|
+
const memory = new OpenClawMemory({
|
|
1447
|
+
supabaseUrl: config.supabaseUrl,
|
|
1448
|
+
supabaseKey: config.supabaseKey,
|
|
1449
|
+
agentId: config.agentId
|
|
1450
|
+
});
|
|
1451
|
+
const data = await memory.exportLearningsJSON({
|
|
1452
|
+
category: options.category,
|
|
1453
|
+
severity: options.severity,
|
|
1454
|
+
since: options.since
|
|
1455
|
+
});
|
|
1456
|
+
const fs = await Promise.resolve().then(() => __importStar(require('fs/promises')));
|
|
1457
|
+
await fs.writeFile(options.output, JSON.stringify(data, null, 2), 'utf-8');
|
|
1458
|
+
console.log(`ā
Learning data exported to ${options.output}`);
|
|
1459
|
+
}
|
|
1460
|
+
catch (err) {
|
|
1461
|
+
console.error('ā Error:', err);
|
|
1462
|
+
process.exit(1);
|
|
1463
|
+
}
|
|
1464
|
+
});
|
|
1465
|
+
// ============ PHASE 9: MIGRATION & IMPORT ============
|
|
1466
|
+
program
|
|
1467
|
+
.command('import-memory-md <path>')
|
|
1468
|
+
.description('Import MEMORY.md into memories table')
|
|
1469
|
+
.action(async (memoryPath) => {
|
|
1470
|
+
const config = loadConfig();
|
|
1471
|
+
if (!config) {
|
|
1472
|
+
console.error('ā No config found. Run `openclaw-memory init` first.');
|
|
1473
|
+
process.exit(1);
|
|
1474
|
+
}
|
|
1475
|
+
try {
|
|
1476
|
+
console.log(`š„ Importing MEMORY.md from: ${memoryPath}\n`);
|
|
1477
|
+
const { parseMemoryMd } = await Promise.resolve().then(() => __importStar(require('./parsers')));
|
|
1478
|
+
const { OpenClawMemory } = await Promise.resolve().then(() => __importStar(require('./index')));
|
|
1479
|
+
const memories = parseMemoryMd(memoryPath);
|
|
1480
|
+
console.log(`Found ${memories.length} memories to import\n`);
|
|
1481
|
+
const memory = new OpenClawMemory({
|
|
1482
|
+
supabaseUrl: config.supabaseUrl,
|
|
1483
|
+
supabaseKey: config.supabaseKey,
|
|
1484
|
+
agentId: config.agentId
|
|
1485
|
+
});
|
|
1486
|
+
let imported = 0;
|
|
1487
|
+
for (const mem of memories) {
|
|
1488
|
+
try {
|
|
1489
|
+
await memory.remember({
|
|
1490
|
+
content: mem.content,
|
|
1491
|
+
category: mem.category,
|
|
1492
|
+
importance: mem.importance,
|
|
1493
|
+
metadata: mem.metadata
|
|
1494
|
+
});
|
|
1495
|
+
imported++;
|
|
1496
|
+
}
|
|
1497
|
+
catch (err) {
|
|
1498
|
+
console.error(`ā ļø Failed to import: ${mem.content.substring(0, 50)}...`, err);
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
console.log(`\nā
Imported ${imported}/${memories.length} memories`);
|
|
1502
|
+
}
|
|
1503
|
+
catch (err) {
|
|
1504
|
+
console.error('ā Import error:', err);
|
|
1505
|
+
process.exit(1);
|
|
1506
|
+
}
|
|
1507
|
+
});
|
|
1508
|
+
program
|
|
1509
|
+
.command('import-daily-logs <directory>')
|
|
1510
|
+
.description('Import memory/*.md daily logs into sessions table')
|
|
1511
|
+
.option('-u, --user-id <id>', 'User ID for sessions', 'default')
|
|
1512
|
+
.action(async (directory, options) => {
|
|
1513
|
+
const config = loadConfig();
|
|
1514
|
+
if (!config) {
|
|
1515
|
+
console.error('ā No config found. Run `openclaw-memory init` first.');
|
|
1516
|
+
process.exit(1);
|
|
1517
|
+
}
|
|
1518
|
+
try {
|
|
1519
|
+
console.log(`š„ Importing daily logs from: ${directory}\n`);
|
|
1520
|
+
const { parseAllDailyLogs } = await Promise.resolve().then(() => __importStar(require('./parsers')));
|
|
1521
|
+
const { OpenClawMemory } = await Promise.resolve().then(() => __importStar(require('./index')));
|
|
1522
|
+
const sessions = parseAllDailyLogs(directory, options.userId);
|
|
1523
|
+
console.log(`Found ${sessions.length} sessions to import\n`);
|
|
1524
|
+
const memory = new OpenClawMemory({
|
|
1525
|
+
supabaseUrl: config.supabaseUrl,
|
|
1526
|
+
supabaseKey: config.supabaseKey,
|
|
1527
|
+
agentId: config.agentId
|
|
1528
|
+
});
|
|
1529
|
+
let imported = 0;
|
|
1530
|
+
for (const sess of sessions) {
|
|
1531
|
+
try {
|
|
1532
|
+
const session = await memory.startSession({
|
|
1533
|
+
userId: sess.user_id,
|
|
1534
|
+
channel: sess.channel
|
|
1535
|
+
});
|
|
1536
|
+
for (const msg of sess.messages) {
|
|
1537
|
+
await memory.addMessage(session.id, {
|
|
1538
|
+
role: msg.role,
|
|
1539
|
+
content: msg.content
|
|
1540
|
+
});
|
|
1541
|
+
}
|
|
1542
|
+
if (sess.ended_at || sess.summary) {
|
|
1543
|
+
await memory.endSession(session.id, { summary: sess.summary });
|
|
1544
|
+
}
|
|
1545
|
+
imported++;
|
|
1546
|
+
}
|
|
1547
|
+
catch (err) {
|
|
1548
|
+
console.error(`ā ļø Failed to import session:`, err);
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1551
|
+
console.log(`\nā
Imported ${imported}/${sessions.length} sessions`);
|
|
1552
|
+
}
|
|
1553
|
+
catch (err) {
|
|
1554
|
+
console.error('ā Import error:', err);
|
|
1555
|
+
process.exit(1);
|
|
1556
|
+
}
|
|
1557
|
+
});
|
|
1558
|
+
program
|
|
1559
|
+
.command('import-todo-md <path>')
|
|
1560
|
+
.description('Import TODO.md into tasks table')
|
|
1561
|
+
.action(async (todoPath) => {
|
|
1562
|
+
const config = loadConfig();
|
|
1563
|
+
if (!config) {
|
|
1564
|
+
console.error('ā No config found. Run `openclaw-memory init` first.');
|
|
1565
|
+
process.exit(1);
|
|
1566
|
+
}
|
|
1567
|
+
try {
|
|
1568
|
+
console.log(`š„ Importing TODO.md from: ${todoPath}\n`);
|
|
1569
|
+
const { parseTodoMd } = await Promise.resolve().then(() => __importStar(require('./parsers')));
|
|
1570
|
+
const { OpenClawMemory } = await Promise.resolve().then(() => __importStar(require('./index')));
|
|
1571
|
+
const tasks = parseTodoMd(todoPath);
|
|
1572
|
+
console.log(`Found ${tasks.length} tasks to import\n`);
|
|
1573
|
+
const memory = new OpenClawMemory({
|
|
1574
|
+
supabaseUrl: config.supabaseUrl,
|
|
1575
|
+
supabaseKey: config.supabaseKey,
|
|
1576
|
+
agentId: config.agentId
|
|
1577
|
+
});
|
|
1578
|
+
let imported = 0;
|
|
1579
|
+
for (const task of tasks) {
|
|
1580
|
+
try {
|
|
1581
|
+
await memory.createTask({
|
|
1582
|
+
title: task.title,
|
|
1583
|
+
description: task.description,
|
|
1584
|
+
priority: task.priority,
|
|
1585
|
+
dueAt: task.due_date,
|
|
1586
|
+
metadata: { ...task.metadata, originalStatus: task.status }
|
|
1587
|
+
});
|
|
1588
|
+
imported++;
|
|
1589
|
+
}
|
|
1590
|
+
catch (err) {
|
|
1591
|
+
console.error(`ā ļø Failed to import task: ${task.title}`, err);
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1594
|
+
console.log(`\nā
Imported ${imported}/${tasks.length} tasks`);
|
|
1595
|
+
}
|
|
1596
|
+
catch (err) {
|
|
1597
|
+
console.error('ā Import error:', err);
|
|
1598
|
+
process.exit(1);
|
|
1599
|
+
}
|
|
1600
|
+
});
|
|
1601
|
+
program
|
|
1602
|
+
.command('import-learnings-md <path>')
|
|
1603
|
+
.description('Import LEARNINGS.md into learnings table')
|
|
1604
|
+
.action(async (learningsPath) => {
|
|
1605
|
+
const config = loadConfig();
|
|
1606
|
+
if (!config) {
|
|
1607
|
+
console.error('ā No config found. Run `openclaw-memory init` first.');
|
|
1608
|
+
process.exit(1);
|
|
1609
|
+
}
|
|
1610
|
+
try {
|
|
1611
|
+
console.log(`š„ Importing LEARNINGS.md from: ${learningsPath}\n`);
|
|
1612
|
+
const { parseLearningsMd } = await Promise.resolve().then(() => __importStar(require('./parsers')));
|
|
1613
|
+
const { OpenClawMemory } = await Promise.resolve().then(() => __importStar(require('./index')));
|
|
1614
|
+
const learnings = parseLearningsMd(learningsPath);
|
|
1615
|
+
console.log(`Found ${learnings.length} learnings to import\n`);
|
|
1616
|
+
const memory = new OpenClawMemory({
|
|
1617
|
+
supabaseUrl: config.supabaseUrl,
|
|
1618
|
+
supabaseKey: config.supabaseKey,
|
|
1619
|
+
agentId: config.agentId
|
|
1620
|
+
});
|
|
1621
|
+
let imported = 0;
|
|
1622
|
+
for (const learning of learnings) {
|
|
1623
|
+
try {
|
|
1624
|
+
// Map parsed category to valid enum value
|
|
1625
|
+
const validCategories = ['error', 'correction', 'improvement', 'capability_gap'];
|
|
1626
|
+
const category = validCategories.includes(learning.category)
|
|
1627
|
+
? learning.category
|
|
1628
|
+
: 'improvement';
|
|
1629
|
+
await memory.learn({
|
|
1630
|
+
category,
|
|
1631
|
+
trigger: learning.trigger,
|
|
1632
|
+
lesson: learning.lesson,
|
|
1633
|
+
metadata: {
|
|
1634
|
+
originalCategory: learning.category,
|
|
1635
|
+
originalImportance: learning.importance
|
|
1636
|
+
}
|
|
1637
|
+
});
|
|
1638
|
+
imported++;
|
|
1639
|
+
}
|
|
1640
|
+
catch (err) {
|
|
1641
|
+
console.error(`ā ļø Failed to import learning:`, err);
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
console.log(`\nā
Imported ${imported}/${learnings.length} learnings`);
|
|
1645
|
+
}
|
|
1646
|
+
catch (err) {
|
|
1647
|
+
console.error('ā Import error:', err);
|
|
1648
|
+
process.exit(1);
|
|
1649
|
+
}
|
|
1650
|
+
});
|
|
1651
|
+
program
|
|
1652
|
+
.command('import-all <workspace>')
|
|
1653
|
+
.description('Import all Clawdbot memory files from workspace (MEMORY.md, memory/, TODO.md, LEARNINGS.md)')
|
|
1654
|
+
.option('-u, --user-id <id>', 'User ID for sessions', 'default')
|
|
1655
|
+
.action(async (workspace, options) => {
|
|
1656
|
+
const config = loadConfig();
|
|
1657
|
+
if (!config) {
|
|
1658
|
+
console.error('ā No config found. Run `openclaw-memory init` first.');
|
|
1659
|
+
process.exit(1);
|
|
1660
|
+
}
|
|
1661
|
+
const { join } = await Promise.resolve().then(() => __importStar(require('path')));
|
|
1662
|
+
const { existsSync } = await Promise.resolve().then(() => __importStar(require('fs')));
|
|
1663
|
+
console.log(`š¦ Importing all memory files from: ${workspace}\n`);
|
|
1664
|
+
const memoryMdPath = join(workspace, 'MEMORY.md');
|
|
1665
|
+
const dailyLogsDir = join(workspace, 'memory');
|
|
1666
|
+
const todoMdPath = join(workspace, 'TODO.md');
|
|
1667
|
+
const learningsMdPath = join(workspace, 'LEARNINGS.md');
|
|
1668
|
+
let totalImported = 0;
|
|
1669
|
+
// Import MEMORY.md
|
|
1670
|
+
if (existsSync(memoryMdPath)) {
|
|
1671
|
+
try {
|
|
1672
|
+
console.log('š Importing MEMORY.md...');
|
|
1673
|
+
const { parseMemoryMd } = await Promise.resolve().then(() => __importStar(require('./parsers')));
|
|
1674
|
+
const { OpenClawMemory } = await Promise.resolve().then(() => __importStar(require('./index')));
|
|
1675
|
+
const memories = parseMemoryMd(memoryMdPath);
|
|
1676
|
+
const memory = new OpenClawMemory({
|
|
1677
|
+
supabaseUrl: config.supabaseUrl,
|
|
1678
|
+
supabaseKey: config.supabaseKey,
|
|
1679
|
+
agentId: config.agentId
|
|
1680
|
+
});
|
|
1681
|
+
for (const mem of memories) {
|
|
1682
|
+
try {
|
|
1683
|
+
await memory.remember({
|
|
1684
|
+
content: mem.content,
|
|
1685
|
+
category: mem.category,
|
|
1686
|
+
importance: mem.importance,
|
|
1687
|
+
metadata: mem.metadata
|
|
1688
|
+
});
|
|
1689
|
+
totalImported++;
|
|
1690
|
+
}
|
|
1691
|
+
catch (err) {
|
|
1692
|
+
// Silent skip
|
|
1693
|
+
}
|
|
1694
|
+
}
|
|
1695
|
+
console.log(`ā
Imported ${memories.length} memories\n`);
|
|
1696
|
+
}
|
|
1697
|
+
catch (err) {
|
|
1698
|
+
console.error('ā ļø Failed to import MEMORY.md:', err);
|
|
1699
|
+
}
|
|
1700
|
+
}
|
|
1701
|
+
// Import daily logs
|
|
1702
|
+
if (existsSync(dailyLogsDir)) {
|
|
1703
|
+
try {
|
|
1704
|
+
console.log('š
Importing daily logs...');
|
|
1705
|
+
const { parseAllDailyLogs } = await Promise.resolve().then(() => __importStar(require('./parsers')));
|
|
1706
|
+
const { OpenClawMemory } = await Promise.resolve().then(() => __importStar(require('./index')));
|
|
1707
|
+
const sessions = parseAllDailyLogs(dailyLogsDir, options.userId);
|
|
1708
|
+
const memory = new OpenClawMemory({
|
|
1709
|
+
supabaseUrl: config.supabaseUrl,
|
|
1710
|
+
supabaseKey: config.supabaseKey,
|
|
1711
|
+
agentId: config.agentId
|
|
1712
|
+
});
|
|
1713
|
+
for (const sess of sessions) {
|
|
1714
|
+
try {
|
|
1715
|
+
const session = await memory.startSession({
|
|
1716
|
+
userId: sess.user_id,
|
|
1717
|
+
channel: sess.channel
|
|
1718
|
+
});
|
|
1719
|
+
for (const msg of sess.messages) {
|
|
1720
|
+
await memory.addMessage(session.id, {
|
|
1721
|
+
role: msg.role,
|
|
1722
|
+
content: msg.content
|
|
1723
|
+
});
|
|
1724
|
+
}
|
|
1725
|
+
if (sess.ended_at || sess.summary) {
|
|
1726
|
+
await memory.endSession(session.id, { summary: sess.summary });
|
|
1727
|
+
}
|
|
1728
|
+
totalImported++;
|
|
1729
|
+
}
|
|
1730
|
+
catch (err) {
|
|
1731
|
+
// Silent skip
|
|
1732
|
+
}
|
|
1733
|
+
}
|
|
1734
|
+
console.log(`ā
Imported ${sessions.length} sessions\n`);
|
|
1735
|
+
}
|
|
1736
|
+
catch (err) {
|
|
1737
|
+
console.error('ā ļø Failed to import daily logs:', err);
|
|
1738
|
+
}
|
|
1739
|
+
}
|
|
1740
|
+
// Import TODO.md
|
|
1741
|
+
if (existsSync(todoMdPath)) {
|
|
1742
|
+
try {
|
|
1743
|
+
console.log('ā
Importing TODO.md...');
|
|
1744
|
+
const { parseTodoMd } = await Promise.resolve().then(() => __importStar(require('./parsers')));
|
|
1745
|
+
const { OpenClawMemory } = await Promise.resolve().then(() => __importStar(require('./index')));
|
|
1746
|
+
const tasks = parseTodoMd(todoMdPath);
|
|
1747
|
+
const memory = new OpenClawMemory({
|
|
1748
|
+
supabaseUrl: config.supabaseUrl,
|
|
1749
|
+
supabaseKey: config.supabaseKey,
|
|
1750
|
+
agentId: config.agentId
|
|
1751
|
+
});
|
|
1752
|
+
for (const task of tasks) {
|
|
1753
|
+
try {
|
|
1754
|
+
await memory.createTask({
|
|
1755
|
+
title: task.title,
|
|
1756
|
+
description: task.description,
|
|
1757
|
+
priority: task.priority,
|
|
1758
|
+
dueAt: task.due_date,
|
|
1759
|
+
metadata: { ...task.metadata, originalStatus: task.status }
|
|
1760
|
+
});
|
|
1761
|
+
totalImported++;
|
|
1762
|
+
}
|
|
1763
|
+
catch (err) {
|
|
1764
|
+
// Silent skip
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1767
|
+
console.log(`ā
Imported ${tasks.length} tasks\n`);
|
|
1768
|
+
}
|
|
1769
|
+
catch (err) {
|
|
1770
|
+
console.error('ā ļø Failed to import TODO.md:', err);
|
|
1771
|
+
}
|
|
1772
|
+
}
|
|
1773
|
+
// Import LEARNINGS.md
|
|
1774
|
+
if (existsSync(learningsMdPath)) {
|
|
1775
|
+
try {
|
|
1776
|
+
console.log('š§ Importing LEARNINGS.md...');
|
|
1777
|
+
const { parseLearningsMd } = await Promise.resolve().then(() => __importStar(require('./parsers')));
|
|
1778
|
+
const { OpenClawMemory } = await Promise.resolve().then(() => __importStar(require('./index')));
|
|
1779
|
+
const learnings = parseLearningsMd(learningsMdPath);
|
|
1780
|
+
const memory = new OpenClawMemory({
|
|
1781
|
+
supabaseUrl: config.supabaseUrl,
|
|
1782
|
+
supabaseKey: config.supabaseKey,
|
|
1783
|
+
agentId: config.agentId
|
|
1784
|
+
});
|
|
1785
|
+
for (const learning of learnings) {
|
|
1786
|
+
try {
|
|
1787
|
+
// Map parsed category to valid enum value
|
|
1788
|
+
const validCategories = ['error', 'correction', 'improvement', 'capability_gap'];
|
|
1789
|
+
const category = validCategories.includes(learning.category)
|
|
1790
|
+
? learning.category
|
|
1791
|
+
: 'improvement';
|
|
1792
|
+
await memory.learn({
|
|
1793
|
+
category,
|
|
1794
|
+
trigger: learning.trigger,
|
|
1795
|
+
lesson: learning.lesson,
|
|
1796
|
+
metadata: {
|
|
1797
|
+
originalCategory: learning.category,
|
|
1798
|
+
originalImportance: learning.importance
|
|
1799
|
+
}
|
|
1800
|
+
});
|
|
1801
|
+
totalImported++;
|
|
1802
|
+
}
|
|
1803
|
+
catch (err) {
|
|
1804
|
+
// Silent skip
|
|
1805
|
+
}
|
|
1806
|
+
}
|
|
1807
|
+
console.log(`ā
Imported ${learnings.length} learnings\n`);
|
|
1808
|
+
}
|
|
1809
|
+
catch (err) {
|
|
1810
|
+
console.error('ā ļø Failed to import LEARNINGS.md:', err);
|
|
1811
|
+
}
|
|
1812
|
+
}
|
|
1813
|
+
console.log(`\nš Migration complete! Total items imported: ${totalImported}`);
|
|
1814
|
+
});
|
|
1815
|
+
program.parse(process.argv);
|