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,176 @@
1
+ import { randomBytes } from "node:crypto";
2
+ import { ensureSchema, query } from "./pool";
3
+ import { VIDEO_RUN_TIMEOUT_ERROR } from "../lib/video-reaper";
4
+ function mapRun(r) {
5
+ return {
6
+ id: r.id,
7
+ identity: r.identity,
8
+ vault: r.vault,
9
+ sourceUrl: r.source_url,
10
+ mediaUrl: r.media_url,
11
+ scraperApiKey: r.scraper_api_key,
12
+ intervalS: Number(r.interval_s),
13
+ maxFrames: r.max_frames,
14
+ detail: r.detail,
15
+ durationS: r.duration_s == null ? null : Number(r.duration_s),
16
+ frameCount: r.frame_count,
17
+ status: r.status,
18
+ error: r.error,
19
+ transcript: r.transcript,
20
+ metrics: r.metrics,
21
+ lenses: r.lenses,
22
+ qc: r.qc,
23
+ reportMd: r.report_md,
24
+ artifactPath: r.artifact_path,
25
+ costUsd: Number(r.cost_usd ?? 0),
26
+ createdAt: new Date(r.created_at),
27
+ updatedAt: new Date(r.updated_at)
28
+ };
29
+ }
30
+ const RUN_COLS = `id, identity, vault, source_url, media_url, scraper_api_key, interval_s, max_frames,
31
+ detail, duration_s, frame_count, status, error, transcript, metrics, lenses, qc, report_md, artifact_path, cost_usd,
32
+ created_at, updated_at`;
33
+ async function createVideoRun(args) {
34
+ await ensureSchema();
35
+ const id = `vid_${randomBytes(8).toString("hex")}`;
36
+ await query(
37
+ `INSERT INTO mem_video_runs (id, identity, vault, source_url, interval_s, max_frames, detail, scraper_api_key, status)
38
+ VALUES ($1,$2,$3,$4,$5,$6,$7,$8,'queued')`,
39
+ [id, args.identity, args.vault, args.sourceUrl, args.intervalS, args.maxFrames, args.detail, args.scraperApiKey]
40
+ );
41
+ return id;
42
+ }
43
+ async function getVideoRun(id, identity) {
44
+ await ensureSchema();
45
+ const res = await query(`SELECT ${RUN_COLS} FROM mem_video_runs WHERE id = $1 AND identity = $2 LIMIT 1`, [id, identity]);
46
+ return res.rows[0] ? mapRun(res.rows[0]) : null;
47
+ }
48
+ const PATCH_COLS = {
49
+ mediaUrl: "media_url",
50
+ durationS: "duration_s",
51
+ frameCount: "frame_count",
52
+ status: "status",
53
+ error: "error",
54
+ transcript: "transcript",
55
+ metrics: "metrics",
56
+ lenses: "lenses",
57
+ qc: "qc",
58
+ reportMd: "report_md",
59
+ artifactPath: "artifact_path",
60
+ costUsd: "cost_usd"
61
+ };
62
+ const JSONB_KEYS = /* @__PURE__ */ new Set(["transcript", "metrics", "lenses", "qc"]);
63
+ async function updateVideoRun(id, patch) {
64
+ await ensureSchema();
65
+ const sets = [];
66
+ const params = [];
67
+ for (const [k, col] of Object.entries(PATCH_COLS)) {
68
+ if (k in patch) {
69
+ const isJson = JSONB_KEYS.has(k);
70
+ params.push(isJson ? JSON.stringify(patch[k]) : patch[k]);
71
+ sets.push(`${col} = $${params.length}${isJson ? "::jsonb" : ""}`);
72
+ }
73
+ }
74
+ if (!sets.length) return;
75
+ params.push(id);
76
+ await query(`UPDATE mem_video_runs SET ${sets.join(", ")}, updated_at = now() WHERE id = $${params.length}`, params);
77
+ }
78
+ async function insertFrames(runId, frames) {
79
+ if (!frames.length) return;
80
+ await ensureSchema();
81
+ const values = [];
82
+ const params = [];
83
+ for (const f of frames) {
84
+ params.push(runId, f.idx, f.tSec, f.imgB64 ?? null);
85
+ values.push(`($${params.length - 3}, $${params.length - 2}, $${params.length - 1}, $${params.length})`);
86
+ }
87
+ await query(
88
+ `INSERT INTO mem_video_frames (run_id, idx, t_sec, img_b64) VALUES ${values.join(", ")}
89
+ ON CONFLICT (run_id, idx) DO UPDATE SET img_b64 = COALESCE(mem_video_frames.img_b64, EXCLUDED.img_b64)`,
90
+ params
91
+ );
92
+ }
93
+ async function setFrameObservation(runId, idx, observation, status) {
94
+ await query(
95
+ `UPDATE mem_video_frames SET observation = $3::jsonb, status = $4, img_b64 = NULL WHERE run_id = $1 AND idx = $2`,
96
+ [runId, idx, JSON.stringify(observation), status]
97
+ );
98
+ }
99
+ async function listPendingImageFrames(runId, limit) {
100
+ await ensureSchema();
101
+ const res = await query(
102
+ `SELECT idx, t_sec, img_b64 FROM mem_video_frames
103
+ WHERE run_id = $1 AND status = 'pending' AND img_b64 IS NOT NULL
104
+ ORDER BY idx ASC LIMIT $2`,
105
+ [runId, limit]
106
+ );
107
+ return res.rows.map((r) => ({ idx: r.idx, tSec: Number(r.t_sec), imgB64: r.img_b64 }));
108
+ }
109
+ async function countPendingFrames(runId) {
110
+ await ensureSchema();
111
+ const res = await query(
112
+ `SELECT COUNT(*)::int AS n FROM mem_video_frames WHERE run_id = $1 AND status = 'pending'`,
113
+ [runId]
114
+ );
115
+ return Number(res.rows[0]?.n ?? 0);
116
+ }
117
+ async function setFrameDetail(runId, idx, detail) {
118
+ await query(
119
+ `UPDATE mem_video_frames SET detail = $3::jsonb WHERE run_id = $1 AND idx = $2`,
120
+ [runId, idx, JSON.stringify(detail)]
121
+ );
122
+ }
123
+ async function listFrames(runId) {
124
+ await ensureSchema();
125
+ const res = await query(
126
+ `SELECT idx, t_sec, observation, detail, status FROM mem_video_frames WHERE run_id = $1 ORDER BY idx ASC`,
127
+ [runId]
128
+ );
129
+ return res.rows.map((r) => ({ idx: r.idx, tSec: Number(r.t_sec), observation: r.observation, detail: r.detail, status: r.status }));
130
+ }
131
+ async function listQueuedVideoRuns(limit) {
132
+ await ensureSchema();
133
+ const res = await query(
134
+ `UPDATE mem_video_runs SET status = 'resolving', updated_at = now()
135
+ WHERE id IN (SELECT id FROM mem_video_runs WHERE status = 'queued' ORDER BY created_at ASC LIMIT $1)
136
+ RETURNING id, identity`,
137
+ [limit]
138
+ );
139
+ return res.rows.map((r) => ({ id: r.id, identity: r.identity }));
140
+ }
141
+ async function reapStaleVideoRun(id, reapMinutes) {
142
+ await ensureSchema();
143
+ const res = await query(
144
+ `UPDATE mem_video_runs
145
+ SET status = 'failed', error = $2, updated_at = now()
146
+ WHERE id = $1
147
+ AND status NOT IN ('done', 'failed')
148
+ AND updated_at < now() - ($3 || ' minutes')::interval
149
+ RETURNING ${RUN_COLS}`,
150
+ [id, VIDEO_RUN_TIMEOUT_ERROR, reapMinutes]
151
+ );
152
+ return res.rows[0] ? mapRun(res.rows[0]) : null;
153
+ }
154
+ async function runProgress(runId) {
155
+ await ensureSchema();
156
+ const res = await query(
157
+ `SELECT COUNT(*)::int AS total, COUNT(observation)::int AS analyzed FROM mem_video_frames WHERE run_id = $1`,
158
+ [runId]
159
+ );
160
+ return { total: Number(res.rows[0]?.total ?? 0), analyzed: Number(res.rows[0]?.analyzed ?? 0) };
161
+ }
162
+ export {
163
+ countPendingFrames,
164
+ createVideoRun,
165
+ getVideoRun,
166
+ insertFrames,
167
+ listFrames,
168
+ listPendingImageFrames,
169
+ listQueuedVideoRuns,
170
+ reapStaleVideoRun,
171
+ runProgress,
172
+ setFrameDetail,
173
+ setFrameObservation,
174
+ updateVideoRun
175
+ };
176
+ //# sourceMappingURL=video-runs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/db/video-runs.ts"],"sourcesContent":["import { randomBytes } from 'node:crypto'\nimport { ensureSchema, query } from './pool'\nimport { VIDEO_RUN_TIMEOUT_ERROR } from '../lib/video-reaper'\n\nexport type VideoRunStatus =\n | 'queued' | 'resolving' | 'analyzing' | 'synthesizing' | 'detail' | 'done' | 'failed'\n\nexport interface VideoRunRow {\n id: string\n identity: string\n vault: string\n sourceUrl: string\n mediaUrl: string | null\n scraperApiKey: string | null\n intervalS: number\n maxFrames: number\n detail: string\n durationS: number | null\n frameCount: number\n status: VideoRunStatus\n error: string | null\n transcript: unknown\n metrics: unknown\n lenses: unknown\n qc: unknown\n reportMd: string | null\n artifactPath: string | null\n costUsd: number\n createdAt: Date\n updatedAt: Date\n}\n\ninterface RawRun {\n id: string; identity: string; vault: string; source_url: string; media_url: string | null\n scraper_api_key: string | null; interval_s: number; max_frames: number; detail: string\n duration_s: number | null; frame_count: number; status: string; error: string | null\n transcript: unknown; metrics: unknown; lenses: unknown; qc: unknown\n report_md: string | null; artifact_path: string | null; cost_usd: number\n created_at: string | Date; updated_at: string | Date\n}\n\nfunction mapRun(r: RawRun): VideoRunRow {\n return {\n id: r.id, identity: r.identity, vault: r.vault, sourceUrl: r.source_url, mediaUrl: r.media_url,\n scraperApiKey: r.scraper_api_key, intervalS: Number(r.interval_s), maxFrames: r.max_frames,\n detail: r.detail, durationS: r.duration_s == null ? null : Number(r.duration_s), frameCount: r.frame_count,\n status: r.status as VideoRunStatus, error: r.error,\n transcript: r.transcript, metrics: r.metrics, lenses: r.lenses, qc: r.qc, reportMd: r.report_md,\n artifactPath: r.artifact_path, costUsd: Number(r.cost_usd ?? 0),\n createdAt: new Date(r.created_at), updatedAt: new Date(r.updated_at),\n }\n}\n\nconst RUN_COLS = `id, identity, vault, source_url, media_url, scraper_api_key, interval_s, max_frames,\n detail, duration_s, frame_count, status, error, transcript, metrics, lenses, qc, report_md, artifact_path, cost_usd,\n created_at, updated_at`\n\nexport async function createVideoRun(args: {\n identity: string; vault: string; sourceUrl: string; intervalS: number; maxFrames: number;\n detail: string; scraperApiKey: string | null\n}): Promise<string> {\n await ensureSchema()\n const id = `vid_${randomBytes(8).toString('hex')}`\n await query(\n `INSERT INTO mem_video_runs (id, identity, vault, source_url, interval_s, max_frames, detail, scraper_api_key, status)\n VALUES ($1,$2,$3,$4,$5,$6,$7,$8,'queued')`,\n [id, args.identity, args.vault, args.sourceUrl, args.intervalS, args.maxFrames, args.detail, args.scraperApiKey],\n )\n return id\n}\n\nexport async function getVideoRun(id: string, identity: string): Promise<VideoRunRow | null> {\n await ensureSchema()\n const res = await query<RawRun>(`SELECT ${RUN_COLS} FROM mem_video_runs WHERE id = $1 AND identity = $2 LIMIT 1`, [id, identity])\n return res.rows[0] ? mapRun(res.rows[0]) : null\n}\n\nconst PATCH_COLS: Record<string, string> = {\n mediaUrl: 'media_url', durationS: 'duration_s', frameCount: 'frame_count', status: 'status',\n error: 'error', transcript: 'transcript', metrics: 'metrics', lenses: 'lenses', qc: 'qc',\n reportMd: 'report_md', artifactPath: 'artifact_path', costUsd: 'cost_usd',\n}\nconst JSONB_KEYS = new Set(['transcript', 'metrics', 'lenses', 'qc'])\n\nexport async function updateVideoRun(id: string, patch: Partial<Record<keyof typeof PATCH_COLS, unknown>>): Promise<void> {\n await ensureSchema()\n const sets: string[] = []\n const params: unknown[] = []\n for (const [k, col] of Object.entries(PATCH_COLS)) {\n if (k in patch) {\n const isJson = JSONB_KEYS.has(k)\n params.push(isJson ? JSON.stringify((patch as Record<string, unknown>)[k]) : (patch as Record<string, unknown>)[k])\n sets.push(`${col} = $${params.length}${isJson ? '::jsonb' : ''}`)\n }\n }\n if (!sets.length) return\n params.push(id)\n await query(`UPDATE mem_video_runs SET ${sets.join(', ')}, updated_at = now() WHERE id = $${params.length}`, params)\n}\n\nexport async function insertFrames(runId: string, frames: Array<{ idx: number; tSec: number; imgB64?: string }>): Promise<void> {\n if (!frames.length) return\n await ensureSchema()\n const values: string[] = []\n const params: unknown[] = []\n for (const f of frames) {\n params.push(runId, f.idx, f.tSec, f.imgB64 ?? null)\n values.push(`($${params.length - 3}, $${params.length - 2}, $${params.length - 1}, $${params.length})`)\n }\n await query(\n `INSERT INTO mem_video_frames (run_id, idx, t_sec, img_b64) VALUES ${values.join(', ')}\n ON CONFLICT (run_id, idx) DO UPDATE SET img_b64 = COALESCE(mem_video_frames.img_b64, EXCLUDED.img_b64)`,\n params,\n )\n}\n\nexport async function setFrameObservation(runId: string, idx: number, observation: unknown, status: 'ok' | 'error'): Promise<void> {\n await query(\n `UPDATE mem_video_frames SET observation = $3::jsonb, status = $4, img_b64 = NULL WHERE run_id = $1 AND idx = $2`,\n [runId, idx, JSON.stringify(observation), status],\n )\n}\n\nexport interface PendingFrame { idx: number; tSec: number; imgB64: string }\n\nexport async function listPendingImageFrames(runId: string, limit: number): Promise<PendingFrame[]> {\n await ensureSchema()\n const res = await query<{ idx: number; t_sec: number; img_b64: string }>(\n `SELECT idx, t_sec, img_b64 FROM mem_video_frames\n WHERE run_id = $1 AND status = 'pending' AND img_b64 IS NOT NULL\n ORDER BY idx ASC LIMIT $2`,\n [runId, limit],\n )\n return res.rows.map(r => ({ idx: r.idx, tSec: Number(r.t_sec), imgB64: r.img_b64 }))\n}\n\nexport async function countPendingFrames(runId: string): Promise<number> {\n await ensureSchema()\n const res = await query<{ n: string }>(\n `SELECT COUNT(*)::int AS n FROM mem_video_frames WHERE run_id = $1 AND status = 'pending'`,\n [runId],\n )\n return Number(res.rows[0]?.n ?? 0)\n}\n\nexport async function setFrameDetail(runId: string, idx: number, detail: unknown): Promise<void> {\n await query(\n `UPDATE mem_video_frames SET detail = $3::jsonb WHERE run_id = $1 AND idx = $2`,\n [runId, idx, JSON.stringify(detail)],\n )\n}\n\nexport interface FrameRow { idx: number; tSec: number; observation: unknown; detail: unknown; status: string }\n\nexport async function listFrames(runId: string): Promise<FrameRow[]> {\n await ensureSchema()\n const res = await query<{ idx: number; t_sec: number; observation: unknown; detail: unknown; status: string }>(\n `SELECT idx, t_sec, observation, detail, status FROM mem_video_frames WHERE run_id = $1 ORDER BY idx ASC`,\n [runId],\n )\n return res.rows.map(r => ({ idx: r.idx, tSec: Number(r.t_sec), observation: r.observation, detail: r.detail, status: r.status }))\n}\n\nexport async function listQueuedVideoRuns(limit: number): Promise<Array<{ id: string; identity: string }>> {\n await ensureSchema()\n const res = await query<{ id: string; identity: string }>(\n `UPDATE mem_video_runs SET status = 'resolving', updated_at = now()\n WHERE id IN (SELECT id FROM mem_video_runs WHERE status = 'queued' ORDER BY created_at ASC LIMIT $1)\n RETURNING id, identity`,\n [limit],\n )\n return res.rows.map(r => ({ id: r.id, identity: r.identity }))\n}\n\nexport async function reapStaleVideoRun(id: string, reapMinutes: number): Promise<VideoRunRow | null> {\n await ensureSchema()\n const res = await query<RawRun>(\n `UPDATE mem_video_runs\n SET status = 'failed', error = $2, updated_at = now()\n WHERE id = $1\n AND status NOT IN ('done', 'failed')\n AND updated_at < now() - ($3 || ' minutes')::interval\n RETURNING ${RUN_COLS}`,\n [id, VIDEO_RUN_TIMEOUT_ERROR, reapMinutes],\n )\n return res.rows[0] ? mapRun(res.rows[0]) : null\n}\n\nexport async function runProgress(runId: string): Promise<{ total: number; analyzed: number }> {\n await ensureSchema()\n const res = await query<{ total: string; analyzed: string }>(\n `SELECT COUNT(*)::int AS total, COUNT(observation)::int AS analyzed FROM mem_video_frames WHERE run_id = $1`,\n [runId],\n )\n return { total: Number(res.rows[0]?.total ?? 0), analyzed: Number(res.rows[0]?.analyzed ?? 0) }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,20 @@
1
+ interface WebhookRecord {
2
+ id: string;
3
+ identity: string;
4
+ keyId: string;
5
+ vault: string;
6
+ label: string | null;
7
+ createdAt: string;
8
+ }
9
+ declare function createWebhook(args: {
10
+ identity: string;
11
+ vault: string;
12
+ label?: string;
13
+ }): Promise<{
14
+ webhook: WebhookRecord;
15
+ secret: string;
16
+ }>;
17
+ declare function listWebhooksForIdentity(identity: string): Promise<WebhookRecord[]>;
18
+ declare function revokeWebhook(id: string, identity: string): Promise<boolean>;
19
+
20
+ export { type WebhookRecord, createWebhook, listWebhooksForIdentity, revokeWebhook };
@@ -0,0 +1,43 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { ensureSchema, query } from "./pool";
3
+ import { issueKeyRow, revokeKeyRow } from "./keys";
4
+ function mapRow(r) {
5
+ return { id: r.id, identity: r.identity, keyId: r.key_id, vault: r.vault, label: r.label, createdAt: r.created_at };
6
+ }
7
+ const COLUMNS = "id, identity, key_id, vault, label, created_at";
8
+ async function createWebhook(args) {
9
+ await ensureSchema();
10
+ const issued = await issueKeyRow({
11
+ identity: args.identity,
12
+ vaults: [args.vault],
13
+ scope: { read: false, write: true, export: false, index: false, admin: false, swap: false },
14
+ plan: "free",
15
+ expiresAt: null
16
+ });
17
+ const id = randomUUID();
18
+ const res = await query(
19
+ `INSERT INTO mem_webhooks (id, identity, key_id, vault, label) VALUES ($1, $2, $3, $4, $5) RETURNING ${COLUMNS}`,
20
+ [id, args.identity, issued.keyId, args.vault, args.label ?? null]
21
+ );
22
+ return { webhook: mapRow(res.rows[0]), secret: issued.secret };
23
+ }
24
+ async function listWebhooksForIdentity(identity) {
25
+ await ensureSchema();
26
+ const res = await query(`SELECT ${COLUMNS} FROM mem_webhooks WHERE identity = $1 ORDER BY created_at DESC`, [identity]);
27
+ return res.rows.map(mapRow);
28
+ }
29
+ async function revokeWebhook(id, identity) {
30
+ await ensureSchema();
31
+ const res = await query(`SELECT ${COLUMNS} FROM mem_webhooks WHERE id = $1 AND identity = $2`, [id, identity]);
32
+ const row = res.rows[0];
33
+ if (!row) return false;
34
+ await revokeKeyRow(row.key_id);
35
+ await query(`DELETE FROM mem_webhooks WHERE id = $1`, [id]);
36
+ return true;
37
+ }
38
+ export {
39
+ createWebhook,
40
+ listWebhooksForIdentity,
41
+ revokeWebhook
42
+ };
43
+ //# sourceMappingURL=webhooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/db/webhooks.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto'\nimport { ensureSchema, query } from './pool'\nimport { issueKeyRow, revokeKeyRow } from './keys'\n\nexport interface WebhookRecord {\n id: string\n identity: string\n keyId: string\n vault: string\n label: string | null\n createdAt: string\n}\n\ninterface RawRow {\n id: string\n identity: string\n key_id: string\n vault: string\n label: string | null\n created_at: string\n}\n\nfunction mapRow(r: RawRow): WebhookRecord {\n return { id: r.id, identity: r.identity, keyId: r.key_id, vault: r.vault, label: r.label, createdAt: r.created_at }\n}\n\nconst COLUMNS = 'id, identity, key_id, vault, label, created_at'\n\nexport async function createWebhook(args: { identity: string; vault: string; label?: string }): Promise<{ webhook: WebhookRecord; secret: string }> {\n await ensureSchema()\n const issued = await issueKeyRow({\n identity: args.identity,\n vaults: [args.vault],\n scope: { read: false, write: true, export: false, index: false, admin: false, swap: false },\n plan: 'free',\n expiresAt: null,\n })\n const id = randomUUID()\n const res = await query<RawRow>(\n `INSERT INTO mem_webhooks (id, identity, key_id, vault, label) VALUES ($1, $2, $3, $4, $5) RETURNING ${COLUMNS}`,\n [id, args.identity, issued.keyId, args.vault, args.label ?? null],\n )\n return { webhook: mapRow(res.rows[0]), secret: issued.secret }\n}\n\nexport async function listWebhooksForIdentity(identity: string): Promise<WebhookRecord[]> {\n await ensureSchema()\n const res = await query<RawRow>(`SELECT ${COLUMNS} FROM mem_webhooks WHERE identity = $1 ORDER BY created_at DESC`, [identity])\n return res.rows.map(mapRow)\n}\n\nexport async function revokeWebhook(id: string, identity: string): Promise<boolean> {\n await ensureSchema()\n const res = await query<RawRow>(`SELECT ${COLUMNS} FROM mem_webhooks WHERE id = $1 AND identity = $2`, [id, identity])\n const row = res.rows[0]\n if (!row) return false\n await revokeKeyRow(row.key_id)\n await query(`DELETE FROM mem_webhooks WHERE id = $1`, [id])\n return true\n}\n"],"mappings":"AAAA,SAAS,kBAAkB;AAC3B,SAAS,cAAc,aAAa;AACpC,SAAS,aAAa,oBAAoB;AAoB1C,SAAS,OAAO,GAA0B;AACxC,SAAO,EAAE,IAAI,EAAE,IAAI,UAAU,EAAE,UAAU,OAAO,EAAE,QAAQ,OAAO,EAAE,OAAO,OAAO,EAAE,OAAO,WAAW,EAAE,WAAW;AACpH;AAEA,MAAM,UAAU;AAEhB,eAAsB,cAAc,MAAgH;AAClJ,QAAM,aAAa;AACnB,QAAM,SAAS,MAAM,YAAY;AAAA,IAC/B,UAAU,KAAK;AAAA,IACf,QAAQ,CAAC,KAAK,KAAK;AAAA,IACnB,OAAO,EAAE,MAAM,OAAO,OAAO,MAAM,QAAQ,OAAO,OAAO,OAAO,OAAO,OAAO,MAAM,MAAM;AAAA,IAC1F,MAAM;AAAA,IACN,WAAW;AAAA,EACb,CAAC;AACD,QAAM,KAAK,WAAW;AACtB,QAAM,MAAM,MAAM;AAAA,IAChB,uGAAuG,OAAO;AAAA,IAC9G,CAAC,IAAI,KAAK,UAAU,OAAO,OAAO,KAAK,OAAO,KAAK,SAAS,IAAI;AAAA,EAClE;AACA,SAAO,EAAE,SAAS,OAAO,IAAI,KAAK,CAAC,CAAC,GAAG,QAAQ,OAAO,OAAO;AAC/D;AAEA,eAAsB,wBAAwB,UAA4C;AACxF,QAAM,aAAa;AACnB,QAAM,MAAM,MAAM,MAAc,UAAU,OAAO,mEAAmE,CAAC,QAAQ,CAAC;AAC9H,SAAO,IAAI,KAAK,IAAI,MAAM;AAC5B;AAEA,eAAsB,cAAc,IAAY,UAAoC;AAClF,QAAM,aAAa;AACnB,QAAM,MAAM,MAAM,MAAc,UAAU,OAAO,sDAAsD,CAAC,IAAI,QAAQ,CAAC;AACrH,QAAM,MAAM,IAAI,KAAK,CAAC;AACtB,MAAI,CAAC,IAAK,QAAO;AACjB,QAAM,aAAa,IAAI,MAAM;AAC7B,QAAM,MAAM,0CAA0C,CAAC,EAAE,CAAC;AAC1D,SAAO;AACT;","names":[]}
@@ -0,0 +1,75 @@
1
+ declare const SCOPE_KEYS: readonly ["read", "write", "export", "index", "admin", "swap"];
2
+ type ScopeKey = (typeof SCOPE_KEYS)[number];
3
+ type Scope = Record<ScopeKey, boolean>;
4
+ type Plan = 'free' | 'pro' | 'team' | 'enterprise';
5
+ interface AuthUser {
6
+ keyId: string;
7
+ identity: string;
8
+ storageOwner: string;
9
+ vaults: string[];
10
+ vaultScopes: Record<string, Scope>;
11
+ scope: Scope;
12
+ plan: string;
13
+ session: string;
14
+ }
15
+ declare class AuthError extends Error {
16
+ readonly code = "auth_error";
17
+ }
18
+ declare class ScopeError extends Error {
19
+ readonly code = "scope_error";
20
+ }
21
+ declare function emptyScope(): Scope;
22
+ declare function leastPrivilegeScope(): Scope;
23
+ declare function fullScope(): Scope;
24
+ declare function normalizeScope(input: Partial<Record<string, boolean>> | undefined): Scope;
25
+ declare function generateSecret(): string;
26
+ declare function hashSecret(secret: string): string;
27
+ declare function resolveSecret(secret: string, session: string): Promise<AuthUser>;
28
+ interface ToolAuthInput {
29
+ apiKey?: string;
30
+ sessionId?: string;
31
+ }
32
+ interface ToolExecContext {
33
+ runtimeContext?: {
34
+ get?: (key: string) => unknown;
35
+ };
36
+ requestContext?: {
37
+ get?: (key: string) => unknown;
38
+ };
39
+ }
40
+ declare function resolveToolAuth(input: ToolAuthInput, context?: ToolExecContext): Promise<AuthUser>;
41
+ declare function intersectScope(a: Scope, b: Scope): Scope;
42
+ interface VaultContext {
43
+ storageOwner: string;
44
+ vaults: string[];
45
+ vaultScopes: Record<string, Scope>;
46
+ scope: Scope;
47
+ }
48
+ declare function resolveVaultContext(args: {
49
+ identity: string;
50
+ baseScope: Scope;
51
+ operator: boolean;
52
+ selfVaults: string[];
53
+ selfVaultScopes: Record<string, Scope>;
54
+ }): Promise<VaultContext>;
55
+ declare const fga: {
56
+ assertVaultAccess(user: AuthUser, vault: string, scopeKey: ScopeKey): void;
57
+ hasVaultAccess(user: AuthUser, vault: string, scopeKey: ScopeKey): boolean;
58
+ };
59
+ declare function oauthIssuer(): string;
60
+ declare function oauthResourceUrl(): string;
61
+ declare function oauthBaseUrl(): string;
62
+ declare function protectedResourceMetadata(): Record<string, unknown>;
63
+ declare function wwwAuthenticateValue(): string;
64
+ declare function isBearerJwt(token: string): boolean;
65
+ declare function verifyOAuthJwt(token: string): Promise<Record<string, unknown>>;
66
+ declare function resolveBearerJwt(token: string, session: string): Promise<AuthUser>;
67
+ declare const mapAuthInfoToUser: (args: {
68
+ authInfo: unknown;
69
+ extra: Record<string, unknown>;
70
+ requestContext?: {
71
+ set?: (key: string, value: unknown) => void;
72
+ };
73
+ }) => Promise<AuthUser | undefined>;
74
+
75
+ export { AuthError, type AuthUser, type Plan, SCOPE_KEYS, type Scope, ScopeError, type ScopeKey, type ToolAuthInput, type VaultContext, emptyScope, fga, fullScope, generateSecret, hashSecret, intersectScope, isBearerJwt, leastPrivilegeScope, mapAuthInfoToUser, normalizeScope, oauthBaseUrl, oauthIssuer, oauthResourceUrl, protectedResourceMetadata, resolveBearerJwt, resolveSecret, resolveToolAuth, resolveVaultContext, verifyOAuthJwt, wwwAuthenticateValue };
@@ -0,0 +1,248 @@
1
+ import { createHash, randomBytes } from "node:crypto";
2
+ import { createRemoteJWKSet, jwtVerify } from "jose";
3
+ import { ensureSchema, query } from "../db/pool";
4
+ import { isOperatorIdentity, OPERATOR_PLAN } from "./operator";
5
+ import { listVaultsForIdentity, provisionDefaultVaults, getActiveContext, getAccountGrant, ownedVaultNames } from "../db/vaults";
6
+ const SCOPE_KEYS = ["read", "write", "export", "index", "admin", "swap"];
7
+ class AuthError extends Error {
8
+ code = "auth_error";
9
+ }
10
+ class ScopeError extends Error {
11
+ code = "scope_error";
12
+ }
13
+ function emptyScope() {
14
+ return { read: false, write: false, export: false, index: false, admin: false, swap: false };
15
+ }
16
+ function leastPrivilegeScope() {
17
+ return { ...emptyScope(), read: true };
18
+ }
19
+ function fullScope() {
20
+ return { read: true, write: true, export: true, index: true, admin: true, swap: true };
21
+ }
22
+ function normalizeScope(input) {
23
+ const out = emptyScope();
24
+ if (!input) return out;
25
+ for (const k of SCOPE_KEYS) {
26
+ if (input[k] === true) out[k] = true;
27
+ }
28
+ return out;
29
+ }
30
+ function generateSecret() {
31
+ return `mk_${randomBytes(24).toString("base64url")}`;
32
+ }
33
+ function hashSecret(secret) {
34
+ return createHash("sha256").update(secret).digest("hex");
35
+ }
36
+ function toStringArray(value) {
37
+ if (Array.isArray(value)) return value.filter((v) => typeof v === "string");
38
+ return [];
39
+ }
40
+ async function resolveSecret(secret, session) {
41
+ await ensureSchema();
42
+ const secretHash = hashSecret(secret);
43
+ const res = await query(
44
+ "SELECT key_id, secret_hash, identity, vaults, scope, plan, expires_at, revoked FROM mem_keys WHERE secret_hash = $1 LIMIT 1",
45
+ [secretHash]
46
+ );
47
+ const row = res.rows[0];
48
+ if (!row) throw new AuthError("Unknown API key.");
49
+ if (row.revoked) throw new AuthError("API key has been revoked.");
50
+ if (row.expires_at && new Date(row.expires_at).getTime() < Date.now()) {
51
+ throw new AuthError("API key has expired.");
52
+ }
53
+ await query(
54
+ "UPDATE mem_keys SET last_used_at = now(), usage_count = usage_count + 1 WHERE key_id = $1",
55
+ [row.key_id]
56
+ );
57
+ const operator = isOperatorIdentity(row.identity);
58
+ const baseScope = operator ? fullScope() : normalizeScope(row.scope);
59
+ const live = await listVaultsForIdentity(row.identity);
60
+ const selfVaults = [.../* @__PURE__ */ new Set([...toStringArray(row.vaults), ...live.map((v) => v.handle)])];
61
+ const selfVaultScopes = {};
62
+ for (const v of live) if (v.role === "shared") selfVaultScopes[v.handle] = v.entitlement;
63
+ const ctx = await resolveVaultContext({
64
+ identity: row.identity,
65
+ baseScope,
66
+ operator,
67
+ selfVaults,
68
+ selfVaultScopes
69
+ });
70
+ return {
71
+ keyId: row.key_id,
72
+ identity: row.identity,
73
+ storageOwner: ctx.storageOwner,
74
+ vaults: ctx.vaults,
75
+ vaultScopes: ctx.vaultScopes,
76
+ scope: ctx.scope,
77
+ plan: operator ? OPERATOR_PLAN : row.plan,
78
+ session
79
+ };
80
+ }
81
+ async function resolveToolAuth(input, context) {
82
+ const fromCtx = context?.runtimeContext?.get?.("user") ?? context?.requestContext?.get?.("user");
83
+ if (fromCtx && typeof fromCtx === "object" && "keyId" in fromCtx) {
84
+ return fromCtx;
85
+ }
86
+ const secret = input.apiKey;
87
+ if (!secret) throw new AuthError("No API key supplied (set apiKey or authenticate via the MCP transport).");
88
+ const session = input.sessionId ?? `sess_${hashSecret(secret).slice(0, 12)}`;
89
+ return resolveSecret(secret, session);
90
+ }
91
+ function intersectScope(a, b) {
92
+ const out = emptyScope();
93
+ for (const k of SCOPE_KEYS) out[k] = a[k] === true && b[k] === true;
94
+ return out;
95
+ }
96
+ async function resolveVaultContext(args) {
97
+ const ctx = await getActiveContext(args.identity);
98
+ if (ctx.activeOwner && ctx.activeOwner !== args.identity) {
99
+ const grant = args.operator ? fullScope() : await getAccountGrant(ctx.activeOwner, args.identity);
100
+ if (grant) {
101
+ return {
102
+ storageOwner: ctx.activeOwner,
103
+ vaults: await ownedVaultNames(ctx.activeOwner),
104
+ vaultScopes: {},
105
+ scope: args.operator ? args.baseScope : intersectScope(args.baseScope, grant)
106
+ };
107
+ }
108
+ }
109
+ return { storageOwner: args.identity, vaults: args.selfVaults, vaultScopes: args.selfVaultScopes, scope: args.baseScope };
110
+ }
111
+ const fga = {
112
+ assertVaultAccess(user, vault, scopeKey) {
113
+ if (!user.vaults.includes(vault)) {
114
+ throw new ScopeError(`Key ${user.keyId} is not entitled to vault "${vault}".`);
115
+ }
116
+ if (!user.scope[scopeKey]) {
117
+ throw new ScopeError(`Key ${user.keyId} lacks "${scopeKey}" scope.`);
118
+ }
119
+ const vs = user.vaultScopes[vault];
120
+ if (vs && vs[scopeKey] !== true) {
121
+ throw new ScopeError(`Vault "${vault}" is shared without "${scopeKey}" permission.`);
122
+ }
123
+ },
124
+ hasVaultAccess(user, vault, scopeKey) {
125
+ if (!user.vaults.includes(vault) || user.scope[scopeKey] !== true) return false;
126
+ const vs = user.vaultScopes[vault];
127
+ return !vs || vs[scopeKey] === true;
128
+ }
129
+ };
130
+ function oauthIssuer() {
131
+ return (process.env.OAUTH_ISSUER ?? "https://mcpscraper.dev").replace(/\/$/, "");
132
+ }
133
+ function oauthResourceUrl() {
134
+ return process.env.OAUTH_RESOURCE ?? "https://memory.mcpscraper.dev/mcp";
135
+ }
136
+ function oauthBaseUrl() {
137
+ return (process.env.OAUTH_RS_BASE_URL ?? "https://memory.mcpscraper.dev").replace(/\/$/, "");
138
+ }
139
+ function protectedResourceMetadata() {
140
+ return {
141
+ resource: oauthResourceUrl(),
142
+ authorization_servers: [oauthIssuer()],
143
+ scopes_supported: ["memory.read", "memory.write", "memory.admin"],
144
+ bearer_methods_supported: ["header"]
145
+ };
146
+ }
147
+ function wwwAuthenticateValue() {
148
+ return `Bearer resource_metadata="${oauthBaseUrl()}/.well-known/oauth-protected-resource"`;
149
+ }
150
+ function looksLikeJwt(token) {
151
+ return !token.startsWith("mk_") && token.split(".").length === 3;
152
+ }
153
+ let jwksSingleton = null;
154
+ function getJwks() {
155
+ if (jwksSingleton) return jwksSingleton;
156
+ jwksSingleton = createRemoteJWKSet(new URL(`${oauthIssuer()}/.well-known/jwks.json`));
157
+ return jwksSingleton;
158
+ }
159
+ function scopeFromOAuth(scopeClaim, operator) {
160
+ if (operator) return fullScope();
161
+ const scopes = (scopeClaim ?? "").split(/\s+/).map((s) => s.trim()).filter(Boolean);
162
+ const out = emptyScope();
163
+ if (scopes.includes("memory.admin")) return fullScope();
164
+ if (scopes.includes("memory.read")) out.read = true;
165
+ if (scopes.includes("memory.write")) {
166
+ out.read = true;
167
+ out.write = true;
168
+ out.export = true;
169
+ out.index = true;
170
+ out.swap = true;
171
+ }
172
+ if (!out.read && !out.write) out.read = true;
173
+ return out;
174
+ }
175
+ function isBearerJwt(token) {
176
+ return looksLikeJwt(token);
177
+ }
178
+ async function verifyOAuthJwt(token) {
179
+ const { payload } = await jwtVerify(token, getJwks(), {
180
+ issuer: oauthIssuer(),
181
+ audience: oauthResourceUrl()
182
+ });
183
+ return payload;
184
+ }
185
+ async function resolveBearerJwt(token, session) {
186
+ await ensureSchema();
187
+ const payload = await verifyOAuthJwt(token);
188
+ const identity = typeof payload.sub === "string" ? payload.sub : "";
189
+ if (!identity) throw new AuthError("Bearer token has no subject.");
190
+ const operator = isOperatorIdentity(identity);
191
+ const scopeClaim = typeof payload.scope === "string" ? payload.scope : void 0;
192
+ let self = await listVaultsForIdentity(identity);
193
+ if (self.length === 0) {
194
+ await provisionDefaultVaults(identity);
195
+ self = await listVaultsForIdentity(identity);
196
+ }
197
+ const selfVaults = self.map((v) => v.handle);
198
+ const selfVaultScopes = {};
199
+ for (const v of self) if (v.role === "shared") selfVaultScopes[v.handle] = v.entitlement;
200
+ const baseScope = scopeFromOAuth(scopeClaim, operator);
201
+ const ctx = await resolveVaultContext({ identity, baseScope, operator, selfVaults, selfVaultScopes });
202
+ const planClaim = typeof payload.plan === "string" ? payload.plan : void 0;
203
+ return {
204
+ keyId: `oauth:${identity}`,
205
+ identity,
206
+ storageOwner: ctx.storageOwner,
207
+ vaults: ctx.vaults,
208
+ vaultScopes: ctx.vaultScopes,
209
+ scope: ctx.scope,
210
+ plan: operator ? OPERATOR_PLAN : planClaim ?? "free",
211
+ session
212
+ };
213
+ }
214
+ const mapAuthInfoToUser = async (args) => {
215
+ const info = args.authInfo;
216
+ const token = info?.token ?? info?.apiKey ?? info?.extra?.apiKey;
217
+ if (!token) return void 0;
218
+ const sessionId = typeof args.extra?.sessionId === "string" ? args.extra.sessionId : `sess_${hashSecret(token).slice(0, 12)}`;
219
+ const user = looksLikeJwt(token) ? await resolveBearerJwt(token, sessionId) : await resolveSecret(token, sessionId);
220
+ args.requestContext?.set?.("user", user);
221
+ return user;
222
+ };
223
+ export {
224
+ AuthError,
225
+ SCOPE_KEYS,
226
+ ScopeError,
227
+ emptyScope,
228
+ fga,
229
+ fullScope,
230
+ generateSecret,
231
+ hashSecret,
232
+ intersectScope,
233
+ isBearerJwt,
234
+ leastPrivilegeScope,
235
+ mapAuthInfoToUser,
236
+ normalizeScope,
237
+ oauthBaseUrl,
238
+ oauthIssuer,
239
+ oauthResourceUrl,
240
+ protectedResourceMetadata,
241
+ resolveBearerJwt,
242
+ resolveSecret,
243
+ resolveToolAuth,
244
+ resolveVaultContext,
245
+ verifyOAuthJwt,
246
+ wwwAuthenticateValue
247
+ };
248
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/lib/auth.ts"],"sourcesContent":["import { createHash, randomBytes } from 'node:crypto'\nimport { createRemoteJWKSet, jwtVerify } from 'jose'\nimport { ensureSchema, query } from '../db/pool'\nimport { isOperatorIdentity, OPERATOR_PLAN } from './operator'\nimport { listVaultsForIdentity, provisionDefaultVaults, getActiveContext, getAccountGrant, ownedVaultNames } from '../db/vaults'\n\nexport const SCOPE_KEYS = ['read', 'write', 'export', 'index', 'admin', 'swap'] as const\nexport type ScopeKey = (typeof SCOPE_KEYS)[number]\nexport type Scope = Record<ScopeKey, boolean>\n\nexport type Plan = 'free' | 'pro' | 'team' | 'enterprise'\n\nexport interface AuthUser {\n keyId: string\n identity: string\n storageOwner: string\n vaults: string[]\n vaultScopes: Record<string, Scope>\n scope: Scope\n plan: string\n session: string\n}\n\nexport class AuthError extends Error {\n readonly code = 'auth_error'\n}\nexport class ScopeError extends Error {\n readonly code = 'scope_error'\n}\n\nexport function emptyScope(): Scope {\n return { read: false, write: false, export: false, index: false, admin: false, swap: false }\n}\n\nexport function leastPrivilegeScope(): Scope {\n return { ...emptyScope(), read: true }\n}\n\nexport function fullScope(): Scope {\n return { read: true, write: true, export: true, index: true, admin: true, swap: true }\n}\n\nexport function normalizeScope(input: Partial<Record<string, boolean>> | undefined): Scope {\n const out = emptyScope()\n if (!input) return out\n for (const k of SCOPE_KEYS) {\n if (input[k] === true) out[k] = true\n }\n return out\n}\n\nexport function generateSecret(): string {\n return `mk_${randomBytes(24).toString('base64url')}`\n}\n\nexport function hashSecret(secret: string): string {\n return createHash('sha256').update(secret).digest('hex')\n}\n\ninterface KeyRecord {\n key_id: string\n secret_hash: string\n identity: string\n vaults: unknown\n scope: unknown\n plan: string\n expires_at: string | null\n revoked: boolean\n}\n\nfunction toStringArray(value: unknown): string[] {\n if (Array.isArray(value)) return value.filter((v): v is string => typeof v === 'string')\n return []\n}\n\nexport async function resolveSecret(secret: string, session: string): Promise<AuthUser> {\n await ensureSchema()\n const secretHash = hashSecret(secret)\n const res = await query<KeyRecord>(\n 'SELECT key_id, secret_hash, identity, vaults, scope, plan, expires_at, revoked FROM mem_keys WHERE secret_hash = $1 LIMIT 1',\n [secretHash],\n )\n const row = res.rows[0]\n if (!row) throw new AuthError('Unknown API key.')\n if (row.revoked) throw new AuthError('API key has been revoked.')\n if (row.expires_at && new Date(row.expires_at).getTime() < Date.now()) {\n throw new AuthError('API key has expired.')\n }\n await query(\n 'UPDATE mem_keys SET last_used_at = now(), usage_count = usage_count + 1 WHERE key_id = $1',\n [row.key_id],\n )\n const operator = isOperatorIdentity(row.identity)\n const baseScope = operator ? fullScope() : normalizeScope(row.scope as Record<string, boolean>)\n const live = await listVaultsForIdentity(row.identity)\n const selfVaults = [...new Set([...toStringArray(row.vaults), ...live.map(v => v.handle)])]\n const selfVaultScopes: Record<string, Scope> = {}\n for (const v of live) if (v.role === 'shared') selfVaultScopes[v.handle] = v.entitlement\n const ctx = await resolveVaultContext({\n identity: row.identity,\n baseScope,\n operator,\n selfVaults,\n selfVaultScopes,\n })\n return {\n keyId: row.key_id,\n identity: row.identity,\n storageOwner: ctx.storageOwner,\n vaults: ctx.vaults,\n vaultScopes: ctx.vaultScopes,\n scope: ctx.scope,\n plan: operator ? OPERATOR_PLAN : row.plan,\n session,\n }\n}\n\nexport interface ToolAuthInput {\n apiKey?: string\n sessionId?: string\n}\n\ninterface ToolExecContext {\n runtimeContext?: { get?: (key: string) => unknown }\n requestContext?: { get?: (key: string) => unknown }\n}\n\nexport async function resolveToolAuth(input: ToolAuthInput, context?: ToolExecContext): Promise<AuthUser> {\n const fromCtx =\n context?.runtimeContext?.get?.('user') ?? context?.requestContext?.get?.('user')\n if (fromCtx && typeof fromCtx === 'object' && 'keyId' in (fromCtx as object)) {\n return fromCtx as AuthUser\n }\n const secret = input.apiKey\n if (!secret) throw new AuthError('No API key supplied (set apiKey or authenticate via the MCP transport).')\n const session = input.sessionId ?? `sess_${hashSecret(secret).slice(0, 12)}`\n return resolveSecret(secret, session)\n}\n\nexport function intersectScope(a: Scope, b: Scope): Scope {\n const out = emptyScope()\n for (const k of SCOPE_KEYS) out[k] = a[k] === true && b[k] === true\n return out\n}\n\nexport interface VaultContext {\n storageOwner: string\n vaults: string[]\n vaultScopes: Record<string, Scope>\n scope: Scope\n}\n\nexport async function resolveVaultContext(args: {\n identity: string\n baseScope: Scope\n operator: boolean\n selfVaults: string[]\n selfVaultScopes: Record<string, Scope>\n}): Promise<VaultContext> {\n const ctx = await getActiveContext(args.identity)\n if (ctx.activeOwner && ctx.activeOwner !== args.identity) {\n const grant = args.operator ? fullScope() : await getAccountGrant(ctx.activeOwner, args.identity)\n if (grant) {\n return {\n storageOwner: ctx.activeOwner,\n vaults: await ownedVaultNames(ctx.activeOwner),\n vaultScopes: {},\n scope: args.operator ? args.baseScope : intersectScope(args.baseScope, grant),\n }\n }\n }\n return { storageOwner: args.identity, vaults: args.selfVaults, vaultScopes: args.selfVaultScopes, scope: args.baseScope }\n}\n\nexport const fga = {\n assertVaultAccess(user: AuthUser, vault: string, scopeKey: ScopeKey): void {\n if (!user.vaults.includes(vault)) {\n throw new ScopeError(`Key ${user.keyId} is not entitled to vault \"${vault}\".`)\n }\n if (!user.scope[scopeKey]) {\n throw new ScopeError(`Key ${user.keyId} lacks \"${scopeKey}\" scope.`)\n }\n const vs = user.vaultScopes[vault]\n if (vs && vs[scopeKey] !== true) {\n throw new ScopeError(`Vault \"${vault}\" is shared without \"${scopeKey}\" permission.`)\n }\n },\n hasVaultAccess(user: AuthUser, vault: string, scopeKey: ScopeKey): boolean {\n if (!user.vaults.includes(vault) || user.scope[scopeKey] !== true) return false\n const vs = user.vaultScopes[vault]\n return !vs || vs[scopeKey] === true\n },\n}\n\nexport function oauthIssuer(): string {\n return (process.env.OAUTH_ISSUER ?? 'https://mcpscraper.dev').replace(/\\/$/, '')\n}\n\nexport function oauthResourceUrl(): string {\n return (\n process.env.OAUTH_RESOURCE ??\n 'https://memory.mcpscraper.dev/mcp'\n )\n}\n\nexport function oauthBaseUrl(): string {\n return (process.env.OAUTH_RS_BASE_URL ?? 'https://memory.mcpscraper.dev').replace(/\\/$/, '')\n}\n\nexport function protectedResourceMetadata(): Record<string, unknown> {\n return {\n resource: oauthResourceUrl(),\n authorization_servers: [oauthIssuer()],\n scopes_supported: ['memory.read', 'memory.write', 'memory.admin'],\n bearer_methods_supported: ['header'],\n }\n}\n\nexport function wwwAuthenticateValue(): string {\n return `Bearer resource_metadata=\"${oauthBaseUrl()}/.well-known/oauth-protected-resource\"`\n}\n\nfunction looksLikeJwt(token: string): boolean {\n return !token.startsWith('mk_') && token.split('.').length === 3\n}\n\nlet jwksSingleton: ReturnType<typeof createRemoteJWKSet> | null = null\n\nfunction getJwks(): ReturnType<typeof createRemoteJWKSet> {\n if (jwksSingleton) return jwksSingleton\n jwksSingleton = createRemoteJWKSet(new URL(`${oauthIssuer()}/.well-known/jwks.json`))\n return jwksSingleton\n}\n\nfunction scopeFromOAuth(scopeClaim: string | undefined, operator: boolean): Scope {\n if (operator) return fullScope()\n const scopes = (scopeClaim ?? '').split(/\\s+/).map(s => s.trim()).filter(Boolean)\n const out = emptyScope()\n if (scopes.includes('memory.admin')) return fullScope()\n if (scopes.includes('memory.read')) out.read = true\n if (scopes.includes('memory.write')) {\n out.read = true\n out.write = true\n out.export = true\n out.index = true\n out.swap = true\n }\n if (!out.read && !out.write) out.read = true\n return out\n}\n\nexport function isBearerJwt(token: string): boolean {\n return looksLikeJwt(token)\n}\n\nexport async function verifyOAuthJwt(token: string): Promise<Record<string, unknown>> {\n const { payload } = await jwtVerify(token, getJwks(), {\n issuer: oauthIssuer(),\n audience: oauthResourceUrl(),\n })\n return payload as Record<string, unknown>\n}\n\nexport async function resolveBearerJwt(token: string, session: string): Promise<AuthUser> {\n await ensureSchema()\n const payload = await verifyOAuthJwt(token)\n const identity = typeof payload.sub === 'string' ? payload.sub : ''\n if (!identity) throw new AuthError('Bearer token has no subject.')\n const operator = isOperatorIdentity(identity)\n const scopeClaim = typeof payload.scope === 'string' ? payload.scope : undefined\n let self = await listVaultsForIdentity(identity)\n if (self.length === 0) {\n await provisionDefaultVaults(identity)\n self = await listVaultsForIdentity(identity)\n }\n const selfVaults = self.map(v => v.handle)\n const selfVaultScopes: Record<string, Scope> = {}\n for (const v of self) if (v.role === 'shared') selfVaultScopes[v.handle] = v.entitlement\n const baseScope = scopeFromOAuth(scopeClaim, operator)\n const ctx = await resolveVaultContext({ identity, baseScope, operator, selfVaults, selfVaultScopes })\n const planClaim = typeof payload.plan === 'string' ? payload.plan : undefined\n return {\n keyId: `oauth:${identity}`,\n identity,\n storageOwner: ctx.storageOwner,\n vaults: ctx.vaults,\n vaultScopes: ctx.vaultScopes,\n scope: ctx.scope,\n plan: operator ? OPERATOR_PLAN : planClaim ?? 'free',\n session,\n }\n}\n\nexport const mapAuthInfoToUser = async (args: {\n authInfo: unknown\n extra: Record<string, unknown>\n requestContext?: { set?: (key: string, value: unknown) => void }\n}): Promise<AuthUser | undefined> => {\n const info = args.authInfo as { token?: string; apiKey?: string; extra?: { apiKey?: string } } | undefined\n const token = info?.token ?? info?.apiKey ?? info?.extra?.apiKey\n if (!token) return undefined\n const sessionId = typeof args.extra?.sessionId === 'string' ? args.extra.sessionId : `sess_${hashSecret(token).slice(0, 12)}`\n const user = looksLikeJwt(token)\n ? await resolveBearerJwt(token, sessionId)\n : await resolveSecret(token, sessionId)\n args.requestContext?.set?.('user', user)\n return user\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,19 @@
1
+ declare const COST_RATES: {
2
+ readonly minimax_in_per_tok: number;
3
+ readonly minimax_out_per_tok: number;
4
+ readonly jina_per_tok: number;
5
+ readonly neon_per_gb_month: 0.35;
6
+ readonly compute_per_run_usd: 0.0005;
7
+ };
8
+ declare const FREE_COST_CAP_USD = 1;
9
+ declare const PRO_COST_BUDGET_USD: number;
10
+ declare const MARGIN_MULTIPLE = 3;
11
+ declare function embedCostUsd(tokens: number): number;
12
+ declare function llmCostUsd(args: {
13
+ inputTokens?: number;
14
+ outputTokens?: number;
15
+ }): number;
16
+ declare function storageCostUsdForMonth(bytes: number): number;
17
+ declare function storageCostUsdForDay(bytes: number): number;
18
+
19
+ export { COST_RATES, FREE_COST_CAP_USD, MARGIN_MULTIPLE, PRO_COST_BUDGET_USD, embedCostUsd, llmCostUsd, storageCostUsdForDay, storageCostUsdForMonth };