lurqrun 0.1.6 → 0.1.7

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 (206) hide show
  1. package/README.md +18 -10
  2. package/dist/auditHandler-JCOJSUYW.js +23 -0
  3. package/dist/bin/lurq.js +3090 -23306
  4. package/dist/browserAuth-B6G27USY.js +95 -0
  5. package/dist/cache-DKI34SVS.js +12 -0
  6. package/dist/check-2FDGUOV7.js +192 -0
  7. package/dist/checkUpgrade-F2WESBPM.js +103 -0
  8. package/dist/chunk-2VMOWSRY.js +300 -0
  9. package/dist/chunk-3GHJR7XR.js +2049 -0
  10. package/dist/chunk-3N3Q3DSW.js +214 -0
  11. package/dist/chunk-4ESO33J6.js +34 -0
  12. package/dist/chunk-4F7ZESPO.js +132 -0
  13. package/dist/chunk-4KCPOMG6.js +68 -0
  14. package/dist/chunk-4ZYCIMY4.js +155 -0
  15. package/dist/chunk-63MUJNZL.js +318 -0
  16. package/dist/chunk-6AAEVQ7I.js +6336 -0
  17. package/dist/chunk-6EQ3ARXA.js +304 -0
  18. package/dist/chunk-6L3U7DZ5.js +23 -0
  19. package/dist/chunk-AHBP533R.js +220 -0
  20. package/dist/chunk-B43LCXBM.js +223 -0
  21. package/dist/chunk-BCANRN5B.js +33 -0
  22. package/dist/chunk-BUALU27S.js +123 -0
  23. package/dist/chunk-BULZAX2U.js +1554 -0
  24. package/dist/chunk-BV32UNLL.js +154 -0
  25. package/dist/chunk-C6GLE626.js +160 -0
  26. package/dist/chunk-CIDWNUAP.js +145 -0
  27. package/dist/chunk-CUP36ND3.js +2126 -0
  28. package/dist/chunk-CW5HJ3LX.js +96 -0
  29. package/dist/chunk-DB2VF5UD.js +315 -0
  30. package/dist/chunk-DWAGCNEL.js +210 -0
  31. package/dist/chunk-EA3DP5HG.js +293 -0
  32. package/dist/chunk-ELGCAM7P.js +184 -0
  33. package/dist/chunk-FW6BD2NO.js +1915 -0
  34. package/dist/chunk-GE37NVLN.js +15 -0
  35. package/dist/chunk-GPTKSNR6.js +171 -0
  36. package/dist/chunk-GRJXQBQZ.js +793 -0
  37. package/dist/chunk-HWBBVLOT.js +92 -0
  38. package/dist/chunk-I2JUKN6Z.js +24 -0
  39. package/dist/chunk-I3M2KJXB.js +56 -0
  40. package/dist/chunk-IQ4SDKKY.js +733 -0
  41. package/dist/chunk-J5FZYN4A.js +116 -0
  42. package/dist/chunk-L6N4JFW2.js +263 -0
  43. package/dist/chunk-LBFGSSF2.js +126 -0
  44. package/dist/chunk-LEGPB7DO.js +81 -0
  45. package/dist/chunk-LO35HLQZ.js +219 -0
  46. package/dist/chunk-LRD4GBYE.js +73 -0
  47. package/dist/chunk-LUAKDHLZ.js +40 -0
  48. package/dist/chunk-LYMPYETA.js +965 -0
  49. package/dist/chunk-M6NYEXCW.js +38 -0
  50. package/dist/chunk-MHUEVBVO.js +35 -0
  51. package/dist/chunk-MYGSLK44.js +37 -0
  52. package/dist/chunk-NH5I4KHH.js +201 -0
  53. package/dist/chunk-OQS4GUSI.js +1391 -0
  54. package/dist/chunk-PSX3GIAN.js +176 -0
  55. package/dist/chunk-Q5VPF4WI.js +26 -0
  56. package/dist/chunk-QKGYDMWB.js +7409 -0
  57. package/dist/chunk-QSL6QN5X.js +115 -0
  58. package/dist/chunk-SHCEBP3J.js +107 -0
  59. package/dist/chunk-SJOVFF7B.js +201 -0
  60. package/dist/chunk-ST2X2GYN.js +373 -0
  61. package/dist/chunk-TNTXWSYA.js +19 -0
  62. package/dist/chunk-U5AT3TD4.js +2128 -0
  63. package/dist/chunk-UDVNA7EA.js +483 -0
  64. package/dist/chunk-VL2E5T2A.js +109 -0
  65. package/dist/chunk-VLRHFMGJ.js +58 -0
  66. package/dist/chunk-WHX2ZFCG.js +4048 -0
  67. package/dist/chunk-WQ23REYT.js +37 -0
  68. package/dist/chunk-XBIDMHSK.js +15425 -0
  69. package/dist/chunk-Y5GI7RL3.js +66 -0
  70. package/dist/client-IRKAMDYZ.js +15 -0
  71. package/dist/commands-FIL5ET6P.js +911 -0
  72. package/dist/compat-R3UTFQ5Q.js +201 -0
  73. package/dist/dashboardIssue-WYYMBFIB.js +192 -0
  74. package/dist/diff-GZA2774U.js +13 -0
  75. package/dist/dist-4XXIXHHZ.js +12945 -0
  76. package/dist/e2b-FVPHDULZ.js +172 -0
  77. package/dist/extract-NKGKLIVT.js +444 -0
  78. package/dist/fetch-QYTUJVG2.js +22 -0
  79. package/dist/firstTouch-QQTJH4QX.js +55 -0
  80. package/dist/handlers-GEJAVZDK.js +53 -0
  81. package/dist/http-ETPYMBGD.js +5360 -0
  82. package/dist/ingestQueue-VKN4KBIF.js +35 -0
  83. package/dist/install-EK3W2COX.js +20 -0
  84. package/dist/installSkill-VQJWJVCL.js +52 -0
  85. package/dist/inventory-5AWSLJYU.js +31 -0
  86. package/dist/local-XPZMRH5R.js +13 -0
  87. package/dist/mcp-3JHD5SUZ.js +472 -0
  88. package/dist/mcpHandlers-QFN4MCFL.js +22 -0
  89. package/dist/mcpScan-QZHWENIP.js +1871 -0
  90. package/dist/mcpScanWorkflow-JPYCBGC4.js +81 -0
  91. package/dist/mcpStack-BYJCZNIV.js +20 -0
  92. package/dist/packages-THVFMWFP.js +42 -0
  93. package/dist/plan-V4OEI3J4.js +463 -0
  94. package/dist/planView-JRXGCG2N.js +79 -0
  95. package/dist/policy-3P6BGRDN.js +115 -0
  96. package/dist/references-6N7VEPHT.js +14 -0
  97. package/dist/release-4U4ABNSJ.js +128 -0
  98. package/dist/remote-YGCXYI4V.js +32 -0
  99. package/dist/reportRuns-CKQQYLY4.js +64 -0
  100. package/dist/sandbox-MEZLAODF.js +39 -0
  101. package/dist/server-KFUD5X57.js +55 -0
  102. package/dist/surface-V62GE554.js +141 -0
  103. package/dist/surface-YL6O67W3.js +36 -0
  104. package/dist/surfaceHandlers-X2IZKA3E.js +20 -0
  105. package/dist/typecheck-X7U5ELMR.js +268 -0
  106. package/dist/types-M5JP2W3D.js +10 -0
  107. package/dist/uninstall-P7RGBX6G.js +174 -0
  108. package/dist/upgrade-NQ5IWFZB.js +596 -0
  109. package/dist/upgradePlan-A4LRKLEG.js +146 -0
  110. package/dist/userConfig-Z3GQE776.js +20 -0
  111. package/dist/watch-DGFMWUDL.js +214 -0
  112. package/package.json +26 -21
  113. package/templates/agent-rules.md +4 -4
  114. package/templates/skill-instructions.md +13 -14
  115. package/dist/bin/lurq.js.map +0 -1
  116. package/dist/index.d.ts +0 -805
  117. package/dist/index.js +0 -23570
  118. package/dist/index.js.map +0 -1
  119. package/drizzle/0000_confused_fabian_cortez.sql +0 -54
  120. package/drizzle/0001_aspiring_timeslip.sql +0 -15
  121. package/drizzle/0002_lyrical_lila_cheney.sql +0 -14
  122. package/drizzle/0003_violet_giant_girl.sql +0 -14
  123. package/drizzle/0004_slim_valeria_richards.sql +0 -15
  124. package/drizzle/0005_petite_luke_cage.sql +0 -12
  125. package/drizzle/0006_calm_nighthawk.sql +0 -3
  126. package/drizzle/0007_slimy_naoko.sql +0 -1
  127. package/drizzle/0008_peaceful_tigra.sql +0 -1
  128. package/drizzle/0009_smooth_harrier.sql +0 -10
  129. package/drizzle/0010_curved_amphibian.sql +0 -2
  130. package/drizzle/0011_compat_provenance_closures.sql +0 -11
  131. package/drizzle/0012_api_surfaces.sql +0 -9
  132. package/drizzle/0013_nice_thanos.sql +0 -1
  133. package/drizzle/0014_luxuriant_malcolm_colcord.sql +0 -10
  134. package/drizzle/0015_past_fat_cobra.sql +0 -11
  135. package/drizzle/0016_futuristic_junta.sql +0 -50
  136. package/drizzle/0017_absent_grandmaster.sql +0 -20
  137. package/drizzle/0018_rainy_proemial_gods.sql +0 -1
  138. package/drizzle/0019_dashing_korvac.sql +0 -11
  139. package/drizzle/0020_normal_vulture.sql +0 -3
  140. package/drizzle/0021_dazzling_blue_shield.sql +0 -17
  141. package/drizzle/0022_massive_ghost_rider.sql +0 -24
  142. package/drizzle/0023_next_shatterstar.sql +0 -1
  143. package/drizzle/0024_dusty_maelstrom.sql +0 -15
  144. package/drizzle/0025_gifted_harpoon.sql +0 -5
  145. package/drizzle/0026_awesome_mordo.sql +0 -1
  146. package/drizzle/0027_charming_ulik.sql +0 -2
  147. package/drizzle/0028_romantic_loners.sql +0 -2
  148. package/drizzle/0029_busy_korvac.sql +0 -16
  149. package/drizzle/0030_steady_mathemanic.sql +0 -6
  150. package/drizzle/0031_redundant_maginty.sql +0 -2
  151. package/drizzle/0032_stack_resolutions.sql +0 -14
  152. package/drizzle/0033_graceful_tyger_tiger.sql +0 -3
  153. package/drizzle/0034_uneven_thunderbird.sql +0 -1
  154. package/drizzle/0035_api_key_scopes.sql +0 -1
  155. package/drizzle/0036_policy_audit.sql +0 -21
  156. package/drizzle/0037_subscription_seats.sql +0 -1
  157. package/drizzle/0038_symbol_offsets.sql +0 -2
  158. package/drizzle/0039_mcp_runtime_scans.sql +0 -80
  159. package/drizzle/0040_subscription_overage_and_interval.sql +0 -4
  160. package/drizzle/0041_account_email.sql +0 -38
  161. package/drizzle/0042_alert_channels.sql +0 -21
  162. package/drizzle/meta/0000_snapshot.json +0 -370
  163. package/drizzle/meta/0001_snapshot.json +0 -482
  164. package/drizzle/meta/0002_snapshot.json +0 -575
  165. package/drizzle/meta/0003_snapshot.json +0 -667
  166. package/drizzle/meta/0004_snapshot.json +0 -769
  167. package/drizzle/meta/0005_snapshot.json +0 -864
  168. package/drizzle/meta/0006_snapshot.json +0 -882
  169. package/drizzle/meta/0007_snapshot.json +0 -876
  170. package/drizzle/meta/0008_snapshot.json +0 -882
  171. package/drizzle/meta/0009_snapshot.json +0 -948
  172. package/drizzle/meta/0010_snapshot.json +0 -969
  173. package/drizzle/meta/0011_snapshot.json +0 -1049
  174. package/drizzle/meta/0012_snapshot.json +0 -1115
  175. package/drizzle/meta/0013_snapshot.json +0 -1121
  176. package/drizzle/meta/0014_snapshot.json +0 -1190
  177. package/drizzle/meta/0015_snapshot.json +0 -1280
  178. package/drizzle/meta/0016_snapshot.json +0 -1683
  179. package/drizzle/meta/0017_snapshot.json +0 -1834
  180. package/drizzle/meta/0018_snapshot.json +0 -1834
  181. package/drizzle/meta/0019_snapshot.json +0 -1909
  182. package/drizzle/meta/0020_snapshot.json +0 -1921
  183. package/drizzle/meta/0021_snapshot.json +0 -2045
  184. package/drizzle/meta/0022_snapshot.json +0 -2246
  185. package/drizzle/meta/0023_snapshot.json +0 -2252
  186. package/drizzle/meta/0024_snapshot.json +0 -2375
  187. package/drizzle/meta/0025_snapshot.json +0 -2407
  188. package/drizzle/meta/0026_snapshot.json +0 -2413
  189. package/drizzle/meta/0027_snapshot.json +0 -2425
  190. package/drizzle/meta/0028_snapshot.json +0 -2438
  191. package/drizzle/meta/0029_snapshot.json +0 -2546
  192. package/drizzle/meta/0030_snapshot.json +0 -2586
  193. package/drizzle/meta/0031_snapshot.json +0 -2598
  194. package/drizzle/meta/0032_snapshot.json +0 -2699
  195. package/drizzle/meta/0033_snapshot.json +0 -2712
  196. package/drizzle/meta/0034_snapshot.json +0 -2719
  197. package/drizzle/meta/0035_snapshot.json +0 -2726
  198. package/drizzle/meta/0036_snapshot.json +0 -2876
  199. package/drizzle/meta/0037_snapshot.json +0 -2883
  200. package/drizzle/meta/0038_snapshot.json +0 -2895
  201. package/drizzle/meta/0039_snapshot.json +0 -3501
  202. package/drizzle/meta/0040_snapshot.json +0 -3527
  203. package/drizzle/meta/0041_snapshot.json +0 -3824
  204. package/drizzle/meta/0042_snapshot.json +0 -3960
  205. package/drizzle/meta/_journal.json +0 -307
  206. package/src/data/seed.json +0 -253
@@ -0,0 +1,965 @@
1
+ import { createRequire as __lurqCreateRequire } from 'node:module'; const require = __lurqCreateRequire(import.meta.url);
2
+ import {
3
+ EMBEDDING_DIM
4
+ } from "./chunk-GPTKSNR6.js";
5
+ import {
6
+ __export,
7
+ init_esm_shims
8
+ } from "./chunk-I3M2KJXB.js";
9
+
10
+ // src/db/schema.ts
11
+ var schema_exports = {};
12
+ __export(schema_exports, {
13
+ UNBOUNDED_ARITY: () => UNBOUNDED_ARITY,
14
+ apiKeys: () => apiKeys,
15
+ apiSurfaces: () => apiSurfaces,
16
+ askSpendDaily: () => askSpendDaily,
17
+ claims: () => claims,
18
+ compatEdges: () => compatEdges,
19
+ compatVerifyQueue: () => compatVerifyQueue,
20
+ discoveryQueue: () => discoveryQueue,
21
+ entities: () => entities,
22
+ environments: () => environments,
23
+ mcpChangeEvents: () => mcpChangeEvents,
24
+ mcpContracts: () => mcpContracts,
25
+ mcpDeployments: () => mcpDeployments,
26
+ mcpObservations: () => mcpObservations,
27
+ mcpPublicReports: () => mcpPublicReports,
28
+ notificationChannels: () => notificationChannels,
29
+ notificationDeliveries: () => notificationDeliveries,
30
+ notificationItems: () => notificationItems,
31
+ notificationPreferences: () => notificationPreferences,
32
+ observations: () => observations,
33
+ ownerUsageDaily: () => ownerUsageDaily,
34
+ packageVersions: () => packageVersions,
35
+ packages: () => packages,
36
+ policyDecisions: () => policyDecisions,
37
+ recommendationOutcomes: () => recommendationOutcomes,
38
+ repoAlerts: () => repoAlerts,
39
+ repos: () => repos,
40
+ resolvedClosures: () => resolvedClosures,
41
+ seedPackages: () => seedPackages,
42
+ selectionPolicies: () => selectionPolicies,
43
+ selectionPolicyChanges: () => selectionPolicyChanges,
44
+ stackResolutions: () => stackResolutions,
45
+ subscriptions: () => subscriptions,
46
+ surfaceQueue: () => surfaceQueue,
47
+ symbols: () => symbols,
48
+ syncRuns: () => syncRuns,
49
+ upgradeRuns: () => upgradeRuns,
50
+ verificationRuns: () => verificationRuns,
51
+ watchState: () => watchState
52
+ });
53
+ init_esm_shims();
54
+ import { sql } from "drizzle-orm";
55
+ import {
56
+ bigint,
57
+ boolean,
58
+ customType,
59
+ date,
60
+ index,
61
+ integer,
62
+ jsonb,
63
+ pgTable,
64
+ primaryKey,
65
+ real,
66
+ serial,
67
+ text,
68
+ timestamp,
69
+ uniqueIndex,
70
+ vector
71
+ } from "drizzle-orm/pg-core";
72
+ var tsvector = customType({
73
+ dataType() {
74
+ return "tsvector";
75
+ }
76
+ });
77
+ var ts = (name) => timestamp(name, { withTimezone: true, mode: "date" });
78
+ var packages = pgTable(
79
+ "packages",
80
+ {
81
+ id: serial("id").primaryKey(),
82
+ name: text("name").notNull(),
83
+ ecosystem: text("ecosystem").$type().notNull().default("npm"),
84
+ // Classification + descriptive text
85
+ category: text("category").$type(),
86
+ /** Whether `category` was hand-curated or inferred at ingest (§2A). */
87
+ categorySource: text("category_source").$type(),
88
+ description: text("description"),
89
+ summary: text("summary"),
90
+ repoUrl: text("repo_url"),
91
+ homepage: text("homepage"),
92
+ latestVersion: text("latest_version"),
93
+ license: text("license"),
94
+ // Lifecycle flags
95
+ deprecated: boolean("deprecated").notNull().default(false),
96
+ archived: boolean("archived").notNull().default(false),
97
+ // Age / maintenance signals
98
+ firstPublishedAt: ts("first_published_at"),
99
+ lastReleaseAt: ts("last_release_at"),
100
+ // Adoption signals
101
+ weeklyDownloads: bigint("weekly_downloads", { mode: "number" }),
102
+ downloadGrowth90d: real("download_growth_90d"),
103
+ stars: integer("stars"),
104
+ /**
105
+ * Dependent counts from deps.dev, kept split rather than summed.
106
+ *
107
+ * `direct_dependents` counts packages that name this one in their own
108
+ * manifest; `indirect_dependents` counts those that inherited it through
109
+ * someone else's tree. The ratio is what distinguishes a library a human
110
+ * chose from plumbing npm dragged in — a distinction weekly downloads and
111
+ * GitHub stars both fail to make, because a monorepo's internal utility
112
+ * inherits its parent's stars and every sibling's download volume.
113
+ */
114
+ directDependents: integer("direct_dependents"),
115
+ indirectDependents: integer("indirect_dependents"),
116
+ openIssues: integer("open_issues"),
117
+ closedIssues: integer("closed_issues"),
118
+ // Reliability / efficiency signals
119
+ scorecard: real("scorecard"),
120
+ bundleMinGzipKb: real("bundle_min_gzip_kb"),
121
+ advisories: jsonb("advisories").$type(),
122
+ // Compatibility metadata (Tier-1): declared peer-deps + engines of the
123
+ // latest version, so a whole-stack peer-range check is one indexed query.
124
+ peerDependencies: jsonb("peer_dependencies").$type(),
125
+ peerDependenciesMeta: jsonb("peer_dependencies_meta").$type(),
126
+ engines: jsonb("engines").$type(),
127
+ /** Where this package runs, classified from its own manifest at ingest.
128
+ * Resolves the `framework` category's frontend/backend ambiguity for the
129
+ * architecture diagram. Null = the manifest did not say clearly enough. */
130
+ runtimeTarget: text("runtime_target").$type(),
131
+ // Computed outputs
132
+ healthScore: integer("health_score"),
133
+ /** Intrinsic-quality axis (§1), adoption-independent. Blends with health at
134
+ * ranking time (composite); never folded into health_score itself. */
135
+ qualityScore: integer("quality_score"),
136
+ confidence: text("confidence").$type(),
137
+ scoreBreakdown: jsonb("score_breakdown").$type(),
138
+ usageGuide: jsonb("usage_guide").$type(),
139
+ embedding: vector("embedding", { dimensions: EMBEDDING_DIM }),
140
+ // Identity of the vector space `embedding` was produced in (e.g.
141
+ // `openai:text-embedding-3-small`, `local`). Vector search filters on the
142
+ // active provider so switching models can't compare incompatible spaces.
143
+ embeddingProvider: text("embedding_provider"),
144
+ // Lexical search vector (§3): name weighted highest (A), then category (B),
145
+ // then summary/description (C). Generated + STORED so it stays in sync with
146
+ // the row automatically; indexed with GIN for fast `@@` matching.
147
+ searchVector: tsvector("search_vector").generatedAlwaysAs(
148
+ sql`setweight(to_tsvector('english', coalesce(name, '')), 'A') || setweight(to_tsvector('english', coalesce(category, '')), 'B') || setweight(to_tsvector('english', coalesce(summary, description, '')), 'C')`
149
+ ),
150
+ // Freshness + bookkeeping
151
+ dataAsOf: timestamp("data_as_of", { withTimezone: true, mode: "date" }),
152
+ /** latest_version this package's direct deps were last expanded for by the
153
+ * discovery graph channel (§2B). Discovery re-scans a package only when this
154
+ * differs from latest_version — deps are version-pinned, so an unchanged
155
+ * version has unchanged neighbors. NULL = never scanned. */
156
+ graphScannedVersion: text("graph_scanned_version"),
157
+ createdAt: ts("created_at").notNull().defaultNow(),
158
+ updatedAt: ts("updated_at").notNull().defaultNow(),
159
+ /** The individual account (api_keys.owner_id) whose on-demand query first
160
+ * caused this package to be ingested — nobody had asked for it before them.
161
+ * Null for crawler/_changes-created packages, or ones ingested before
162
+ * dashboard accounts existed. Stamped once via a standalone WHERE ... IS NULL
163
+ * update kept out of upsertPackage, so re-syncs can never clobber it. */
164
+ firstRequestedByOwnerId: text("first_requested_by_owner_id")
165
+ },
166
+ (table) => [
167
+ uniqueIndex("packages_ecosystem_name_idx").on(table.ecosystem, table.name),
168
+ index("packages_category_idx").on(table.category),
169
+ index("packages_health_score_idx").on(table.healthScore),
170
+ // pgvector HNSW index for cosine similarity search (§11).
171
+ index("packages_embedding_idx").using("hnsw", table.embedding.op("vector_cosine_ops")),
172
+ // GIN index for lexical full-text search (§3).
173
+ index("packages_search_vector_idx").using("gin", table.searchVector),
174
+ index("packages_first_requested_by_idx").on(table.firstRequestedByOwnerId)
175
+ ]
176
+ );
177
+ var syncRuns = pgTable("sync_runs", {
178
+ id: serial("id").primaryKey(),
179
+ startedAt: ts("started_at").notNull().defaultNow(),
180
+ finishedAt: ts("finished_at"),
181
+ packagesSeen: integer("packages_seen").notNull().default(0),
182
+ packagesUpdated: integer("packages_updated").notNull().default(0),
183
+ errors: jsonb("errors").$type().notNull().default([]),
184
+ status: text("status").$type().notNull().default("running"),
185
+ /** Which deployment started this run, `<environment>/<service>`, or null when
186
+ * the host injects neither (local runs). Diagnostic only: two rows in the
187
+ * same minute with *different* origins is a duplicate scheduler — a second
188
+ * environment or a second service tile running the same cron — which is
189
+ * otherwise invisible from the data and doubles the outbound request rate
190
+ * until npm starts answering 429. */
191
+ origin: text("origin")
192
+ });
193
+ var seedPackages = pgTable("seed_packages", {
194
+ name: text("name").primaryKey(),
195
+ category: text("category").$type(),
196
+ addedAt: ts("added_at").notNull().defaultNow()
197
+ });
198
+ var discoveryQueue = pgTable(
199
+ "discovery_queue",
200
+ {
201
+ id: serial("id").primaryKey(),
202
+ name: text("name").notNull().unique(),
203
+ discoveredVia: text("discovered_via").$type().notNull(),
204
+ /** Lightweight quality-only pre-score (§2B). Null until the gate runs. */
205
+ preScore: integer("pre_score"),
206
+ status: text("status").$type().notNull().default("pending"),
207
+ /** Failed ingests bump this; past DISCOVERY.maxIngestAttempts the candidate
208
+ * is marked `failed` and stops being retried. Without it a package that
209
+ * throws after its summary call — a malformed manifest, a constraint
210
+ * violation — is re-ingested every cycle forever, paying the LLM each time
211
+ * and never reaching the write that would take it off the queue. Same
212
+ * treatment `compat_verify_queue.attempts` already gives a failing set. */
213
+ attempts: integer("attempts").notNull().default(0),
214
+ discoveredAt: ts("discovered_at").notNull().defaultNow()
215
+ },
216
+ (table) => [index("discovery_queue_status_idx").on(table.status)]
217
+ );
218
+ var compatVerifyQueue = pgTable(
219
+ "compat_verify_queue",
220
+ {
221
+ id: serial("id").primaryKey(),
222
+ /** Canonical order-independent key of the package set — dedups pending requests. */
223
+ setKey: text("set_key").notNull().unique(),
224
+ /** The package names to co-install in the sandbox. */
225
+ packages: jsonb("packages").$type().notNull(),
226
+ /** Failed drains bump this; the worker drops a set that keeps failing. */
227
+ attempts: integer("attempts").notNull().default(0),
228
+ requestedAt: ts("requested_at").notNull().defaultNow()
229
+ },
230
+ (table) => [index("compat_verify_queue_requested_idx").on(table.requestedAt)]
231
+ );
232
+ var apiKeys = pgTable(
233
+ "api_keys",
234
+ {
235
+ id: serial("id").primaryKey(),
236
+ /** sha256 hex of the full key — the only form persisted. */
237
+ keyHash: text("key_hash").notNull().unique(),
238
+ /** Identifiable display prefix, e.g. `lurq_live_ab12cd` (safe to show/log). */
239
+ prefix: text("prefix").notNull(),
240
+ /** Free-text label (owner / org / purpose). */
241
+ label: text("label"),
242
+ /** Reserved for self-serve issuance: maps to a Clerk user id. */
243
+ ownerId: text("owner_id"),
244
+ tier: text("tier").notNull().default("free"),
245
+ /**
246
+ * Permissions beyond the default read-and-call access (see KEY_SCOPES).
247
+ * Empty by default: `lurq setup` writes a key into agent config files, and
248
+ * a key an agent holds must never be able to loosen the rules it runs under.
249
+ */
250
+ scopes: text("scopes").array().notNull().default(sql`'{}'::text[]`),
251
+ createdAt: ts("created_at").notNull().defaultNow(),
252
+ lastUsedAt: ts("last_used_at"),
253
+ revokedAt: ts("revoked_at")
254
+ },
255
+ (table) => [index("api_keys_owner_idx").on(table.ownerId)]
256
+ );
257
+ var packageVersions = pgTable(
258
+ "package_versions",
259
+ {
260
+ packageName: text("package_name").notNull(),
261
+ version: text("version").notNull(),
262
+ publishedAt: ts("published_at"),
263
+ /** Peers/engines declared by this exact version. Lifted from the registry
264
+ * document at ingest (it already carries every version's manifest), so a
265
+ * conflict check against the versions a repo actually RUNS is one indexed
266
+ * query rather than a registry read per dependency. */
267
+ peerDependencies: jsonb("peer_dependencies").$type(),
268
+ engines: jsonb("engines").$type()
269
+ },
270
+ (table) => [
271
+ primaryKey({ columns: [table.packageName, table.version] }),
272
+ index("package_versions_name_published_idx").on(table.packageName, table.publishedAt)
273
+ ]
274
+ );
275
+ var watchState = pgTable("watch_state", {
276
+ id: text("id").primaryKey(),
277
+ seq: text("seq").notNull(),
278
+ updatedAt: ts("updated_at")
279
+ });
280
+ var verificationRuns = pgTable(
281
+ "verification_runs",
282
+ {
283
+ id: serial("id").primaryKey(),
284
+ packageName: text("package_name").notNull(),
285
+ version: text("version").notNull(),
286
+ driver: text("driver").notNull(),
287
+ moduleSystem: text("module_system").notNull(),
288
+ installed: boolean("installed").notNull(),
289
+ imported: boolean("imported"),
290
+ ranScripts: boolean("ran_scripts").notNull().default(false),
291
+ durationMs: integer("duration_ms"),
292
+ error: text("error"),
293
+ ranAt: ts("ran_at")
294
+ },
295
+ (table) => [index("verification_runs_pkg_idx").on(table.packageName, table.version)]
296
+ );
297
+ var compatEdges = pgTable(
298
+ "compat_edges",
299
+ {
300
+ id: serial("id").primaryKey(),
301
+ packageA: text("package_a").notNull(),
302
+ versionA: text("version_a").notNull(),
303
+ packageB: text("package_b").notNull(),
304
+ versionB: text("version_b").notNull(),
305
+ status: text("status").$type().notNull(),
306
+ /** Evidence class (§4B). Existing rows are sandbox co-installs, so the column
307
+ * defaults to `verified` to preserve their meaning. */
308
+ provenance: text("provenance").$type().notNull().default("verified"),
309
+ /** Distinct resolved graphs an `observed` edge was witnessed in (confidence).
310
+ * Ignored for verified/conflict. Accumulates on conflict, never overwritten. */
311
+ witnessCount: integer("witness_count").notNull().default(0),
312
+ driver: text("driver").notNull(),
313
+ ranAt: ts("ran_at")
314
+ },
315
+ (table) => [
316
+ uniqueIndex("compat_edges_pair_idx").on(
317
+ table.packageA,
318
+ table.versionA,
319
+ table.packageB,
320
+ table.versionB
321
+ )
322
+ ]
323
+ );
324
+ var resolvedClosures = pgTable(
325
+ "resolved_closures",
326
+ {
327
+ id: serial("id").primaryKey(),
328
+ packageName: text("package_name").notNull(),
329
+ version: text("version").notNull(),
330
+ /** Full closure: [{ name, version }, …] — every node in node_modules. */
331
+ nodes: jsonb("nodes").$type().notNull(),
332
+ fetchedAt: ts("fetched_at").notNull().defaultNow()
333
+ },
334
+ (table) => [uniqueIndex("resolved_closures_pkg_idx").on(table.packageName, table.version)]
335
+ );
336
+ var apiSurfaces = pgTable(
337
+ "api_surfaces",
338
+ {
339
+ id: serial("id").primaryKey(),
340
+ packageName: text("package_name").notNull(),
341
+ version: text("version").notNull(),
342
+ /** Normalized export list: [{ name, kind, signature }, …]. */
343
+ surface: jsonb("surface").$type().notNull(),
344
+ extractedAt: ts("extracted_at").notNull().defaultNow()
345
+ },
346
+ (table) => [uniqueIndex("api_surfaces_pkg_idx").on(table.packageName, table.version)]
347
+ );
348
+ var recommendationOutcomes = pgTable(
349
+ "recommendation_outcomes",
350
+ {
351
+ id: serial("id").primaryKey(),
352
+ /** The individual user this outcome belongs to (api_keys.owner_id). Null for
353
+ * anonymous/operator-issued keys. This is what turns the flywheel from a
354
+ * global blob into a per-user asset — "what did *this* person succeed
355
+ * with." Server-injected from the authenticated key, never caller-supplied. */
356
+ ownerId: text("owner_id"),
357
+ packageName: text("package_name").notNull(),
358
+ accepted: boolean("accepted").notNull(),
359
+ buildSignal: text("build_signal").$type(),
360
+ /** The original need text the recommendation was for — ties outcome back to
361
+ * the ask. Optional, length-capped at the trust boundary; never source code. */
362
+ need: text("need"),
363
+ createdAt: ts("created_at").notNull().defaultNow()
364
+ },
365
+ (table) => [
366
+ index("recommendation_outcomes_pkg_idx").on(table.packageName),
367
+ index("recommendation_outcomes_owner_idx").on(table.ownerId)
368
+ ]
369
+ );
370
+ var askSpendDaily = pgTable(
371
+ "ask_spend_daily",
372
+ {
373
+ ownerId: text("owner_id").notNull(),
374
+ /** UTC day, 'YYYY-MM-DD'. */
375
+ date: date("date").notNull(),
376
+ usdMicros: bigint("usd_micros", { mode: "number" }).notNull().default(0)
377
+ },
378
+ (table) => [primaryKey({ columns: [table.ownerId, table.date] })]
379
+ );
380
+ var ownerUsageDaily = pgTable(
381
+ "owner_usage_daily",
382
+ {
383
+ ownerId: text("owner_id").notNull(),
384
+ /** UTC day, 'YYYY-MM-DD'. */
385
+ date: date("date").notNull(),
386
+ /** recommend | evaluate | compare | compat | verify | usage | diagram | plan | report_outcome */
387
+ tool: text("tool").notNull(),
388
+ count: integer("count").notNull().default(0)
389
+ },
390
+ (table) => [
391
+ primaryKey({ columns: [table.ownerId, table.date, table.tool] }),
392
+ index("owner_usage_daily_owner_date_idx").on(table.ownerId, table.date)
393
+ ]
394
+ );
395
+ var repos = pgTable(
396
+ "repos",
397
+ {
398
+ id: serial("id").primaryKey(),
399
+ /** Clerk user id — same identity as api_keys.owner_id. */
400
+ ownerId: text("owner_id").notNull(),
401
+ /** GitHub App installation this repo is reachable through. */
402
+ installationId: bigint("installation_id", { mode: "number" }).notNull(),
403
+ /** `owner/name`. */
404
+ fullName: text("full_name").notNull(),
405
+ defaultBranch: text("default_branch"),
406
+ isPrivate: boolean("is_private").notNull().default(false),
407
+ policy: jsonb("policy").$type().notNull(),
408
+ manifests: jsonb("manifests").$type(),
409
+ /** Install command detected from the lockfile at scan time, so the generated
410
+ * workflow uses the repo's actual package manager instead of assuming npm. */
411
+ installCommand: text("install_command"),
412
+ drift: jsonb("drift").$type(),
413
+ lastScanAt: ts("last_scan_at"),
414
+ /** Last scan failure, surfaced in the dashboard. Null once a scan succeeds —
415
+ * a stale error next to fresh drift numbers reads as a current outage. */
416
+ lastScanError: text("last_scan_error"),
417
+ createdAt: ts("created_at").notNull().defaultNow()
418
+ },
419
+ (table) => [
420
+ uniqueIndex("repos_owner_full_name_idx").on(table.ownerId, table.fullName),
421
+ index("repos_installation_idx").on(table.installationId)
422
+ ]
423
+ );
424
+ var upgradeRuns = pgTable(
425
+ "upgrade_runs",
426
+ {
427
+ id: serial("id").primaryKey(),
428
+ /** Resolved server-side from the authenticated API key. Never caller-supplied. */
429
+ ownerId: text("owner_id").notNull(),
430
+ /** Links to `repos` when the repo is also connected. Null when the workflow
431
+ * runs somewhere lurq has no GitHub App installation — the CLI works
432
+ * standalone, and losing those runs would bias every impact figure. */
433
+ repoId: integer("repo_id"),
434
+ /** `owner/name` from GITHUB_REPOSITORY. */
435
+ repoFullName: text("repo_full_name").notNull(),
436
+ packageName: text("package_name").notNull(),
437
+ fromVersion: text("from_version").notNull(),
438
+ toVersion: text("to_version").notNull(),
439
+ severity: text("severity").$type().notNull(),
440
+ status: text("status").$type().notNull(),
441
+ /** Referenced symbols the upgrade removes or re-shapes. */
442
+ symbolsAffected: jsonb("symbols_affected").$type().notNull().default([]),
443
+ /** How many references those symbols have in the repo. */
444
+ callSites: integer("call_sites").notNull().default(0),
445
+ /** Distinct files containing them. Opt-in per repo policy. */
446
+ callSiteFiles: jsonb("call_site_files").$type(),
447
+ filesChanged: integer("files_changed"),
448
+ /** Null when the repo has no test script — distinct from `false`, which
449
+ * means the suite ran and failed. */
450
+ testsPassed: boolean("tests_passed"),
451
+ prUrl: text("pr_url"),
452
+ /**
453
+ * The GitHub Actions run, so a dashboard row links back to real logs.
454
+ * NOT NULL with an empty-string default rather than nullable: it is part of
455
+ * the dedup key below, and Postgres treats NULLs as distinct — a nullable
456
+ * column there would silently let every re-post insert a duplicate. Empty
457
+ * means "posted outside Actions" (a local CLI run), which then dedups to one
458
+ * row per package+target and updates in place.
459
+ */
460
+ runUrl: text("run_url").notNull().default(""),
461
+ createdAt: ts("created_at").notNull().defaultNow()
462
+ },
463
+ (table) => [
464
+ index("upgrade_runs_owner_created_idx").on(table.ownerId, table.createdAt),
465
+ index("upgrade_runs_repo_idx").on(table.repoId),
466
+ // One row per package per Actions run: a re-post of the same run must update
467
+ // rather than duplicate, or a retried job would double every impact figure.
468
+ uniqueIndex("upgrade_runs_dedup_idx").on(
469
+ table.ownerId,
470
+ table.repoFullName,
471
+ table.packageName,
472
+ table.toVersion,
473
+ table.runUrl
474
+ )
475
+ ]
476
+ );
477
+ var repoAlerts = pgTable(
478
+ "repo_alerts",
479
+ {
480
+ id: serial("id").primaryKey(),
481
+ /** Clerk user id, copied from the repo — alerts are read owner-scoped. */
482
+ ownerId: text("owner_id").notNull(),
483
+ repoId: integer("repo_id").notNull(),
484
+ /** Denormalized so the feed renders without a join, as in `upgrade_runs`. */
485
+ repoFullName: text("repo_full_name").notNull(),
486
+ packageName: text("package_name").notNull(),
487
+ /** The range this repo declared when the release landed. */
488
+ range: text("range").notNull(),
489
+ /** What that range resolved to at the last scan. Null when the index had no
490
+ * version timeline for the package yet — never guessed. */
491
+ fromVersion: text("from_version"),
492
+ /** The release that triggered this. */
493
+ toVersion: text("to_version").notNull(),
494
+ /**
495
+ * True when the declared range already admits `toVersion` — i.e. the next
496
+ * clean install silently takes the new major. That is the urgent case and the
497
+ * one no drift number expresses: the repo is not "behind", it is about to
498
+ * move on its own.
499
+ */
500
+ inRange: boolean("in_range").notNull(),
501
+ createdAt: ts("created_at").notNull().defaultNow()
502
+ },
503
+ (table) => [
504
+ index("repo_alerts_owner_created_idx").on(table.ownerId, table.createdAt),
505
+ // The notification sender scans recent alerts across every owner.
506
+ index("repo_alerts_created_idx").on(table.createdAt),
507
+ // One alert per repo per release. A re-sync of the same version — and the
508
+ // watcher re-syncs on every publish, including non-latest backports — must
509
+ // not re-notify.
510
+ uniqueIndex("repo_alerts_dedup_idx").on(table.repoId, table.packageName, table.toVersion)
511
+ ]
512
+ );
513
+ var surfaceQueue = pgTable(
514
+ "surface_queue",
515
+ {
516
+ id: serial("id").primaryKey(),
517
+ packageName: text("package_name").notNull(),
518
+ /** Null means "whatever latest resolves to at extraction time". */
519
+ version: text("version"),
520
+ /**
521
+ * Which extractor services this row.
522
+ *
523
+ * One queue, two drains: `package_surface` rows are fetched as tarballs and
524
+ * read statically, `mcp_server` rows are installed and probed over stdio.
525
+ * The discriminator is not optional bookkeeping — without it the npm drain
526
+ * picks up MCP rows and tries to extract exports from a server it should
527
+ * have handshaked with, and the failure is silent because "no exports
528
+ * found" is a legitimate answer for a package that has none.
529
+ */
530
+ kind: text("kind").$type().notNull().default("package_surface"),
531
+ /** Dedup key: `kind:name@version`. */
532
+ specKey: text("spec_key").notNull().unique(),
533
+ /** Failed drains bump this; the worker drops a spec that keeps failing. */
534
+ attempts: integer("attempts").notNull().default(0),
535
+ requestedAt: ts("requested_at").notNull().defaultNow()
536
+ },
537
+ (table) => [index("surface_queue_requested_idx").on(table.kind, table.requestedAt)]
538
+ );
539
+ var entities = pgTable(
540
+ "entities",
541
+ {
542
+ id: serial("id").primaryKey(),
543
+ kind: text("kind").$type().notNull(),
544
+ /** Registry or authority: npm, api.stripe.com, … */
545
+ namespace: text("namespace").notNull(),
546
+ name: text("name").notNull(),
547
+ /** Null for unversioned kinds. */
548
+ version: text("version"),
549
+ /** `kind:namespace:name:version` */
550
+ canonicalKey: text("canonical_key").notNull(),
551
+ /**
552
+ * 0 = the public graph; a real id = a private deployment's tenant. The spec
553
+ * models public as NULL, but Postgres UNIQUE treats NULLs as distinct, so
554
+ * (key, NULL) would insert unlimited duplicates and the dedup silently fails.
555
+ * A non-null sentinel is the cheap fix that works on every PG version.
556
+ */
557
+ tenantId: bigint("tenant_id", { mode: "number" }).notNull().default(0),
558
+ /** Tarball digest. THE extraction cache key: unchanged digest, no re-extraction.
559
+ * This is what keeps cost sublinear as the index grows (§5, §6.5). */
560
+ artifactHash: text("artifact_hash"),
561
+ /** Dependent count. Drives sampling AND refresh priority — and note priority
562
+ * is NOT popularity: the study found drift concentrates at LOW in-degree
563
+ * (18.7% vs 8.3%), which is exactly where models have the weakest priors. */
564
+ inDegree: integer("in_degree"),
565
+ firstSeen: ts("first_seen").notNull().defaultNow()
566
+ },
567
+ (table) => [
568
+ uniqueIndex("entities_canonical_idx").on(table.canonicalKey, table.tenantId),
569
+ index("entities_kind_idx").on(table.kind, table.namespace, table.name),
570
+ index("entities_artifact_idx").on(table.artifactHash)
571
+ ]
572
+ );
573
+ var UNBOUNDED_ARITY = -1;
574
+ var symbols = pgTable(
575
+ "symbols",
576
+ {
577
+ id: serial("id").primaryKey(),
578
+ entityId: integer("entity_id").notNull().references(() => entities.id),
579
+ /** 'default', 'foo', 'foo.bar' */
580
+ path: text("path").notNull(),
581
+ /** function | class | object | primitive | type_only */
582
+ kind: text("kind").$type().notNull(),
583
+ /** `fn.length` equivalent; null when not statically determinable. */
584
+ arity: integer("arity"),
585
+ /** 'local' | 'external:<pkg>' */
586
+ origin: text("origin").notNull().default("local"),
587
+ deprecated: boolean("deprecated").notNull().default(false),
588
+ tier: text("tier").$type().notNull(),
589
+ /** Full declaration text; tier C only (§6.2). */
590
+ signature: text("signature"),
591
+ sourceFile: text("source_file"),
592
+ sourceLine: integer("source_line"),
593
+ /** Character offset of the declaration. Two exports sharing file and offset
594
+ * are one value under two names, which is how a diff read from storage finds
595
+ * a proven rename. Null on rows extracted before it was recorded. */
596
+ sourceOffset: integer("source_offset"),
597
+ /** Most arguments the function reads: null when not measured, UNBOUNDED_ARITY
598
+ * for a rest parameter or `arguments`. See SurfaceSymbol.maxArity. */
599
+ maxArity: integer("max_arity")
600
+ },
601
+ // Keyed by TIER as well as path: a package version has one surface per tier and
602
+ // they are not interchangeable (§6.4.3). Without the tier in the key, storing a
603
+ // tier-C surface would silently overwrite the tier-A one that answers runtime
604
+ // existence — replacing the authoritative answer with a type-level guess.
605
+ (table) => [
606
+ uniqueIndex("symbols_entity_path_tier_idx").on(table.entityId, table.path, table.tier)
607
+ ]
608
+ );
609
+ var environments = pgTable("environments", {
610
+ id: serial("id").primaryKey(),
611
+ os: text("os").notNull(),
612
+ arch: text("arch").notNull(),
613
+ runtime: text("runtime").notNull(),
614
+ runtimeVer: text("runtime_ver").notNull(),
615
+ resolver: text("resolver"),
616
+ /** Hash of the above; the dedup key. */
617
+ fingerprint: text("fingerprint").notNull().unique()
618
+ });
619
+ var claims = pgTable(
620
+ "claims",
621
+ {
622
+ id: serial("id").primaryKey(),
623
+ subjectId: integer("subject_id").notNull().references(() => entities.id),
624
+ /** Null for unary claims ("does this install at all"). */
625
+ objectId: integer("object_id").references(() => entities.id),
626
+ relation: text("relation").notNull(),
627
+ /** NULL for DECLARED claims: a shipped-JS surface reads the same on every
628
+ * machine, so there is no environment to fingerprint. Executed claims
629
+ * (co-install, conflict, behaviour) always carry one (§5). */
630
+ environmentId: integer("environment_id").references(() => environments.id),
631
+ tenantId: bigint("tenant_id", { mode: "number" }).notNull().default(0)
632
+ },
633
+ (table) => [
634
+ uniqueIndex("claims_tuple_idx").on(
635
+ table.subjectId,
636
+ table.objectId,
637
+ table.relation,
638
+ table.environmentId,
639
+ table.tenantId
640
+ )
641
+ ]
642
+ );
643
+ var observations = pgTable(
644
+ "observations",
645
+ {
646
+ id: serial("id").primaryKey(),
647
+ claimId: integer("claim_id").notNull().references(() => claims.id),
648
+ verdict: text("verdict").$type().notNull(),
649
+ /** What KIND of evidence backs the verdict (§4.1) — orthogonal to the verdict
650
+ * itself. A declared surface fact must never read as behavioural proof. */
651
+ class: text("class").$type().notNull().default("executed"),
652
+ /** Which extraction tier produced it; null for executed claims. */
653
+ tier: text("tier").$type(),
654
+ /** Evidence that makes the verdict auditable. Null only for `unknown`. */
655
+ evidence: text("evidence"),
656
+ oracleId: text("oracle_id").notNull(),
657
+ oracleVer: text("oracle_ver").notNull(),
658
+ costMillis: integer("cost_millis"),
659
+ observedAt: ts("observed_at").notNull().defaultNow()
660
+ },
661
+ (table) => [index("observations_claim_idx").on(table.claimId, table.observedAt)]
662
+ );
663
+ var selectionPolicies = pgTable("selection_policies", {
664
+ ownerId: text("owner_id").primaryKey(),
665
+ policy: jsonb("policy").$type().notNull(),
666
+ updatedAt: ts("updated_at").notNull().defaultNow()
667
+ });
668
+ var selectionPolicyChanges = pgTable(
669
+ "selection_policy_changes",
670
+ {
671
+ id: serial("id").primaryKey(),
672
+ ownerId: text("owner_id").notNull(),
673
+ /** `dashboard`, or the display prefix of the API key that pushed it. */
674
+ actor: text("actor").notNull(),
675
+ before: jsonb("before").$type().notNull(),
676
+ after: jsonb("after").$type().notNull(),
677
+ createdAt: ts("created_at").notNull().defaultNow()
678
+ },
679
+ (table) => [index("selection_policy_changes_owner_idx").on(table.ownerId, table.createdAt)]
680
+ );
681
+ var policyDecisions = pgTable(
682
+ "policy_decisions",
683
+ {
684
+ id: serial("id").primaryKey(),
685
+ ownerId: text("owner_id").notNull(),
686
+ packageName: text("package_name").notNull(),
687
+ rule: text("rule").$type().notNull(),
688
+ /** `blocked` under enforce, `warned` under warn. */
689
+ action: text("action").$type().notNull(),
690
+ /** The tool the agent called: recommend | evaluate. */
691
+ tool: text("tool").notNull(),
692
+ createdAt: ts("created_at").notNull().defaultNow()
693
+ },
694
+ (table) => [index("policy_decisions_owner_idx").on(table.ownerId, table.createdAt)]
695
+ );
696
+ var subscriptions = pgTable(
697
+ "subscriptions",
698
+ {
699
+ /** Clerk user id. One subscription per account. */
700
+ ownerId: text("owner_id").primaryKey(),
701
+ /** Stable across subscriptions: a customer who cancels and returns reuses it. */
702
+ stripeCustomerId: text("stripe_customer_id").notNull().unique(),
703
+ /** Null between creating a customer and completing a first checkout. */
704
+ stripeSubscriptionId: text("stripe_subscription_id").unique(),
705
+ tier: text("tier").$type().notNull().default("free"),
706
+ /** Raw Stripe subscription status. `null` = never subscribed. */
707
+ status: text("status"),
708
+ /** When the paid period lapses. Display only; `status` is what gates access. */
709
+ currentPeriodEnd: ts("current_period_end"),
710
+ /** Set when the user cancels but has paid through the period. */
711
+ cancelAtPeriodEnd: boolean("cancel_at_period_end").notNull().default(false),
712
+ /**
713
+ * Seats bought, from the Stripe subscription quantity. Only per-seat plans
714
+ * read it, and `billedSeats` floors it at the plan's minimum, so a flat plan
715
+ * or a pre-seat row carrying the default 1 resolves correctly either way.
716
+ */
717
+ seats: integer("seats").notNull().default(1),
718
+ /** The subscription carries the metered overage item, so calls past the pool bill. */
719
+ overageEnabled: boolean("overage_enabled").notNull().default(false),
720
+ /** Calendar month ('YYYY-MM') that `overageReported` counts. */
721
+ overageMonth: text("overage_month"),
722
+ /** Overage calls already sent to Stripe for `overageMonth`. */
723
+ overageReported: integer("overage_reported").notNull().default(0),
724
+ /** The plan item's Stripe interval ('month' | 'year'). Null for manual grants. */
725
+ billingInterval: text("billing_interval"),
726
+ /**
727
+ * Stripe delivers out of order and retries, so a late duplicate of an older
728
+ * event must not overwrite newer state. The webhook drops any event whose
729
+ * timestamp is older than the one that produced the current row.
730
+ */
731
+ lastEventAt: ts("last_event_at"),
732
+ createdAt: ts("created_at").notNull().defaultNow(),
733
+ updatedAt: ts("updated_at").notNull().defaultNow()
734
+ },
735
+ (table) => [index("subscriptions_customer_idx").on(table.stripeCustomerId)]
736
+ );
737
+ var stackResolutions = pgTable(
738
+ "stack_resolutions",
739
+ {
740
+ id: serial("id").primaryKey(),
741
+ /** Canonical `name@version|name@version|…`, sorted. The cache key. */
742
+ setKey: text("set_key").notNull().unique(),
743
+ /** The exact members this verdict is about. */
744
+ packages: jsonb("packages").$type().notNull(),
745
+ /** Names only, for publish-driven invalidation. */
746
+ names: text("names").array().notNull(),
747
+ /** True when npm produced a lockfile for the set. */
748
+ resolved: boolean("resolved").notNull(),
749
+ /** 'ERESOLVE' for a proven conflict; null when it resolved. */
750
+ reason: text("reason"),
751
+ /** npm's own error text on a conflict — it names the packages and ranges. */
752
+ detail: text("detail"),
753
+ resolvedAt: ts("resolved_at").notNull().defaultNow()
754
+ },
755
+ (table) => [
756
+ index("stack_resolutions_names_idx").using("gin", table.names),
757
+ index("stack_resolutions_resolved_at_idx").on(table.resolvedAt)
758
+ ]
759
+ );
760
+ var mcpContracts = pgTable(
761
+ "mcp_contracts",
762
+ {
763
+ contentHash: text("content_hash").primaryKey(),
764
+ /** Schema-only hash; equal means calls validate identically. */
765
+ contractHash: text("contract_hash").notNull(),
766
+ tools: jsonb("tools").$type().notNull(),
767
+ prompts: jsonb("prompts").$type().notNull(),
768
+ resourceTemplates: jsonb("resource_templates").$type().notNull(),
769
+ instructions: text("instructions"),
770
+ toolCount: integer("tool_count").notNull(),
771
+ bytes: integer("bytes").notNull(),
772
+ /** Computed server-side at insert, never taken from the client. */
773
+ analysis: jsonb("analysis").$type().notNull(),
774
+ analyzerVersion: text("analyzer_version").notNull(),
775
+ createdAt: ts("created_at").notNull().defaultNow()
776
+ },
777
+ (table) => [index("mcp_contracts_contract_idx").on(table.contractHash)]
778
+ );
779
+ var mcpDeployments = pgTable(
780
+ "mcp_deployments",
781
+ {
782
+ id: serial("id").primaryKey(),
783
+ ownerId: text("owner_id").notNull(),
784
+ serverKey: text("server_key").notNull(),
785
+ configFingerprint: text("config_fingerprint").notNull(),
786
+ /** The name the user gave it, as last seen. Display only. */
787
+ alias: text("alias").notNull(),
788
+ registry: text("registry").$type().notNull(),
789
+ packageName: text("package_name"),
790
+ transport: text("transport").notNull(),
791
+ /** Self-reported by the server in its handshake. */
792
+ serverName: text("server_name"),
793
+ serverVersion: text("server_version"),
794
+ lastStatus: text("last_status").$type().notNull(),
795
+ lastError: text("last_error"),
796
+ /** The last contract actually read. Kept when a later scan fails. */
797
+ lastContentHash: text("last_content_hash"),
798
+ worstSeverity: text("worst_severity").$type(),
799
+ firstSeenAt: ts("first_seen_at").notNull().defaultNow(),
800
+ lastScannedAt: ts("last_scanned_at").notNull().defaultNow(),
801
+ lastChangedAt: ts("last_changed_at")
802
+ },
803
+ (table) => [
804
+ uniqueIndex("mcp_deployments_identity_idx").on(table.ownerId, table.serverKey, table.configFingerprint),
805
+ index("mcp_deployments_owner_idx").on(table.ownerId, table.lastScannedAt)
806
+ ]
807
+ );
808
+ var mcpObservations = pgTable(
809
+ "mcp_observations",
810
+ {
811
+ id: serial("id").primaryKey(),
812
+ deploymentId: integer("deployment_id").notNull().references(() => mcpDeployments.id),
813
+ ownerId: text("owner_id").notNull(),
814
+ status: text("status").$type().notNull(),
815
+ contentHash: text("content_hash"),
816
+ serverVersion: text("server_version"),
817
+ error: text("error"),
818
+ /** `cli` or `ci`: who ran the scan that opened this interval. */
819
+ source: text("source").notNull(),
820
+ scanCount: integer("scan_count").notNull().default(1),
821
+ firstSeenAt: ts("first_seen_at").notNull().defaultNow(),
822
+ lastSeenAt: ts("last_seen_at").notNull().defaultNow()
823
+ },
824
+ (table) => [index("mcp_observations_deployment_idx").on(table.deploymentId, table.firstSeenAt)]
825
+ );
826
+ var mcpChangeEvents = pgTable(
827
+ "mcp_change_events",
828
+ {
829
+ id: serial("id").primaryKey(),
830
+ deploymentId: integer("deployment_id").notNull().references(() => mcpDeployments.id),
831
+ ownerId: text("owner_id").notNull(),
832
+ fromHash: text("from_hash").notNull(),
833
+ toHash: text("to_hash").notNull(),
834
+ severity: text("severity").$type().notNull(),
835
+ summary: text("summary").notNull(),
836
+ diff: jsonb("diff").$type().notNull(),
837
+ createdAt: ts("created_at").notNull().defaultNow(),
838
+ acknowledgedAt: ts("acknowledged_at")
839
+ },
840
+ (table) => [
841
+ uniqueIndex("mcp_change_events_pair_idx").on(table.deploymentId, table.fromHash, table.toHash),
842
+ index("mcp_change_events_owner_idx").on(table.ownerId, table.createdAt),
843
+ index("mcp_change_events_created_idx").on(table.createdAt)
844
+ ]
845
+ );
846
+ var mcpPublicReports = pgTable(
847
+ "mcp_public_reports",
848
+ {
849
+ registry: text("registry").notNull(),
850
+ packageName: text("package_name").notNull(),
851
+ version: text("version").notNull(),
852
+ contentHash: text("content_hash").notNull(),
853
+ ownerId: text("owner_id").notNull(),
854
+ reportedAt: ts("reported_at").notNull().defaultNow()
855
+ },
856
+ (table) => [
857
+ primaryKey({ columns: [table.registry, table.packageName, table.version, table.contentHash, table.ownerId] }),
858
+ index("mcp_public_reports_version_idx").on(table.registry, table.packageName, table.version)
859
+ ]
860
+ );
861
+ var notificationPreferences = pgTable("notification_preferences", {
862
+ ownerId: text("owner_id").primaryKey(),
863
+ urgentEmail: boolean("urgent_email").notNull().default(true),
864
+ weeklyDigest: boolean("weekly_digest").notNull().default(false),
865
+ unsubscribeToken: text("unsubscribe_token").notNull().unique(),
866
+ lastDigestAt: ts("last_digest_at"),
867
+ createdAt: ts("created_at").notNull().defaultNow(),
868
+ updatedAt: ts("updated_at").notNull().defaultNow()
869
+ });
870
+ var notificationDeliveries = pgTable(
871
+ "notification_deliveries",
872
+ {
873
+ id: serial("id").primaryKey(),
874
+ ownerId: text("owner_id").notNull(),
875
+ kind: text("kind").$type().notNull(),
876
+ /** Set on a channel delivery: the Slack/Discord/Teams/webhook it went to. */
877
+ channelId: integer("channel_id"),
878
+ status: text("status").$type().notNull(),
879
+ idempotencyKey: text("idempotency_key").notNull().unique(),
880
+ attempts: integer("attempts").notNull().default(0),
881
+ /** Why it failed or was skipped. Our own words, never a recipient address. */
882
+ error: text("error"),
883
+ providerId: text("provider_id"),
884
+ createdAt: ts("created_at").notNull().defaultNow(),
885
+ sentAt: ts("sent_at")
886
+ },
887
+ (table) => [
888
+ index("notification_deliveries_owner_idx").on(table.ownerId, table.kind, table.createdAt),
889
+ index("notification_deliveries_status_idx").on(table.status, table.createdAt)
890
+ ]
891
+ );
892
+ var notificationItems = pgTable(
893
+ "notification_items",
894
+ {
895
+ itemKey: text("item_key").primaryKey(),
896
+ ownerId: text("owner_id").notNull(),
897
+ deliveryId: integer("delivery_id").notNull().references(() => notificationDeliveries.id),
898
+ createdAt: ts("created_at").notNull().defaultNow()
899
+ },
900
+ (table) => [index("notification_items_delivery_idx").on(table.deliveryId)]
901
+ );
902
+ var notificationChannels = pgTable(
903
+ "notification_channels",
904
+ {
905
+ id: serial("id").primaryKey(),
906
+ ownerId: text("owner_id").notNull(),
907
+ kind: text("kind").$type().notNull(),
908
+ label: text("label"),
909
+ urlCiphertext: text("url_ciphertext").notNull(),
910
+ /** Enough of the URL to recognise it: host and the last four characters. */
911
+ urlHint: text("url_hint").notNull(),
912
+ /** Webhook kind only: the HMAC secret receivers verify with. Encrypted. */
913
+ signingSecretCiphertext: text("signing_secret_ciphertext"),
914
+ minSeverity: text("min_severity").$type().notNull().default("high"),
915
+ enabled: boolean("enabled").notNull().default(true),
916
+ consecutiveFailures: integer("consecutive_failures").notNull().default(0),
917
+ /** Why lurq switched it off, in words for the owner. Cleared on re-enable. */
918
+ disabledReason: text("disabled_reason"),
919
+ lastDeliveredAt: ts("last_delivered_at"),
920
+ lastError: text("last_error"),
921
+ createdAt: ts("created_at").notNull().defaultNow(),
922
+ updatedAt: ts("updated_at").notNull().defaultNow(),
923
+ deletedAt: ts("deleted_at")
924
+ },
925
+ (table) => [index("notification_channels_owner_idx").on(table.ownerId)]
926
+ );
927
+
928
+ export {
929
+ packages,
930
+ syncRuns,
931
+ seedPackages,
932
+ discoveryQueue,
933
+ compatVerifyQueue,
934
+ apiKeys,
935
+ packageVersions,
936
+ watchState,
937
+ verificationRuns,
938
+ compatEdges,
939
+ apiSurfaces,
940
+ recommendationOutcomes,
941
+ askSpendDaily,
942
+ ownerUsageDaily,
943
+ repos,
944
+ upgradeRuns,
945
+ repoAlerts,
946
+ surfaceQueue,
947
+ entities,
948
+ UNBOUNDED_ARITY,
949
+ symbols,
950
+ claims,
951
+ observations,
952
+ selectionPolicies,
953
+ selectionPolicyChanges,
954
+ policyDecisions,
955
+ subscriptions,
956
+ stackResolutions,
957
+ mcpContracts,
958
+ mcpDeployments,
959
+ mcpObservations,
960
+ mcpChangeEvents,
961
+ mcpPublicReports,
962
+ notificationPreferences,
963
+ notificationChannels,
964
+ schema_exports
965
+ };