supabase-memory.mjs
111 lines 3.6 KB
Raw
sha256:65ccb454656ea5acdea0a10e559b78bcde1eb6ff753ecc2911bc99d1c3d7cadd feat(calendar): enforce agent context tiers in retrieval AP… Human minor ⚠ breaking 1 day ago
1 /**
2 * Import memories from a Supabase table into the Knowtation memory event log
3 * and optionally as vault notes.
4 *
5 * Input: JSON config string or file with { url, key, table?, vault_notes? }
6 * Or CLI: knowtation import supabase-memory '{"url":"...","key":"..."}'
7 */
8
9 import fs from 'fs';
10 import path from 'path';
11 import { writeNote } from '../write.mjs';
12 import { normalizeSlug } from '../vault.mjs';
13
14 /**
15 * @param {string} input — JSON string or path to JSON config file
16 * @param {{ vaultPath: string, outputBase: string, project?: string, tags: string[], dryRun: boolean, onMemoryEvent?: Function }} ctx
17 * @returns {Promise<{ imported: { path: string, source_id?: string }[], count: number }>}
18 */
19 export async function importSupabaseMemory(input, ctx) {
20 const { vaultPath, outputBase, project, tags, dryRun, onMemoryEvent } = ctx;
21
22 let opts;
23 try {
24 if (fs.existsSync(input) && fs.statSync(input).isFile()) {
25 opts = JSON.parse(fs.readFileSync(input, 'utf8'));
26 } else {
27 opts = JSON.parse(input);
28 }
29 } catch (e) {
30 throw new Error(`Invalid supabase-memory input. Provide JSON: {"url":"...","key":"..."}. Error: ${e.message}`);
31 }
32
33 const { url, key, table, vault_notes } = opts;
34 if (!url || !key) {
35 throw new Error('supabase-memory import requires "url" and "key" in the input JSON.');
36 }
37
38 const tableName = table || 'memories';
39 const writeVaultNotes = vault_notes !== false;
40
41 let createClient;
42 try {
43 const mod = await import('@supabase/supabase-js');
44 createClient = mod.createClient;
45 } catch (_) {
46 throw new Error('supabase-memory import requires @supabase/supabase-js. Run: npm install @supabase/supabase-js');
47 }
48
49 const client = createClient(url, key);
50
51 const PAGE_SIZE = 500;
52 const imported = [];
53 let offset = 0;
54 const now = new Date().toISOString().slice(0, 10);
55
56 while (true) {
57 const { data: rows, error } = await client
58 .from(tableName)
59 .select('*')
60 .order('created_at', { ascending: true })
61 .range(offset, offset + PAGE_SIZE - 1);
62
63 if (error) throw new Error(`Supabase query failed: ${error.message}`);
64 if (!rows || rows.length === 0) break;
65
66 for (const row of rows) {
67 const body = row.memory || row.content || row.text || JSON.stringify(row);
68 const memId = row.id || row.memory_id || `sb_${offset + imported.length}`;
69 const sourceId = String(memId).slice(0, 128);
70 const date = row.created_at || row.updated_at || now;
71 const d = typeof date === 'number'
72 ? new Date(date * 1000).toISOString().slice(0, 10)
73 : String(date).slice(0, 10);
74
75 let outputRel = null;
76 if (writeVaultNotes) {
77 const safeName = `supabase_${String(memId).replace(/[^a-zA-Z0-9_-]/g, '_').slice(0, 40)}`;
78 outputRel = path.join(outputBase, `${safeName}.md`).replace(/\\/g, '/');
79
80 const frontmatter = {
81 source: 'supabase',
82 source_id: sourceId,
83 date: d,
84 ...(project && { project: normalizeSlug(project) }),
85 ...(tags.length && { tags }),
86 };
87
88 if (!dryRun) writeNote(vaultPath, outputRel, { body: String(body).trim(), frontmatter });
89 }
90
91 imported.push({ path: outputRel || sourceId, source_id: sourceId });
92
93 if (!dryRun && typeof onMemoryEvent === 'function') {
94 try {
95 onMemoryEvent({
96 source: 'supabase',
97 source_id: sourceId,
98 date: d,
99 text: String(body).trim().slice(0, 500),
100 path: outputRel,
101 });
102 } catch (_) {}
103 }
104 }
105
106 offset += rows.length;
107 if (rows.length < PAGE_SIZE) break;
108 }
109
110 return { imported, count: imported.length };
111 }
File History 2 commits
sha256:65ccb454656ea5acdea0a10e559b78bcde1eb6ff753ecc2911bc99d1c3d7cadd feat(calendar): enforce agent context tiers in retrieval AP… Human minor 1 day ago
sha256:9103f98c89257ed2b01c237cea895dabb3e85ea337dccb1161c175e4422355b6 docs: accept Calendar Events v0 spec with Phase 0 security … Human 1 day ago