mcpscraper-memory-db 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.
Files changed (104) hide show
  1. package/README.md +40 -0
  2. package/dist/db/chat-link.d.ts +7 -0
  3. package/dist/db/chat-link.js +50 -0
  4. package/dist/db/chat-link.js.map +1 -0
  5. package/dist/db/facts.d.ts +60 -0
  6. package/dist/db/facts.js +186 -0
  7. package/dist/db/facts.js.map +1 -0
  8. package/dist/db/interlink-state.d.ts +34 -0
  9. package/dist/db/interlink-state.js +42 -0
  10. package/dist/db/interlink-state.js.map +1 -0
  11. package/dist/db/keys.d.ts +44 -0
  12. package/dist/db/keys.js +118 -0
  13. package/dist/db/keys.js.map +1 -0
  14. package/dist/db/messages.d.ts +77 -0
  15. package/dist/db/messages.js +198 -0
  16. package/dist/db/messages.js.map +1 -0
  17. package/dist/db/notes.d.ts +109 -0
  18. package/dist/db/notes.js +249 -0
  19. package/dist/db/notes.js.map +1 -0
  20. package/dist/db/pool.d.ts +7 -0
  21. package/dist/db/pool.js +37 -0
  22. package/dist/db/pool.js.map +1 -0
  23. package/dist/db/presence.d.ts +4 -0
  24. package/dist/db/presence.js +25 -0
  25. package/dist/db/presence.js.map +1 -0
  26. package/dist/db/props-snapshots.d.ts +18 -0
  27. package/dist/db/props-snapshots.js +37 -0
  28. package/dist/db/props-snapshots.js.map +1 -0
  29. package/dist/db/provenance.d.ts +26 -0
  30. package/dist/db/provenance.js +43 -0
  31. package/dist/db/provenance.js.map +1 -0
  32. package/dist/db/schedule-entitlements.d.ts +24 -0
  33. package/dist/db/schedule-entitlements.js +70 -0
  34. package/dist/db/schedule-entitlements.js.map +1 -0
  35. package/dist/db/schedule-link.d.ts +7 -0
  36. package/dist/db/schedule-link.js +50 -0
  37. package/dist/db/schedule-link.js.map +1 -0
  38. package/dist/db/scheduled-actions.d.ts +58 -0
  39. package/dist/db/scheduled-actions.js +139 -0
  40. package/dist/db/scheduled-actions.js.map +1 -0
  41. package/dist/db/schema.d.ts +3 -0
  42. package/dist/db/schema.js +340 -0
  43. package/dist/db/schema.js.map +1 -0
  44. package/dist/db/secure-notes.d.ts +26 -0
  45. package/dist/db/secure-notes.js +59 -0
  46. package/dist/db/secure-notes.js.map +1 -0
  47. package/dist/db/shares.d.ts +45 -0
  48. package/dist/db/shares.js +141 -0
  49. package/dist/db/shares.js.map +1 -0
  50. package/dist/db/tables.d.ts +50 -0
  51. package/dist/db/tables.js +226 -0
  52. package/dist/db/tables.js.map +1 -0
  53. package/dist/db/trust.d.ts +10 -0
  54. package/dist/db/trust.js +88 -0
  55. package/dist/db/trust.js.map +1 -0
  56. package/dist/db/usage.d.ts +30 -0
  57. package/dist/db/usage.js +106 -0
  58. package/dist/db/usage.js.map +1 -0
  59. package/dist/db/vaults.d.ts +98 -0
  60. package/dist/db/vaults.js +316 -0
  61. package/dist/db/vaults.js.map +1 -0
  62. package/dist/db/video-runs.d.ts +70 -0
  63. package/dist/db/video-runs.js +176 -0
  64. package/dist/db/video-runs.js.map +1 -0
  65. package/dist/db/webhooks.d.ts +20 -0
  66. package/dist/db/webhooks.js +43 -0
  67. package/dist/db/webhooks.js.map +1 -0
  68. package/dist/lib/auth.d.ts +75 -0
  69. package/dist/lib/auth.js +248 -0
  70. package/dist/lib/auth.js.map +1 -0
  71. package/dist/lib/cost-rates.d.ts +19 -0
  72. package/dist/lib/cost-rates.js +36 -0
  73. package/dist/lib/cost-rates.js.map +1 -0
  74. package/dist/lib/credit-debit.d.ts +15 -0
  75. package/dist/lib/credit-debit.js +21 -0
  76. package/dist/lib/credit-debit.js.map +1 -0
  77. package/dist/lib/operator.d.ts +5 -0
  78. package/dist/lib/operator.js +14 -0
  79. package/dist/lib/operator.js.map +1 -0
  80. package/dist/lib/schedule-cadence.d.ts +6 -0
  81. package/dist/lib/schedule-cadence.js +66 -0
  82. package/dist/lib/schedule-cadence.js.map +1 -0
  83. package/dist/lib/secret-crypto.d.ts +4 -0
  84. package/dist/lib/secret-crypto.js +28 -0
  85. package/dist/lib/secret-crypto.js.map +1 -0
  86. package/dist/lib/vault-contracts.d.ts +18 -0
  87. package/dist/lib/vault-contracts.js +248 -0
  88. package/dist/lib/vault-contracts.js.map +1 -0
  89. package/dist/lib/video-reaper.d.ts +13 -0
  90. package/dist/lib/video-reaper.js +19 -0
  91. package/dist/lib/video-reaper.js.map +1 -0
  92. package/dist/rag/embedder.d.ts +6 -0
  93. package/dist/rag/embedder.js +93 -0
  94. package/dist/rag/embedder.js.map +1 -0
  95. package/dist/rag/index-pipeline.d.ts +22 -0
  96. package/dist/rag/index-pipeline.js +62 -0
  97. package/dist/rag/index-pipeline.js.map +1 -0
  98. package/dist/rag/query-tool.d.ts +47 -0
  99. package/dist/rag/query-tool.js +47 -0
  100. package/dist/rag/query-tool.js.map +1 -0
  101. package/dist/rag/vector-store.d.ts +7 -0
  102. package/dist/rag/vector-store.js +25 -0
  103. package/dist/rag/vector-store.js.map +1 -0
  104. package/package.json +47 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/db/notes.ts"],"sourcesContent":["import { ensureSchema, query } from './pool'\nimport { removeDocument } from '../rag/index-pipeline'\nimport { EMBED_DIM } from '../rag/embedder'\nimport { INDEX_NAME } from '../rag/vector-store'\n\nconst EMBED_BYTES_PER_CHUNK = EMBED_DIM * 4\n\nexport type NoteKind = 'note' | 'library' | 'capture' | 'decision' | 'message'\n\nexport type NoteProps = Record<string, unknown>\n\nexport interface NoteRecord {\n id: string\n vault: string\n path: string\n title: string\n content: string\n kind: NoteKind\n source: string\n props: NoteProps\n capturedAt: string\n createdAt: string\n updatedAt: string\n revision: number\n updatedBy: string | null\n}\n\nexport interface RawNoteRow {\n id: string\n vault: string\n path: string\n title: string\n content: string\n kind: string\n source: string\n props: unknown\n captured_at: string\n created_at: string\n updated_at: string\n revision: number\n updated_by: string | null\n}\n\nexport function noteId(vault: string, path: string): string {\n return `${vault}::${path}`\n}\n\nexport function mapNote(r: RawNoteRow): NoteRecord {\n return {\n id: r.id,\n vault: r.vault,\n path: r.path,\n title: r.title,\n content: r.content,\n kind: r.kind as NoteKind,\n source: r.source,\n props: (r.props && typeof r.props === 'object' ? (r.props as NoteProps) : {}),\n capturedAt: r.captured_at,\n createdAt: r.created_at,\n updatedAt: r.updated_at,\n revision: r.revision,\n updatedBy: r.updated_by,\n }\n}\n\nexport const NOTE_COLUMNS = 'id, vault, path, title, content, kind, source, props, captured_at, created_at, updated_at, revision, updated_by'\n\nexport interface PutNoteArgs {\n vault: string\n path: string\n title?: string\n content: string\n kind?: NoteKind\n source?: string\n props?: NoteProps\n capturedAt?: string\n updatedBy?: string\n}\n\nexport async function putNoteRow(args: PutNoteArgs): Promise<NoteRecord> {\n await ensureSchema()\n const id = noteId(args.vault, args.path)\n const res = await query<RawNoteRow>(\n `INSERT INTO mem_notes (id, vault, path, title, content, kind, source, props, captured_at, updated_at, revision, updated_by)\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8::jsonb, COALESCE($9::timestamptz, now()), now(), 1, $10)\n ON CONFLICT (vault, path) DO UPDATE SET\n title = EXCLUDED.title, content = EXCLUDED.content, kind = EXCLUDED.kind,\n source = EXCLUDED.source, props = EXCLUDED.props, captured_at = EXCLUDED.captured_at,\n updated_at = now(), revision = mem_notes.revision + 1, updated_by = EXCLUDED.updated_by\n RETURNING ${NOTE_COLUMNS}`,\n [id, args.vault, args.path, args.title ?? args.path, args.content, args.kind ?? 'note', args.source ?? '', JSON.stringify(args.props ?? {}), args.capturedAt ?? null, args.updatedBy ?? null],\n )\n return mapNote(res.rows[0])\n}\n\nexport type PutNoteCheckedResult = { ok: true; note: NoteRecord } | { ok: false; conflict: true; current: NoteRecord }\n\nexport async function putNoteRowChecked(args: PutNoteArgs & { baseRevision: number }): Promise<PutNoteCheckedResult> {\n await ensureSchema()\n const id = noteId(args.vault, args.path)\n const res = await query<RawNoteRow>(\n `INSERT INTO mem_notes (id, vault, path, title, content, kind, source, props, captured_at, updated_at, revision, updated_by)\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8::jsonb, COALESCE($9::timestamptz, now()), now(), 1, $10)\n ON CONFLICT (vault, path) DO UPDATE SET\n title = EXCLUDED.title, content = EXCLUDED.content, kind = EXCLUDED.kind,\n source = EXCLUDED.source, props = EXCLUDED.props, captured_at = EXCLUDED.captured_at,\n updated_at = now(), revision = mem_notes.revision + 1, updated_by = EXCLUDED.updated_by\n WHERE mem_notes.revision = $11\n RETURNING ${NOTE_COLUMNS}`,\n [id, args.vault, args.path, args.title ?? args.path, args.content, args.kind ?? 'note', args.source ?? '', JSON.stringify(args.props ?? {}), args.capturedAt ?? null, args.updatedBy ?? null, args.baseRevision],\n )\n if (res.rows[0]) return { ok: true, note: mapNote(res.rows[0]) }\n const current = await getNoteRow(args.vault, args.path)\n if (!current) return { ok: true, note: await putNoteRow(args) }\n return { ok: false, conflict: true, current }\n}\n\nexport async function getNoteRow(vault: string, path: string): Promise<NoteRecord | null> {\n await ensureSchema()\n const res = await query<RawNoteRow>(\n `SELECT ${NOTE_COLUMNS} FROM mem_notes WHERE vault = $1 AND path = $2 LIMIT 1`,\n [vault, path],\n )\n return res.rows[0] ? mapNote(res.rows[0]) : null\n}\n\nexport async function getNoteById(id: string): Promise<NoteRecord | null> {\n await ensureSchema()\n const res = await query<RawNoteRow>(`SELECT ${NOTE_COLUMNS} FROM mem_notes WHERE id = $1 LIMIT 1`, [id])\n return res.rows[0] ? mapNote(res.rows[0]) : null\n}\n\nexport async function listNoteRows(vault: string, kind?: NoteKind, tags?: string[]): Promise<NoteRecord[]> {\n await ensureSchema()\n const params: unknown[] = [vault]\n let where = 'vault = $1'\n if (kind) {\n params.push(kind)\n where += ` AND kind = $${params.length}`\n }\n if (tags && tags.length > 0) {\n params.push(JSON.stringify(tags))\n where += ` AND (props->'tags')::jsonb ?| ARRAY(SELECT jsonb_array_elements_text($${params.length}::jsonb))`\n }\n const res = await query<RawNoteRow>(\n `SELECT ${NOTE_COLUMNS}\n FROM mem_notes WHERE ${where} ORDER BY updated_at DESC`,\n params,\n )\n return res.rows.map(mapNote)\n}\n\nexport async function listAllNoteRows(): Promise<NoteRecord[]> {\n await ensureSchema()\n const res = await query<RawNoteRow>(\n `SELECT ${NOTE_COLUMNS} FROM mem_notes ORDER BY vault ASC, updated_at DESC`,\n )\n return res.rows.map(mapNote)\n}\n\nexport async function resolveNoteRefs(vault: string, refs: string[]): Promise<Map<string, NoteRecord>> {\n const notes = await listNoteRows(vault)\n const byPath = new Map(notes.map(n => [n.path, n]))\n const byTitle = new Map(notes.map(n => [n.title.toLowerCase(), n]))\n const resolved = new Map<string, NoteRecord>()\n for (const ref of refs) {\n const note = byPath.get(ref) ?? byTitle.get(ref.toLowerCase())\n if (note) resolved.set(ref, note)\n }\n return resolved\n}\n\nexport async function exportVaultRows(vault: string): Promise<NoteRecord[]> {\n return listNoteRows(vault)\n}\n\nexport async function captureRow(args: { vault: string; content: string; source: string; capturedAt?: string; title?: string; folder?: string; props?: NoteProps }): Promise<NoteRecord> {\n const stamp = args.capturedAt ?? new Date().toISOString()\n const base = `captures/${stamp.replace(/[:.]/g, '-')}`\n const path = args.folder ? `${args.folder}/${base}` : base\n return putNoteRow({\n vault: args.vault,\n path,\n title: args.title ?? `Capture ${stamp}`,\n content: args.content,\n kind: 'capture',\n source: args.source,\n props: args.props,\n capturedAt: stamp,\n })\n}\n\nexport async function deleteNoteRow(vault: string, path: string): Promise<{ deleted: boolean }> {\n await ensureSchema()\n const res = await query(`DELETE FROM mem_notes WHERE vault = $1 AND path = $2`, [vault, path])\n const deleted = (res.rowCount ?? 0) > 0\n if (deleted) {\n try {\n await removeDocument({ vault, id: noteId(vault, path) })\n } catch {\n void 0\n }\n }\n return { deleted }\n}\n\nexport type DeleteNoteCheckedResult = { ok: true; deleted: boolean } | { ok: false; conflict: true; current: NoteRecord }\n\nexport async function deleteNoteRowChecked(vault: string, path: string, baseRevision: number): Promise<DeleteNoteCheckedResult> {\n await ensureSchema()\n const res = await query(`DELETE FROM mem_notes WHERE vault = $1 AND path = $2 AND revision = $3`, [vault, path, baseRevision])\n if ((res.rowCount ?? 0) > 0) {\n try {\n await removeDocument({ vault, id: noteId(vault, path) })\n } catch {\n void 0\n }\n return { ok: true, deleted: true }\n }\n const current = await getNoteRow(vault, path)\n if (!current) return { ok: true, deleted: false }\n return { ok: false, conflict: true, current }\n}\n\nexport async function deleteNotesForVault(vault: string): Promise<number> {\n await ensureSchema()\n const res = await query(`DELETE FROM mem_notes WHERE vault = $1`, [vault])\n return res.rowCount ?? 0\n}\n\nexport interface VaultStorageResult {\n perVault: { vault: string; bytes: number; notes: number }[]\n totalBytes: number\n}\n\nexport async function vaultStorage(vaults?: string[]): Promise<VaultStorageResult> {\n if (vaults && vaults.length === 0) return { perVault: [], totalBytes: 0 }\n await ensureSchema()\n const params: unknown[] = []\n let where = ''\n if (vaults && vaults.length > 0) {\n params.push(vaults)\n where = `WHERE vault = ANY($1)`\n }\n const res = await query<{ vault: string; notes: string; bytes: string }>(\n `SELECT vault, COUNT(*)::int AS notes, COALESCE(SUM(octet_length(content)), 0)::bigint AS bytes\n FROM mem_notes ${where} GROUP BY vault ORDER BY vault ASC`,\n params,\n )\n const embedChunks = await embedChunksByVault(vaults)\n const perVault = res.rows.map(r => {\n const contentBytes = Number(r.bytes)\n const embedBytes = (embedChunks.get(r.vault) ?? 0) * EMBED_BYTES_PER_CHUNK\n return { vault: r.vault, bytes: contentBytes + embedBytes, notes: Number(r.notes) }\n })\n const totalBytes = perVault.reduce((sum, v) => sum + v.bytes, 0)\n return { perVault, totalBytes }\n}\n\nasync function embedChunksByVault(vaults?: string[]): Promise<Map<string, number>> {\n const out = new Map<string, number>()\n if (vaults && vaults.length === 0) return out\n const params: unknown[] = []\n let where = ''\n if (vaults && vaults.length > 0) {\n params.push(vaults)\n where = `WHERE metadata->>'vault' = ANY($1)`\n }\n try {\n const res = await query<{ vault: string; chunks: string }>(\n `SELECT metadata->>'vault' AS vault, COUNT(*)::bigint AS chunks\n FROM \"${INDEX_NAME}\" ${where} GROUP BY 1`,\n params,\n )\n for (const r of res.rows) if (r.vault) out.set(r.vault, Number(r.chunks))\n } catch {\n return out\n }\n return out\n}\n\nexport async function listCapturesInRange(args: { vault: string; from: string; to: string }): Promise<NoteRecord[]> {\n await ensureSchema()\n const res = await query<RawNoteRow>(\n `SELECT ${NOTE_COLUMNS}\n FROM mem_notes\n WHERE vault = $1 AND captured_at >= $2::timestamptz AND captured_at <= $3::timestamptz\n ORDER BY captured_at DESC`,\n [args.vault, args.from, args.to],\n )\n return res.rows.map(mapNote)\n}\n\nexport interface SuggestHit {\n vault: string\n path: string\n title: string\n kind: NoteKind\n}\n\nexport async function suggestNotesAcrossVaults(vaults: string[], prefix: string, limit: number): Promise<SuggestHit[]> {\n await ensureSchema()\n if (vaults.length === 0 || !prefix.trim()) return []\n const res = await query<{ vault: string; path: string; title: string; kind: string; rank: number }>(\n `SELECT vault, path, title, kind,\n CASE WHEN title ILIKE $2 || '%' THEN 0 ELSE 1 END AS rank\n FROM mem_notes\n WHERE vault = ANY($1) AND kind != 'message' AND title ILIKE '%' || $2 || '%'\n ORDER BY rank ASC, updated_at DESC\n LIMIT $3`,\n [vaults, prefix.trim(), limit],\n )\n return res.rows.map(r => ({ vault: r.vault, path: r.path, title: r.title, kind: r.kind as NoteKind }))\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,7 @@
1
+ import pg from 'pg';
2
+
3
+ declare function getPool(): pg.Pool;
4
+ declare function query<T extends pg.QueryResultRow = pg.QueryResultRow>(text: string, params?: ReadonlyArray<unknown>): Promise<pg.QueryResult<T>>;
5
+ declare function ensureSchema(): Promise<void>;
6
+
7
+ export { ensureSchema, getPool, query };
@@ -0,0 +1,37 @@
1
+ import pg from "pg";
2
+ pg.types.setTypeParser(1184, (value) => value);
3
+ pg.types.setTypeParser(1114, (value) => value);
4
+ pg.types.setTypeParser(1082, (value) => value);
5
+ let poolSingleton = null;
6
+ function getPool() {
7
+ if (poolSingleton) return poolSingleton;
8
+ const connectionString = process.env.DATABASE_URL;
9
+ if (!connectionString) {
10
+ throw new Error("DATABASE_URL is not set. mcp-memory requires an external Neon/Postgres connection string.");
11
+ }
12
+ poolSingleton = new pg.Pool({
13
+ connectionString,
14
+ max: 5,
15
+ ssl: connectionString.includes("localhost") ? void 0 : { rejectUnauthorized: false }
16
+ });
17
+ return poolSingleton;
18
+ }
19
+ async function query(text, params = []) {
20
+ const pool = getPool();
21
+ return pool.query(text, params);
22
+ }
23
+ let schemaReady = null;
24
+ function ensureSchema() {
25
+ if (schemaReady) return schemaReady;
26
+ schemaReady = (async () => {
27
+ const { schemaSql } = await import("./schema");
28
+ await query(schemaSql);
29
+ })();
30
+ return schemaReady;
31
+ }
32
+ export {
33
+ ensureSchema,
34
+ getPool,
35
+ query
36
+ };
37
+ //# sourceMappingURL=pool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/db/pool.ts"],"sourcesContent":["import pg from 'pg'\n\npg.types.setTypeParser(1184, (value: string) => value)\npg.types.setTypeParser(1114, (value: string) => value)\npg.types.setTypeParser(1082, (value: string) => value)\n\nlet poolSingleton: pg.Pool | null = null\n\nexport function getPool(): pg.Pool {\n if (poolSingleton) return poolSingleton\n const connectionString = process.env.DATABASE_URL\n if (!connectionString) {\n throw new Error('DATABASE_URL is not set. mcp-memory requires an external Neon/Postgres connection string.')\n }\n poolSingleton = new pg.Pool({\n connectionString,\n max: 5,\n ssl: connectionString.includes('localhost') ? undefined : { rejectUnauthorized: false },\n })\n return poolSingleton\n}\n\nexport async function query<T extends pg.QueryResultRow = pg.QueryResultRow>(\n text: string,\n params: ReadonlyArray<unknown> = [],\n): Promise<pg.QueryResult<T>> {\n const pool = getPool()\n return pool.query<T>(text, params as unknown[])\n}\n\nlet schemaReady: Promise<void> | null = null\n\nexport function ensureSchema(): Promise<void> {\n if (schemaReady) return schemaReady\n schemaReady = (async () => {\n const { schemaSql } = await import('./schema')\n await query(schemaSql)\n })()\n return schemaReady\n}\n"],"mappings":"AAAA,OAAO,QAAQ;AAEf,GAAG,MAAM,cAAc,MAAM,CAAC,UAAkB,KAAK;AACrD,GAAG,MAAM,cAAc,MAAM,CAAC,UAAkB,KAAK;AACrD,GAAG,MAAM,cAAc,MAAM,CAAC,UAAkB,KAAK;AAErD,IAAI,gBAAgC;AAE7B,SAAS,UAAmB;AACjC,MAAI,cAAe,QAAO;AAC1B,QAAM,mBAAmB,QAAQ,IAAI;AACrC,MAAI,CAAC,kBAAkB;AACrB,UAAM,IAAI,MAAM,2FAA2F;AAAA,EAC7G;AACA,kBAAgB,IAAI,GAAG,KAAK;AAAA,IAC1B;AAAA,IACA,KAAK;AAAA,IACL,KAAK,iBAAiB,SAAS,WAAW,IAAI,SAAY,EAAE,oBAAoB,MAAM;AAAA,EACxF,CAAC;AACD,SAAO;AACT;AAEA,eAAsB,MACpB,MACA,SAAiC,CAAC,GACN;AAC5B,QAAM,OAAO,QAAQ;AACrB,SAAO,KAAK,MAAS,MAAM,MAAmB;AAChD;AAEA,IAAI,cAAoC;AAEjC,SAAS,eAA8B;AAC5C,MAAI,YAAa,QAAO;AACxB,iBAAe,YAAY;AACzB,UAAM,EAAE,UAAU,IAAI,MAAM,OAAO,UAAU;AAC7C,UAAM,MAAM,SAAS;AAAA,EACvB,GAAG;AACH,SAAO;AACT;","names":[]}
@@ -0,0 +1,4 @@
1
+ declare function touchPresence(noteId: string, identity: string): Promise<void>;
2
+ declare function othersPresent(noteId: string, identity: string): Promise<string[]>;
3
+
4
+ export { othersPresent, touchPresence };
@@ -0,0 +1,25 @@
1
+ import { ensureSchema, query } from "./pool";
2
+ const PRESENCE_TTL_SECONDS = 120;
3
+ async function touchPresence(noteId, identity) {
4
+ await ensureSchema();
5
+ await query(
6
+ `INSERT INTO mem_note_presence (note_id, identity, heartbeat_at)
7
+ VALUES ($1, $2, now())
8
+ ON CONFLICT (note_id, identity) DO UPDATE SET heartbeat_at = now()`,
9
+ [noteId, identity]
10
+ );
11
+ }
12
+ async function othersPresent(noteId, identity) {
13
+ await ensureSchema();
14
+ const res = await query(
15
+ `SELECT identity FROM mem_note_presence
16
+ WHERE note_id = $1 AND identity != $2 AND heartbeat_at > now() - interval '${PRESENCE_TTL_SECONDS} seconds'`,
17
+ [noteId, identity]
18
+ );
19
+ return res.rows.map((r) => r.identity);
20
+ }
21
+ export {
22
+ othersPresent,
23
+ touchPresence
24
+ };
25
+ //# sourceMappingURL=presence.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/db/presence.ts"],"sourcesContent":["import { ensureSchema, query } from './pool'\n\nconst PRESENCE_TTL_SECONDS = 120\n\nexport async function touchPresence(noteId: string, identity: string): Promise<void> {\n await ensureSchema()\n await query(\n `INSERT INTO mem_note_presence (note_id, identity, heartbeat_at)\n VALUES ($1, $2, now())\n ON CONFLICT (note_id, identity) DO UPDATE SET heartbeat_at = now()`,\n [noteId, identity],\n )\n}\n\nexport async function othersPresent(noteId: string, identity: string): Promise<string[]> {\n await ensureSchema()\n const res = await query<{ identity: string }>(\n `SELECT identity FROM mem_note_presence\n WHERE note_id = $1 AND identity != $2 AND heartbeat_at > now() - interval '${PRESENCE_TTL_SECONDS} seconds'`,\n [noteId, identity],\n )\n return res.rows.map(r => r.identity)\n}\n"],"mappings":"AAAA,SAAS,cAAc,aAAa;AAEpC,MAAM,uBAAuB;AAE7B,eAAsB,cAAc,QAAgB,UAAiC;AACnF,QAAM,aAAa;AACnB,QAAM;AAAA,IACJ;AAAA;AAAA;AAAA,IAGA,CAAC,QAAQ,QAAQ;AAAA,EACnB;AACF;AAEA,eAAsB,cAAc,QAAgB,UAAqC;AACvF,QAAM,aAAa;AACnB,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,kFAC8E,oBAAoB;AAAA,IAClG,CAAC,QAAQ,QAAQ;AAAA,EACnB;AACA,SAAO,IAAI,KAAK,IAAI,OAAK,EAAE,QAAQ;AACrC;","names":[]}
@@ -0,0 +1,18 @@
1
+ interface PropsSnapshotRecord {
2
+ id: number;
3
+ vault: string;
4
+ path: string;
5
+ prevProps: Record<string, unknown>;
6
+ runId: string | null;
7
+ createdAt: string;
8
+ }
9
+ declare function snapshotProps(args: {
10
+ vault: string;
11
+ path: string;
12
+ prevProps: Record<string, unknown>;
13
+ runId?: string;
14
+ }): Promise<void>;
15
+ declare function listPropsSnapshots(vault: string, path: string, limit?: number): Promise<PropsSnapshotRecord[]>;
16
+ declare function pruneOldPropsSnapshots(retentionDays?: number): Promise<number>;
17
+
18
+ export { type PropsSnapshotRecord, listPropsSnapshots, pruneOldPropsSnapshots, snapshotProps };
@@ -0,0 +1,37 @@
1
+ import { ensureSchema, query } from "./pool";
2
+ function mapRow(r) {
3
+ return {
4
+ id: r.id,
5
+ vault: r.vault,
6
+ path: r.path,
7
+ prevProps: r.prev_props && typeof r.prev_props === "object" ? r.prev_props : {},
8
+ runId: r.run_id,
9
+ createdAt: r.created_at
10
+ };
11
+ }
12
+ async function snapshotProps(args) {
13
+ await ensureSchema();
14
+ await query(
15
+ `INSERT INTO mem_props_snapshots (vault, path, prev_props, run_id) VALUES ($1, $2, $3, $4)`,
16
+ [args.vault, args.path, JSON.stringify(args.prevProps ?? {}), args.runId ?? null]
17
+ );
18
+ }
19
+ async function listPropsSnapshots(vault, path, limit = 20) {
20
+ await ensureSchema();
21
+ const res = await query(
22
+ `SELECT id, vault, path, prev_props, run_id, created_at FROM mem_props_snapshots WHERE vault = $1 AND path = $2 ORDER BY created_at DESC LIMIT $3`,
23
+ [vault, path, limit]
24
+ );
25
+ return res.rows.map(mapRow);
26
+ }
27
+ async function pruneOldPropsSnapshots(retentionDays = 30) {
28
+ await ensureSchema();
29
+ const res = await query(`DELETE FROM mem_props_snapshots WHERE created_at < now() - ($1 || ' days')::interval`, [retentionDays]);
30
+ return res.rowCount ?? 0;
31
+ }
32
+ export {
33
+ listPropsSnapshots,
34
+ pruneOldPropsSnapshots,
35
+ snapshotProps
36
+ };
37
+ //# sourceMappingURL=props-snapshots.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/db/props-snapshots.ts"],"sourcesContent":["import { ensureSchema, query } from './pool'\n\nexport interface PropsSnapshotRecord {\n id: number\n vault: string\n path: string\n prevProps: Record<string, unknown>\n runId: string | null\n createdAt: string\n}\n\ninterface RawRow {\n id: number\n vault: string\n path: string\n prev_props: unknown\n run_id: string | null\n created_at: string\n}\n\nfunction mapRow(r: RawRow): PropsSnapshotRecord {\n return {\n id: r.id,\n vault: r.vault,\n path: r.path,\n prevProps: r.prev_props && typeof r.prev_props === 'object' ? (r.prev_props as Record<string, unknown>) : {},\n runId: r.run_id,\n createdAt: r.created_at,\n }\n}\n\nexport async function snapshotProps(args: {\n vault: string\n path: string\n prevProps: Record<string, unknown>\n runId?: string\n}): Promise<void> {\n await ensureSchema()\n await query(\n `INSERT INTO mem_props_snapshots (vault, path, prev_props, run_id) VALUES ($1, $2, $3, $4)`,\n [args.vault, args.path, JSON.stringify(args.prevProps ?? {}), args.runId ?? null],\n )\n}\n\nexport async function listPropsSnapshots(vault: string, path: string, limit = 20): Promise<PropsSnapshotRecord[]> {\n await ensureSchema()\n const res = await query<RawRow>(\n `SELECT id, vault, path, prev_props, run_id, created_at FROM mem_props_snapshots WHERE vault = $1 AND path = $2 ORDER BY created_at DESC LIMIT $3`,\n [vault, path, limit],\n )\n return res.rows.map(mapRow)\n}\n\nexport async function pruneOldPropsSnapshots(retentionDays = 30): Promise<number> {\n await ensureSchema()\n const res = await query(`DELETE FROM mem_props_snapshots WHERE created_at < now() - ($1 || ' days')::interval`, [retentionDays])\n return res.rowCount ?? 0\n}\n"],"mappings":"AAAA,SAAS,cAAc,aAAa;AAoBpC,SAAS,OAAO,GAAgC;AAC9C,SAAO;AAAA,IACL,IAAI,EAAE;AAAA,IACN,OAAO,EAAE;AAAA,IACT,MAAM,EAAE;AAAA,IACR,WAAW,EAAE,cAAc,OAAO,EAAE,eAAe,WAAY,EAAE,aAAyC,CAAC;AAAA,IAC3G,OAAO,EAAE;AAAA,IACT,WAAW,EAAE;AAAA,EACf;AACF;AAEA,eAAsB,cAAc,MAKlB;AAChB,QAAM,aAAa;AACnB,QAAM;AAAA,IACJ;AAAA,IACA,CAAC,KAAK,OAAO,KAAK,MAAM,KAAK,UAAU,KAAK,aAAa,CAAC,CAAC,GAAG,KAAK,SAAS,IAAI;AAAA,EAClF;AACF;AAEA,eAAsB,mBAAmB,OAAe,MAAc,QAAQ,IAAoC;AAChH,QAAM,aAAa;AACnB,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,IACA,CAAC,OAAO,MAAM,KAAK;AAAA,EACrB;AACA,SAAO,IAAI,KAAK,IAAI,MAAM;AAC5B;AAEA,eAAsB,uBAAuB,gBAAgB,IAAqB;AAChF,QAAM,aAAa;AACnB,QAAM,MAAM,MAAM,MAAM,wFAAwF,CAAC,aAAa,CAAC;AAC/H,SAAO,IAAI,YAAY;AACzB;","names":[]}
@@ -0,0 +1,26 @@
1
+ interface ProvenanceEntry {
2
+ keyId: string | null;
3
+ identity: string;
4
+ session: string | null;
5
+ op: string;
6
+ target?: string;
7
+ detail?: Record<string, unknown>;
8
+ }
9
+ declare function logProvenance(entry: ProvenanceEntry): Promise<void>;
10
+ interface ProvenanceRow {
11
+ id: string;
12
+ keyId: string | null;
13
+ identity: string;
14
+ session: string | null;
15
+ op: string;
16
+ target: string | null;
17
+ detail: Record<string, unknown>;
18
+ ts: string;
19
+ }
20
+ declare function listProvenance(filter: {
21
+ identity?: string;
22
+ op?: string;
23
+ limit?: number;
24
+ }): Promise<ProvenanceRow[]>;
25
+
26
+ export { type ProvenanceEntry, type ProvenanceRow, listProvenance, logProvenance };
@@ -0,0 +1,43 @@
1
+ import { ensureSchema, query } from "./pool";
2
+ async function logProvenance(entry) {
3
+ await ensureSchema();
4
+ await query(
5
+ `INSERT INTO mem_provenance (key_id, identity, session, op, target, detail)
6
+ VALUES ($1, $2, $3, $4, $5, $6::jsonb)`,
7
+ [entry.keyId, entry.identity, entry.session ?? null, entry.op, entry.target ?? null, JSON.stringify(entry.detail ?? {})]
8
+ );
9
+ }
10
+ async function listProvenance(filter) {
11
+ await ensureSchema();
12
+ const clauses = [];
13
+ const params = [];
14
+ if (filter.identity) {
15
+ params.push(filter.identity);
16
+ clauses.push(`identity = $${params.length}`);
17
+ }
18
+ if (filter.op) {
19
+ params.push(filter.op);
20
+ clauses.push(`op = $${params.length}`);
21
+ }
22
+ const where = clauses.length ? `WHERE ${clauses.join(" AND ")}` : "";
23
+ params.push(Math.min(filter.limit ?? 100, 1e3));
24
+ const res = await query(
25
+ `SELECT id, key_id, identity, session, op, target, detail, ts FROM mem_provenance ${where} ORDER BY ts DESC LIMIT $${params.length}`,
26
+ params
27
+ );
28
+ return res.rows.map((r) => ({
29
+ id: String(r.id),
30
+ keyId: r.key_id,
31
+ identity: r.identity,
32
+ session: r.session,
33
+ op: r.op,
34
+ target: r.target,
35
+ detail: r.detail,
36
+ ts: r.ts
37
+ }));
38
+ }
39
+ export {
40
+ listProvenance,
41
+ logProvenance
42
+ };
43
+ //# sourceMappingURL=provenance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/db/provenance.ts"],"sourcesContent":["import { ensureSchema, query } from './pool'\n\nexport interface ProvenanceEntry {\n keyId: string | null\n identity: string\n session: string | null\n op: string\n target?: string\n detail?: Record<string, unknown>\n}\n\nexport async function logProvenance(entry: ProvenanceEntry): Promise<void> {\n await ensureSchema()\n await query(\n `INSERT INTO mem_provenance (key_id, identity, session, op, target, detail)\n VALUES ($1, $2, $3, $4, $5, $6::jsonb)`,\n [entry.keyId, entry.identity, entry.session ?? null, entry.op, entry.target ?? null, JSON.stringify(entry.detail ?? {})],\n )\n}\n\nexport interface ProvenanceRow {\n id: string\n keyId: string | null\n identity: string\n session: string | null\n op: string\n target: string | null\n detail: Record<string, unknown>\n ts: string\n}\n\ninterface RawProvRow {\n id: string\n key_id: string | null\n identity: string\n session: string | null\n op: string\n target: string | null\n detail: Record<string, unknown>\n ts: string\n}\n\nexport async function listProvenance(filter: { identity?: string; op?: string; limit?: number }): Promise<ProvenanceRow[]> {\n await ensureSchema()\n const clauses: string[] = []\n const params: unknown[] = []\n if (filter.identity) {\n params.push(filter.identity)\n clauses.push(`identity = $${params.length}`)\n }\n if (filter.op) {\n params.push(filter.op)\n clauses.push(`op = $${params.length}`)\n }\n const where = clauses.length ? `WHERE ${clauses.join(' AND ')}` : ''\n params.push(Math.min(filter.limit ?? 100, 1000))\n const res = await query<RawProvRow>(\n `SELECT id, key_id, identity, session, op, target, detail, ts FROM mem_provenance ${where} ORDER BY ts DESC LIMIT $${params.length}`,\n params,\n )\n return res.rows.map(r => ({\n id: String(r.id),\n keyId: r.key_id,\n identity: r.identity,\n session: r.session,\n op: r.op,\n target: r.target,\n detail: r.detail,\n ts: r.ts,\n }))\n}\n"],"mappings":"AAAA,SAAS,cAAc,aAAa;AAWpC,eAAsB,cAAc,OAAuC;AACzE,QAAM,aAAa;AACnB,QAAM;AAAA,IACJ;AAAA;AAAA,IAEA,CAAC,MAAM,OAAO,MAAM,UAAU,MAAM,WAAW,MAAM,MAAM,IAAI,MAAM,UAAU,MAAM,KAAK,UAAU,MAAM,UAAU,CAAC,CAAC,CAAC;AAAA,EACzH;AACF;AAwBA,eAAsB,eAAe,QAAsF;AACzH,QAAM,aAAa;AACnB,QAAM,UAAoB,CAAC;AAC3B,QAAM,SAAoB,CAAC;AAC3B,MAAI,OAAO,UAAU;AACnB,WAAO,KAAK,OAAO,QAAQ;AAC3B,YAAQ,KAAK,eAAe,OAAO,MAAM,EAAE;AAAA,EAC7C;AACA,MAAI,OAAO,IAAI;AACb,WAAO,KAAK,OAAO,EAAE;AACrB,YAAQ,KAAK,SAAS,OAAO,MAAM,EAAE;AAAA,EACvC;AACA,QAAM,QAAQ,QAAQ,SAAS,SAAS,QAAQ,KAAK,OAAO,CAAC,KAAK;AAClE,SAAO,KAAK,KAAK,IAAI,OAAO,SAAS,KAAK,GAAI,CAAC;AAC/C,QAAM,MAAM,MAAM;AAAA,IAChB,oFAAoF,KAAK,4BAA4B,OAAO,MAAM;AAAA,IAClI;AAAA,EACF;AACA,SAAO,IAAI,KAAK,IAAI,QAAM;AAAA,IACxB,IAAI,OAAO,EAAE,EAAE;AAAA,IACf,OAAO,EAAE;AAAA,IACT,UAAU,EAAE;AAAA,IACZ,SAAS,EAAE;AAAA,IACX,IAAI,EAAE;AAAA,IACN,QAAQ,EAAE;AAAA,IACV,QAAQ,EAAE;AAAA,IACV,IAAI,EAAE;AAAA,EACR,EAAE;AACJ;","names":[]}
@@ -0,0 +1,24 @@
1
+ interface ScheduleEntitlement {
2
+ identity: string;
3
+ enabled: boolean;
4
+ quotaPerPeriod: number;
5
+ usedThisPeriod: number;
6
+ periodStart: string;
7
+ }
8
+ declare function getEntitlement(identity: string): Promise<ScheduleEntitlement | null>;
9
+ declare function setEntitlement(args: {
10
+ identity: string;
11
+ enabled: boolean;
12
+ quotaPerPeriod?: number;
13
+ scraperApiKey?: string;
14
+ }): Promise<void>;
15
+ declare function getScraperApiKey(identity: string): Promise<string | null>;
16
+ type ConsumeQuotaResult = {
17
+ ok: true;
18
+ } | {
19
+ ok: false;
20
+ reason: 'not_enabled' | 'quota_exceeded';
21
+ };
22
+ declare function tryConsumeQuota(identity: string): Promise<ConsumeQuotaResult>;
23
+
24
+ export { type ConsumeQuotaResult, type ScheduleEntitlement, getEntitlement, getScraperApiKey, setEntitlement, tryConsumeQuota };
@@ -0,0 +1,70 @@
1
+ import { ensureSchema, query } from "./pool";
2
+ import { encryptSecret, decryptSecret } from "../lib/secret-crypto";
3
+ function mapRow(r) {
4
+ return {
5
+ identity: r.identity,
6
+ enabled: r.enabled,
7
+ quotaPerPeriod: r.quota_per_period,
8
+ usedThisPeriod: r.used_this_period,
9
+ periodStart: r.period_start
10
+ };
11
+ }
12
+ async function rollPeriodIfNeeded(identity) {
13
+ await query(
14
+ `UPDATE mem_schedule_entitlements
15
+ SET used_this_period = 0, period_start = date_trunc('month', now())::date, updated_at = now()
16
+ WHERE identity = $1 AND period_start < date_trunc('month', now())::date`,
17
+ [identity]
18
+ );
19
+ }
20
+ async function getEntitlement(identity) {
21
+ await ensureSchema();
22
+ await rollPeriodIfNeeded(identity);
23
+ const res = await query(`SELECT * FROM mem_schedule_entitlements WHERE identity = $1 LIMIT 1`, [identity]);
24
+ return res.rows[0] ? mapRow(res.rows[0]) : null;
25
+ }
26
+ async function setEntitlement(args) {
27
+ await ensureSchema();
28
+ const scraperApiKeyEnc = args.scraperApiKey ? encryptSecret(args.scraperApiKey) : null;
29
+ await query(
30
+ `INSERT INTO mem_schedule_entitlements (identity, enabled, quota_per_period, used_this_period, period_start, scraper_api_key_enc)
31
+ VALUES ($1, $2, $3, 0, date_trunc('month', now())::date, $4)
32
+ ON CONFLICT (identity) DO UPDATE SET
33
+ enabled = EXCLUDED.enabled,
34
+ quota_per_period = COALESCE($3, mem_schedule_entitlements.quota_per_period),
35
+ scraper_api_key_enc = COALESCE($4, mem_schedule_entitlements.scraper_api_key_enc),
36
+ updated_at = now()`,
37
+ [args.identity, args.enabled, args.quotaPerPeriod ?? 1e3, scraperApiKeyEnc]
38
+ );
39
+ }
40
+ async function getScraperApiKey(identity) {
41
+ await ensureSchema();
42
+ const res = await query(
43
+ `SELECT scraper_api_key_enc FROM mem_schedule_entitlements WHERE identity = $1 LIMIT 1`,
44
+ [identity]
45
+ );
46
+ const enc = res.rows[0]?.scraper_api_key_enc;
47
+ return enc ? decryptSecret(enc) : null;
48
+ }
49
+ async function tryConsumeQuota(identity) {
50
+ await ensureSchema();
51
+ await rollPeriodIfNeeded(identity);
52
+ const res = await query(
53
+ `UPDATE mem_schedule_entitlements
54
+ SET used_this_period = used_this_period + 1, updated_at = now()
55
+ WHERE identity = $1 AND enabled = true AND used_this_period < quota_per_period
56
+ RETURNING used_this_period`,
57
+ [identity]
58
+ );
59
+ if ((res.rowCount ?? 0) > 0) return { ok: true };
60
+ const current = await getEntitlement(identity);
61
+ if (!current || !current.enabled) return { ok: false, reason: "not_enabled" };
62
+ return { ok: false, reason: "quota_exceeded" };
63
+ }
64
+ export {
65
+ getEntitlement,
66
+ getScraperApiKey,
67
+ setEntitlement,
68
+ tryConsumeQuota
69
+ };
70
+ //# sourceMappingURL=schedule-entitlements.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/db/schedule-entitlements.ts"],"sourcesContent":["import { ensureSchema, query } from './pool'\nimport { encryptSecret, decryptSecret } from '../lib/secret-crypto'\n\nexport interface ScheduleEntitlement {\n identity: string\n enabled: boolean\n quotaPerPeriod: number\n usedThisPeriod: number\n periodStart: string\n}\n\ninterface RawRow {\n identity: string\n enabled: boolean\n quota_per_period: number\n used_this_period: number\n period_start: string\n scraper_api_key_enc: string | null\n}\n\nfunction mapRow(r: RawRow): ScheduleEntitlement {\n return {\n identity: r.identity,\n enabled: r.enabled,\n quotaPerPeriod: r.quota_per_period,\n usedThisPeriod: r.used_this_period,\n periodStart: r.period_start,\n }\n}\n\nasync function rollPeriodIfNeeded(identity: string): Promise<void> {\n await query(\n `UPDATE mem_schedule_entitlements\n SET used_this_period = 0, period_start = date_trunc('month', now())::date, updated_at = now()\n WHERE identity = $1 AND period_start < date_trunc('month', now())::date`,\n [identity],\n )\n}\n\nexport async function getEntitlement(identity: string): Promise<ScheduleEntitlement | null> {\n await ensureSchema()\n await rollPeriodIfNeeded(identity)\n const res = await query<RawRow>(`SELECT * FROM mem_schedule_entitlements WHERE identity = $1 LIMIT 1`, [identity])\n return res.rows[0] ? mapRow(res.rows[0]) : null\n}\n\nexport async function setEntitlement(args: {\n identity: string\n enabled: boolean\n quotaPerPeriod?: number\n scraperApiKey?: string\n}): Promise<void> {\n await ensureSchema()\n const scraperApiKeyEnc = args.scraperApiKey ? encryptSecret(args.scraperApiKey) : null\n await query(\n `INSERT INTO mem_schedule_entitlements (identity, enabled, quota_per_period, used_this_period, period_start, scraper_api_key_enc)\n VALUES ($1, $2, $3, 0, date_trunc('month', now())::date, $4)\n ON CONFLICT (identity) DO UPDATE SET\n enabled = EXCLUDED.enabled,\n quota_per_period = COALESCE($3, mem_schedule_entitlements.quota_per_period),\n scraper_api_key_enc = COALESCE($4, mem_schedule_entitlements.scraper_api_key_enc),\n updated_at = now()`,\n [args.identity, args.enabled, args.quotaPerPeriod ?? 1000, scraperApiKeyEnc],\n )\n}\n\nexport async function getScraperApiKey(identity: string): Promise<string | null> {\n await ensureSchema()\n const res = await query<{ scraper_api_key_enc: string | null }>(\n `SELECT scraper_api_key_enc FROM mem_schedule_entitlements WHERE identity = $1 LIMIT 1`,\n [identity],\n )\n const enc = res.rows[0]?.scraper_api_key_enc\n return enc ? decryptSecret(enc) : null\n}\n\nexport type ConsumeQuotaResult = { ok: true } | { ok: false; reason: 'not_enabled' | 'quota_exceeded' }\n\nexport async function tryConsumeQuota(identity: string): Promise<ConsumeQuotaResult> {\n await ensureSchema()\n await rollPeriodIfNeeded(identity)\n const res = await query<{ used_this_period: number }>(\n `UPDATE mem_schedule_entitlements\n SET used_this_period = used_this_period + 1, updated_at = now()\n WHERE identity = $1 AND enabled = true AND used_this_period < quota_per_period\n RETURNING used_this_period`,\n [identity],\n )\n if ((res.rowCount ?? 0) > 0) return { ok: true }\n const current = await getEntitlement(identity)\n if (!current || !current.enabled) return { ok: false, reason: 'not_enabled' }\n return { ok: false, reason: 'quota_exceeded' }\n}\n"],"mappings":"AAAA,SAAS,cAAc,aAAa;AACpC,SAAS,eAAe,qBAAqB;AAmB7C,SAAS,OAAO,GAAgC;AAC9C,SAAO;AAAA,IACL,UAAU,EAAE;AAAA,IACZ,SAAS,EAAE;AAAA,IACX,gBAAgB,EAAE;AAAA,IAClB,gBAAgB,EAAE;AAAA,IAClB,aAAa,EAAE;AAAA,EACjB;AACF;AAEA,eAAe,mBAAmB,UAAiC;AACjE,QAAM;AAAA,IACJ;AAAA;AAAA;AAAA,IAGA,CAAC,QAAQ;AAAA,EACX;AACF;AAEA,eAAsB,eAAe,UAAuD;AAC1F,QAAM,aAAa;AACnB,QAAM,mBAAmB,QAAQ;AACjC,QAAM,MAAM,MAAM,MAAc,uEAAuE,CAAC,QAAQ,CAAC;AACjH,SAAO,IAAI,KAAK,CAAC,IAAI,OAAO,IAAI,KAAK,CAAC,CAAC,IAAI;AAC7C;AAEA,eAAsB,eAAe,MAKnB;AAChB,QAAM,aAAa;AACnB,QAAM,mBAAmB,KAAK,gBAAgB,cAAc,KAAK,aAAa,IAAI;AAClF,QAAM;AAAA,IACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOA,CAAC,KAAK,UAAU,KAAK,SAAS,KAAK,kBAAkB,KAAM,gBAAgB;AAAA,EAC7E;AACF;AAEA,eAAsB,iBAAiB,UAA0C;AAC/E,QAAM,aAAa;AACnB,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,IACA,CAAC,QAAQ;AAAA,EACX;AACA,QAAM,MAAM,IAAI,KAAK,CAAC,GAAG;AACzB,SAAO,MAAM,cAAc,GAAG,IAAI;AACpC;AAIA,eAAsB,gBAAgB,UAA+C;AACnF,QAAM,aAAa;AACnB,QAAM,mBAAmB,QAAQ;AACjC,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA;AAAA;AAAA;AAAA,IAIA,CAAC,QAAQ;AAAA,EACX;AACA,OAAK,IAAI,YAAY,KAAK,EAAG,QAAO,EAAE,IAAI,KAAK;AAC/C,QAAM,UAAU,MAAM,eAAe,QAAQ;AAC7C,MAAI,CAAC,WAAW,CAAC,QAAQ,QAAS,QAAO,EAAE,IAAI,OAAO,QAAQ,cAAc;AAC5E,SAAO,EAAE,IAAI,OAAO,QAAQ,iBAAiB;AAC/C;","names":[]}
@@ -0,0 +1,7 @@
1
+ declare function getOrCreateScheduleLinkSecret(identity: string): Promise<string>;
2
+ declare class ScheduleLinkSecretNotRetrievable extends Error {
3
+ constructor();
4
+ }
5
+ declare function revokeScheduleLink(identity: string): Promise<boolean>;
6
+
7
+ export { ScheduleLinkSecretNotRetrievable, getOrCreateScheduleLinkSecret, revokeScheduleLink };
@@ -0,0 +1,50 @@
1
+ import { ensureSchema, query } from "./pool";
2
+ import { issueKeyRow, revokeKeyRow, getKeyById } from "./keys";
3
+ import { fullScope } from "../lib/auth";
4
+ async function getScheduleLinkKeyId(identity) {
5
+ await ensureSchema();
6
+ const res = await query(
7
+ `SELECT schedule_link_key_id FROM mem_settings WHERE identity = $1 LIMIT 1`,
8
+ [identity]
9
+ );
10
+ return res.rows[0]?.schedule_link_key_id ?? null;
11
+ }
12
+ async function setScheduleLinkKeyId(identity, keyId) {
13
+ await ensureSchema();
14
+ await query(
15
+ `INSERT INTO mem_settings (identity, schedule_link_key_id, updated_at)
16
+ VALUES ($1, $2, now())
17
+ ON CONFLICT (identity) DO UPDATE SET schedule_link_key_id = EXCLUDED.schedule_link_key_id, updated_at = now()`,
18
+ [identity, keyId]
19
+ );
20
+ }
21
+ async function getOrCreateScheduleLinkSecret(identity) {
22
+ const existingId = await getScheduleLinkKeyId(identity);
23
+ if (existingId) {
24
+ const existing = await getKeyById(existingId);
25
+ if (existing && !existing.revoked) {
26
+ throw new ScheduleLinkSecretNotRetrievable();
27
+ }
28
+ }
29
+ const issued = await issueKeyRow({ identity, vaults: [], scope: fullScope(), plan: "free", expiresAt: null });
30
+ await setScheduleLinkKeyId(identity, issued.keyId);
31
+ return issued.secret;
32
+ }
33
+ class ScheduleLinkSecretNotRetrievable extends Error {
34
+ constructor() {
35
+ super("A schedule link already exists for this identity; its secret cannot be re-displayed (only revoked and reissued).");
36
+ }
37
+ }
38
+ async function revokeScheduleLink(identity) {
39
+ const existingId = await getScheduleLinkKeyId(identity);
40
+ if (!existingId) return false;
41
+ await revokeKeyRow(existingId);
42
+ await setScheduleLinkKeyId(identity, null);
43
+ return true;
44
+ }
45
+ export {
46
+ ScheduleLinkSecretNotRetrievable,
47
+ getOrCreateScheduleLinkSecret,
48
+ revokeScheduleLink
49
+ };
50
+ //# sourceMappingURL=schedule-link.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/db/schedule-link.ts"],"sourcesContent":["import { ensureSchema, query } from './pool'\nimport { issueKeyRow, revokeKeyRow, getKeyById } from './keys'\nimport { fullScope } from '../lib/auth'\n\nasync function getScheduleLinkKeyId(identity: string): Promise<string | null> {\n await ensureSchema()\n const res = await query<{ schedule_link_key_id: string | null }>(\n `SELECT schedule_link_key_id FROM mem_settings WHERE identity = $1 LIMIT 1`,\n [identity],\n )\n return res.rows[0]?.schedule_link_key_id ?? null\n}\n\nasync function setScheduleLinkKeyId(identity: string, keyId: string | null): Promise<void> {\n await ensureSchema()\n await query(\n `INSERT INTO mem_settings (identity, schedule_link_key_id, updated_at)\n VALUES ($1, $2, now())\n ON CONFLICT (identity) DO UPDATE SET schedule_link_key_id = EXCLUDED.schedule_link_key_id, updated_at = now()`,\n [identity, keyId],\n )\n}\n\nexport async function getOrCreateScheduleLinkSecret(identity: string): Promise<string> {\n const existingId = await getScheduleLinkKeyId(identity)\n if (existingId) {\n const existing = await getKeyById(existingId)\n if (existing && !existing.revoked) {\n throw new ScheduleLinkSecretNotRetrievable()\n }\n }\n const issued = await issueKeyRow({ identity, vaults: [], scope: fullScope(), plan: 'free', expiresAt: null })\n await setScheduleLinkKeyId(identity, issued.keyId)\n return issued.secret\n}\n\nexport class ScheduleLinkSecretNotRetrievable extends Error {\n constructor() {\n super('A schedule link already exists for this identity; its secret cannot be re-displayed (only revoked and reissued).')\n }\n}\n\nexport async function revokeScheduleLink(identity: string): Promise<boolean> {\n const existingId = await getScheduleLinkKeyId(identity)\n if (!existingId) return false\n await revokeKeyRow(existingId)\n await setScheduleLinkKeyId(identity, null)\n return true\n}\n"],"mappings":"AAAA,SAAS,cAAc,aAAa;AACpC,SAAS,aAAa,cAAc,kBAAkB;AACtD,SAAS,iBAAiB;AAE1B,eAAe,qBAAqB,UAA0C;AAC5E,QAAM,aAAa;AACnB,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,IACA,CAAC,QAAQ;AAAA,EACX;AACA,SAAO,IAAI,KAAK,CAAC,GAAG,wBAAwB;AAC9C;AAEA,eAAe,qBAAqB,UAAkB,OAAqC;AACzF,QAAM,aAAa;AACnB,QAAM;AAAA,IACJ;AAAA;AAAA;AAAA,IAGA,CAAC,UAAU,KAAK;AAAA,EAClB;AACF;AAEA,eAAsB,8BAA8B,UAAmC;AACrF,QAAM,aAAa,MAAM,qBAAqB,QAAQ;AACtD,MAAI,YAAY;AACd,UAAM,WAAW,MAAM,WAAW,UAAU;AAC5C,QAAI,YAAY,CAAC,SAAS,SAAS;AACjC,YAAM,IAAI,iCAAiC;AAAA,IAC7C;AAAA,EACF;AACA,QAAM,SAAS,MAAM,YAAY,EAAE,UAAU,QAAQ,CAAC,GAAG,OAAO,UAAU,GAAG,MAAM,QAAQ,WAAW,KAAK,CAAC;AAC5G,QAAM,qBAAqB,UAAU,OAAO,KAAK;AACjD,SAAO,OAAO;AAChB;AAEO,MAAM,yCAAyC,MAAM;AAAA,EAC1D,cAAc;AACZ,UAAM,kHAAkH;AAAA,EAC1H;AACF;AAEA,eAAsB,mBAAmB,UAAoC;AAC3E,QAAM,aAAa,MAAM,qBAAqB,QAAQ;AACtD,MAAI,CAAC,WAAY,QAAO;AACxB,QAAM,aAAa,UAAU;AAC7B,QAAM,qBAAqB,UAAU,IAAI;AACzC,SAAO;AACT;","names":[]}
@@ -0,0 +1,58 @@
1
+ import { ScheduleCadence } from '../lib/schedule-cadence.js';
2
+
3
+ type ScheduledActionStatus = 'active' | 'paused' | 'deleted' | 'completed';
4
+ interface ScheduledActionRecord {
5
+ id: string;
6
+ identity: string;
7
+ description: string;
8
+ vault: string;
9
+ cadence: ScheduleCadence;
10
+ timeOfDay: string | null;
11
+ timezone: string;
12
+ status: ScheduledActionStatus;
13
+ nextRunAt: string;
14
+ lastRunAt: string | null;
15
+ lastRunStatus: string | null;
16
+ createdAt: string;
17
+ updatedAt: string;
18
+ system: string | null;
19
+ }
20
+ declare function createScheduledAction(args: {
21
+ identity: string;
22
+ description: string;
23
+ vault: string;
24
+ cadence: ScheduleCadence;
25
+ timeOfDay?: string | null;
26
+ timezone?: string | null;
27
+ nextRunAt: string;
28
+ system?: string | null;
29
+ }): Promise<ScheduledActionRecord>;
30
+ declare function getSystemScheduledAction(identity: string, system: string): Promise<ScheduledActionRecord | null>;
31
+ declare function getScheduledAction(id: string, identity: string): Promise<ScheduledActionRecord | null>;
32
+ declare function listScheduledActionsForIdentity(identity: string): Promise<ScheduledActionRecord[]>;
33
+ declare function patchScheduledAction(args: {
34
+ id: string;
35
+ identity: string;
36
+ status?: 'active' | 'paused';
37
+ description?: string;
38
+ cadence?: ScheduleCadence;
39
+ timeOfDay?: string | null;
40
+ timezone?: string | null;
41
+ nextRunAt?: string;
42
+ }): Promise<ScheduledActionRecord | null>;
43
+ type DeleteScheduledActionResult = {
44
+ deleted: true;
45
+ } | {
46
+ deleted: false;
47
+ reason: 'not_found' | 'system';
48
+ };
49
+ declare function deleteScheduledAction(id: string, identity: string): Promise<DeleteScheduledActionResult>;
50
+ declare function listDueScheduledActions(nowIso: string, limit: number): Promise<ScheduledActionRecord[]>;
51
+ declare function markScheduledActionRan(id: string, args: {
52
+ lastRunAt: string;
53
+ nextRunAt: string;
54
+ lastRunStatus: string;
55
+ status?: ScheduledActionStatus;
56
+ }): Promise<void>;
57
+
58
+ export { type DeleteScheduledActionResult, type ScheduledActionRecord, type ScheduledActionStatus, createScheduledAction, deleteScheduledAction, getScheduledAction, getSystemScheduledAction, listDueScheduledActions, listScheduledActionsForIdentity, markScheduledActionRan, patchScheduledAction };
@@ -0,0 +1,139 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { ensureSchema, query } from "./pool";
3
+ function mapRow(r) {
4
+ return {
5
+ id: r.id,
6
+ identity: r.identity,
7
+ description: r.description,
8
+ vault: r.vault,
9
+ cadence: r.cadence,
10
+ timeOfDay: r.time_of_day,
11
+ timezone: r.timezone,
12
+ status: r.status,
13
+ nextRunAt: r.next_run_at,
14
+ lastRunAt: r.last_run_at,
15
+ lastRunStatus: r.last_run_status,
16
+ createdAt: r.created_at,
17
+ updatedAt: r.updated_at,
18
+ system: r.system
19
+ };
20
+ }
21
+ const COLUMNS = "id, identity, description, vault, cadence, time_of_day, timezone, status, next_run_at, last_run_at, last_run_status, created_at, updated_at, system";
22
+ async function createScheduledAction(args) {
23
+ await ensureSchema();
24
+ const id = randomUUID();
25
+ const res = await query(
26
+ `INSERT INTO mem_scheduled_actions (id, identity, description, vault, cadence, time_of_day, timezone, status, next_run_at, system)
27
+ VALUES ($1, $2, $3, $4, $5, $6, $7, 'active', $8, $9)
28
+ RETURNING ${COLUMNS}`,
29
+ [id, args.identity, args.description, args.vault, args.cadence, args.timeOfDay ?? null, args.timezone ?? "UTC", args.nextRunAt, args.system ?? null]
30
+ );
31
+ return mapRow(res.rows[0]);
32
+ }
33
+ async function getSystemScheduledAction(identity, system) {
34
+ await ensureSchema();
35
+ const res = await query(
36
+ `SELECT ${COLUMNS} FROM mem_scheduled_actions WHERE identity = $1 AND system = $2 AND status != 'deleted' LIMIT 1`,
37
+ [identity, system]
38
+ );
39
+ return res.rows[0] ? mapRow(res.rows[0]) : null;
40
+ }
41
+ async function getScheduledAction(id, identity) {
42
+ await ensureSchema();
43
+ const res = await query(
44
+ `SELECT ${COLUMNS} FROM mem_scheduled_actions WHERE id = $1 AND identity = $2 AND status != 'deleted' LIMIT 1`,
45
+ [id, identity]
46
+ );
47
+ return res.rows[0] ? mapRow(res.rows[0]) : null;
48
+ }
49
+ async function listScheduledActionsForIdentity(identity) {
50
+ await ensureSchema();
51
+ const res = await query(
52
+ `SELECT ${COLUMNS} FROM mem_scheduled_actions WHERE identity = $1 AND status != 'deleted' ORDER BY created_at DESC`,
53
+ [identity]
54
+ );
55
+ return res.rows.map(mapRow);
56
+ }
57
+ async function patchScheduledAction(args) {
58
+ await ensureSchema();
59
+ const sets = [];
60
+ const params = [];
61
+ if (args.status !== void 0) {
62
+ params.push(args.status);
63
+ sets.push(`status = $${params.length}`);
64
+ }
65
+ if (args.description !== void 0) {
66
+ params.push(args.description);
67
+ sets.push(`description = $${params.length}`);
68
+ }
69
+ if (args.cadence !== void 0) {
70
+ params.push(args.cadence);
71
+ sets.push(`cadence = $${params.length}`);
72
+ }
73
+ if (args.timeOfDay !== void 0) {
74
+ params.push(args.timeOfDay);
75
+ sets.push(`time_of_day = $${params.length}`);
76
+ }
77
+ if (args.timezone !== void 0) {
78
+ params.push(args.timezone);
79
+ sets.push(`timezone = $${params.length}`);
80
+ }
81
+ if (args.nextRunAt !== void 0) {
82
+ params.push(args.nextRunAt);
83
+ sets.push(`next_run_at = $${params.length}`);
84
+ }
85
+ if (sets.length === 0) return getScheduledAction(args.id, args.identity);
86
+ sets.push("updated_at = now()");
87
+ params.push(args.id, args.identity);
88
+ const res = await query(
89
+ `UPDATE mem_scheduled_actions SET ${sets.join(", ")}
90
+ WHERE id = $${params.length - 1} AND identity = $${params.length} AND status != 'deleted'
91
+ RETURNING ${COLUMNS}`,
92
+ params
93
+ );
94
+ return res.rows[0] ? mapRow(res.rows[0]) : null;
95
+ }
96
+ async function deleteScheduledAction(id, identity) {
97
+ await ensureSchema();
98
+ const existing = await getScheduledAction(id, identity);
99
+ if (!existing) return { deleted: false, reason: "not_found" };
100
+ if (existing.system) return { deleted: false, reason: "system" };
101
+ const res = await query(
102
+ `UPDATE mem_scheduled_actions SET status = 'deleted', updated_at = now() WHERE id = $1 AND identity = $2 AND status != 'deleted'`,
103
+ [id, identity]
104
+ );
105
+ return (res.rowCount ?? 0) > 0 ? { deleted: true } : { deleted: false, reason: "not_found" };
106
+ }
107
+ async function listDueScheduledActions(nowIso, limit) {
108
+ await ensureSchema();
109
+ const res = await query(
110
+ `SELECT ${COLUMNS} FROM mem_scheduled_actions WHERE status = 'active' AND next_run_at <= $1 ORDER BY next_run_at ASC LIMIT $2`,
111
+ [nowIso, limit]
112
+ );
113
+ return res.rows.map(mapRow);
114
+ }
115
+ async function markScheduledActionRan(id, args) {
116
+ await ensureSchema();
117
+ if (args.status !== void 0) {
118
+ await query(
119
+ `UPDATE mem_scheduled_actions SET last_run_at = $2, next_run_at = $3, last_run_status = $4, status = $5, updated_at = now() WHERE id = $1`,
120
+ [id, args.lastRunAt, args.nextRunAt, args.lastRunStatus, args.status]
121
+ );
122
+ return;
123
+ }
124
+ await query(
125
+ `UPDATE mem_scheduled_actions SET last_run_at = $2, next_run_at = $3, last_run_status = $4, updated_at = now() WHERE id = $1`,
126
+ [id, args.lastRunAt, args.nextRunAt, args.lastRunStatus]
127
+ );
128
+ }
129
+ export {
130
+ createScheduledAction,
131
+ deleteScheduledAction,
132
+ getScheduledAction,
133
+ getSystemScheduledAction,
134
+ listDueScheduledActions,
135
+ listScheduledActionsForIdentity,
136
+ markScheduledActionRan,
137
+ patchScheduledAction
138
+ };
139
+ //# sourceMappingURL=scheduled-actions.js.map