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,141 @@
1
+ import { randomBytes } from "node:crypto";
2
+ import { ensureSchema, query } from "./pool";
3
+ function defaultNotePermissions() {
4
+ return { read: true, edit: false, delete: false, reshare: false };
5
+ }
6
+ function normalizeNotePermissions(input) {
7
+ if (!input) return defaultNotePermissions();
8
+ return { read: true, edit: input.edit === true, delete: input.delete === true, reshare: input.reshare === true };
9
+ }
10
+ function mapShare(r) {
11
+ return {
12
+ shareId: r.share_id,
13
+ noteId: r.note_id,
14
+ ownerIdentity: r.owner_identity,
15
+ srcVault: r.src_vault,
16
+ srcPath: r.src_path,
17
+ grantee: r.grantee,
18
+ permissions: normalizeNotePermissions(r.permissions),
19
+ status: r.status,
20
+ bundleRoot: r.bundle_root,
21
+ createdAt: r.created_at,
22
+ updatedAt: r.updated_at
23
+ };
24
+ }
25
+ const SHARE_COLUMNS = "share_id, note_id, owner_identity, src_vault, src_path, grantee, permissions, status, bundle_root, created_at, updated_at";
26
+ async function createShare(args) {
27
+ await ensureSchema();
28
+ const shareId = `share_${randomBytes(12).toString("base64url")}`;
29
+ const status = args.autoAccept ? "accepted" : "pending";
30
+ const res = await query(
31
+ `INSERT INTO mem_note_shares (share_id, note_id, owner_identity, src_vault, src_path, grantee, permissions, status, bundle_root, updated_at)
32
+ VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb, $8, $9, now())
33
+ ON CONFLICT (note_id, grantee) DO UPDATE SET
34
+ permissions = EXCLUDED.permissions, status = $8, bundle_root = EXCLUDED.bundle_root, updated_at = now()
35
+ RETURNING ${SHARE_COLUMNS}`,
36
+ [shareId, args.noteId, args.ownerIdentity, args.srcVault, args.srcPath, args.grantee, JSON.stringify(args.permissions), status, args.bundleRoot ?? null]
37
+ );
38
+ return mapShare(res.rows[0]);
39
+ }
40
+ async function getShare(shareId) {
41
+ await ensureSchema();
42
+ const res = await query(`SELECT ${SHARE_COLUMNS} FROM mem_note_shares WHERE share_id = $1 LIMIT 1`, [shareId]);
43
+ return res.rows[0] ? mapShare(res.rows[0]) : null;
44
+ }
45
+ async function getShareByNoteAndGrantee(noteId, grantee) {
46
+ await ensureSchema();
47
+ const res = await query(`SELECT ${SHARE_COLUMNS} FROM mem_note_shares WHERE note_id = $1 AND grantee = $2 LIMIT 1`, [noteId, grantee]);
48
+ return res.rows[0] ? mapShare(res.rows[0]) : null;
49
+ }
50
+ async function getAcceptedShareByNoteAndGrantee(noteId, grantee) {
51
+ await ensureSchema();
52
+ const res = await query(
53
+ `SELECT ${SHARE_COLUMNS} FROM mem_note_shares WHERE note_id = $1 AND grantee = $2 AND status = 'accepted' LIMIT 1`,
54
+ [noteId, grantee]
55
+ );
56
+ return res.rows[0] ? mapShare(res.rows[0]) : null;
57
+ }
58
+ async function listPendingForGrantee(grantee) {
59
+ await ensureSchema();
60
+ const res = await query(
61
+ `SELECT ${SHARE_COLUMNS} FROM mem_note_shares WHERE grantee = $1 AND status = 'pending' ORDER BY created_at ASC`,
62
+ [grantee]
63
+ );
64
+ return res.rows.map(mapShare);
65
+ }
66
+ async function listAcceptedForGrantee(grantee) {
67
+ await ensureSchema();
68
+ const res = await query(
69
+ `SELECT ${SHARE_COLUMNS} FROM mem_note_shares WHERE grantee = $1 AND status = 'accepted' ORDER BY created_at ASC`,
70
+ [grantee]
71
+ );
72
+ return res.rows.map(mapShare);
73
+ }
74
+ async function acceptShareRow(shareId, grantee) {
75
+ await ensureSchema();
76
+ const res = await query(
77
+ `UPDATE mem_note_shares SET status = 'accepted', updated_at = now()
78
+ WHERE share_id = $1 AND grantee = $2 AND status = 'pending'
79
+ RETURNING ${SHARE_COLUMNS}`,
80
+ [shareId, grantee]
81
+ );
82
+ return res.rows[0] ? mapShare(res.rows[0]) : null;
83
+ }
84
+ async function declineShareRow(shareId, grantee) {
85
+ await ensureSchema();
86
+ const res = await query(
87
+ `UPDATE mem_note_shares SET status = 'declined', updated_at = now()
88
+ WHERE share_id = $1 AND grantee = $2 AND status = 'pending'
89
+ RETURNING ${SHARE_COLUMNS}`,
90
+ [shareId, grantee]
91
+ );
92
+ return res.rows[0] ? mapShare(res.rows[0]) : null;
93
+ }
94
+ async function unlinkShareRow(shareId, grantee) {
95
+ await ensureSchema();
96
+ const res = await query(
97
+ `DELETE FROM mem_note_shares WHERE share_id = $1 AND grantee = $2 AND status = 'accepted'`,
98
+ [shareId, grantee]
99
+ );
100
+ return (res.rowCount ?? 0) > 0;
101
+ }
102
+ async function revokeShareRow(shareId, ownerIdentity) {
103
+ await ensureSchema();
104
+ const res = await query(
105
+ `UPDATE mem_note_shares SET status = 'revoked', updated_at = now()
106
+ WHERE share_id = $1 AND owner_identity = $2 AND status IN ('pending', 'accepted')
107
+ RETURNING ${SHARE_COLUMNS}`,
108
+ [shareId, ownerIdentity]
109
+ );
110
+ return res.rows[0] ? mapShare(res.rows[0]) : null;
111
+ }
112
+ async function deleteSharesForNote(noteId) {
113
+ await ensureSchema();
114
+ const res = await query(`DELETE FROM mem_note_shares WHERE note_id = $1`, [noteId]);
115
+ return res.rowCount ?? 0;
116
+ }
117
+ async function resolveAcceptedShare(shareId, grantee) {
118
+ await ensureSchema();
119
+ const res = await query(
120
+ `SELECT ${SHARE_COLUMNS} FROM mem_note_shares WHERE share_id = $1 AND grantee = $2 AND status = 'accepted' LIMIT 1`,
121
+ [shareId, grantee]
122
+ );
123
+ return res.rows[0] ? mapShare(res.rows[0]) : null;
124
+ }
125
+ export {
126
+ acceptShareRow,
127
+ createShare,
128
+ declineShareRow,
129
+ defaultNotePermissions,
130
+ deleteSharesForNote,
131
+ getAcceptedShareByNoteAndGrantee,
132
+ getShare,
133
+ getShareByNoteAndGrantee,
134
+ listAcceptedForGrantee,
135
+ listPendingForGrantee,
136
+ normalizeNotePermissions,
137
+ resolveAcceptedShare,
138
+ revokeShareRow,
139
+ unlinkShareRow
140
+ };
141
+ //# sourceMappingURL=shares.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/db/shares.ts"],"sourcesContent":["import { randomBytes } from 'node:crypto'\nimport { ensureSchema, query } from './pool'\n\nexport interface NotePermissions {\n read: boolean\n edit: boolean\n delete: boolean\n reshare: boolean\n}\n\nexport function defaultNotePermissions(): NotePermissions {\n return { read: true, edit: false, delete: false, reshare: false }\n}\n\nexport function normalizeNotePermissions(input?: Partial<Record<keyof NotePermissions, boolean>>): NotePermissions {\n if (!input) return defaultNotePermissions()\n return { read: true, edit: input.edit === true, delete: input.delete === true, reshare: input.reshare === true }\n}\n\nexport type ShareStatus = 'pending' | 'accepted' | 'declined' | 'revoked'\n\nexport interface NoteShare {\n shareId: string\n noteId: string\n ownerIdentity: string\n srcVault: string\n srcPath: string\n grantee: string\n permissions: NotePermissions\n status: ShareStatus\n bundleRoot: string | null\n createdAt: string\n updatedAt: string\n}\n\ninterface RawShareRow {\n share_id: string\n note_id: string\n owner_identity: string\n src_vault: string\n src_path: string\n grantee: string\n permissions: unknown\n status: string\n bundle_root: string | null\n created_at: string\n updated_at: string\n}\n\nfunction mapShare(r: RawShareRow): NoteShare {\n return {\n shareId: r.share_id,\n noteId: r.note_id,\n ownerIdentity: r.owner_identity,\n srcVault: r.src_vault,\n srcPath: r.src_path,\n grantee: r.grantee,\n permissions: normalizeNotePermissions(r.permissions as Record<string, boolean>),\n status: r.status as ShareStatus,\n bundleRoot: r.bundle_root,\n createdAt: r.created_at,\n updatedAt: r.updated_at,\n }\n}\n\nconst SHARE_COLUMNS = 'share_id, note_id, owner_identity, src_vault, src_path, grantee, permissions, status, bundle_root, created_at, updated_at'\n\nexport async function createShare(args: {\n noteId: string\n ownerIdentity: string\n srcVault: string\n srcPath: string\n grantee: string\n permissions: NotePermissions\n bundleRoot?: string\n autoAccept?: boolean\n}): Promise<NoteShare> {\n await ensureSchema()\n const shareId = `share_${randomBytes(12).toString('base64url')}`\n const status = args.autoAccept ? 'accepted' : 'pending'\n const res = await query<RawShareRow>(\n `INSERT INTO mem_note_shares (share_id, note_id, owner_identity, src_vault, src_path, grantee, permissions, status, bundle_root, updated_at)\n VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb, $8, $9, now())\n ON CONFLICT (note_id, grantee) DO UPDATE SET\n permissions = EXCLUDED.permissions, status = $8, bundle_root = EXCLUDED.bundle_root, updated_at = now()\n RETURNING ${SHARE_COLUMNS}`,\n [shareId, args.noteId, args.ownerIdentity, args.srcVault, args.srcPath, args.grantee, JSON.stringify(args.permissions), status, args.bundleRoot ?? null],\n )\n return mapShare(res.rows[0])\n}\n\nexport async function getShare(shareId: string): Promise<NoteShare | null> {\n await ensureSchema()\n const res = await query<RawShareRow>(`SELECT ${SHARE_COLUMNS} FROM mem_note_shares WHERE share_id = $1 LIMIT 1`, [shareId])\n return res.rows[0] ? mapShare(res.rows[0]) : null\n}\n\nexport async function getShareByNoteAndGrantee(noteId: string, grantee: string): Promise<NoteShare | null> {\n await ensureSchema()\n const res = await query<RawShareRow>(`SELECT ${SHARE_COLUMNS} FROM mem_note_shares WHERE note_id = $1 AND grantee = $2 LIMIT 1`, [noteId, grantee])\n return res.rows[0] ? mapShare(res.rows[0]) : null\n}\n\nexport async function getAcceptedShareByNoteAndGrantee(noteId: string, grantee: string): Promise<NoteShare | null> {\n await ensureSchema()\n const res = await query<RawShareRow>(\n `SELECT ${SHARE_COLUMNS} FROM mem_note_shares WHERE note_id = $1 AND grantee = $2 AND status = 'accepted' LIMIT 1`,\n [noteId, grantee],\n )\n return res.rows[0] ? mapShare(res.rows[0]) : null\n}\n\nexport async function listPendingForGrantee(grantee: string): Promise<NoteShare[]> {\n await ensureSchema()\n const res = await query<RawShareRow>(\n `SELECT ${SHARE_COLUMNS} FROM mem_note_shares WHERE grantee = $1 AND status = 'pending' ORDER BY created_at ASC`,\n [grantee],\n )\n return res.rows.map(mapShare)\n}\n\nexport async function listAcceptedForGrantee(grantee: string): Promise<NoteShare[]> {\n await ensureSchema()\n const res = await query<RawShareRow>(\n `SELECT ${SHARE_COLUMNS} FROM mem_note_shares WHERE grantee = $1 AND status = 'accepted' ORDER BY created_at ASC`,\n [grantee],\n )\n return res.rows.map(mapShare)\n}\n\nexport async function acceptShareRow(shareId: string, grantee: string): Promise<NoteShare | null> {\n await ensureSchema()\n const res = await query<RawShareRow>(\n `UPDATE mem_note_shares SET status = 'accepted', updated_at = now()\n WHERE share_id = $1 AND grantee = $2 AND status = 'pending'\n RETURNING ${SHARE_COLUMNS}`,\n [shareId, grantee],\n )\n return res.rows[0] ? mapShare(res.rows[0]) : null\n}\n\nexport async function declineShareRow(shareId: string, grantee: string): Promise<NoteShare | null> {\n await ensureSchema()\n const res = await query<RawShareRow>(\n `UPDATE mem_note_shares SET status = 'declined', updated_at = now()\n WHERE share_id = $1 AND grantee = $2 AND status = 'pending'\n RETURNING ${SHARE_COLUMNS}`,\n [shareId, grantee],\n )\n return res.rows[0] ? mapShare(res.rows[0]) : null\n}\n\nexport async function unlinkShareRow(shareId: string, grantee: string): Promise<boolean> {\n await ensureSchema()\n const res = await query(\n `DELETE FROM mem_note_shares WHERE share_id = $1 AND grantee = $2 AND status = 'accepted'`,\n [shareId, grantee],\n )\n return (res.rowCount ?? 0) > 0\n}\n\nexport async function revokeShareRow(shareId: string, ownerIdentity: string): Promise<NoteShare | null> {\n await ensureSchema()\n const res = await query<RawShareRow>(\n `UPDATE mem_note_shares SET status = 'revoked', updated_at = now()\n WHERE share_id = $1 AND owner_identity = $2 AND status IN ('pending', 'accepted')\n RETURNING ${SHARE_COLUMNS}`,\n [shareId, ownerIdentity],\n )\n return res.rows[0] ? mapShare(res.rows[0]) : null\n}\n\nexport async function deleteSharesForNote(noteId: string): Promise<number> {\n await ensureSchema()\n const res = await query(`DELETE FROM mem_note_shares WHERE note_id = $1`, [noteId])\n return res.rowCount ?? 0\n}\n\nexport async function resolveAcceptedShare(shareId: string, grantee: string): Promise<NoteShare | null> {\n await ensureSchema()\n const res = await query<RawShareRow>(\n `SELECT ${SHARE_COLUMNS} FROM mem_note_shares WHERE share_id = $1 AND grantee = $2 AND status = 'accepted' LIMIT 1`,\n [shareId, grantee],\n )\n return res.rows[0] ? mapShare(res.rows[0]) : null\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,50 @@
1
+ declare const TABLE_NAME_RE: RegExp;
2
+ declare const COLUMN_NAME_RE: RegExp;
3
+ type ColumnType = 'text' | 'number' | 'integer' | 'boolean' | 'date' | 'timestamp' | 'json';
4
+ declare class TableValidationError extends Error {
5
+ }
6
+ interface ColumnDef {
7
+ name: string;
8
+ type: ColumnType;
9
+ }
10
+ type FilterOp = 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'like' | 'in';
11
+ interface TableFilter {
12
+ column: string;
13
+ op: FilterOp;
14
+ value?: unknown;
15
+ }
16
+ interface TableSort {
17
+ column: string;
18
+ direction?: 'asc' | 'desc';
19
+ }
20
+ declare function schemaNameFor(identity: string): string;
21
+ declare function listUserTables(identity: string): Promise<string[]>;
22
+ declare function tableExists(identity: string, tableName: string): Promise<boolean>;
23
+ interface TableColumnInfo {
24
+ name: string;
25
+ type: string;
26
+ }
27
+ declare function describeUserTable(identity: string, tableName: string): Promise<TableColumnInfo[]>;
28
+ declare function createUserTable(identity: string, tableName: string, columns: ColumnDef[]): Promise<{
29
+ created: boolean;
30
+ columns: ColumnDef[];
31
+ }>;
32
+ declare function dropUserTable(identity: string, tableName: string): Promise<boolean>;
33
+ declare function insertUserTableRows(identity: string, tableName: string, rows: Array<Record<string, unknown>>): Promise<{
34
+ inserted: number;
35
+ }>;
36
+ interface TableQueryOptions {
37
+ filters?: TableFilter[];
38
+ sort?: TableSort;
39
+ limit?: number;
40
+ offset?: number;
41
+ }
42
+ declare function queryUserTable(identity: string, tableName: string, opts?: TableQueryOptions): Promise<{
43
+ rows: Record<string, unknown>[];
44
+ count: number;
45
+ }>;
46
+ declare function deleteUserTableRows(identity: string, tableName: string, filters: TableFilter[]): Promise<{
47
+ deleted: number;
48
+ }>;
49
+
50
+ export { COLUMN_NAME_RE, type ColumnDef, type ColumnType, type FilterOp, TABLE_NAME_RE, type TableColumnInfo, type TableFilter, type TableQueryOptions, type TableSort, TableValidationError, createUserTable, deleteUserTableRows, describeUserTable, dropUserTable, insertUserTableRows, listUserTables, queryUserTable, schemaNameFor, tableExists };
@@ -0,0 +1,226 @@
1
+ import { createHash } from "node:crypto";
2
+ import { ensureSchema as ensureNotesSchema, query } from "./pool";
3
+ const TABLE_NAME_RE = /^[a-z][a-z0-9_]{0,62}$/;
4
+ const COLUMN_NAME_RE = /^[a-z][a-z0-9_]{0,62}$/;
5
+ const COLUMN_TYPE_SQL = {
6
+ text: "TEXT",
7
+ number: "DOUBLE PRECISION",
8
+ integer: "BIGINT",
9
+ boolean: "BOOLEAN",
10
+ date: "DATE",
11
+ timestamp: "TIMESTAMPTZ",
12
+ json: "JSONB"
13
+ };
14
+ const RESERVED_COLUMN_NAMES = /* @__PURE__ */ new Set(["id", "created_at", "updated_at"]);
15
+ const MAX_COLUMNS_PER_TABLE = 40;
16
+ const MAX_TABLES_PER_USER = 50;
17
+ const MAX_ROWS_PER_INSERT = 500;
18
+ const MAX_ROWS_PER_QUERY = 2e3;
19
+ const DEFAULT_ROWS_PER_QUERY = 100;
20
+ class TableValidationError extends Error {
21
+ }
22
+ const FILTER_OP_SQL = {
23
+ eq: "=",
24
+ neq: "!=",
25
+ gt: ">",
26
+ gte: ">=",
27
+ lt: "<",
28
+ lte: "<=",
29
+ like: "ILIKE",
30
+ in: "= ANY"
31
+ };
32
+ function assertSafeIdentifier(name, re, kind) {
33
+ if (!re.test(name)) {
34
+ throw new TableValidationError(`invalid ${kind} name "${name}" \u2014 use lowercase letters, numbers, underscores, starting with a letter`);
35
+ }
36
+ }
37
+ function schemaNameFor(identity) {
38
+ const hash = createHash("sha256").update(identity).digest("hex").slice(0, 32);
39
+ return `tbl_${hash}`;
40
+ }
41
+ async function ensureUserSchema(identity) {
42
+ await ensureNotesSchema();
43
+ await query(
44
+ `CREATE TABLE IF NOT EXISTS mem_table_schemas (
45
+ identity TEXT PRIMARY KEY,
46
+ schema_name TEXT UNIQUE NOT NULL,
47
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now()
48
+ )`
49
+ );
50
+ const schemaName = schemaNameFor(identity);
51
+ await query(`CREATE SCHEMA IF NOT EXISTS "${schemaName}"`);
52
+ await query(
53
+ `INSERT INTO mem_table_schemas (identity, schema_name) VALUES ($1, $2)
54
+ ON CONFLICT (identity) DO NOTHING`,
55
+ [identity, schemaName]
56
+ );
57
+ return schemaName;
58
+ }
59
+ async function listUserTables(identity) {
60
+ const schemaName = await ensureUserSchema(identity);
61
+ const res = await query(
62
+ `SELECT table_name FROM information_schema.tables WHERE table_schema = $1 ORDER BY table_name`,
63
+ [schemaName]
64
+ );
65
+ return res.rows.map((r) => r.table_name);
66
+ }
67
+ async function tableExists(identity, tableName) {
68
+ const schemaName = await ensureUserSchema(identity);
69
+ const res = await query(
70
+ `SELECT 1 FROM information_schema.tables WHERE table_schema = $1 AND table_name = $2 LIMIT 1`,
71
+ [schemaName, tableName]
72
+ );
73
+ return res.rows.length > 0;
74
+ }
75
+ async function describeUserTable(identity, tableName) {
76
+ assertSafeIdentifier(tableName, TABLE_NAME_RE, "table");
77
+ const schemaName = await ensureUserSchema(identity);
78
+ const res = await query(
79
+ `SELECT column_name, data_type FROM information_schema.columns
80
+ WHERE table_schema = $1 AND table_name = $2 ORDER BY ordinal_position`,
81
+ [schemaName, tableName]
82
+ );
83
+ return res.rows.map((r) => ({ name: r.column_name, type: r.data_type }));
84
+ }
85
+ async function createUserTable(identity, tableName, columns) {
86
+ assertSafeIdentifier(tableName, TABLE_NAME_RE, "table");
87
+ if (columns.length === 0) throw new TableValidationError("at least one column is required");
88
+ if (columns.length > MAX_COLUMNS_PER_TABLE) throw new TableValidationError(`at most ${MAX_COLUMNS_PER_TABLE} columns per table`);
89
+ const seen = /* @__PURE__ */ new Set();
90
+ for (const col of columns) {
91
+ assertSafeIdentifier(col.name, COLUMN_NAME_RE, "column");
92
+ if (RESERVED_COLUMN_NAMES.has(col.name)) throw new TableValidationError(`column name "${col.name}" is reserved`);
93
+ if (seen.has(col.name)) throw new TableValidationError(`duplicate column name "${col.name}"`);
94
+ seen.add(col.name);
95
+ if (!(col.type in COLUMN_TYPE_SQL)) throw new TableValidationError(`unsupported column type "${col.type}"`);
96
+ }
97
+ const schemaName = await ensureUserSchema(identity);
98
+ const existing = await listUserTables(identity);
99
+ if (!existing.includes(tableName) && existing.length >= MAX_TABLES_PER_USER) {
100
+ throw new TableValidationError(`at most ${MAX_TABLES_PER_USER} tables per account`);
101
+ }
102
+ const columnSql = columns.map((c) => `"${c.name}" ${COLUMN_TYPE_SQL[c.type]}`).join(", ");
103
+ await query(
104
+ `CREATE TABLE IF NOT EXISTS "${schemaName}"."${tableName}" (
105
+ id BIGSERIAL PRIMARY KEY,
106
+ ${columnSql},
107
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
108
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
109
+ )`
110
+ );
111
+ return { created: !existing.includes(tableName), columns };
112
+ }
113
+ async function dropUserTable(identity, tableName) {
114
+ assertSafeIdentifier(tableName, TABLE_NAME_RE, "table");
115
+ const schemaName = await ensureUserSchema(identity);
116
+ if (!await tableExists(identity, tableName)) return false;
117
+ await query(`DROP TABLE IF EXISTS "${schemaName}"."${tableName}"`);
118
+ return true;
119
+ }
120
+ async function knownColumns(identity, tableName) {
121
+ const cols = await describeUserTable(identity, tableName);
122
+ if (cols.length === 0) throw new TableValidationError(`table "${tableName}" does not exist`);
123
+ return new Set(cols.map((c) => c.name));
124
+ }
125
+ function assertKnownColumns(names, known) {
126
+ for (const name of names) {
127
+ if (name === "id" || name === "created_at" || name === "updated_at") continue;
128
+ if (!known.has(name)) throw new TableValidationError(`unknown column "${name}"`);
129
+ }
130
+ }
131
+ async function insertUserTableRows(identity, tableName, rows) {
132
+ assertSafeIdentifier(tableName, TABLE_NAME_RE, "table");
133
+ if (rows.length === 0) throw new TableValidationError("at least one row is required");
134
+ if (rows.length > MAX_ROWS_PER_INSERT) throw new TableValidationError(`at most ${MAX_ROWS_PER_INSERT} rows per insert`);
135
+ const schemaName = await ensureUserSchema(identity);
136
+ const known = await knownColumns(identity, tableName);
137
+ const columnNames = [...new Set(rows.flatMap((r) => Object.keys(r)))];
138
+ assertKnownColumns(columnNames, known);
139
+ if (columnNames.length === 0) throw new TableValidationError("rows must have at least one column value");
140
+ const valueRows = [];
141
+ const params = [];
142
+ let i = 1;
143
+ for (const row of rows) {
144
+ const placeholders = columnNames.map((col) => {
145
+ params.push(Object.prototype.hasOwnProperty.call(row, col) ? row[col] : null);
146
+ return `$${i++}`;
147
+ });
148
+ valueRows.push(`(${placeholders.join(", ")})`);
149
+ }
150
+ const columnSql = columnNames.map((c) => `"${c}"`).join(", ");
151
+ const res = await query(
152
+ `INSERT INTO "${schemaName}"."${tableName}" (${columnSql}) VALUES ${valueRows.join(", ")}`,
153
+ params
154
+ );
155
+ return { inserted: res.rowCount ?? 0 };
156
+ }
157
+ function buildWhereClause(filters, known, startParamIndex) {
158
+ if (filters.length === 0) return { sql: "", params: [] };
159
+ const parts = [];
160
+ const params = [];
161
+ let i = startParamIndex;
162
+ for (const f of filters) {
163
+ assertKnownColumns([f.column], known);
164
+ const opSql = FILTER_OP_SQL[f.op];
165
+ if (!opSql) throw new TableValidationError(`unsupported filter op "${f.op}"`);
166
+ if (f.op === "in") {
167
+ if (!Array.isArray(f.value)) throw new TableValidationError(`"in" filter requires an array value`);
168
+ parts.push(`"${f.column}" = ANY($${i++})`);
169
+ params.push(f.value);
170
+ } else {
171
+ parts.push(`"${f.column}" ${opSql} $${i++}`);
172
+ params.push(f.op === "like" ? `%${f.value}%` : f.value);
173
+ }
174
+ }
175
+ return { sql: `WHERE ${parts.join(" AND ")}`, params };
176
+ }
177
+ async function queryUserTable(identity, tableName, opts = {}) {
178
+ assertSafeIdentifier(tableName, TABLE_NAME_RE, "table");
179
+ const schemaName = await ensureUserSchema(identity);
180
+ const known = await knownColumns(identity, tableName);
181
+ const filters = opts.filters ?? [];
182
+ const { sql: whereSql, params: whereParams } = buildWhereClause(filters, known, 1);
183
+ let orderSql = "ORDER BY id ASC";
184
+ if (opts.sort) {
185
+ assertKnownColumns([opts.sort.column], known);
186
+ const dir = opts.sort.direction === "desc" ? "DESC" : "ASC";
187
+ orderSql = `ORDER BY "${opts.sort.column}" ${dir}`;
188
+ }
189
+ const limit = Math.min(Math.max(1, opts.limit ?? DEFAULT_ROWS_PER_QUERY), MAX_ROWS_PER_QUERY);
190
+ const offset = Math.max(0, opts.offset ?? 0);
191
+ const limitParamIndex = whereParams.length + 1;
192
+ const offsetParamIndex = whereParams.length + 2;
193
+ const res = await query(
194
+ `SELECT * FROM "${schemaName}"."${tableName}" ${whereSql} ${orderSql} LIMIT $${limitParamIndex} OFFSET $${offsetParamIndex}`,
195
+ [...whereParams, limit, offset]
196
+ );
197
+ const countRes = await query(
198
+ `SELECT COUNT(*) AS count FROM "${schemaName}"."${tableName}" ${whereSql}`,
199
+ whereParams
200
+ );
201
+ return { rows: res.rows, count: Number(countRes.rows[0]?.count ?? 0) };
202
+ }
203
+ async function deleteUserTableRows(identity, tableName, filters) {
204
+ assertSafeIdentifier(tableName, TABLE_NAME_RE, "table");
205
+ if (filters.length === 0) throw new TableValidationError("at least one filter is required \u2014 refusing an unfiltered delete");
206
+ const schemaName = await ensureUserSchema(identity);
207
+ const known = await knownColumns(identity, tableName);
208
+ const { sql: whereSql, params } = buildWhereClause(filters, known, 1);
209
+ const res = await query(`DELETE FROM "${schemaName}"."${tableName}" ${whereSql}`, params);
210
+ return { deleted: res.rowCount ?? 0 };
211
+ }
212
+ export {
213
+ COLUMN_NAME_RE,
214
+ TABLE_NAME_RE,
215
+ TableValidationError,
216
+ createUserTable,
217
+ deleteUserTableRows,
218
+ describeUserTable,
219
+ dropUserTable,
220
+ insertUserTableRows,
221
+ listUserTables,
222
+ queryUserTable,
223
+ schemaNameFor,
224
+ tableExists
225
+ };
226
+ //# sourceMappingURL=tables.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/db/tables.ts"],"sourcesContent":["import { createHash } from 'node:crypto'\nimport { ensureSchema as ensureNotesSchema, query } from './pool'\n\nexport const TABLE_NAME_RE = /^[a-z][a-z0-9_]{0,62}$/\nexport const COLUMN_NAME_RE = /^[a-z][a-z0-9_]{0,62}$/\n\nexport type ColumnType = 'text' | 'number' | 'integer' | 'boolean' | 'date' | 'timestamp' | 'json'\n\nconst COLUMN_TYPE_SQL: Record<ColumnType, string> = {\n text: 'TEXT',\n number: 'DOUBLE PRECISION',\n integer: 'BIGINT',\n boolean: 'BOOLEAN',\n date: 'DATE',\n timestamp: 'TIMESTAMPTZ',\n json: 'JSONB',\n}\n\nconst RESERVED_COLUMN_NAMES = new Set(['id', 'created_at', 'updated_at'])\nconst MAX_COLUMNS_PER_TABLE = 40\nconst MAX_TABLES_PER_USER = 50\nconst MAX_ROWS_PER_INSERT = 500\nconst MAX_ROWS_PER_QUERY = 2000\nconst DEFAULT_ROWS_PER_QUERY = 100\n\nexport class TableValidationError extends Error {}\n\nexport interface ColumnDef {\n name: string\n type: ColumnType\n}\n\nexport type FilterOp = 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'like' | 'in'\n\nconst FILTER_OP_SQL: Record<FilterOp, string> = {\n eq: '=',\n neq: '!=',\n gt: '>',\n gte: '>=',\n lt: '<',\n lte: '<=',\n like: 'ILIKE',\n in: '= ANY',\n}\n\nexport interface TableFilter {\n column: string\n op: FilterOp\n value?: unknown\n}\n\nexport interface TableSort {\n column: string\n direction?: 'asc' | 'desc'\n}\n\nfunction assertSafeIdentifier(name: string, re: RegExp, kind: string): void {\n if (!re.test(name)) {\n throw new TableValidationError(`invalid ${kind} name \"${name}\" — use lowercase letters, numbers, underscores, starting with a letter`)\n }\n}\n\nexport function schemaNameFor(identity: string): string {\n const hash = createHash('sha256').update(identity).digest('hex').slice(0, 32)\n return `tbl_${hash}`\n}\n\nasync function ensureUserSchema(identity: string): Promise<string> {\n await ensureNotesSchema()\n await query(\n `CREATE TABLE IF NOT EXISTS mem_table_schemas (\n identity TEXT PRIMARY KEY,\n schema_name TEXT UNIQUE NOT NULL,\n created_at TIMESTAMPTZ NOT NULL DEFAULT now()\n )`,\n )\n const schemaName = schemaNameFor(identity)\n await query(`CREATE SCHEMA IF NOT EXISTS \"${schemaName}\"`)\n await query(\n `INSERT INTO mem_table_schemas (identity, schema_name) VALUES ($1, $2)\n ON CONFLICT (identity) DO NOTHING`,\n [identity, schemaName],\n )\n return schemaName\n}\n\nexport async function listUserTables(identity: string): Promise<string[]> {\n const schemaName = await ensureUserSchema(identity)\n const res = await query<{ table_name: string }>(\n `SELECT table_name FROM information_schema.tables WHERE table_schema = $1 ORDER BY table_name`,\n [schemaName],\n )\n return res.rows.map(r => r.table_name)\n}\n\nexport async function tableExists(identity: string, tableName: string): Promise<boolean> {\n const schemaName = await ensureUserSchema(identity)\n const res = await query(\n `SELECT 1 FROM information_schema.tables WHERE table_schema = $1 AND table_name = $2 LIMIT 1`,\n [schemaName, tableName],\n )\n return res.rows.length > 0\n}\n\nexport interface TableColumnInfo {\n name: string\n type: string\n}\n\nexport async function describeUserTable(identity: string, tableName: string): Promise<TableColumnInfo[]> {\n assertSafeIdentifier(tableName, TABLE_NAME_RE, 'table')\n const schemaName = await ensureUserSchema(identity)\n const res = await query<{ column_name: string; data_type: string }>(\n `SELECT column_name, data_type FROM information_schema.columns\n WHERE table_schema = $1 AND table_name = $2 ORDER BY ordinal_position`,\n [schemaName, tableName],\n )\n return res.rows.map(r => ({ name: r.column_name, type: r.data_type }))\n}\n\nexport async function createUserTable(identity: string, tableName: string, columns: ColumnDef[]): Promise<{ created: boolean; columns: ColumnDef[] }> {\n assertSafeIdentifier(tableName, TABLE_NAME_RE, 'table')\n if (columns.length === 0) throw new TableValidationError('at least one column is required')\n if (columns.length > MAX_COLUMNS_PER_TABLE) throw new TableValidationError(`at most ${MAX_COLUMNS_PER_TABLE} columns per table`)\n const seen = new Set<string>()\n for (const col of columns) {\n assertSafeIdentifier(col.name, COLUMN_NAME_RE, 'column')\n if (RESERVED_COLUMN_NAMES.has(col.name)) throw new TableValidationError(`column name \"${col.name}\" is reserved`)\n if (seen.has(col.name)) throw new TableValidationError(`duplicate column name \"${col.name}\"`)\n seen.add(col.name)\n if (!(col.type in COLUMN_TYPE_SQL)) throw new TableValidationError(`unsupported column type \"${col.type}\"`)\n }\n\n const schemaName = await ensureUserSchema(identity)\n const existing = await listUserTables(identity)\n if (!existing.includes(tableName) && existing.length >= MAX_TABLES_PER_USER) {\n throw new TableValidationError(`at most ${MAX_TABLES_PER_USER} tables per account`)\n }\n\n const columnSql = columns.map(c => `\"${c.name}\" ${COLUMN_TYPE_SQL[c.type]}`).join(', ')\n await query(\n `CREATE TABLE IF NOT EXISTS \"${schemaName}\".\"${tableName}\" (\n id BIGSERIAL PRIMARY KEY,\n ${columnSql},\n created_at TIMESTAMPTZ NOT NULL DEFAULT now(),\n updated_at TIMESTAMPTZ NOT NULL DEFAULT now()\n )`,\n )\n return { created: !existing.includes(tableName), columns }\n}\n\nexport async function dropUserTable(identity: string, tableName: string): Promise<boolean> {\n assertSafeIdentifier(tableName, TABLE_NAME_RE, 'table')\n const schemaName = await ensureUserSchema(identity)\n if (!(await tableExists(identity, tableName))) return false\n await query(`DROP TABLE IF EXISTS \"${schemaName}\".\"${tableName}\"`)\n return true\n}\n\nasync function knownColumns(identity: string, tableName: string): Promise<Set<string>> {\n const cols = await describeUserTable(identity, tableName)\n if (cols.length === 0) throw new TableValidationError(`table \"${tableName}\" does not exist`)\n return new Set(cols.map(c => c.name))\n}\n\nfunction assertKnownColumns(names: string[], known: Set<string>): void {\n for (const name of names) {\n if (name === 'id' || name === 'created_at' || name === 'updated_at') continue\n if (!known.has(name)) throw new TableValidationError(`unknown column \"${name}\"`)\n }\n}\n\nexport async function insertUserTableRows(identity: string, tableName: string, rows: Array<Record<string, unknown>>): Promise<{ inserted: number }> {\n assertSafeIdentifier(tableName, TABLE_NAME_RE, 'table')\n if (rows.length === 0) throw new TableValidationError('at least one row is required')\n if (rows.length > MAX_ROWS_PER_INSERT) throw new TableValidationError(`at most ${MAX_ROWS_PER_INSERT} rows per insert`)\n const schemaName = await ensureUserSchema(identity)\n const known = await knownColumns(identity, tableName)\n\n const columnNames = [...new Set(rows.flatMap(r => Object.keys(r)))]\n assertKnownColumns(columnNames, known)\n if (columnNames.length === 0) throw new TableValidationError('rows must have at least one column value')\n\n const valueRows: string[] = []\n const params: unknown[] = []\n let i = 1\n for (const row of rows) {\n const placeholders = columnNames.map(col => {\n params.push(Object.prototype.hasOwnProperty.call(row, col) ? row[col] : null)\n return `$${i++}`\n })\n valueRows.push(`(${placeholders.join(', ')})`)\n }\n\n const columnSql = columnNames.map(c => `\"${c}\"`).join(', ')\n const res = await query(\n `INSERT INTO \"${schemaName}\".\"${tableName}\" (${columnSql}) VALUES ${valueRows.join(', ')}`,\n params,\n )\n return { inserted: res.rowCount ?? 0 }\n}\n\nfunction buildWhereClause(filters: TableFilter[], known: Set<string>, startParamIndex: number): { sql: string; params: unknown[] } {\n if (filters.length === 0) return { sql: '', params: [] }\n const parts: string[] = []\n const params: unknown[] = []\n let i = startParamIndex\n for (const f of filters) {\n assertKnownColumns([f.column], known)\n const opSql = FILTER_OP_SQL[f.op]\n if (!opSql) throw new TableValidationError(`unsupported filter op \"${f.op}\"`)\n if (f.op === 'in') {\n if (!Array.isArray(f.value)) throw new TableValidationError(`\"in\" filter requires an array value`)\n parts.push(`\"${f.column}\" = ANY($${i++})`)\n params.push(f.value)\n } else {\n parts.push(`\"${f.column}\" ${opSql} $${i++}`)\n params.push(f.op === 'like' ? `%${f.value}%` : f.value)\n }\n }\n return { sql: `WHERE ${parts.join(' AND ')}`, params }\n}\n\nexport interface TableQueryOptions {\n filters?: TableFilter[]\n sort?: TableSort\n limit?: number\n offset?: number\n}\n\nexport async function queryUserTable(identity: string, tableName: string, opts: TableQueryOptions = {}): Promise<{ rows: Record<string, unknown>[]; count: number }> {\n assertSafeIdentifier(tableName, TABLE_NAME_RE, 'table')\n const schemaName = await ensureUserSchema(identity)\n const known = await knownColumns(identity, tableName)\n\n const filters = opts.filters ?? []\n const { sql: whereSql, params: whereParams } = buildWhereClause(filters, known, 1)\n\n let orderSql = 'ORDER BY id ASC'\n if (opts.sort) {\n assertKnownColumns([opts.sort.column], known)\n const dir = opts.sort.direction === 'desc' ? 'DESC' : 'ASC'\n orderSql = `ORDER BY \"${opts.sort.column}\" ${dir}`\n }\n\n const limit = Math.min(Math.max(1, opts.limit ?? DEFAULT_ROWS_PER_QUERY), MAX_ROWS_PER_QUERY)\n const offset = Math.max(0, opts.offset ?? 0)\n const limitParamIndex = whereParams.length + 1\n const offsetParamIndex = whereParams.length + 2\n\n const res = await query(\n `SELECT * FROM \"${schemaName}\".\"${tableName}\" ${whereSql} ${orderSql} LIMIT $${limitParamIndex} OFFSET $${offsetParamIndex}`,\n [...whereParams, limit, offset],\n )\n\n const countRes = await query<{ count: string }>(\n `SELECT COUNT(*) AS count FROM \"${schemaName}\".\"${tableName}\" ${whereSql}`,\n whereParams,\n )\n\n return { rows: res.rows as Record<string, unknown>[], count: Number(countRes.rows[0]?.count ?? 0) }\n}\n\nexport async function deleteUserTableRows(identity: string, tableName: string, filters: TableFilter[]): Promise<{ deleted: number }> {\n assertSafeIdentifier(tableName, TABLE_NAME_RE, 'table')\n if (filters.length === 0) throw new TableValidationError('at least one filter is required — refusing an unfiltered delete')\n const schemaName = await ensureUserSchema(identity)\n const known = await knownColumns(identity, tableName)\n const { sql: whereSql, params } = buildWhereClause(filters, known, 1)\n const res = await query(`DELETE FROM \"${schemaName}\".\"${tableName}\" ${whereSql}`, params)\n return { deleted: res.rowCount ?? 0 }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,10 @@
1
+ declare function addApprovedSender(ownerIdentity: string, senderIdentity: string): Promise<void>;
2
+ declare function removeApprovedSender(ownerIdentity: string, senderIdentity: string): Promise<number>;
3
+ declare function listApprovedSenders(ownerIdentity: string): Promise<string[]>;
4
+ declare function allowsUnapprovedSenders(identity: string): Promise<boolean>;
5
+ declare function setAllowUnapprovedSenders(identity: string, allow: boolean): Promise<void>;
6
+ declare function setIsAgent(identity: string, isAgent: boolean): Promise<void>;
7
+ declare function isAgentIdentities(identities: string[]): Promise<Set<string>>;
8
+ declare function maySend(sender: string, recipient: string): Promise<boolean>;
9
+
10
+ export { addApprovedSender, allowsUnapprovedSenders, isAgentIdentities, listApprovedSenders, maySend, removeApprovedSender, setAllowUnapprovedSenders, setIsAgent };
@@ -0,0 +1,88 @@
1
+ import { ensureSchema, query } from "./pool";
2
+ import { getAccountGrant } from "./vaults";
3
+ async function addApprovedSender(ownerIdentity, senderIdentity) {
4
+ await ensureSchema();
5
+ await query(
6
+ `INSERT INTO mem_approved_senders (owner_identity, sender_identity) VALUES ($1, $2)
7
+ ON CONFLICT (owner_identity, sender_identity) DO NOTHING`,
8
+ [ownerIdentity, senderIdentity]
9
+ );
10
+ }
11
+ async function removeApprovedSender(ownerIdentity, senderIdentity) {
12
+ await ensureSchema();
13
+ const res = await query(
14
+ `DELETE FROM mem_approved_senders WHERE owner_identity = $1 AND sender_identity = $2`,
15
+ [ownerIdentity, senderIdentity]
16
+ );
17
+ return res.rowCount ?? 0;
18
+ }
19
+ async function listApprovedSenders(ownerIdentity) {
20
+ await ensureSchema();
21
+ const res = await query(
22
+ `SELECT sender_identity FROM mem_approved_senders WHERE owner_identity = $1 ORDER BY created_at ASC`,
23
+ [ownerIdentity]
24
+ );
25
+ return res.rows.map((r) => r.sender_identity);
26
+ }
27
+ async function isApprovedSender(ownerIdentity, senderIdentity) {
28
+ await ensureSchema();
29
+ const res = await query(
30
+ `SELECT 1 FROM mem_approved_senders WHERE owner_identity = $1 AND sender_identity = $2 LIMIT 1`,
31
+ [ownerIdentity, senderIdentity]
32
+ );
33
+ return (res.rowCount ?? 0) > 0;
34
+ }
35
+ async function allowsUnapprovedSenders(identity) {
36
+ await ensureSchema();
37
+ const res = await query(
38
+ `SELECT allow_unapproved_senders FROM mem_settings WHERE identity = $1 LIMIT 1`,
39
+ [identity]
40
+ );
41
+ return res.rows[0]?.allow_unapproved_senders ?? false;
42
+ }
43
+ async function setAllowUnapprovedSenders(identity, allow) {
44
+ await ensureSchema();
45
+ await query(
46
+ `INSERT INTO mem_settings (identity, allow_unapproved_senders, updated_at)
47
+ VALUES ($1, $2, now())
48
+ ON CONFLICT (identity) DO UPDATE SET allow_unapproved_senders = EXCLUDED.allow_unapproved_senders, updated_at = now()`,
49
+ [identity, allow]
50
+ );
51
+ }
52
+ async function setIsAgent(identity, isAgent) {
53
+ await ensureSchema();
54
+ await query(
55
+ `INSERT INTO mem_settings (identity, is_agent, updated_at)
56
+ VALUES ($1, $2, now())
57
+ ON CONFLICT (identity) DO UPDATE SET is_agent = EXCLUDED.is_agent, updated_at = now()`,
58
+ [identity, isAgent]
59
+ );
60
+ }
61
+ async function isAgentIdentities(identities) {
62
+ await ensureSchema();
63
+ if (identities.length === 0) return /* @__PURE__ */ new Set();
64
+ const res = await query(
65
+ `SELECT identity FROM mem_settings WHERE identity = ANY($1) AND is_agent = TRUE`,
66
+ [identities]
67
+ );
68
+ return new Set(res.rows.map((r) => r.identity));
69
+ }
70
+ async function maySend(sender, recipient) {
71
+ if (sender === recipient) return true;
72
+ if (await isApprovedSender(recipient, sender)) return true;
73
+ if (await allowsUnapprovedSenders(recipient)) return true;
74
+ if (await getAccountGrant(recipient, sender) !== null) return true;
75
+ if (await getAccountGrant(sender, recipient) !== null) return true;
76
+ return false;
77
+ }
78
+ export {
79
+ addApprovedSender,
80
+ allowsUnapprovedSenders,
81
+ isAgentIdentities,
82
+ listApprovedSenders,
83
+ maySend,
84
+ removeApprovedSender,
85
+ setAllowUnapprovedSenders,
86
+ setIsAgent
87
+ };
88
+ //# sourceMappingURL=trust.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/db/trust.ts"],"sourcesContent":["import { ensureSchema, query } from './pool'\nimport { getAccountGrant } from './vaults'\n\nexport async function addApprovedSender(ownerIdentity: string, senderIdentity: string): Promise<void> {\n await ensureSchema()\n await query(\n `INSERT INTO mem_approved_senders (owner_identity, sender_identity) VALUES ($1, $2)\n ON CONFLICT (owner_identity, sender_identity) DO NOTHING`,\n [ownerIdentity, senderIdentity],\n )\n}\n\nexport async function removeApprovedSender(ownerIdentity: string, senderIdentity: string): Promise<number> {\n await ensureSchema()\n const res = await query(\n `DELETE FROM mem_approved_senders WHERE owner_identity = $1 AND sender_identity = $2`,\n [ownerIdentity, senderIdentity],\n )\n return res.rowCount ?? 0\n}\n\nexport async function listApprovedSenders(ownerIdentity: string): Promise<string[]> {\n await ensureSchema()\n const res = await query<{ sender_identity: string }>(\n `SELECT sender_identity FROM mem_approved_senders WHERE owner_identity = $1 ORDER BY created_at ASC`,\n [ownerIdentity],\n )\n return res.rows.map(r => r.sender_identity)\n}\n\nasync function isApprovedSender(ownerIdentity: string, senderIdentity: string): Promise<boolean> {\n await ensureSchema()\n const res = await query(\n `SELECT 1 FROM mem_approved_senders WHERE owner_identity = $1 AND sender_identity = $2 LIMIT 1`,\n [ownerIdentity, senderIdentity],\n )\n return (res.rowCount ?? 0) > 0\n}\n\nexport async function allowsUnapprovedSenders(identity: string): Promise<boolean> {\n await ensureSchema()\n const res = await query<{ allow_unapproved_senders: boolean }>(\n `SELECT allow_unapproved_senders FROM mem_settings WHERE identity = $1 LIMIT 1`,\n [identity],\n )\n return res.rows[0]?.allow_unapproved_senders ?? false\n}\n\nexport async function setAllowUnapprovedSenders(identity: string, allow: boolean): Promise<void> {\n await ensureSchema()\n await query(\n `INSERT INTO mem_settings (identity, allow_unapproved_senders, updated_at)\n VALUES ($1, $2, now())\n ON CONFLICT (identity) DO UPDATE SET allow_unapproved_senders = EXCLUDED.allow_unapproved_senders, updated_at = now()`,\n [identity, allow],\n )\n}\n\nexport async function setIsAgent(identity: string, isAgent: boolean): Promise<void> {\n await ensureSchema()\n await query(\n `INSERT INTO mem_settings (identity, is_agent, updated_at)\n VALUES ($1, $2, now())\n ON CONFLICT (identity) DO UPDATE SET is_agent = EXCLUDED.is_agent, updated_at = now()`,\n [identity, isAgent],\n )\n}\n\nexport async function isAgentIdentities(identities: string[]): Promise<Set<string>> {\n await ensureSchema()\n if (identities.length === 0) return new Set()\n const res = await query<{ identity: string }>(\n `SELECT identity FROM mem_settings WHERE identity = ANY($1) AND is_agent = TRUE`,\n [identities],\n )\n return new Set(res.rows.map(r => r.identity))\n}\n\nexport async function maySend(sender: string, recipient: string): Promise<boolean> {\n if (sender === recipient) return true\n if (await isApprovedSender(recipient, sender)) return true\n if (await allowsUnapprovedSenders(recipient)) return true\n if ((await getAccountGrant(recipient, sender)) !== null) return true\n if ((await getAccountGrant(sender, recipient)) !== null) return true\n return false\n}\n"],"mappings":"AAAA,SAAS,cAAc,aAAa;AACpC,SAAS,uBAAuB;AAEhC,eAAsB,kBAAkB,eAAuB,gBAAuC;AACpG,QAAM,aAAa;AACnB,QAAM;AAAA,IACJ;AAAA;AAAA,IAEA,CAAC,eAAe,cAAc;AAAA,EAChC;AACF;AAEA,eAAsB,qBAAqB,eAAuB,gBAAyC;AACzG,QAAM,aAAa;AACnB,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,IACA,CAAC,eAAe,cAAc;AAAA,EAChC;AACA,SAAO,IAAI,YAAY;AACzB;AAEA,eAAsB,oBAAoB,eAA0C;AAClF,QAAM,aAAa;AACnB,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,IACA,CAAC,aAAa;AAAA,EAChB;AACA,SAAO,IAAI,KAAK,IAAI,OAAK,EAAE,eAAe;AAC5C;AAEA,eAAe,iBAAiB,eAAuB,gBAA0C;AAC/F,QAAM,aAAa;AACnB,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,IACA,CAAC,eAAe,cAAc;AAAA,EAChC;AACA,UAAQ,IAAI,YAAY,KAAK;AAC/B;AAEA,eAAsB,wBAAwB,UAAoC;AAChF,QAAM,aAAa;AACnB,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,IACA,CAAC,QAAQ;AAAA,EACX;AACA,SAAO,IAAI,KAAK,CAAC,GAAG,4BAA4B;AAClD;AAEA,eAAsB,0BAA0B,UAAkB,OAA+B;AAC/F,QAAM,aAAa;AACnB,QAAM;AAAA,IACJ;AAAA;AAAA;AAAA,IAGA,CAAC,UAAU,KAAK;AAAA,EAClB;AACF;AAEA,eAAsB,WAAW,UAAkB,SAAiC;AAClF,QAAM,aAAa;AACnB,QAAM;AAAA,IACJ;AAAA;AAAA;AAAA,IAGA,CAAC,UAAU,OAAO;AAAA,EACpB;AACF;AAEA,eAAsB,kBAAkB,YAA4C;AAClF,QAAM,aAAa;AACnB,MAAI,WAAW,WAAW,EAAG,QAAO,oBAAI,IAAI;AAC5C,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,IACA,CAAC,UAAU;AAAA,EACb;AACA,SAAO,IAAI,IAAI,IAAI,KAAK,IAAI,OAAK,EAAE,QAAQ,CAAC;AAC9C;AAEA,eAAsB,QAAQ,QAAgB,WAAqC;AACjF,MAAI,WAAW,UAAW,QAAO;AACjC,MAAI,MAAM,iBAAiB,WAAW,MAAM,EAAG,QAAO;AACtD,MAAI,MAAM,wBAAwB,SAAS,EAAG,QAAO;AACrD,MAAK,MAAM,gBAAgB,WAAW,MAAM,MAAO,KAAM,QAAO;AAChE,MAAK,MAAM,gBAAgB,QAAQ,SAAS,MAAO,KAAM,QAAO;AAChE,SAAO;AACT;","names":[]}
@@ -0,0 +1,30 @@
1
+ type CostSource = 'llm' | 'embed' | 'storage' | 'compute';
2
+ interface RecordCostArgs {
3
+ identity: string;
4
+ source: CostSource;
5
+ op: string;
6
+ tokens?: number;
7
+ costUsd: number;
8
+ vault?: string;
9
+ rawCostUsd?: number;
10
+ }
11
+ declare function recordCost(args: RecordCostArgs): Promise<void>;
12
+ interface CostSummary {
13
+ total: number;
14
+ bySource: Record<string, number>;
15
+ }
16
+ declare function userCostThisPeriod(identity: string, period?: string): Promise<CostSummary>;
17
+ interface TierCost {
18
+ plan: string;
19
+ users: number;
20
+ totalUsd: number;
21
+ avgUsdPerUser: number;
22
+ }
23
+ declare function costByTier(period?: string): Promise<TierCost[]>;
24
+ declare function snapshotStorageCostForIdentity(identity: string, vaults: string[]): Promise<number>;
25
+ declare function snapshotAllStorageCost(): Promise<{
26
+ identities: number;
27
+ totalUsd: number;
28
+ }>;
29
+
30
+ export { type CostSource, type CostSummary, type RecordCostArgs, type TierCost, costByTier, recordCost, snapshotAllStorageCost, snapshotStorageCostForIdentity, userCostThisPeriod };