@intx/db 0.1.2 → 0.3.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 (241) hide show
  1. package/LICENSE +176 -0
  2. package/README.md +23 -4
  3. package/dist/approval-store.d.ts +52 -0
  4. package/dist/approval-store.js +79 -0
  5. package/dist/asset-resolution.d.ts +52 -0
  6. package/dist/asset-resolution.js +66 -0
  7. package/dist/catalog-resolution.d.ts +76 -0
  8. package/dist/catalog-resolution.js +151 -0
  9. package/dist/client.d.ts +19 -0
  10. package/dist/client.js +18 -0
  11. package/dist/config.d.ts +11 -0
  12. package/dist/config.js +16 -0
  13. package/dist/connection.d.ts +3 -0
  14. package/dist/connection.js +26 -0
  15. package/dist/credential-resolution.d.ts +222 -0
  16. package/dist/credential-resolution.js +255 -0
  17. package/dist/grant-store.d.ts +3 -0
  18. package/dist/grant-store.js +59 -0
  19. package/dist/index.d.ts +22 -0
  20. package/dist/index.js +20 -0
  21. package/dist/migrate.d.ts +36 -0
  22. package/dist/migrate.js +135 -0
  23. package/dist/model-source-resolution.d.ts +115 -0
  24. package/dist/model-source-resolution.js +291 -0
  25. package/dist/parse-row.d.ts +373 -0
  26. package/dist/parse-row.js +232 -0
  27. package/dist/pg-error.d.ts +3 -0
  28. package/dist/pg-error.js +19 -0
  29. package/dist/pricing.d.ts +15 -0
  30. package/dist/pricing.js +24 -0
  31. package/dist/rekey-credential-secrets.d.ts +13 -0
  32. package/dist/rekey-credential-secrets.js +61 -0
  33. package/dist/schema/approvals.d.ts +245 -0
  34. package/dist/schema/approvals.js +52 -0
  35. package/dist/schema/assets.d.ts +143 -0
  36. package/{src/schema/assets.ts → dist/schema/assets.js} +7 -16
  37. package/dist/schema/auth.d.ts +606 -0
  38. package/dist/schema/auth.js +47 -0
  39. package/dist/schema/catalog.d.ts +811 -0
  40. package/dist/schema/catalog.js +108 -0
  41. package/dist/schema/column-types.d.ts +26 -0
  42. package/dist/schema/column-types.js +15 -0
  43. package/dist/schema/credentials.d.ts +305 -0
  44. package/{src/schema/credentials.ts → dist/schema/credentials.js} +16 -22
  45. package/dist/schema/git-tokens.d.ts +256 -0
  46. package/{src/schema/git-tokens.ts → dist/schema/git-tokens.js} +16 -39
  47. package/dist/schema/grants.d.ts +211 -0
  48. package/dist/schema/grants.js +30 -0
  49. package/dist/schema/index.d.ts +25 -0
  50. package/dist/schema/index.js +25 -0
  51. package/dist/schema/messages.d.ts +414 -0
  52. package/dist/schema/messages.js +82 -0
  53. package/dist/schema/oauth-clients.d.ts +246 -0
  54. package/{src/schema/oauth-clients.ts → dist/schema/oauth-clients.js} +8 -14
  55. package/dist/schema/offerings.d.ts +160 -0
  56. package/dist/schema/offerings.js +24 -0
  57. package/dist/schema/principals.d.ts +128 -0
  58. package/dist/schema/principals.js +23 -0
  59. package/dist/schema/providers.d.ts +237 -0
  60. package/dist/schema/providers.js +22 -0
  61. package/dist/schema/roles.d.ts +242 -0
  62. package/dist/schema/roles.js +37 -0
  63. package/dist/schema/session-assets.d.ts +92 -0
  64. package/dist/schema/session-assets.js +45 -0
  65. package/dist/schema/sessions.d.ts +143 -0
  66. package/dist/schema/sessions.js +29 -0
  67. package/dist/schema/sidecar-allocation.d.ts +406 -0
  68. package/dist/schema/sidecar-allocation.js +57 -0
  69. package/dist/schema/sidecar.d.ts +145 -0
  70. package/dist/schema/sidecar.js +28 -0
  71. package/dist/schema/signal-correlations.d.ts +177 -0
  72. package/dist/schema/signal-correlations.js +31 -0
  73. package/dist/schema/tenants.d.ts +235 -0
  74. package/{src/schema/tenants.ts → dist/schema/tenants.js} +10 -27
  75. package/dist/schema/wallets.d.ts +354 -0
  76. package/dist/schema/wallets.js +43 -0
  77. package/dist/schema/workflow-definitions.d.ts +371 -0
  78. package/dist/schema/workflow-definitions.js +97 -0
  79. package/dist/schema/workflow-run-dispatch.d.ts +328 -0
  80. package/dist/schema/workflow-run-dispatch.js +56 -0
  81. package/dist/schema/workflow-run-execution.d.ts +126 -0
  82. package/dist/schema/workflow-run-execution.js +48 -0
  83. package/dist/schema/workflow-run-launch-spec.d.ts +200 -0
  84. package/dist/schema/workflow-run-launch-spec.js +26 -0
  85. package/dist/schema/workflow-run.d.ts +230 -0
  86. package/dist/schema/workflow-run.js +102 -0
  87. package/dist/sidecar-allocation-store.d.ts +171 -0
  88. package/dist/sidecar-allocation-store.js +512 -0
  89. package/dist/signal-correlation-store.d.ts +36 -0
  90. package/dist/signal-correlation-store.js +63 -0
  91. package/dist/tenant-hierarchy.d.ts +23 -0
  92. package/dist/tenant-hierarchy.js +60 -0
  93. package/dist/workflow-definition-store.d.ts +55 -0
  94. package/dist/workflow-definition-store.js +93 -0
  95. package/dist/workflow-run-dispatch-store.d.ts +80 -0
  96. package/dist/workflow-run-dispatch-store.js +298 -0
  97. package/dist/workflow-run-launch-spec-store.d.ts +13 -0
  98. package/dist/workflow-run-launch-spec-store.js +30 -0
  99. package/dist/workflow-run-store.d.ts +54 -0
  100. package/dist/workflow-run-store.js +85 -0
  101. package/migrations/0029_loose_warlock.sql +1 -0
  102. package/migrations/0030_session_asset_audit_split.sql +5 -0
  103. package/migrations/0031_gigantic_nicolaos.sql +68 -0
  104. package/migrations/0032_lethal_misty_knight.sql +1 -0
  105. package/migrations/0033_old_payback.sql +1 -0
  106. package/migrations/0034_sleepy_songbird.sql +11 -0
  107. package/migrations/0035_violet_giant_man.sql +3 -0
  108. package/migrations/0036_sharp_the_executioner.sql +2 -0
  109. package/migrations/0037_credential_use_backfill.sql +39 -0
  110. package/migrations/0038_chilly_blacklash.sql +37 -0
  111. package/migrations/0039_quick_carnage.sql +2 -0
  112. package/migrations/0040_reshape_approval_origin.sql +24 -0
  113. package/migrations/0041_make_approval_timeout_at_nullable.sql +1 -0
  114. package/migrations/0042_youthful_mantis.sql +2 -0
  115. package/migrations/0043_signal_correlation_deployment_fk.sql +1 -0
  116. package/migrations/0044_model_offering_quirks.sql +1 -0
  117. package/migrations/0045_create_workflow_run.sql +13 -0
  118. package/migrations/0046_approval_signal_correlation_run_fk.sql +2 -0
  119. package/migrations/0047_create_workflow_definition.sql +30 -0
  120. package/migrations/0048_workflow_definition_origin_agent_id.sql +2 -0
  121. package/migrations/0049_curly_omega_flight.sql +2 -0
  122. package/migrations/0050_late_crystal.sql +10 -0
  123. package/migrations/0051_funny_madelyne_pryor.sql +1 -0
  124. package/migrations/0052_drop_inference_turn_instance_fk.sql +1 -0
  125. package/migrations/0053_session_mail_session_id_index.sql +1 -0
  126. package/migrations/0054_rekey_instance_grants_to_workflow_run.sql +10 -0
  127. package/migrations/0055_backfill_anchor_workflow_runs.sql +69 -0
  128. package/migrations/0056_repoint_deployment_fks_to_anchor_run.sql +32 -0
  129. package/migrations/0057_drop_workflow_deployment_projection.sql +22 -0
  130. package/migrations/0058_repoint_offering_fk_to_definition.sql +41 -0
  131. package/migrations/0059_repoint_transaction_fk_to_run.sql +10 -0
  132. package/migrations/0060_drop_session_mail_instance_fk.sql +6 -0
  133. package/migrations/0061_drop_session_asset_instance_fk.sql +7 -0
  134. package/migrations/0062_drop_agent_asset_table.sql +11 -0
  135. package/migrations/0063_repoint_agent_role_fk_to_definition.sql +39 -0
  136. package/migrations/0064_repoint_agent_session_fk_to_definition.sql +47 -0
  137. package/migrations/0065_add_workflow_definition_model_requirements.sql +37 -0
  138. package/migrations/0066_rekey_agent_definition_principals_to_workflow.sql +27 -0
  139. package/migrations/0067_add_workflow_definition_kind.sql +10 -0
  140. package/migrations/0068_drop_agent_tables_and_origin_agent_id.sql +31 -0
  141. package/migrations/0069_drop_workflow_definition_kind.sql +10 -0
  142. package/migrations/0070_flashy_proteus.sql +13 -0
  143. package/migrations/0072_add_workflow_definition_credential_bindings.sql +1 -0
  144. package/migrations/0073_grant_target_exactly_one_check.sql +13 -0
  145. package/migrations/0074_add_provider_api_base_url.sql +1 -0
  146. package/migrations/0075_workflow_run_launch_spec.sql +17 -0
  147. package/migrations/0076_sidecar_allocation.sql +40 -0
  148. package/migrations/0077_workflow_run_dispatch.sql +29 -0
  149. package/migrations/0078_workflow_run_dispatch_kind.sql +2 -0
  150. package/migrations/0079_rename_workflow_run_deployment_id_to_anchor_run_id.sql +4 -0
  151. package/migrations/0080_rename_correlation_approval_deployment_id_to_anchor_run_id.sql +9 -0
  152. package/migrations/0081_workflow_definition_content_hash_and_approved_wire_hash.sql +4 -0
  153. package/migrations/0082_blue_black_queen.sql +1 -0
  154. package/migrations/0083_replace_launch_spec_snapshot_with_frozen_bundle.sql +3 -0
  155. package/migrations/0084_delete_orphaned_credential_grants.sql +22 -0
  156. package/migrations/meta/0029_snapshot.json +2664 -0
  157. package/migrations/meta/0030_snapshot.json +2670 -0
  158. package/migrations/meta/0031_snapshot.json +3100 -0
  159. package/migrations/meta/0032_snapshot.json +3106 -0
  160. package/migrations/meta/0033_snapshot.json +3112 -0
  161. package/migrations/meta/0034_snapshot.json +3198 -0
  162. package/migrations/meta/0035_snapshot.json +3225 -0
  163. package/migrations/meta/0036_snapshot.json +3237 -0
  164. package/migrations/meta/0037_snapshot.json +3237 -0
  165. package/migrations/meta/0038_snapshot.json +3470 -0
  166. package/migrations/meta/0039_snapshot.json +3470 -0
  167. package/migrations/meta/0040_snapshot.json +3489 -0
  168. package/migrations/meta/0041_snapshot.json +3489 -0
  169. package/migrations/meta/0042_snapshot.json +3489 -0
  170. package/migrations/meta/0043_snapshot.json +3498 -0
  171. package/migrations/meta/0044_snapshot.json +3504 -0
  172. package/migrations/meta/0045_snapshot.json +3589 -0
  173. package/migrations/meta/0046_snapshot.json +3607 -0
  174. package/migrations/meta/0047_snapshot.json +3832 -0
  175. package/migrations/meta/0048_snapshot.json +3853 -0
  176. package/migrations/meta/0049_snapshot.json +3854 -0
  177. package/migrations/meta/0050_snapshot.json +3924 -0
  178. package/migrations/meta/0051_snapshot.json +3940 -0
  179. package/migrations/meta/0052_snapshot.json +3931 -0
  180. package/migrations/meta/0053_snapshot.json +3952 -0
  181. package/migrations/meta/0054_snapshot.json +3952 -0
  182. package/migrations/meta/0055_snapshot.json +3952 -0
  183. package/migrations/meta/0056_snapshot.json +3952 -0
  184. package/migrations/meta/0057_snapshot.json +3839 -0
  185. package/migrations/meta/0058_snapshot.json +3839 -0
  186. package/migrations/meta/0059_snapshot.json +3839 -0
  187. package/migrations/meta/0060_snapshot.json +3830 -0
  188. package/migrations/meta/0061_snapshot.json +3821 -0
  189. package/migrations/meta/0062_snapshot.json +3723 -0
  190. package/migrations/meta/0063_snapshot.json +3723 -0
  191. package/migrations/meta/0064_snapshot.json +3723 -0
  192. package/migrations/meta/0065_snapshot.json +3729 -0
  193. package/migrations/meta/0066_snapshot.json +3729 -0
  194. package/migrations/meta/0067_snapshot.json +3736 -0
  195. package/migrations/meta/0068_snapshot.json +3347 -0
  196. package/migrations/meta/0069_snapshot.json +3340 -0
  197. package/migrations/meta/0070_snapshot.json +3444 -0
  198. package/migrations/meta/0072_snapshot.json +3450 -0
  199. package/migrations/meta/0073_snapshot.json +3455 -0
  200. package/migrations/meta/0074_snapshot.json +3461 -0
  201. package/migrations/meta/0075_snapshot.json +3567 -0
  202. package/migrations/meta/0076_snapshot.json +3851 -0
  203. package/migrations/meta/0077_snapshot.json +4067 -0
  204. package/migrations/meta/0078_snapshot.json +4078 -0
  205. package/migrations/meta/0079_snapshot.json +4078 -0
  206. package/migrations/meta/0080_snapshot.json +4078 -0
  207. package/migrations/meta/0081_snapshot.json +4096 -0
  208. package/migrations/meta/0082_snapshot.json +4102 -0
  209. package/migrations/meta/0083_snapshot.json +4096 -0
  210. package/migrations/meta/0084_snapshot.json +4096 -0
  211. package/migrations/meta/_journal.json +385 -0
  212. package/package.json +25 -11
  213. package/drizzle.config.ts +0 -23
  214. package/src/client.ts +0 -24
  215. package/src/config.ts +0 -19
  216. package/src/connection.ts +0 -27
  217. package/src/credential-resolution.ts +0 -378
  218. package/src/grant-store.ts +0 -51
  219. package/src/index.ts +0 -32
  220. package/src/migrate.test.ts +0 -35
  221. package/src/migrate.ts +0 -168
  222. package/src/parse-row.test.ts +0 -113
  223. package/src/parse-row.ts +0 -185
  224. package/src/schema/agent-assets.ts +0 -21
  225. package/src/schema/agents.ts +0 -49
  226. package/src/schema/auth.ts +0 -51
  227. package/src/schema/grants.ts +0 -26
  228. package/src/schema/index.ts +0 -19
  229. package/src/schema/instances.ts +0 -36
  230. package/src/schema/messages.ts +0 -108
  231. package/src/schema/offerings.ts +0 -20
  232. package/src/schema/principals.ts +0 -28
  233. package/src/schema/providers.ts +0 -23
  234. package/src/schema/roles.ts +0 -51
  235. package/src/schema/session-assets.ts +0 -49
  236. package/src/schema/sessions.ts +0 -26
  237. package/src/schema/sidecar.ts +0 -14
  238. package/src/schema/wallets.ts +0 -44
  239. package/src/tenant-hierarchy.ts +0 -34
  240. package/tsconfig.json +0 -4
  241. package/tsconfig.tsbuildinfo +0 -1
@@ -1,378 +0,0 @@
1
- import { eq, and, isNull } from "drizzle-orm";
2
- import { type } from "arktype";
3
-
4
- import { getLogger } from "@intx/log";
5
- import { CredentialRequirement as CredentialRequirementType } from "@intx/types";
6
- import type { InferenceSource } from "@intx/types/runtime";
7
-
8
- import type { DB } from "./client";
9
- import { agent } from "./schema/agents";
10
- import { agentSession } from "./schema/sessions";
11
- import { credential } from "./schema/credentials";
12
- import { oauthClient } from "./schema/oauth-clients";
13
- import { provider } from "./schema/providers";
14
- import { getAncestorChain } from "./tenant-hierarchy";
15
-
16
- const log = getLogger(["db", "credentials"]);
17
-
18
- const CredentialRequirements = CredentialRequirementType.array();
19
-
20
- export const ProviderMetadata = type({ baseURL: "string" });
21
-
22
- const AgentModelConfig = type({ defaultModel: "string" });
23
-
24
- /**
25
- * Resolves a provider by name, walking up the tenant hierarchy.
26
- * Returns the first match (child shadows parent).
27
- */
28
- export async function resolveProviderByName(
29
- db: DB["db"],
30
- tenantId: string,
31
- name: string,
32
- ) {
33
- const chain = await getAncestorChain(db, tenantId);
34
-
35
- for (const tid of chain) {
36
- const row = await db.query.provider.findFirst({
37
- where: and(eq(provider.tenantId, tid), eq(provider.name, name)),
38
- });
39
- if (row) return row;
40
- }
41
-
42
- return null;
43
- }
44
-
45
- /**
46
- * Resolves an OAuth client for a provider, walking up the tenant hierarchy.
47
- * Returns the first match (child shadows parent).
48
- */
49
- export async function resolveOAuthClient(
50
- db: DB["db"],
51
- tenantId: string,
52
- providerId: string,
53
- ) {
54
- const chain = await getAncestorChain(db, tenantId);
55
-
56
- for (const tid of chain) {
57
- const row = await db.query.oauthClient.findFirst({
58
- where: and(
59
- eq(oauthClient.tenantId, tid),
60
- eq(oauthClient.providerId, providerId),
61
- ),
62
- });
63
- if (row) return row;
64
- }
65
-
66
- return null;
67
- }
68
-
69
- /**
70
- * Resolves a credential by name, walking up the tenant hierarchy.
71
- * Returns the first match (child shadows parent).
72
- */
73
- export async function resolveCredentialByName(
74
- db: DB["db"],
75
- tenantId: string,
76
- name: string,
77
- ) {
78
- const chain = await getAncestorChain(db, tenantId);
79
-
80
- for (const tid of chain) {
81
- const row = await db.query.credential.findFirst({
82
- where: and(eq(credential.tenantId, tid), eq(credential.name, name)),
83
- });
84
- if (row) return row;
85
- }
86
-
87
- return null;
88
- }
89
-
90
- /**
91
- * Resolves a credential by ID, validating that it belongs to the
92
- * given tenant or one of its ancestors.
93
- */
94
- export async function resolveCredentialById(
95
- db: DB["db"],
96
- tenantId: string,
97
- credentialId: string,
98
- ) {
99
- const row = await db.query.credential.findFirst({
100
- where: eq(credential.id, credentialId),
101
- });
102
-
103
- if (!row) return null;
104
-
105
- const chain = await getAncestorChain(db, tenantId);
106
- if (!chain.includes(row.tenantId)) return null;
107
-
108
- return row;
109
- }
110
-
111
- type CredentialRequirement = {
112
- providerName: string;
113
- scopes?: string[];
114
- source: "tenant" | "creator" | "invoker";
115
- name?: string;
116
- };
117
-
118
- /**
119
- * Resolves a credential matching an agent definition requirement.
120
- * Used at agent launch time by the control plane.
121
- */
122
- export async function resolveCredentialRequirement(
123
- db: DB["db"],
124
- tenantId: string,
125
- requirement: CredentialRequirement,
126
- creatorPrincipalId: string | null,
127
- invokerPrincipalId: string | null,
128
- ) {
129
- const resolvedProvider = await resolveProviderByName(
130
- db,
131
- tenantId,
132
- requirement.providerName,
133
- );
134
- if (!resolvedProvider) return null;
135
-
136
- const chain = await getAncestorChain(db, tenantId);
137
-
138
- const principalFilter =
139
- requirement.source === "tenant"
140
- ? null
141
- : requirement.source === "creator"
142
- ? creatorPrincipalId
143
- : invokerPrincipalId;
144
-
145
- for (const tid of chain) {
146
- const conditions = [
147
- eq(credential.tenantId, tid),
148
- eq(credential.providerId, resolvedProvider.id),
149
- eq(credential.status, "active"),
150
- ];
151
-
152
- if (principalFilter === null) {
153
- conditions.push(isNull(credential.principalId));
154
- } else if (principalFilter) {
155
- conditions.push(eq(credential.principalId, principalFilter));
156
- }
157
-
158
- if (requirement.name) {
159
- conditions.push(eq(credential.name, requirement.name));
160
- }
161
-
162
- const rows = await db.query.credential.findMany({
163
- where: and(...conditions),
164
- });
165
-
166
- const matching = rows.filter((row) => {
167
- if (!requirement.scopes || requirement.scopes.length === 0) return true;
168
- const rowScopes = row.scopes ?? [];
169
- return requirement.scopes.every((s) => rowScopes.includes(s));
170
- });
171
-
172
- const [sole] = matching;
173
- if (matching.length === 1 && sole) return sole;
174
- if (matching.length > 1) {
175
- throw new Error(
176
- `Ambiguous credential match: ${matching.length} credentials match ` +
177
- `provider=${requirement.providerName} source=${requirement.source} ` +
178
- `in tenant ${tid}. Specify a name to disambiguate.`,
179
- );
180
- }
181
- }
182
-
183
- return null;
184
- }
185
-
186
- /**
187
- * Outcome of resolving one credential requirement to an
188
- * `InferenceSource`. Callers map `failed` variants to their own
189
- * error-handling convention (the API route surfaces 409s; the
190
- * background credential pushers log and continue).
191
- */
192
- export type CredentialOutcome =
193
- | { ok: true; source: InferenceSource }
194
- | {
195
- ok: false;
196
- reason: "credential_error";
197
- requirement: CredentialRequirement;
198
- message: string;
199
- }
200
- | {
201
- ok: false;
202
- reason: "credential_missing";
203
- requirement: CredentialRequirement;
204
- }
205
- | {
206
- ok: false;
207
- reason: "skipped";
208
- requirement: CredentialRequirement;
209
- }
210
- | {
211
- ok: false;
212
- reason: "provider_missing";
213
- credentialId: string;
214
- }
215
- | {
216
- ok: false;
217
- reason: "provider_misconfigured";
218
- providerName: string;
219
- summary: string;
220
- };
221
-
222
- /**
223
- * Resolve one credential requirement to an `InferenceSource`, stamping
224
- * `defaultModel` as the model identity. Skips silently (`reason:
225
- * "skipped"`) when the requirement targets a principal that does not
226
- * exist (creator without a creator id, invoker without a session
227
- * principal); all other failure modes are surfaced with structured
228
- * reasons.
229
- */
230
- export async function resolveOneCredential(
231
- db: DB["db"],
232
- tenantId: string,
233
- req: CredentialRequirement,
234
- creatorPrincipalId: string | null,
235
- invokerPrincipalId: string | null,
236
- defaultModel: string,
237
- ): Promise<CredentialOutcome> {
238
- // Reject requirements whose targeted principal does not exist. Without
239
- // these guards a null creator or invoker would fall through to
240
- // `resolveCredentialRequirement`, where the principal filter becomes
241
- // `isNull(credential.principalId)` — the tenant-credential lookup — and
242
- // a tenant credential would be returned as if it satisfied the
243
- // creator- or invoker-source requirement.
244
- if (req.source === "creator" && !creatorPrincipalId) {
245
- return { ok: false, reason: "skipped", requirement: req };
246
- }
247
- if (req.source === "invoker" && !invokerPrincipalId) {
248
- return { ok: false, reason: "skipped", requirement: req };
249
- }
250
-
251
- let resolved;
252
- try {
253
- resolved = await resolveCredentialRequirement(
254
- db,
255
- tenantId,
256
- req,
257
- creatorPrincipalId,
258
- invokerPrincipalId,
259
- );
260
- } catch (err: unknown) {
261
- return {
262
- ok: false,
263
- reason: "credential_error",
264
- requirement: req,
265
- message: err instanceof Error ? err.message : String(err),
266
- };
267
- }
268
- if (!resolved) {
269
- return { ok: false, reason: "credential_missing", requirement: req };
270
- }
271
-
272
- const providerRow = await db.query.provider.findFirst({
273
- where: eq(provider.id, resolved.providerId),
274
- });
275
- if (!providerRow) {
276
- return { ok: false, reason: "provider_missing", credentialId: resolved.id };
277
- }
278
-
279
- const metadata = ProviderMetadata(providerRow.metadata ?? {});
280
- if (metadata instanceof type.errors) {
281
- return {
282
- ok: false,
283
- reason: "provider_misconfigured",
284
- providerName: providerRow.name,
285
- summary: metadata.summary,
286
- };
287
- }
288
-
289
- return {
290
- ok: true,
291
- source: {
292
- id: `${providerRow.plugin}:${defaultModel}`,
293
- provider: providerRow.plugin,
294
- baseURL: metadata.baseURL,
295
- apiKey: resolved.secret,
296
- model: defaultModel,
297
- },
298
- };
299
- }
300
-
301
- /**
302
- * Resolve the full `InferenceSource[]` for a single running instance by
303
- * re-resolving each credential requirement from the agent definition.
304
- *
305
- * Failure modes (invalid agent definition, malformed `modelConfig`, any
306
- * per-credential resolution failure) collapse to an empty array with
307
- * structured `db.credentials` log lines. Callers that need to surface
308
- * per-credential outcomes to operators should call `resolveOneCredential`
309
- * directly — see `packages/hub-api/src/routes/instances.ts` for the
310
- * 409-mapping pattern.
311
- */
312
- export async function resolveInstanceSources(
313
- db: DB["db"],
314
- tenantId: string,
315
- instance: { agentId: string; sessionId: string | null },
316
- ): Promise<InferenceSource[]> {
317
- const agentRow = await db.query.agent.findFirst({
318
- where: eq(agent.id, instance.agentId),
319
- });
320
- if (!agentRow) return [];
321
-
322
- const requirements = CredentialRequirements(
323
- agentRow.credentialRequirements ?? [],
324
- );
325
- if (requirements instanceof type.errors) {
326
- log.warn`Invalid credential requirements for agent ${agentRow.id}: ${requirements.summary}`;
327
- return [];
328
- }
329
-
330
- const modelConfig = AgentModelConfig(agentRow.modelConfig ?? {});
331
- if (modelConfig instanceof type.errors) {
332
- log.warn`Invalid modelConfig for agent ${agentRow.id}: ${modelConfig.summary}`;
333
- return [];
334
- }
335
- const defaultModel = modelConfig.defaultModel;
336
-
337
- let invokerPrincipalId: string | null = null;
338
- if (instance.sessionId) {
339
- const session = await db.query.agentSession.findFirst({
340
- where: eq(agentSession.id, instance.sessionId),
341
- });
342
- if (session) {
343
- invokerPrincipalId = session.principalId;
344
- }
345
- }
346
-
347
- const sources: InferenceSource[] = [];
348
- for (const req of requirements) {
349
- const outcome = await resolveOneCredential(
350
- db,
351
- tenantId,
352
- req,
353
- agentRow.creatorPrincipalId,
354
- invokerPrincipalId,
355
- defaultModel,
356
- );
357
- if (outcome.ok) {
358
- sources.push(outcome.source);
359
- continue;
360
- }
361
- switch (outcome.reason) {
362
- case "skipped":
363
- break;
364
- case "credential_error":
365
- log.warn`Failed to resolve credential for provider ${outcome.requirement.providerName}: ${outcome.message}`;
366
- break;
367
- case "credential_missing":
368
- break;
369
- case "provider_missing":
370
- break;
371
- case "provider_misconfigured":
372
- log.warn`Invalid provider metadata for provider ${outcome.providerName}: ${outcome.summary}`;
373
- break;
374
- }
375
- }
376
-
377
- return sources;
378
- }
@@ -1,51 +0,0 @@
1
- import { eq, and, or, isNull, gt, inArray } from "drizzle-orm";
2
-
3
- import type { GrantRule, GrantStore } from "@intx/types/authz";
4
-
5
- import type { DB } from "./client";
6
- import { grant } from "./schema/grants";
7
- import { principalRole } from "./schema/roles";
8
- import { parseGrantRow } from "./parse-row";
9
-
10
- function toGrantRule(row: typeof grant.$inferSelect): GrantRule {
11
- const parsed = parseGrantRow(row);
12
- return {
13
- id: parsed.id,
14
- resource: parsed.resource,
15
- action: parsed.action,
16
- effect: parsed.effect,
17
- origin: parsed.origin,
18
- conditions: parsed.conditions,
19
- expiresAt: parsed.expiresAt,
20
- roleId: parsed.roleId,
21
- principalId: parsed.principalId,
22
- };
23
- }
24
-
25
- export function createGrantStore(db: DB["db"]): GrantStore {
26
- return {
27
- async collectGrants(principalId, tenantId) {
28
- const roleAssignments = await db.query.principalRole.findMany({
29
- where: eq(principalRole.principalId, principalId),
30
- });
31
- const roleIds = roleAssignments.map((a) => a.roleId);
32
-
33
- const now = new Date();
34
-
35
- const ownership = [eq(grant.principalId, principalId)];
36
- if (roleIds.length > 0) {
37
- ownership.push(inArray(grant.roleId, roleIds));
38
- }
39
-
40
- const rows = await db.query.grant.findMany({
41
- where: and(
42
- eq(grant.tenantId, tenantId),
43
- or(...ownership),
44
- or(isNull(grant.expiresAt), gt(grant.expiresAt, now)),
45
- ),
46
- });
47
-
48
- return rows.map(toGrantRule);
49
- },
50
- };
51
- }
package/src/index.ts DELETED
@@ -1,32 +0,0 @@
1
- export { createDB, type DB } from "./client";
2
- export type { DBConfig } from "./config";
3
- export { runMigrations, dropSchema } from "./migrate";
4
- export { createGrantStore } from "./grant-store";
5
- export { getAncestorChain } from "./tenant-hierarchy";
6
- export {
7
- resolveProviderByName,
8
- resolveOAuthClient,
9
- resolveCredentialByName,
10
- resolveCredentialById,
11
- resolveCredentialRequirement,
12
- resolveOneCredential,
13
- resolveInstanceSources,
14
- type CredentialOutcome,
15
- ProviderMetadata,
16
- } from "./credential-resolution";
17
- export {
18
- parseAgentRow,
19
- parseAgentVersionRow,
20
- parseGrantRow,
21
- parseOfferingRow,
22
- parseCredentialRow,
23
- parseProviderRow,
24
- parseTenantRow,
25
- parseWalletRow,
26
- parseTransactionRow,
27
- parseOAuthClientRow,
28
- parseGitTokenRow,
29
- parseSidecarStatus,
30
- parseTurnPartType,
31
- } from "./parse-row";
32
- export * as schema from "./schema";
@@ -1,35 +0,0 @@
1
- import { describe, test, expect } from "bun:test";
2
-
3
- import { rewriteSchemaQualifiedReferences } from "./migrate";
4
-
5
- describe('migration "public" substitution', () => {
6
- test("rewrites schema-qualified references", () => {
7
- const sql =
8
- 'ALTER TABLE "x" ADD CONSTRAINT "fk" FOREIGN KEY ("u") REFERENCES "public"."user"("id");';
9
- expect(rewriteSchemaQualifiedReferences(sql, '"test_schema"')).toBe(
10
- 'ALTER TABLE "x" ADD CONSTRAINT "fk" FOREIGN KEY ("u") REFERENCES "test_schema"."user"("id");',
11
- );
12
- });
13
-
14
- test("leaves bare quoted public in string literals alone", () => {
15
- const sql = `INSERT INTO "x" VALUES ('"public"');`;
16
- expect(rewriteSchemaQualifiedReferences(sql, '"t"')).toBe(sql);
17
- });
18
-
19
- test("substitutes multiple occurrences on a single line", () => {
20
- const sql = 'REFERENCES "public"."a"("id"), "public"."b"("id")';
21
- expect(rewriteSchemaQualifiedReferences(sql, '"s"')).toBe(
22
- 'REFERENCES "s"."a"("id"), "s"."b"("id")',
23
- );
24
- });
25
-
26
- test("no-op when target schema is public", () => {
27
- const sql = 'REFERENCES "public"."user"("id")';
28
- expect(rewriteSchemaQualifiedReferences(sql, '"public"')).toBe(sql);
29
- });
30
-
31
- test("does not match a trailing apostrophe (bare reference)", () => {
32
- const sql = `SELECT '"public"' AS literal;`;
33
- expect(rewriteSchemaQualifiedReferences(sql, '"s"')).toBe(sql);
34
- });
35
- });
package/src/migrate.ts DELETED
@@ -1,168 +0,0 @@
1
- import { readdir, readFile } from "node:fs/promises";
2
- import path from "node:path";
3
-
4
- import { type } from "arktype";
5
- import postgres from "postgres";
6
-
7
- import { DBConfig } from "./config";
8
-
9
- // Resolution of the migrations directory: the package layout pins
10
- // the drizzle-generated SQL at `<pkgRoot>/migrations`. This file
11
- // lives at `<pkgRoot>/src/migrate.ts`, so the directory is one level
12
- // up from `__dirname`. Resolving via `import.meta` keeps the runtime
13
- // honest about where it is reading SQL from instead of relying on a
14
- // cwd-relative path that breaks under callers that change `cwd`.
15
- const MIGRATIONS_DIR = path.resolve(
16
- path.dirname(new URL(import.meta.url).pathname),
17
- "..",
18
- "migrations",
19
- );
20
-
21
- function quoteIdentifier(name: string): string {
22
- return `"${name.replace(/"/g, '""')}"`;
23
- }
24
-
25
- /**
26
- * Rewrite drizzle-generated schema-qualified references like
27
- * `REFERENCES "public"."<table>"` so they point at the target
28
- * schema. The pattern matches `"public".` only when followed by
29
- * another quoted identifier, leaving bare `"public"` inside string
30
- * literals alone.
31
- */
32
- export function rewriteSchemaQualifiedReferences(
33
- sql: string,
34
- schemaIdent: string,
35
- ): string {
36
- return sql.replace(/"public"\.(?=")/g, `${schemaIdent}.`);
37
- }
38
-
39
- /**
40
- * Construct the single-connection postgres client used by the
41
- * migration and teardown entry points. Both paths share the same SSL
42
- * passthrough and suppress NOTICE-level diagnostics (cascade reports,
43
- * "schema already exists") that postgres.js logs by default but are
44
- * informational only in this harness context. The optional `searchPath`
45
- * pins `search_path` on connection-open so unqualified `CREATE TABLE`
46
- * statements land in the caller's schema.
47
- */
48
- function createMigrationClient(
49
- config: DBConfig,
50
- searchPath?: string,
51
- ): ReturnType<typeof postgres> {
52
- return postgres({
53
- host: config.host,
54
- port: config.port,
55
- user: config.user,
56
- password: config.password,
57
- database: config.database,
58
- max: 1,
59
- onnotice: () => undefined,
60
- ...(config.ssl !== undefined && { ssl: config.ssl }),
61
- ...(searchPath !== undefined && {
62
- connection: { search_path: searchPath },
63
- }),
64
- });
65
- }
66
-
67
- /**
68
- * Apply the drizzle-generated migration SQL into the given postgres
69
- * schema. The schema is created if absent; nothing else is dropped.
70
- *
71
- * The migration source files reference the destination schema
72
- * literally (e.g. `"public"."user"` in FK constraints). To make the
73
- * same source apply cleanly into an arbitrary schema, we substitute
74
- * the literal token `"public"` with the quoted target schema before
75
- * executing each file. This is a textual substitution rather than a
76
- * postgres-side `search_path` trick because the FK references are
77
- * fully qualified; `search_path` would not redirect them.
78
- *
79
- * The substitution is bounded: the migration source is
80
- * machine-generated and never contains the string `"public"` other
81
- * than in schema-qualified identifiers, so there is no ambiguity to
82
- * worry about. The substitution is a no-op when the target schema is
83
- * literally `public`, which is the common case.
84
- */
85
- export async function runMigrations(
86
- configRaw: unknown,
87
- options: { schema: string },
88
- ): Promise<void> {
89
- const config = DBConfig(configRaw);
90
- if (config instanceof type.errors) {
91
- throw new Error(`Invalid database config: ${config.summary}`);
92
- }
93
-
94
- const { schema } = options;
95
- if (schema.length === 0) {
96
- throw new Error("runMigrations: schema name must not be empty");
97
- }
98
-
99
- const schemaIdent = quoteIdentifier(schema);
100
-
101
- // Pin search_path on the migration connection so unqualified
102
- // `CREATE TABLE "name"` statements land in the target schema.
103
- // The FK references in the source SQL are already schema-qualified
104
- // (`"public"."user"`); we rewrite those to the target schema
105
- // below. Together these two mechanisms route every object the
106
- // migration touches into the caller's schema.
107
- const sql = createMigrationClient(config, schemaIdent);
108
-
109
- try {
110
- // The CREATE SCHEMA must run on a connection that does not
111
- // require the schema to already exist for search_path to be
112
- // applied. postgres.js sets the GUC after connection-open, so
113
- // CREATE SCHEMA IF NOT EXISTS here is the first statement and
114
- // creates the schema before search_path matters.
115
- await sql.unsafe(`CREATE SCHEMA IF NOT EXISTS ${schemaIdent}`);
116
-
117
- const files = (await readdir(MIGRATIONS_DIR))
118
- .filter((f) => f.endsWith(".sql"))
119
- .sort();
120
-
121
- if (files.length === 0) {
122
- throw new Error(
123
- `runMigrations: no .sql files found in ${MIGRATIONS_DIR}`,
124
- );
125
- }
126
-
127
- for (const file of files) {
128
- const raw = await readFile(path.join(MIGRATIONS_DIR, file), "utf-8");
129
- const rendered = rewriteSchemaQualifiedReferences(raw, schemaIdent);
130
- // drizzle emits multi-statement files separated by its own
131
- // statement-breakpoint marker. Split on it and execute each
132
- // statement individually so a syntax error in one statement
133
- // surfaces with the right context.
134
- const statements = rendered
135
- .split("--> statement-breakpoint")
136
- .map((s) => s.trim())
137
- .filter((s) => s.length > 0);
138
- for (const stmt of statements) {
139
- await sql.unsafe(stmt);
140
- }
141
- }
142
- } finally {
143
- await sql.end();
144
- }
145
- }
146
-
147
- /**
148
- * Drop the given schema along with everything in it. Used by the
149
- * integration-test harness to tear down per-test schemas.
150
- */
151
- export async function dropSchema(
152
- configRaw: unknown,
153
- options: { schema: string },
154
- ): Promise<void> {
155
- const config = DBConfig(configRaw);
156
- if (config instanceof type.errors) {
157
- throw new Error(`Invalid database config: ${config.summary}`);
158
- }
159
-
160
- const sql = createMigrationClient(config);
161
-
162
- try {
163
- const schemaIdent = quoteIdentifier(options.schema);
164
- await sql.unsafe(`DROP SCHEMA IF EXISTS ${schemaIdent} CASCADE`);
165
- } finally {
166
- await sql.end();
167
- }
168
- }