@opentrust/db 7.1.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 (83) hide show
  1. package/dist/client.d.ts +3 -0
  2. package/dist/client.d.ts.map +1 -0
  3. package/dist/client.js +51 -0
  4. package/dist/dialect.d.ts +3 -0
  5. package/dist/dialect.d.ts.map +1 -0
  6. package/dist/dialect.js +12 -0
  7. package/dist/generate.d.ts +2 -0
  8. package/dist/generate.d.ts.map +1 -0
  9. package/dist/generate.js +20 -0
  10. package/dist/helpers.d.ts +11 -0
  11. package/dist/helpers.d.ts.map +1 -0
  12. package/dist/helpers.js +32 -0
  13. package/dist/index.d.ts +13 -0
  14. package/dist/index.d.ts.map +1 -0
  15. package/dist/index.js +12 -0
  16. package/dist/migrate.d.ts +2 -0
  17. package/dist/migrate.d.ts.map +1 -0
  18. package/dist/migrate.js +61 -0
  19. package/dist/queries/agents.d.ts +25 -0
  20. package/dist/queries/agents.d.ts.map +1 -0
  21. package/dist/queries/agents.js +46 -0
  22. package/dist/queries/auth.d.ts +18 -0
  23. package/dist/queries/auth.d.ts.map +1 -0
  24. package/dist/queries/auth.js +77 -0
  25. package/dist/queries/detection-results.d.ts +24 -0
  26. package/dist/queries/detection-results.d.ts.map +1 -0
  27. package/dist/queries/detection-results.js +43 -0
  28. package/dist/queries/observations.d.ts +58 -0
  29. package/dist/queries/observations.d.ts.map +1 -0
  30. package/dist/queries/observations.js +212 -0
  31. package/dist/queries/policies.d.ts +25 -0
  32. package/dist/queries/policies.d.ts.map +1 -0
  33. package/dist/queries/policies.js +38 -0
  34. package/dist/queries/scanners.d.ts +25 -0
  35. package/dist/queries/scanners.d.ts.map +1 -0
  36. package/dist/queries/scanners.js +56 -0
  37. package/dist/queries/settings.d.ts +8 -0
  38. package/dist/queries/settings.d.ts.map +1 -0
  39. package/dist/queries/settings.js +30 -0
  40. package/dist/queries/usage.d.ts +18 -0
  41. package/dist/queries/usage.d.ts.map +1 -0
  42. package/dist/queries/usage.js +54 -0
  43. package/dist/schema/index.d.ts +4415 -0
  44. package/dist/schema/index.d.ts.map +1 -0
  45. package/dist/schema/index.js +19 -0
  46. package/dist/schema/mysql.d.ts +1479 -0
  47. package/dist/schema/mysql.d.ts.map +1 -0
  48. package/dist/schema/mysql.js +151 -0
  49. package/dist/schema/pg.d.ts +1479 -0
  50. package/dist/schema/pg.d.ts.map +1 -0
  51. package/dist/schema/pg.js +151 -0
  52. package/dist/schema/sqlite.d.ts +1479 -0
  53. package/dist/schema/sqlite.d.ts.map +1 -0
  54. package/dist/schema/sqlite.js +153 -0
  55. package/dist/seed.d.ts +2 -0
  56. package/dist/seed.d.ts.map +1 -0
  57. package/dist/seed.js +49 -0
  58. package/drizzle/sqlite/0000_serious_martin_li.sql +143 -0
  59. package/drizzle/sqlite/meta/0000_snapshot.json +945 -0
  60. package/drizzle/sqlite/meta/_journal.json +13 -0
  61. package/drizzle.config.mysql.ts +10 -0
  62. package/drizzle.config.pg.ts +10 -0
  63. package/drizzle.config.sqlite.ts +10 -0
  64. package/package.json +55 -0
  65. package/src/client.ts +66 -0
  66. package/src/dialect.ts +13 -0
  67. package/src/generate.ts +26 -0
  68. package/src/helpers.ts +47 -0
  69. package/src/index.ts +12 -0
  70. package/src/migrate.ts +74 -0
  71. package/src/queries/agents.ts +68 -0
  72. package/src/queries/auth.ts +94 -0
  73. package/src/queries/detection-results.ts +58 -0
  74. package/src/queries/observations.ts +275 -0
  75. package/src/queries/policies.ts +59 -0
  76. package/src/queries/scanners.ts +74 -0
  77. package/src/queries/settings.ts +34 -0
  78. package/src/queries/usage.ts +69 -0
  79. package/src/schema/index.ts +22 -0
  80. package/src/schema/mysql.ts +207 -0
  81. package/src/schema/pg.ts +208 -0
  82. package/src/schema/sqlite.ts +199 -0
  83. package/src/seed.ts +56 -0
@@ -0,0 +1,212 @@
1
+ import { eq, and, desc, count, sql } from "drizzle-orm";
2
+ import { toolCallObservations, agentPermissions } from "../schema/index.js";
3
+ import { DEFAULT_TENANT_ID } from "@opentrust/shared";
4
+ // ─── Tool name → category / access pattern inference ────────────
5
+ const ACCESS_READ_PREFIXES = ["list", "get", "search", "read", "fetch", "find", "query", "check", "view"];
6
+ const ACCESS_WRITE_PREFIXES = ["create", "send", "write", "update", "edit", "post", "put", "add", "set"];
7
+ const ACCESS_ADMIN_PREFIXES = ["delete", "remove", "execute", "run", "admin", "destroy", "revoke", "drop"];
8
+ export function inferCategory(toolName) {
9
+ // "github_create_issue" → "github"
10
+ // "slack_send_message" → "slack"
11
+ // "read_file" → "filesystem"
12
+ const lower = toolName.toLowerCase();
13
+ if (lower.startsWith("read_file") || lower.startsWith("write_file") || lower.startsWith("list_dir")) {
14
+ return "filesystem";
15
+ }
16
+ if (lower.startsWith("execute_command") || lower.startsWith("run_command") || lower === "bash") {
17
+ return "shell";
18
+ }
19
+ const underscoreIdx = lower.indexOf("_");
20
+ if (underscoreIdx > 0) {
21
+ return lower.slice(0, underscoreIdx);
22
+ }
23
+ return lower;
24
+ }
25
+ export function inferAccessPattern(toolName) {
26
+ const lower = toolName.toLowerCase();
27
+ // Strip category prefix: "github_create_issue" → "create_issue"
28
+ const underscoreIdx = lower.indexOf("_");
29
+ const action = underscoreIdx > 0 ? lower.slice(underscoreIdx + 1) : lower;
30
+ for (const prefix of ACCESS_ADMIN_PREFIXES) {
31
+ if (action.startsWith(prefix))
32
+ return "admin";
33
+ }
34
+ for (const prefix of ACCESS_WRITE_PREFIXES) {
35
+ if (action.startsWith(prefix))
36
+ return "write";
37
+ }
38
+ for (const prefix of ACCESS_READ_PREFIXES) {
39
+ if (action.startsWith(prefix))
40
+ return "read";
41
+ }
42
+ return "unknown";
43
+ }
44
+ // ─── Query Functions ────────────────────────────────────────────
45
+ export function observationQueries(db) {
46
+ return {
47
+ /**
48
+ * Record a tool call observation.
49
+ */
50
+ async record(data) {
51
+ const category = inferCategory(data.toolName);
52
+ const accessPattern = inferAccessPattern(data.toolName);
53
+ const tenantId = data.tenantId ?? DEFAULT_TENANT_ID;
54
+ await db.insert(toolCallObservations).values({
55
+ agentId: data.agentId,
56
+ sessionKey: data.sessionKey ?? null,
57
+ toolName: data.toolName,
58
+ category,
59
+ accessPattern,
60
+ paramsJson: data.params ?? null,
61
+ phase: data.phase,
62
+ resultJson: data.result ?? null,
63
+ error: data.error ?? null,
64
+ durationMs: data.durationMs ?? null,
65
+ blocked: data.blocked ?? false,
66
+ blockReason: data.blockReason ?? null,
67
+ tenantId,
68
+ });
69
+ // Upsert permission on "after" phase (or "before" if blocked)
70
+ if (data.phase === "after" || data.blocked) {
71
+ await this.upsertPermission({
72
+ agentId: data.agentId,
73
+ toolName: data.toolName,
74
+ category,
75
+ accessPattern,
76
+ params: data.params,
77
+ hasError: !!data.error,
78
+ tenantId,
79
+ });
80
+ }
81
+ },
82
+ /**
83
+ * Upsert an agent permission entry based on observed tool call.
84
+ */
85
+ async upsertPermission(data) {
86
+ const now = new Date().toISOString();
87
+ // Check if permission already exists
88
+ const existing = await db
89
+ .select()
90
+ .from(agentPermissions)
91
+ .where(and(eq(agentPermissions.tenantId, data.tenantId), eq(agentPermissions.agentId, data.agentId), eq(agentPermissions.toolName, data.toolName)))
92
+ .limit(1);
93
+ if (existing.length > 0) {
94
+ const perm = existing[0];
95
+ const targets = perm.targetsJson || [];
96
+ const newTargets = extractTargets(data.params);
97
+ const mergedTargets = mergeTargets(targets, newTargets);
98
+ await db
99
+ .update(agentPermissions)
100
+ .set({
101
+ callCount: (perm.callCount ?? 0) + 1,
102
+ errorCount: (perm.errorCount ?? 0) + (data.hasError ? 1 : 0),
103
+ lastSeen: now,
104
+ targetsJson: mergedTargets,
105
+ })
106
+ .where(eq(agentPermissions.id, perm.id));
107
+ }
108
+ else {
109
+ const targets = extractTargets(data.params);
110
+ await db.insert(agentPermissions).values({
111
+ tenantId: data.tenantId,
112
+ agentId: data.agentId,
113
+ toolName: data.toolName,
114
+ category: data.category,
115
+ accessPattern: data.accessPattern,
116
+ targetsJson: targets,
117
+ callCount: 1,
118
+ errorCount: data.hasError ? 1 : 0,
119
+ firstSeen: now,
120
+ lastSeen: now,
121
+ });
122
+ }
123
+ },
124
+ /**
125
+ * Get recent observations, optionally filtered by agentId.
126
+ */
127
+ async findRecent(opts = {}) {
128
+ const tenantId = opts.tenantId ?? DEFAULT_TENANT_ID;
129
+ const limit = opts.limit ?? 50;
130
+ const conditions = [eq(toolCallObservations.tenantId, tenantId)];
131
+ if (opts.agentId) {
132
+ conditions.push(eq(toolCallObservations.agentId, opts.agentId));
133
+ }
134
+ return db
135
+ .select()
136
+ .from(toolCallObservations)
137
+ .where(and(...conditions))
138
+ .orderBy(desc(toolCallObservations.timestamp))
139
+ .limit(limit);
140
+ },
141
+ /**
142
+ * Get the aggregated permission profile for an agent.
143
+ */
144
+ async getPermissions(agentId, tenantId = DEFAULT_TENANT_ID) {
145
+ return db
146
+ .select()
147
+ .from(agentPermissions)
148
+ .where(and(eq(agentPermissions.tenantId, tenantId), eq(agentPermissions.agentId, agentId)))
149
+ .orderBy(desc(agentPermissions.callCount));
150
+ },
151
+ /**
152
+ * Get permissions for all agents (overview).
153
+ */
154
+ async getAllPermissions(tenantId = DEFAULT_TENANT_ID) {
155
+ return db
156
+ .select()
157
+ .from(agentPermissions)
158
+ .where(eq(agentPermissions.tenantId, tenantId))
159
+ .orderBy(agentPermissions.agentId, desc(agentPermissions.callCount));
160
+ },
161
+ /**
162
+ * Find first-seen tool calls (anomalies) — permissions with callCount = 1.
163
+ */
164
+ async findAnomalies(tenantId = DEFAULT_TENANT_ID, limit = 20) {
165
+ return db
166
+ .select()
167
+ .from(agentPermissions)
168
+ .where(and(eq(agentPermissions.tenantId, tenantId), eq(agentPermissions.callCount, 1)))
169
+ .orderBy(desc(agentPermissions.firstSeen))
170
+ .limit(limit);
171
+ },
172
+ /**
173
+ * Get observation count summary per agent.
174
+ */
175
+ async summary(tenantId = DEFAULT_TENANT_ID) {
176
+ return db
177
+ .select({
178
+ agentId: toolCallObservations.agentId,
179
+ totalCalls: count(),
180
+ blockedCalls: sql `sum(case when ${toolCallObservations.blocked} = true then 1 else 0 end)`,
181
+ uniqueTools: sql `count(distinct ${toolCallObservations.toolName})`,
182
+ })
183
+ .from(toolCallObservations)
184
+ .where(eq(toolCallObservations.tenantId, tenantId))
185
+ .groupBy(toolCallObservations.agentId);
186
+ },
187
+ };
188
+ }
189
+ // ─── Helpers ────────────────────────────────────────────────────
190
+ /** Extract likely target identifiers from tool call params. */
191
+ function extractTargets(params) {
192
+ if (!params)
193
+ return [];
194
+ const targets = [];
195
+ const targetKeys = ["repo", "repository", "channel", "to", "email", "path", "file", "url", "owner", "user", "org"];
196
+ for (const key of targetKeys) {
197
+ const val = params[key];
198
+ if (typeof val === "string" && val.length > 0 && val.length < 200) {
199
+ targets.push(val);
200
+ }
201
+ }
202
+ return targets;
203
+ }
204
+ /** Merge new targets into existing list, capped at 50 entries. */
205
+ function mergeTargets(existing, incoming) {
206
+ const set = new Set(existing);
207
+ for (const t of incoming) {
208
+ set.add(t);
209
+ }
210
+ const merged = [...set];
211
+ return merged.length > 50 ? merged.slice(-50) : merged;
212
+ }
@@ -0,0 +1,25 @@
1
+ import type { Database } from "../client.js";
2
+ export declare function policyQueries(db: Database): {
3
+ findAll(tenantId?: string): Promise<any>;
4
+ findById(id: string, tenantId?: string): Promise<any>;
5
+ create(data: {
6
+ name: string;
7
+ description?: string | null;
8
+ scannerIds: string[];
9
+ action: string;
10
+ sensitivityThreshold?: number;
11
+ tenantId?: string;
12
+ }): Promise<unknown>;
13
+ update(id: string, data: Partial<{
14
+ name: string;
15
+ description: string | null;
16
+ scannerIds: string[];
17
+ action: string;
18
+ sensitivityThreshold: number;
19
+ isEnabled: boolean;
20
+ }>, tenantId?: string): Promise<unknown>;
21
+ delete(id: string, tenantId?: string): Promise<void>;
22
+ /** Get all enabled policies for detection flow */
23
+ getEnabled(tenantId?: string): Promise<any>;
24
+ };
25
+ //# sourceMappingURL=policies.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policies.d.ts","sourceRoot":"","sources":["../../src/queries/policies.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAK7C,wBAAgB,aAAa,CAAC,EAAE,EAAE,QAAQ;uBAEd,MAAM;iBAIX,MAAM,aAAY,MAAM;iBAKxB;QACjB,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC5B,UAAU,EAAE,MAAM,EAAE,CAAC;QACrB,MAAM,EAAE,MAAM,CAAC;QACf,oBAAoB,CAAC,EAAE,MAAM,CAAC;QAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB;eAQgB,MAAM,QAAQ,OAAO,CAAC;QACrC,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;QAC3B,UAAU,EAAE,MAAM,EAAE,CAAC;QACrB,MAAM,EAAE,MAAM,CAAC;QACf,oBAAoB,EAAE,MAAM,CAAC;QAC7B,SAAS,EAAE,OAAO,CAAC;KACpB,CAAC,aAAY,MAAM;eAOH,MAAM,aAAY,MAAM;IAIzC,kDAAkD;0BACvB,MAAM;EAOpC"}
@@ -0,0 +1,38 @@
1
+ import { eq, and } from "drizzle-orm";
2
+ import { policies } from "../schema/index.js";
3
+ import { insertReturning, updateReturning } from "../helpers.js";
4
+ import { DEFAULT_TENANT_ID } from "@opentrust/shared";
5
+ export function policyQueries(db) {
6
+ return {
7
+ async findAll(tenantId = DEFAULT_TENANT_ID) {
8
+ return db.select().from(policies).where(eq(policies.tenantId, tenantId)).orderBy(policies.createdAt);
9
+ },
10
+ async findById(id, tenantId = DEFAULT_TENANT_ID) {
11
+ const result = await db.select().from(policies).where(and(eq(policies.id, id), eq(policies.tenantId, tenantId))).limit(1);
12
+ return result[0] ?? null;
13
+ },
14
+ async create(data) {
15
+ return insertReturning(db, policies, {
16
+ ...data,
17
+ sensitivityThreshold: data.sensitivityThreshold ?? 0.5,
18
+ tenantId: data.tenantId ?? DEFAULT_TENANT_ID,
19
+ });
20
+ },
21
+ async update(id, data, tenantId = DEFAULT_TENANT_ID) {
22
+ return updateReturning(db, policies, and(eq(policies.id, id), eq(policies.tenantId, tenantId)), {
23
+ ...data,
24
+ updatedAt: new Date().toISOString(),
25
+ });
26
+ },
27
+ async delete(id, tenantId = DEFAULT_TENANT_ID) {
28
+ await db.delete(policies).where(and(eq(policies.id, id), eq(policies.tenantId, tenantId)));
29
+ },
30
+ /** Get all enabled policies for detection flow */
31
+ async getEnabled(tenantId = DEFAULT_TENANT_ID) {
32
+ return db
33
+ .select()
34
+ .from(policies)
35
+ .where(and(eq(policies.isEnabled, true), eq(policies.tenantId, tenantId)));
36
+ },
37
+ };
38
+ }
@@ -0,0 +1,25 @@
1
+ import type { Database } from "../client.js";
2
+ export declare function scannerQueries(db: Database): {
3
+ /** Get all scanners (defaults + overrides) */
4
+ getAll(tenantId?: string): Promise<any>;
5
+ /** Get all system default scanners */
6
+ getDefaults(tenantId?: string): Promise<any>;
7
+ /** Get enabled scanners for detection */
8
+ getEnabled(tenantId?: string): Promise<any>;
9
+ /** Upsert a scanner override */
10
+ upsert(data: {
11
+ scannerId: string;
12
+ name: string;
13
+ description: string;
14
+ isEnabled: boolean;
15
+ tenantId?: string;
16
+ }): Promise<unknown>;
17
+ /** Create a system default scanner */
18
+ createDefault(data: {
19
+ scannerId: string;
20
+ name: string;
21
+ description: string;
22
+ tenantId?: string;
23
+ }): Promise<unknown>;
24
+ };
25
+ //# sourceMappingURL=scanners.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scanners.d.ts","sourceRoot":"","sources":["../../src/queries/scanners.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAK7C,wBAAgB,cAAc,CAAC,EAAE,EAAE,QAAQ;IAEvC,8CAA8C;sBACvB,MAAM;IAQ7B,sCAAsC;2BACV,MAAM;IAQlC,yCAAyC;0BACd,MAAM;IAQjC,gCAAgC;iBACb;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,EAAE,MAAM,CAAC;QACpB,SAAS,EAAE,OAAO,CAAC;QACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB;IAsBD,sCAAsC;wBACZ;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE;EAQxG"}
@@ -0,0 +1,56 @@
1
+ import { eq, and } from "drizzle-orm";
2
+ import { scannerDefinitions } from "../schema/index.js";
3
+ import { insertReturning } from "../helpers.js";
4
+ import { DEFAULT_TENANT_ID } from "@opentrust/shared";
5
+ export function scannerQueries(db) {
6
+ return {
7
+ /** Get all scanners (defaults + overrides) */
8
+ async getAll(tenantId = DEFAULT_TENANT_ID) {
9
+ return db
10
+ .select()
11
+ .from(scannerDefinitions)
12
+ .where(eq(scannerDefinitions.tenantId, tenantId))
13
+ .orderBy(scannerDefinitions.scannerId);
14
+ },
15
+ /** Get all system default scanners */
16
+ async getDefaults(tenantId = DEFAULT_TENANT_ID) {
17
+ return db
18
+ .select()
19
+ .from(scannerDefinitions)
20
+ .where(and(eq(scannerDefinitions.isDefault, true), eq(scannerDefinitions.tenantId, tenantId)))
21
+ .orderBy(scannerDefinitions.scannerId);
22
+ },
23
+ /** Get enabled scanners for detection */
24
+ async getEnabled(tenantId = DEFAULT_TENANT_ID) {
25
+ return db
26
+ .select()
27
+ .from(scannerDefinitions)
28
+ .where(and(eq(scannerDefinitions.isEnabled, true), eq(scannerDefinitions.tenantId, tenantId)))
29
+ .orderBy(scannerDefinitions.scannerId);
30
+ },
31
+ /** Upsert a scanner override */
32
+ async upsert(data) {
33
+ const tid = data.tenantId ?? DEFAULT_TENANT_ID;
34
+ // Delete existing non-default with same scannerId for this tenant
35
+ await db
36
+ .delete(scannerDefinitions)
37
+ .where(and(eq(scannerDefinitions.scannerId, data.scannerId), eq(scannerDefinitions.isDefault, false), eq(scannerDefinitions.tenantId, tid)));
38
+ return insertReturning(db, scannerDefinitions, {
39
+ scannerId: data.scannerId,
40
+ name: data.name,
41
+ description: data.description,
42
+ isEnabled: data.isEnabled,
43
+ isDefault: false,
44
+ tenantId: tid,
45
+ });
46
+ },
47
+ /** Create a system default scanner */
48
+ async createDefault(data) {
49
+ return insertReturning(db, scannerDefinitions, {
50
+ ...data,
51
+ isDefault: true,
52
+ tenantId: data.tenantId ?? DEFAULT_TENANT_ID,
53
+ });
54
+ },
55
+ };
56
+ }
@@ -0,0 +1,8 @@
1
+ import type { Database } from "../client.js";
2
+ export declare function settingsQueries(db: Database): {
3
+ get(key: string): Promise<string | null>;
4
+ set(key: string, value: string): Promise<void>;
5
+ getAll(): Promise<Record<string, string>>;
6
+ delete(key: string): Promise<void>;
7
+ };
8
+ //# sourceMappingURL=settings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../../src/queries/settings.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAG7C,wBAAgB,eAAe,CAAC,EAAE,EAAE,QAAQ;aAEzB,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;aAK/B,MAAM,SAAS,MAAM;cASpB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;gBAS7B,MAAM;EAI3B"}
@@ -0,0 +1,30 @@
1
+ import { eq } from "drizzle-orm";
2
+ import { settings } from "../schema/index.js";
3
+ export function settingsQueries(db) {
4
+ return {
5
+ async get(key) {
6
+ const result = await db.select().from(settings).where(eq(settings.key, key)).limit(1);
7
+ return result[0]?.value ?? null;
8
+ },
9
+ async set(key, value) {
10
+ const existing = await this.get(key);
11
+ if (existing !== null) {
12
+ await db.update(settings).set({ value, updatedAt: new Date().toISOString() }).where(eq(settings.key, key));
13
+ }
14
+ else {
15
+ await db.insert(settings).values({ key, value });
16
+ }
17
+ },
18
+ async getAll() {
19
+ const rows = await db.select().from(settings);
20
+ const result = {};
21
+ for (const row of rows) {
22
+ result[row.key] = row.value;
23
+ }
24
+ return result;
25
+ },
26
+ async delete(key) {
27
+ await db.delete(settings).where(eq(settings.key, key));
28
+ },
29
+ };
30
+ }
@@ -0,0 +1,18 @@
1
+ import type { Database } from "../client.js";
2
+ export declare function usageQueries(db: Database): {
3
+ log(data: {
4
+ agentId?: string | null;
5
+ endpoint: string;
6
+ statusCode: number;
7
+ responseSafe: boolean | null;
8
+ categories?: string[];
9
+ latencyMs: number;
10
+ requestId: string;
11
+ tenantId?: string;
12
+ }): Promise<void>;
13
+ countInPeriod(start: Date | string, end: Date | string, tenantId?: string): Promise<any>;
14
+ summary(start: Date | string, end: Date | string, tenantId?: string): Promise<any>;
15
+ daily(start: Date | string, end: Date | string, tenantId?: string): Promise<any>;
16
+ countRecent(minutes?: number, tenantId?: string): Promise<any>;
17
+ };
18
+ //# sourceMappingURL=usage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usage.d.ts","sourceRoot":"","sources":["../../src/queries/usage.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAI7C,wBAAgB,YAAY,CAAC,EAAE,EAAE,QAAQ;cAErB;QACd,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACxB,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,EAAE,MAAM,CAAC;QACnB,YAAY,EAAE,OAAO,GAAG,IAAI,CAAC;QAC7B,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;QACtB,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB;yBAQ0B,IAAI,GAAG,MAAM,OAAO,IAAI,GAAG,MAAM,aAAY,MAAM;mBAQzD,IAAI,GAAG,MAAM,OAAO,IAAI,GAAG,MAAM,aAAY,MAAM;iBAYrD,IAAI,GAAG,MAAM,OAAO,IAAI,GAAG,MAAM,aAAY,MAAM;0BAe3C,MAAM,aAAgB,MAAM;EAS1D"}
@@ -0,0 +1,54 @@
1
+ import { eq, gte, lte, sql, count, and } from "drizzle-orm";
2
+ import { usageLogs } from "../schema/index.js";
3
+ import { DEFAULT_TENANT_ID } from "@opentrust/shared";
4
+ export function usageQueries(db) {
5
+ return {
6
+ async log(data) {
7
+ await db.insert(usageLogs).values({
8
+ ...data,
9
+ categories: data.categories ?? [],
10
+ tenantId: data.tenantId ?? DEFAULT_TENANT_ID,
11
+ });
12
+ },
13
+ async countInPeriod(start, end, tenantId = DEFAULT_TENANT_ID) {
14
+ const result = await db
15
+ .select({ count: count() })
16
+ .from(usageLogs)
17
+ .where(and(gte(usageLogs.createdAt, start), lte(usageLogs.createdAt, end), eq(usageLogs.tenantId, tenantId)));
18
+ return result[0]?.count ?? 0;
19
+ },
20
+ async summary(start, end, tenantId = DEFAULT_TENANT_ID) {
21
+ const result = await db
22
+ .select({
23
+ totalCalls: count(),
24
+ safeCount: sql `sum(case when ${usageLogs.responseSafe} = true then 1 else 0 end)`,
25
+ unsafeCount: sql `sum(case when ${usageLogs.responseSafe} = false then 1 else 0 end)`,
26
+ })
27
+ .from(usageLogs)
28
+ .where(and(gte(usageLogs.createdAt, start), lte(usageLogs.createdAt, end), eq(usageLogs.tenantId, tenantId)));
29
+ return result[0] ?? { totalCalls: 0, safeCount: 0, unsafeCount: 0 };
30
+ },
31
+ async daily(start, end, tenantId = DEFAULT_TENANT_ID) {
32
+ const result = await db
33
+ .select({
34
+ date: sql `date(${usageLogs.createdAt})`,
35
+ count: count(),
36
+ safeCount: sql `sum(case when ${usageLogs.responseSafe} = true then 1 else 0 end)`,
37
+ unsafeCount: sql `sum(case when ${usageLogs.responseSafe} = false then 1 else 0 end)`,
38
+ })
39
+ .from(usageLogs)
40
+ .where(and(gte(usageLogs.createdAt, start), lte(usageLogs.createdAt, end), eq(usageLogs.tenantId, tenantId)))
41
+ .groupBy(sql `date(${usageLogs.createdAt})`)
42
+ .orderBy(sql `date(${usageLogs.createdAt})`);
43
+ return result;
44
+ },
45
+ async countRecent(minutes = 1, tenantId = DEFAULT_TENANT_ID) {
46
+ const since = new Date(Date.now() - minutes * 60_000).toISOString();
47
+ const result = await db
48
+ .select({ count: count() })
49
+ .from(usageLogs)
50
+ .where(and(gte(usageLogs.createdAt, since), eq(usageLogs.tenantId, tenantId)));
51
+ return result[0]?.count ?? 0;
52
+ },
53
+ };
54
+ }