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,146 @@
1
+ import { createRequire as __lurqCreateRequire } from 'node:module'; const require = __lurqCreateRequire(import.meta.url);
2
+ import {
3
+ parseManifest
4
+ } from "./chunk-DWAGCNEL.js";
5
+ import "./chunk-B43LCXBM.js";
6
+ import "./chunk-BCANRN5B.js";
7
+ import "./chunk-SJOVFF7B.js";
8
+ import "./chunk-GRJXQBQZ.js";
9
+ import "./chunk-WHX2ZFCG.js";
10
+ import {
11
+ fetchUpgradePlan
12
+ } from "./chunk-4ZYCIMY4.js";
13
+ import "./chunk-MYGSLK44.js";
14
+ import "./chunk-Y5GI7RL3.js";
15
+ import "./chunk-GPTKSNR6.js";
16
+ import {
17
+ init_esm_shims
18
+ } from "./chunk-I3M2KJXB.js";
19
+
20
+ // src/cli/upgradePlan.ts
21
+ init_esm_shims();
22
+ import { readFileSync, readdirSync, statSync } from "fs";
23
+ import { join, relative } from "path";
24
+ var SKIP = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", ".next", "coverage", "vendor"]);
25
+ var MAX_MANIFESTS = 25;
26
+ var MAX_DEPTH = 4;
27
+ function findManifests(dir, maxDepth = MAX_DEPTH) {
28
+ const found = [];
29
+ const walk = (current, depth) => {
30
+ if (found.length >= MAX_MANIFESTS || depth > maxDepth) return;
31
+ let entries;
32
+ try {
33
+ entries = readdirSync(current);
34
+ } catch {
35
+ return;
36
+ }
37
+ if (entries.includes("package.json")) found.push(join(current, "package.json"));
38
+ for (const entry of entries) {
39
+ if (SKIP.has(entry) || entry.startsWith(".")) continue;
40
+ const child = join(current, entry);
41
+ try {
42
+ if (statSync(child).isDirectory()) walk(child, depth + 1);
43
+ } catch {
44
+ }
45
+ }
46
+ };
47
+ walk(dir, 0);
48
+ return found;
49
+ }
50
+ function readManifests(dir) {
51
+ const out = [];
52
+ for (const file of findManifests(dir)) {
53
+ try {
54
+ const parsed = parseManifest(
55
+ relative(dir, file) || "package.json",
56
+ JSON.parse(readFileSync(file, "utf8"))
57
+ );
58
+ if (parsed) out.push(parsed);
59
+ } catch {
60
+ }
61
+ }
62
+ return out;
63
+ }
64
+ function mergedDeps(manifests) {
65
+ const out = {};
66
+ for (const manifest of manifests) {
67
+ for (const [name, range] of Object.entries(manifest.deps)) {
68
+ out[name] ??= range;
69
+ }
70
+ }
71
+ return out;
72
+ }
73
+ function detectRepoFullName(explicit) {
74
+ const value = explicit ?? process.env.GITHUB_REPOSITORY ?? null;
75
+ return value && /^[\w.-]+\/[\w.-]+$/.test(value) ? value : null;
76
+ }
77
+ async function buildUpgradePlan(dir, opts = {}) {
78
+ const manifests = readManifests(dir);
79
+ const deps = mergedDeps(manifests);
80
+ if (Object.keys(deps).length === 0) {
81
+ throw new Error(`No package.json with dependencies found under ${dir}`);
82
+ }
83
+ const plan = await fetchUpgradePlan(deps, { ...opts, repo: detectRepoFullName(opts.repo) });
84
+ return { ...plan, manifests: manifests.length, deps: Object.keys(deps).length };
85
+ }
86
+ var VERDICT_LABEL = {
87
+ "removes-exports": "REMOVES ",
88
+ "arity-changed": "ARITY ",
89
+ clean: "CLEAN ",
90
+ unknown: "UNKNOWN "
91
+ };
92
+ function formatUpgradePlan(plan) {
93
+ const out = [
94
+ `lurq upgrade plan (${plan.deps} dependencies across ${plan.manifests} manifest(s))`
95
+ ];
96
+ if (plan.scopeSource === "repo-policy") {
97
+ out.push(
98
+ `policy: scope=${plan.scope}` + (plan.outOfScope ? `, ${plan.outOfScope} upgrade(s) held back` : ", nothing held back")
99
+ );
100
+ }
101
+ out.push("");
102
+ if (plan.upgrades.length === 0) {
103
+ out.push("Everything lurq tracks is already on its latest release.");
104
+ }
105
+ for (const upgrade of plan.upgrades) {
106
+ const held = upgrade.inScope === false ? " [HELD]" : "";
107
+ out.push(
108
+ `${VERDICT_LABEL[upgrade.verdict]} ${upgrade.package} ${upgrade.fromVersion} \u2192 ${upgrade.toVersion}` + (upgrade.advisories ? ` (${upgrade.advisories} advisory)` : "") + held
109
+ );
110
+ if (upgrade.scopeReason) out.push(` held: ${upgrade.scopeReason}`);
111
+ if (upgrade.removed.length) {
112
+ const renamed = new Map((upgrade.renamed ?? []).map((r) => [r.path, r.to]));
113
+ const label = (path) => renamed.has(path) ? `${path} \u2192 ${renamed.get(path).join(" | ")}` : path;
114
+ out.push(` removes ${upgrade.removed.length}: ${upgrade.removed.slice(0, 8).map(label).join(", ")}`);
115
+ }
116
+ for (const change of upgrade.arityChanged.slice(0, 5)) {
117
+ out.push(` arity ${change.path}: ${change.from ?? "?"} \u2192 ${change.to ?? "?"}`);
118
+ }
119
+ if (upgrade.hops?.length) {
120
+ out.push(` via ${[upgrade.fromVersion, ...upgrade.hops.map((h) => h.toVersion)].join(" \u2192 ")}`);
121
+ for (const hop of upgrade.hops) {
122
+ const detail = hop.removed.length ? `removes ${hop.removed.slice(0, 5).join(", ")}` : hop.verdict === "unknown" ? "not yet analysed" : "no removals";
123
+ out.push(` ${hop.fromVersion} \u2192 ${hop.toVersion}: ${detail}`);
124
+ }
125
+ }
126
+ if (upgrade.sequenceNote) out.push(` ${upgrade.sequenceNote}`);
127
+ if (upgrade.declaredIn?.length > 1) {
128
+ out.push(` declared in ${upgrade.declaredIn.map((d) => d.path).join(", ")}`);
129
+ }
130
+ if (upgrade.inconclusive) out.push(` ${upgrade.inconclusive}`);
131
+ }
132
+ const caveats = [];
133
+ if (plan.untracked > 0) caveats.push(`${plan.untracked} dependency(ies) not in the lurq index`);
134
+ if (plan.omitted > 0) caveats.push(`${plan.omitted} further upgrade(s) not shown`);
135
+ if (plan.pending > 0) caveats.push(`${plan.pending} awaiting surface extraction`);
136
+ if (caveats.length) out.push("", `NOT COVERED: ${caveats.join("; ")}.`);
137
+ return out.join("\n");
138
+ }
139
+ export {
140
+ buildUpgradePlan,
141
+ detectRepoFullName,
142
+ findManifests,
143
+ formatUpgradePlan,
144
+ mergedDeps,
145
+ readManifests
146
+ };
@@ -0,0 +1,20 @@
1
+ import { createRequire as __lurqCreateRequire } from 'node:module'; const require = __lurqCreateRequire(import.meta.url);
2
+ import {
3
+ clearUserConfig,
4
+ lurqHome,
5
+ readUserConfig,
6
+ resolveApiKey,
7
+ resolveEndpoint,
8
+ userConfigPath,
9
+ writeUserConfig
10
+ } from "./chunk-Y5GI7RL3.js";
11
+ import "./chunk-I3M2KJXB.js";
12
+ export {
13
+ clearUserConfig,
14
+ lurqHome,
15
+ readUserConfig,
16
+ resolveApiKey,
17
+ resolveEndpoint,
18
+ userConfigPath,
19
+ writeUserConfig
20
+ };
@@ -0,0 +1,214 @@
1
+ import { createRequire as __lurqCreateRequire } from 'node:module'; const require = __lurqCreateRequire(import.meta.url);
2
+ import {
3
+ enqueueSurface,
4
+ getMcpServerNames
5
+ } from "./chunk-LO35HLQZ.js";
6
+ import {
7
+ syncOnePackage
8
+ } from "./chunk-FW6BD2NO.js";
9
+ import "./chunk-BUALU27S.js";
10
+ import "./chunk-LRD4GBYE.js";
11
+ import "./chunk-BCANRN5B.js";
12
+ import "./chunk-2VMOWSRY.js";
13
+ import "./chunk-4ESO33J6.js";
14
+ import "./chunk-CW5HJ3LX.js";
15
+ import "./chunk-AHBP533R.js";
16
+ import "./chunk-SJOVFF7B.js";
17
+ import "./chunk-HWBBVLOT.js";
18
+ import {
19
+ getAllPackageNames
20
+ } from "./chunk-NH5I4KHH.js";
21
+ import "./chunk-VLRHFMGJ.js";
22
+ import "./chunk-WQ23REYT.js";
23
+ import {
24
+ discoveryQueue,
25
+ watchState
26
+ } from "./chunk-LYMPYETA.js";
27
+ import "./chunk-3GHJR7XR.js";
28
+ import "./chunk-GRJXQBQZ.js";
29
+ import "./chunk-WHX2ZFCG.js";
30
+ import "./chunk-LUAKDHLZ.js";
31
+ import {
32
+ logger
33
+ } from "./chunk-MYGSLK44.js";
34
+ import "./chunk-GPTKSNR6.js";
35
+ import {
36
+ init_esm_shims
37
+ } from "./chunk-I3M2KJXB.js";
38
+
39
+ // src/pipeline/watch.ts
40
+ init_esm_shims();
41
+
42
+ // src/db/discovery.ts
43
+ init_esm_shims();
44
+ import { eq, sql } from "drizzle-orm";
45
+ async function getQueuedNames(db) {
46
+ const rows = await db.select({ name: discoveryQueue.name }).from(discoveryQueue);
47
+ return new Set(rows.map((r) => r.name));
48
+ }
49
+ async function enqueueCandidates(db, candidates) {
50
+ if (candidates.length === 0) return 0;
51
+ const rows = candidates.map((c) => ({ name: c.name, discoveredVia: c.via }));
52
+ const inserted = await db.insert(discoveryQueue).values(rows).onConflictDoNothing({ target: discoveryQueue.name }).returning({ id: discoveryQueue.id });
53
+ return inserted.length;
54
+ }
55
+
56
+ // src/db/watch.ts
57
+ init_esm_shims();
58
+ import { eq as eq2 } from "drizzle-orm";
59
+ async function getWatchCursor(db, id) {
60
+ const rows = await db.select({ seq: watchState.seq }).from(watchState).where(eq2(watchState.id, id)).limit(1);
61
+ return rows[0]?.seq ?? null;
62
+ }
63
+ async function setWatchCursor(db, id, seq) {
64
+ await db.insert(watchState).values({ id, seq, updatedAt: /* @__PURE__ */ new Date() }).onConflictDoUpdate({ target: watchState.id, set: { seq, updatedAt: /* @__PURE__ */ new Date() } });
65
+ }
66
+
67
+ // src/pipeline/watch.ts
68
+ var FEED_ID = "npm-changes";
69
+ var FEED_URL = "https://replicate.npmjs.com/_changes";
70
+ var PAGE_SIZE = 1e3;
71
+ var IDLE_POLL_MS = 3e4;
72
+ var TRACKED_REFRESH_MS = 5 * 6e4;
73
+ var MAX_BACKOFF_MS = 3e4;
74
+ var ENQUEUE_BATCH = 200;
75
+ function parseChange(obj) {
76
+ if (typeof obj !== "object" || obj === null) return null;
77
+ const rec = obj;
78
+ if (typeof rec.id !== "string" || rec.seq == null) return null;
79
+ if (typeof rec.seq !== "number" && typeof rec.seq !== "string") return null;
80
+ return { seq: rec.seq, id: rec.id, deleted: Boolean(rec.deleted) };
81
+ }
82
+ function parseChangesPage(body) {
83
+ if (typeof body !== "object" || body === null) return { changes: [], lastSeq: null };
84
+ const { results, last_seq: lastSeq } = body;
85
+ const changes = Array.isArray(results) ? results.map(parseChange).filter((c) => c !== null) : [];
86
+ return {
87
+ changes,
88
+ lastSeq: typeof lastSeq === "number" || typeof lastSeq === "string" ? String(lastSeq) : null
89
+ };
90
+ }
91
+ function routeChange(change, tracked, queued) {
92
+ if (change.deleted) return "skip";
93
+ if (tracked.has(change.id)) return "resync";
94
+ if (queued.has(change.id)) return "skip";
95
+ return "enqueue";
96
+ }
97
+ async function getJson(url, fetchImpl, signal) {
98
+ const res = await fetchImpl(url, { signal });
99
+ if (!res.ok) throw new Error(`feed responded ${res.status}`);
100
+ return res.json();
101
+ }
102
+ async function fetchHeadSeq(fetchImpl = fetch, signal) {
103
+ const body = await getJson(`${FEED_URL}?descending=true&limit=1`, fetchImpl, signal);
104
+ return parseChangesPage(body).lastSeq;
105
+ }
106
+ async function loadSeenSets(db) {
107
+ const [tracked, queued, mcp] = await Promise.all([
108
+ getAllPackageNames(db),
109
+ getQueuedNames(db),
110
+ // Derived from what has actually been probed, so the roster grows with real
111
+ // demand instead of with a curated list somebody has to maintain. Failure
112
+ // is non-fatal: the npm half of the loop must keep running regardless.
113
+ getMcpServerNames(db).catch(() => [])
114
+ ]);
115
+ return [new Set(tracked), queued, new Set(mcp)];
116
+ }
117
+ async function watchNpmChanges(db, opts = {}) {
118
+ const { signal, fetchImpl = fetch } = opts;
119
+ let backoff = 1e3;
120
+ let [tracked, queued, mcpServers] = await loadSeenSets(db);
121
+ let seenAt = Date.now();
122
+ let cursor = await getWatchCursor(db, FEED_ID) ?? opts.since ?? await fetchHeadSeq(fetchImpl, signal).catch(() => null);
123
+ if (cursor == null) {
124
+ logger.warn("watch: could not establish a starting sequence; giving up.");
125
+ return;
126
+ }
127
+ logger.info(
128
+ `watch: following from seq=${cursor} (${tracked.size} tracked, ${queued.size} queued, ${mcpServers.size} mcp)`
129
+ );
130
+ while (!signal?.aborted) {
131
+ try {
132
+ const url = `${FEED_URL}?since=${encodeURIComponent(cursor)}&limit=${PAGE_SIZE}`;
133
+ const { changes, lastSeq } = parseChangesPage(await getJson(url, fetchImpl, signal));
134
+ backoff = 1e3;
135
+ if (Date.now() - seenAt > TRACKED_REFRESH_MS) {
136
+ [tracked, queued, mcpServers] = await loadSeenSets(db);
137
+ seenAt = Date.now();
138
+ }
139
+ const buffered = [];
140
+ const flush = async () => {
141
+ if (buffered.length === 0) return;
142
+ const batch = buffered.map((name) => ({ name, via: "npm-changes" }));
143
+ const inserted = await enqueueCandidates(db, batch).catch((err) => {
144
+ logger.warn(`watch: enqueue of ${batch.length} candidate(s) failed: ${String(err)}`);
145
+ return 0;
146
+ });
147
+ if (inserted > 0) logger.info(`watch: queued ${inserted} new discovery candidate(s)`);
148
+ buffered.length = 0;
149
+ };
150
+ let resynced = 0;
151
+ let mcpQueued = 0;
152
+ for (const change of changes) {
153
+ if (signal?.aborted) break;
154
+ const route = routeChange(change, tracked, queued);
155
+ if (route === "resync") {
156
+ await syncOnePackage(db, change.id).catch(
157
+ (err) => logger.warn(`watch: re-sync failed for ${change.id}: ${String(err)}`)
158
+ );
159
+ resynced++;
160
+ await enqueueSurface(db, change.id, null).catch(
161
+ (err) => logger.warn(`watch: surface enqueue failed for ${change.id}: ${String(err)}`)
162
+ );
163
+ if (mcpServers.has(change.id)) {
164
+ await enqueueSurface(db, change.id, null, "mcp_server").catch(
165
+ (err) => logger.warn(`watch: mcp probe enqueue failed for ${change.id}: ${String(err)}`)
166
+ );
167
+ mcpQueued++;
168
+ }
169
+ } else if (route === "enqueue") {
170
+ queued.add(change.id);
171
+ buffered.push(change.id);
172
+ if (buffered.length >= ENQUEUE_BATCH) await flush();
173
+ }
174
+ }
175
+ await flush();
176
+ if (lastSeq !== null && lastSeq !== cursor) {
177
+ cursor = lastSeq;
178
+ await setWatchCursor(db, FEED_ID, cursor);
179
+ }
180
+ if (changes.length > 0) {
181
+ logger.info(
182
+ `watch: ${changes.length} change(s), ${resynced} re-synced` + (mcpQueued ? `, ${mcpQueued} mcp re-probe(s)` : "") + `, seq=${cursor}`
183
+ );
184
+ }
185
+ if (changes.length < PAGE_SIZE) await sleep(IDLE_POLL_MS, signal);
186
+ } catch (err) {
187
+ if (signal?.aborted) break;
188
+ logger.warn(`watch: ${String(err)}, retrying in ${backoff}ms`);
189
+ await sleep(backoff, signal);
190
+ backoff = Math.min(backoff * 2, MAX_BACKOFF_MS);
191
+ }
192
+ }
193
+ logger.info("watch: stopped.");
194
+ }
195
+ function sleep(ms, signal) {
196
+ return new Promise((resolve) => {
197
+ const timer = setTimeout(resolve, ms);
198
+ signal?.addEventListener(
199
+ "abort",
200
+ () => {
201
+ clearTimeout(timer);
202
+ resolve();
203
+ },
204
+ { once: true }
205
+ );
206
+ });
207
+ }
208
+ export {
209
+ fetchHeadSeq,
210
+ parseChange,
211
+ parseChangesPage,
212
+ routeChange,
213
+ watchNpmChanges
214
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lurqrun",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "description": "Verify npm packages before your AI coding agent installs them: hallucinated and typosquatted names, advisories, API drift and version conflicts. MCP server, CLI and agent skill for Claude Code, Cursor and VS Code.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -40,13 +40,32 @@
40
40
  ],
41
41
  "files": [
42
42
  "dist",
43
- "src/data/seed.json",
44
43
  "templates",
45
- "drizzle",
46
44
  "README.md"
47
45
  ],
48
46
  "engines": {
49
- "node": ">=20"
47
+ "node": ">=20.17.0"
48
+ },
49
+ "lurq": {
50
+ "inlinedDependencies": [
51
+ "@inquirer/prompts",
52
+ "@modelcontextprotocol/sdk",
53
+ "commander",
54
+ "dotenv",
55
+ "semver",
56
+ "yaml",
57
+ "zod"
58
+ ],
59
+ "selfHostDependencies": [
60
+ "drizzle-orm",
61
+ "express",
62
+ "express-rate-limit",
63
+ "helmet",
64
+ "ioredis",
65
+ "postgres",
66
+ "rate-limit-redis",
67
+ "stripe"
68
+ ]
50
69
  },
51
70
  "scripts": {
52
71
  "build": "tsup",
@@ -66,29 +85,15 @@
66
85
  "db:migrate": "tsx src/bin/operator.ts db migrate",
67
86
  "operator": "tsx src/bin/operator.ts",
68
87
  "operator:prod": "LURQ_ENV_FILE=.env.production tsx src/bin/operator.ts",
88
+ "prepack": "node scripts/publish-manifest.mjs strip",
89
+ "postpack": "node scripts/publish-manifest.mjs restore",
69
90
  "prepublishOnly": "node scripts/assert-publish-from-main.mjs && npm test && npm run build",
70
91
  "content:drift": "tsx scripts/gen-drift.ts",
71
92
  "content:landing": "tsx scripts/build-landing-content.mts",
72
93
  "content:releases": "tsx scripts/gen-releases.ts"
73
94
  },
74
95
  "dependencies": {
75
- "@inquirer/prompts": "^8.5.2",
76
- "@modelcontextprotocol/sdk": "^1.29.0",
77
- "commander": "^12.1.0",
78
- "dotenv": "^16.4.5",
79
- "drizzle-orm": "^0.45.2",
80
- "express": "^5.2.1",
81
- "express-rate-limit": "^8.5.2",
82
- "helmet": "^8.2.0",
83
- "ioredis": "^5.11.1",
84
- "postgres": "^3.4.9",
85
- "rate-limit-redis": "^4.3.1",
86
- "semver": "^7.8.5",
87
- "stripe": "^22.6.0",
88
- "typescript": "^5.5.4",
89
- "update-notifier": "^7.3.1",
90
- "yaml": "^2.9.0",
91
- "zod": "^3.23.8"
96
+ "typescript": "^5.5.4"
92
97
  },
93
98
  "devDependencies": {
94
99
  "@eslint/js": "^9.9.0",
@@ -8,13 +8,13 @@ Use it instead of recalling package facts:
8
8
 
9
9
  - `verify <pkg>` before adding any dependency. Catches hallucinated, typosquatted,
10
10
  deprecated, and advisory-carrying names. Cheapest and highest-value call.
11
- - `recommend "<need>"` before picking a library or hand-rolling something that may already
12
- exist. Returns scored candidates with `proven` / `emerging` / `unproven` labels.
13
- - `compare <pkgs>` to choose between options; `evaluate <pkg>` for the full evidence read.
11
+ - `compare <pkgs>` to choose between candidate libraries you already know of; `evaluate <pkg>`
12
+ for the full evidence read. lurq checks candidates, it does not search for them.
14
13
  - `compat <pkgs>` before committing to a multi-package stack. Healthy packages can still
15
14
  refuse to install together.
16
15
  - `usage <pkg> --known <version-you-remember>` before writing code against an API that may
17
- have moved. Returns real exported symbols plus the delta from the version you know.
16
+ have moved. Returns real exported symbols plus the delta from the version you know. Large
17
+ surfaces are paged: pass `query` with part of a name to find a symbol directly.
18
18
  - `resolve_surface <pkg>` when you need to know whether a symbol exists at runtime, not
19
19
  just in the types. A removed type breaks `tsc`; a removed runtime export breaks the
20
20
  program. UNKNOWN means "not extracted yet", never "absent".
@@ -16,12 +16,10 @@ reads like a guess.
16
16
 
17
17
  ## Call lurq when you are about to:
18
18
 
19
- - **Pick a library for a need** → `recommend` with a natural-language description
20
- (e.g. "a form library for React", "an ORM for Postgres"). Returns up to 5 scored
21
- candidates. Do this *before* settling on a dependency from memory.
22
- - **Hand-roll something that might already exist** → `recommend` first
23
- (e.g. "debounce a function", "deep clone an object", "parse dates"). Don't rebuild a
24
- well-maintained, proven package.
19
+ - **Pick a library for a need** → name the candidates you know and `compare` them, then
20
+ `verify` the one you choose. lurq checks candidates against evidence; it does not
21
+ search for them, so the list is yours to bring. The same applies before hand-rolling
22
+ something a well-maintained package may already do.
25
23
  - **Install a specific package** → `verify` with the exact name *before* adding it.
26
24
  This catches hallucinated, deprecated, and typosquatted names (e.g. `lodahs` vs
27
25
  `lodash`) and packages with known advisories. Cheap, and the highest-value call here.
@@ -31,17 +29,21 @@ reads like a guess.
31
29
  and a usage guide (what it is, when to use it, how it fits).
32
30
  - **Adding dependencies in a team codebase** → `policy` once, before choosing. It lists
33
31
  the packages and thresholds the team's policy refuses, so you pick an allowed package
34
- first instead of being refused after. `recommend` and `evaluate` enforce it either way.
32
+ first instead of being refused after. `evaluate` enforces it either way.
35
33
  - **Commit to a multi-package stack** → `compat` with the whole set. Individually healthy
36
34
  packages can still refuse to install together; this returns the exact clashing peer or
37
- engine constraints, plus any conflicts already proven in a sandbox. Read-only and
38
- instant — it does not run an install.
35
+ engine constraints, plus any conflicts already proven in a sandbox. Read-only — it never
36
+ runs an install. A set checked before answers immediately; a new one is resolved live
37
+ from registry metadata and can take up to ~25 seconds.
39
38
  - **Write code against a package whose API may have moved** → `usage` with the package
40
39
  and, if you know it, `knownVersion`. Returns the real exported symbols and signatures
41
40
  extracted from that version's shipped `.d.ts`, plus the precise delta from the version
42
41
  you remember: what was added, removed, renamed, or changed. None of this is in your
43
42
  training data, and it is the difference between calling a function that exists and one
44
- that used to.
43
+ that used to. Large surfaces are paged 80 symbols at a time: pass `query` with part of a
44
+ name to go straight to a symbol, or `offset` for the next page. `shallow: true` means the
45
+ API lives on an interface's members that are not listed (DefinitelyTyped's `export =`
46
+ shape, e.g. lodash); read its type declarations or use `resolve_surface`.
45
47
  - **Check whether a symbol actually exists at runtime** → `resolve_surface` with the
46
48
  package (and version, if you have one). `usage` reads the shipped `.d.ts`; this reads
47
49
  the shipped JavaScript, and the difference matters: a removed *type* breaks `tsc`, a
@@ -82,16 +84,13 @@ reads like a guess.
82
84
  can catch it, and the symptom is a malformed call that looks like a model mistake.
83
85
  **Privilege widening** is a tool that stopped being read-only or started being
84
86
  destructive; nothing breaks, which is what makes it worse than a break.
85
- - **Build a whole project from a spec** → `plan` with the program description. Returns a
86
- scored package per component plus a Mermaid roadmap. It recommends building blocks
87
- slot-by-slot from the index; it does not invent an architecture from a bare prompt.
88
87
  - **Visualize a stack you have already chosen** → `diagram` with the package names. A
89
88
  labeled starting point by layer — not a validated architecture.
90
89
  - **Unsure whether lurq covers the situation** → `capabilities` with what you are trying
91
90
  to do, in plain words. Returns the matching tools and commands rather than prose, so a
92
91
  capability you did not know about becomes a call you can make. Cheaper than guessing,
93
92
  and far cheaper than skipping a check that exists.
94
- - **After you act on a recommendation** → `report_outcome` (optional) with whether you
93
+ - **After you act on lurq's evidence about a package** → `report_outcome` (optional) with whether you
95
94
  used the package and whether it built. No source code, just the coarse decision and a
96
95
  build signal. It is how lurq learns which packages agents actually succeed with.
97
96