lurqrun 0.1.6 → 0.1.8

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 (239) hide show
  1. package/README.md +311 -139
  2. package/dist/auditHandler-VWCXSDVO.js +24 -0
  3. package/dist/autopilotInit-Q62MX5M6.js +178 -0
  4. package/dist/bin/lurq.js +3255 -23319
  5. package/dist/browserAuth-J7APSD4P.js +96 -0
  6. package/dist/cache-AEAKCV4I.js +12 -0
  7. package/dist/check-2JTQDQA6.js +2613 -0
  8. package/dist/check-PQSQ2MMA.js +192 -0
  9. package/dist/checkUpgrade-5YW77OBL.js +24 -0
  10. package/dist/chunk-2GAYNIHG.js +40 -0
  11. package/dist/chunk-2O6Z37IT.js +423 -0
  12. package/dist/chunk-2WD4QAH4.js +116 -0
  13. package/dist/chunk-3676XOBU.js +2235 -0
  14. package/dist/chunk-3D2MPGOQ.js +181 -0
  15. package/dist/chunk-3GHJR7XR.js +2049 -0
  16. package/dist/chunk-4ESO33J6.js +34 -0
  17. package/dist/chunk-4JHDSVIN.js +1229 -0
  18. package/dist/chunk-4KCPOMG6.js +68 -0
  19. package/dist/chunk-53SG6XWA.js +141 -0
  20. package/dist/chunk-5ABNDB3I.js +37 -0
  21. package/dist/chunk-5ALIJFDA.js +114 -0
  22. package/dist/chunk-63MUJNZL.js +318 -0
  23. package/dist/chunk-64ZF7KO4.js +1571 -0
  24. package/dist/chunk-6DM6UQFD.js +318 -0
  25. package/dist/chunk-6EQ3ARXA.js +304 -0
  26. package/dist/chunk-6L67QMCP.js +92 -0
  27. package/dist/chunk-72Y2FDVS.js +172 -0
  28. package/dist/chunk-A3TYI2TF.js +288 -0
  29. package/dist/chunk-B2UUYRBX.js +536 -0
  30. package/dist/chunk-BJT2LHAR.js +320 -0
  31. package/dist/chunk-BM3LF5T6.js +132 -0
  32. package/dist/chunk-BN3NM47Q.js +214 -0
  33. package/dist/chunk-BUALU27S.js +123 -0
  34. package/dist/chunk-C6GLE626.js +160 -0
  35. package/dist/chunk-D3K6IZVR.js +12845 -0
  36. package/dist/chunk-D3VTZS7V.js +222 -0
  37. package/dist/chunk-DM37MTOO.js +636 -0
  38. package/dist/chunk-DONTW3NE.js +171 -0
  39. package/dist/chunk-DSROTZ3Z.js +38 -0
  40. package/dist/chunk-ELGCAM7P.js +184 -0
  41. package/dist/chunk-EQCXENKZ.js +130 -0
  42. package/dist/chunk-EYAKM6YN.js +96 -0
  43. package/dist/chunk-FDFPMBCA.js +112 -0
  44. package/dist/chunk-FVYFV5HA.js +154 -0
  45. package/dist/chunk-GE37NVLN.js +15 -0
  46. package/dist/chunk-GM5Y555O.js +143 -0
  47. package/dist/chunk-GRJXQBQZ.js +793 -0
  48. package/dist/chunk-HWFX4BC4.js +177 -0
  49. package/dist/chunk-I3M2KJXB.js +56 -0
  50. package/dist/chunk-IFNONGRQ.js +603 -0
  51. package/dist/chunk-J3U2CPSK.js +136 -0
  52. package/dist/chunk-J6XQ7OPZ.js +74 -0
  53. package/dist/chunk-KMV4Y6R3.js +127 -0
  54. package/dist/chunk-KP5ZDDPE.js +340 -0
  55. package/dist/chunk-KYKSZ3XZ.js +202 -0
  56. package/dist/chunk-LEGPB7DO.js +81 -0
  57. package/dist/chunk-LUAKDHLZ.js +40 -0
  58. package/dist/chunk-MHUEVBVO.js +35 -0
  59. package/dist/chunk-MYGSLK44.js +37 -0
  60. package/dist/chunk-NEZNAPPN.js +41 -0
  61. package/dist/chunk-NPVN2THM.js +197 -0
  62. package/dist/chunk-NYZU5NCW.js +23 -0
  63. package/dist/chunk-O4KHCV6I.js +320 -0
  64. package/dist/chunk-ONDVTXUO.js +240 -0
  65. package/dist/chunk-OTJFHESY.js +176 -0
  66. package/dist/chunk-P6MIKSPY.js +243 -0
  67. package/dist/chunk-Q5VPF4WI.js +26 -0
  68. package/dist/chunk-QCRBX3LV.js +165 -0
  69. package/dist/chunk-QKGYDMWB.js +7409 -0
  70. package/dist/chunk-QSL6QN5X.js +115 -0
  71. package/dist/chunk-QUIMQ7YL.js +73 -0
  72. package/dist/chunk-RG3YLU6E.js +49 -0
  73. package/dist/chunk-SHCEBP3J.js +107 -0
  74. package/dist/chunk-SHZFOSXC.js +462 -0
  75. package/dist/chunk-SOXFMMXC.js +151 -0
  76. package/dist/chunk-TDTYV6QI.js +119 -0
  77. package/dist/chunk-TNTXWSYA.js +19 -0
  78. package/dist/chunk-TUXQ32WE.js +2251 -0
  79. package/dist/chunk-VGCDRK3S.js +37 -0
  80. package/dist/chunk-VLRHFMGJ.js +58 -0
  81. package/dist/chunk-WHX2ZFCG.js +4048 -0
  82. package/dist/chunk-X4W64MHU.js +2005 -0
  83. package/dist/chunk-XBQA4I36.js +92 -0
  84. package/dist/chunk-XGOV6KV4.js +413 -0
  85. package/dist/chunk-XIFQIFSZ.js +940 -0
  86. package/dist/chunk-XLXPVN3Y.js +15393 -0
  87. package/dist/chunk-ZJMLQ37G.js +90 -0
  88. package/dist/client-MU4EUDZV.js +15 -0
  89. package/dist/commands-UOOTMBKA.js +1023 -0
  90. package/dist/compat-Q5DVPITN.js +201 -0
  91. package/dist/constants-DJFOFZ44.js +24 -0
  92. package/dist/dashboardIssue-KBKLQMGC.js +274 -0
  93. package/dist/diff-I4SNUHU3.js +13 -0
  94. package/dist/dist-4XXIXHHZ.js +12945 -0
  95. package/dist/domains-VRT4BRVZ.js +14 -0
  96. package/dist/e2b-FVPHDULZ.js +172 -0
  97. package/dist/env-UN6KGZ7S.js +15 -0
  98. package/dist/envCheck-KUPTORFQ.js +71 -0
  99. package/dist/extract-6WG265BL.js +455 -0
  100. package/dist/fetch-PJ3SC32M.js +22 -0
  101. package/dist/firstTouch-COJCMOB3.js +55 -0
  102. package/dist/fix-3EDWNKW3.js +220 -0
  103. package/dist/handlers-4Q37WIFT.js +55 -0
  104. package/dist/hook-NA66XJLC.js +412 -0
  105. package/dist/http-25GJ6SKK.js +6928 -0
  106. package/dist/ingestQueue-FSVJC3AA.js +37 -0
  107. package/dist/install-G2HHXYJ2.js +20 -0
  108. package/dist/installSkill-5TIMYIJZ.js +70 -0
  109. package/dist/inventory-5AWSLJYU.js +31 -0
  110. package/dist/local-XPZMRH5R.js +13 -0
  111. package/dist/manifest-ENMIC54B.js +13 -0
  112. package/dist/mcp-PENLL2PK.js +500 -0
  113. package/dist/mcpConfig-3LYPRY5Q.js +80 -0
  114. package/dist/mcpHandlers-PO7GENA7.js +22 -0
  115. package/dist/mcpScan-ULP626TQ.js +1735 -0
  116. package/dist/mcpScanWorkflow-27O44FOE.js +98 -0
  117. package/dist/mcpStack-ZNYIFXZ5.js +20 -0
  118. package/dist/model-ASMZWLM7.js +214 -0
  119. package/dist/packages-RWF2LXST.js +40 -0
  120. package/dist/plan-SDILIDZT.js +465 -0
  121. package/dist/planView-ZAKRTGVS.js +82 -0
  122. package/dist/policy-JAGNMQJI.js +115 -0
  123. package/dist/probe-3IK646WN.js +610 -0
  124. package/dist/publicUpgrades-WTBGLDIK.js +42 -0
  125. package/dist/references-I6Y4BSRR.js +16 -0
  126. package/dist/release-7IU2R4YG.js +128 -0
  127. package/dist/remote-FN6225AH.js +42 -0
  128. package/dist/rename-OS6JYBZQ.js +204 -0
  129. package/dist/reportRuns-O4X7ZFTX.js +77 -0
  130. package/dist/safeFetch-COERXGFN.js +19 -0
  131. package/dist/sandbox-SAPSRKEI.js +39 -0
  132. package/dist/sarif-WLVJFJ7U.js +125 -0
  133. package/dist/server-OLTY3NYT.js +59 -0
  134. package/dist/surface-7MHE3RL7.js +143 -0
  135. package/dist/surface-JS2XU3U3.js +38 -0
  136. package/dist/surfaceHandlers-4B6TMZ34.js +20 -0
  137. package/dist/typecheck-NGOKNAJY.js +282 -0
  138. package/dist/types-M5JP2W3D.js +10 -0
  139. package/dist/uninstall-I2IJP5JZ.js +200 -0
  140. package/dist/upgrade-FS26DJGG.js +650 -0
  141. package/dist/upgradePlan-ZS5IOVMZ.js +181 -0
  142. package/dist/upkeepHandler-VFGYXJKB.js +13 -0
  143. package/dist/userConfig-ICASHI47.js +20 -0
  144. package/dist/watch-OCNEUXG6.js +204 -0
  145. package/package.json +26 -20
  146. package/templates/agent-rules.md +4 -4
  147. package/templates/skill-instructions.md +35 -17
  148. package/dist/bin/lurq.js.map +0 -1
  149. package/dist/index.d.ts +0 -805
  150. package/dist/index.js +0 -23570
  151. package/dist/index.js.map +0 -1
  152. package/drizzle/0000_confused_fabian_cortez.sql +0 -54
  153. package/drizzle/0001_aspiring_timeslip.sql +0 -15
  154. package/drizzle/0002_lyrical_lila_cheney.sql +0 -14
  155. package/drizzle/0003_violet_giant_girl.sql +0 -14
  156. package/drizzle/0004_slim_valeria_richards.sql +0 -15
  157. package/drizzle/0005_petite_luke_cage.sql +0 -12
  158. package/drizzle/0006_calm_nighthawk.sql +0 -3
  159. package/drizzle/0007_slimy_naoko.sql +0 -1
  160. package/drizzle/0008_peaceful_tigra.sql +0 -1
  161. package/drizzle/0009_smooth_harrier.sql +0 -10
  162. package/drizzle/0010_curved_amphibian.sql +0 -2
  163. package/drizzle/0011_compat_provenance_closures.sql +0 -11
  164. package/drizzle/0012_api_surfaces.sql +0 -9
  165. package/drizzle/0013_nice_thanos.sql +0 -1
  166. package/drizzle/0014_luxuriant_malcolm_colcord.sql +0 -10
  167. package/drizzle/0015_past_fat_cobra.sql +0 -11
  168. package/drizzle/0016_futuristic_junta.sql +0 -50
  169. package/drizzle/0017_absent_grandmaster.sql +0 -20
  170. package/drizzle/0018_rainy_proemial_gods.sql +0 -1
  171. package/drizzle/0019_dashing_korvac.sql +0 -11
  172. package/drizzle/0020_normal_vulture.sql +0 -3
  173. package/drizzle/0021_dazzling_blue_shield.sql +0 -17
  174. package/drizzle/0022_massive_ghost_rider.sql +0 -24
  175. package/drizzle/0023_next_shatterstar.sql +0 -1
  176. package/drizzle/0024_dusty_maelstrom.sql +0 -15
  177. package/drizzle/0025_gifted_harpoon.sql +0 -5
  178. package/drizzle/0026_awesome_mordo.sql +0 -1
  179. package/drizzle/0027_charming_ulik.sql +0 -2
  180. package/drizzle/0028_romantic_loners.sql +0 -2
  181. package/drizzle/0029_busy_korvac.sql +0 -16
  182. package/drizzle/0030_steady_mathemanic.sql +0 -6
  183. package/drizzle/0031_redundant_maginty.sql +0 -2
  184. package/drizzle/0032_stack_resolutions.sql +0 -14
  185. package/drizzle/0033_graceful_tyger_tiger.sql +0 -3
  186. package/drizzle/0034_uneven_thunderbird.sql +0 -1
  187. package/drizzle/0035_api_key_scopes.sql +0 -1
  188. package/drizzle/0036_policy_audit.sql +0 -21
  189. package/drizzle/0037_subscription_seats.sql +0 -1
  190. package/drizzle/0038_symbol_offsets.sql +0 -2
  191. package/drizzle/0039_mcp_runtime_scans.sql +0 -80
  192. package/drizzle/0040_subscription_overage_and_interval.sql +0 -4
  193. package/drizzle/0041_account_email.sql +0 -38
  194. package/drizzle/0042_alert_channels.sql +0 -21
  195. package/drizzle/meta/0000_snapshot.json +0 -370
  196. package/drizzle/meta/0001_snapshot.json +0 -482
  197. package/drizzle/meta/0002_snapshot.json +0 -575
  198. package/drizzle/meta/0003_snapshot.json +0 -667
  199. package/drizzle/meta/0004_snapshot.json +0 -769
  200. package/drizzle/meta/0005_snapshot.json +0 -864
  201. package/drizzle/meta/0006_snapshot.json +0 -882
  202. package/drizzle/meta/0007_snapshot.json +0 -876
  203. package/drizzle/meta/0008_snapshot.json +0 -882
  204. package/drizzle/meta/0009_snapshot.json +0 -948
  205. package/drizzle/meta/0010_snapshot.json +0 -969
  206. package/drizzle/meta/0011_snapshot.json +0 -1049
  207. package/drizzle/meta/0012_snapshot.json +0 -1115
  208. package/drizzle/meta/0013_snapshot.json +0 -1121
  209. package/drizzle/meta/0014_snapshot.json +0 -1190
  210. package/drizzle/meta/0015_snapshot.json +0 -1280
  211. package/drizzle/meta/0016_snapshot.json +0 -1683
  212. package/drizzle/meta/0017_snapshot.json +0 -1834
  213. package/drizzle/meta/0018_snapshot.json +0 -1834
  214. package/drizzle/meta/0019_snapshot.json +0 -1909
  215. package/drizzle/meta/0020_snapshot.json +0 -1921
  216. package/drizzle/meta/0021_snapshot.json +0 -2045
  217. package/drizzle/meta/0022_snapshot.json +0 -2246
  218. package/drizzle/meta/0023_snapshot.json +0 -2252
  219. package/drizzle/meta/0024_snapshot.json +0 -2375
  220. package/drizzle/meta/0025_snapshot.json +0 -2407
  221. package/drizzle/meta/0026_snapshot.json +0 -2413
  222. package/drizzle/meta/0027_snapshot.json +0 -2425
  223. package/drizzle/meta/0028_snapshot.json +0 -2438
  224. package/drizzle/meta/0029_snapshot.json +0 -2546
  225. package/drizzle/meta/0030_snapshot.json +0 -2586
  226. package/drizzle/meta/0031_snapshot.json +0 -2598
  227. package/drizzle/meta/0032_snapshot.json +0 -2699
  228. package/drizzle/meta/0033_snapshot.json +0 -2712
  229. package/drizzle/meta/0034_snapshot.json +0 -2719
  230. package/drizzle/meta/0035_snapshot.json +0 -2726
  231. package/drizzle/meta/0036_snapshot.json +0 -2876
  232. package/drizzle/meta/0037_snapshot.json +0 -2883
  233. package/drizzle/meta/0038_snapshot.json +0 -2895
  234. package/drizzle/meta/0039_snapshot.json +0 -3501
  235. package/drizzle/meta/0040_snapshot.json +0 -3527
  236. package/drizzle/meta/0041_snapshot.json +0 -3824
  237. package/drizzle/meta/0042_snapshot.json +0 -3960
  238. package/drizzle/meta/_journal.json +0 -307
  239. package/src/data/seed.json +0 -253
@@ -0,0 +1,136 @@
1
+ import { createRequire as __lurqCreateRequire } from 'node:module'; const require = __lurqCreateRequire(import.meta.url);
2
+ import {
3
+ isPrivateAddress,
4
+ safeLookup
5
+ } from "./chunk-3D2MPGOQ.js";
6
+ import {
7
+ init_esm_shims
8
+ } from "./chunk-I3M2KJXB.js";
9
+
10
+ // src/core/safeFetch.ts
11
+ init_esm_shims();
12
+ import { isIP } from "net";
13
+ import { Agent, fetch as undiciFetch } from "undici";
14
+ var SAFE_FETCH_DEFAULTS = {
15
+ /** Time allowed for response headers. Streams are not cut off after that. */
16
+ headersTimeoutMs: 1e4,
17
+ /** Silence allowed between body chunks. */
18
+ bodyTimeoutMs: 3e4,
19
+ /** Discovery documents are kilobytes; a tool list rarely passes a megabyte. */
20
+ maxBytes: 5e6,
21
+ maxRedirects: 3,
22
+ userAgent: "lurq-probe/1 (+https://lurq.run/probe)"
23
+ };
24
+ var REDIRECTS = /* @__PURE__ */ new Set([301, 302, 303, 307, 308]);
25
+ var LOCAL_SUFFIXES = [".localhost", ".local", ".internal", ".home.arpa", ".lan"];
26
+ var UnsafeUrlError = class extends Error {
27
+ code = "EUNSAFE";
28
+ constructor(message) {
29
+ super(message);
30
+ this.name = "UnsafeUrlError";
31
+ }
32
+ };
33
+ var ResponseTooLargeError = class extends Error {
34
+ code = "ETOOLARGE";
35
+ constructor(limit) {
36
+ super(`response body exceeded ${limit} bytes`);
37
+ this.name = "ResponseTooLargeError";
38
+ }
39
+ };
40
+ var publicHttpsOnly = (url) => {
41
+ if (url.protocol !== "https:")
42
+ throw new UnsafeUrlError(`only https URLs can be checked (got ${url.protocol})`);
43
+ if (url.username || url.password)
44
+ throw new UnsafeUrlError("URLs with embedded credentials are refused");
45
+ const host = url.hostname.toLowerCase().replace(/^\[|\]$/g, "");
46
+ if (!host || host === "localhost" || LOCAL_SUFFIXES.some((s) => host.endsWith(s))) {
47
+ throw new UnsafeUrlError(`${host || "that host"} is not reachable on the public internet`);
48
+ }
49
+ if (isIP(host) && isPrivateAddress(host)) {
50
+ throw new UnsafeUrlError(`${host} is a private or reserved address`);
51
+ }
52
+ };
53
+ function parse(input) {
54
+ try {
55
+ return new URL(input.toString());
56
+ } catch {
57
+ throw new UnsafeUrlError("not a valid URL");
58
+ }
59
+ }
60
+ function capped(body, limit) {
61
+ let seen = 0;
62
+ return body.pipeThrough(
63
+ new TransformStream({
64
+ transform(chunk, controller) {
65
+ seen += chunk.byteLength;
66
+ if (seen > limit) controller.error(new ResponseTooLargeError(limit));
67
+ else controller.enqueue(chunk);
68
+ }
69
+ })
70
+ );
71
+ }
72
+ function createSafeFetch(opts = {}) {
73
+ const policy = opts.policy ?? publicHttpsOnly;
74
+ const maxBytes = opts.maxBytes ?? SAFE_FETCH_DEFAULTS.maxBytes;
75
+ const maxRedirects = opts.maxRedirects ?? SAFE_FETCH_DEFAULTS.maxRedirects;
76
+ const userAgent = opts.userAgent ?? SAFE_FETCH_DEFAULTS.userAgent;
77
+ const dispatcher = new Agent({
78
+ connect: { lookup: opts.lookup ?? safeLookup() },
79
+ headersTimeout: opts.headersTimeoutMs ?? SAFE_FETCH_DEFAULTS.headersTimeoutMs,
80
+ bodyTimeout: opts.bodyTimeoutMs ?? SAFE_FETCH_DEFAULTS.bodyTimeoutMs
81
+ });
82
+ const safeFetch = async (input, init = {}) => {
83
+ let url = parse(input);
84
+ let method = (init.method ?? "GET").toUpperCase();
85
+ let body = init.body ?? null;
86
+ const headers = new Headers(init.headers);
87
+ if (!headers.has("user-agent")) headers.set("user-agent", userAgent);
88
+ for (let hop = 0; ; hop++) {
89
+ policy(url);
90
+ const res = await undiciFetch(url, {
91
+ method,
92
+ headers,
93
+ body,
94
+ redirect: "manual",
95
+ signal: init.signal ?? void 0,
96
+ dispatcher
97
+ });
98
+ const location = res.headers.get("location");
99
+ if (REDIRECTS.has(res.status) && location) {
100
+ await res.body?.cancel().catch(() => {
101
+ });
102
+ if (hop >= maxRedirects) throw new UnsafeUrlError(`more than ${maxRedirects} redirects`);
103
+ url = parse(new URL(location, url));
104
+ if (res.status === 303 || (res.status === 301 || res.status === 302) && method === "POST") {
105
+ method = "GET";
106
+ body = null;
107
+ headers.delete("content-type");
108
+ }
109
+ continue;
110
+ }
111
+ const final = res.body ? new Response(capped(res.body, maxBytes), {
112
+ status: res.status,
113
+ statusText: res.statusText,
114
+ headers: res.headers
115
+ }) : res;
116
+ if (final !== res || !res.url) Object.defineProperty(final, "url", { value: url.toString() });
117
+ return final;
118
+ }
119
+ };
120
+ safeFetch.close = () => dispatcher.close();
121
+ return safeFetch;
122
+ }
123
+ var shared = null;
124
+ function sharedSafeFetch() {
125
+ shared ??= createSafeFetch();
126
+ return shared;
127
+ }
128
+
129
+ export {
130
+ SAFE_FETCH_DEFAULTS,
131
+ UnsafeUrlError,
132
+ ResponseTooLargeError,
133
+ publicHttpsOnly,
134
+ createSafeFetch,
135
+ sharedSafeFetch
136
+ };
@@ -0,0 +1,74 @@
1
+ import { createRequire as __lurqCreateRequire } from 'node:module'; const require = __lurqCreateRequire(import.meta.url);
2
+ import {
3
+ init_esm_shims
4
+ } from "./chunk-I3M2KJXB.js";
5
+
6
+ // src/core/userConfig.ts
7
+ init_esm_shims();
8
+ import {
9
+ chmodSync,
10
+ existsSync,
11
+ mkdirSync,
12
+ readFileSync,
13
+ renameSync,
14
+ rmSync,
15
+ writeFileSync
16
+ } from "fs";
17
+ import { homedir } from "os";
18
+ import { dirname, join } from "path";
19
+ function lurqHome() {
20
+ return process.env.LURQ_HOME || join(homedir(), ".lurq");
21
+ }
22
+ function userConfigPath() {
23
+ return join(lurqHome(), "config.json");
24
+ }
25
+ function readUserConfig() {
26
+ const path = userConfigPath();
27
+ if (!existsSync(path)) return {};
28
+ try {
29
+ const parsed = JSON.parse(readFileSync(path, "utf8"));
30
+ return parsed && typeof parsed === "object" ? parsed : {};
31
+ } catch {
32
+ return {};
33
+ }
34
+ }
35
+ function writeUserConfig(patch) {
36
+ const path = userConfigPath();
37
+ const next = { ...readUserConfig(), ...patch };
38
+ mkdirSync(dirname(path), { recursive: true });
39
+ const tmp = `${path}.${process.pid}.tmp`;
40
+ try {
41
+ writeFileSync(tmp, JSON.stringify(next, null, 2) + "\n", { mode: 384 });
42
+ chmodSync(tmp, 384);
43
+ renameSync(tmp, path);
44
+ } catch (err) {
45
+ try {
46
+ rmSync(tmp, { force: true });
47
+ } catch {
48
+ }
49
+ throw err;
50
+ }
51
+ return path;
52
+ }
53
+ function clearUserConfig() {
54
+ const path = userConfigPath();
55
+ if (!existsSync(path)) return false;
56
+ rmSync(path, { force: true });
57
+ return true;
58
+ }
59
+ function resolveApiKey(override) {
60
+ return override?.trim() || process.env.LURQ_API_KEY?.trim() || readUserConfig().apiKey?.trim();
61
+ }
62
+ function resolveEndpoint(override) {
63
+ return override?.trim() || process.env.LURQ_ENDPOINT?.trim() || readUserConfig().endpoint?.trim();
64
+ }
65
+
66
+ export {
67
+ lurqHome,
68
+ userConfigPath,
69
+ readUserConfig,
70
+ writeUserConfig,
71
+ clearUserConfig,
72
+ resolveApiKey,
73
+ resolveEndpoint
74
+ };
@@ -0,0 +1,127 @@
1
+ import { createRequire as __lurqCreateRequire } from 'node:module'; const require = __lurqCreateRequire(import.meta.url);
2
+ import {
3
+ init_esm_shims
4
+ } from "./chunk-I3M2KJXB.js";
5
+
6
+ // src/policy/parse.ts
7
+ init_esm_shims();
8
+ var CONFIDENCES = ["unproven", "promising", "emerging", "proven"];
9
+ var SEVERITIES = ["info", "low", "moderate", "high", "critical"];
10
+ var MODES = ["enforce", "warn"];
11
+ var MAX_ENTRIES = 500;
12
+ var MAX_LEN = 214;
13
+ var LIMITS = {
14
+ minWeeklyDownloads: { min: 0, max: 1e8 },
15
+ maxStaleMonths: { min: 1, max: 240 },
16
+ maxBundleKb: { min: 1, max: 1e5 },
17
+ minPackageAgeDays: { min: 1, max: 365 }
18
+ };
19
+ var Invalid = class extends Error {
20
+ };
21
+ function fail(message) {
22
+ throw new Invalid(message);
23
+ }
24
+ function bounded(input, field) {
25
+ if (input === null || input === void 0) return null;
26
+ const { min, max } = LIMITS[field];
27
+ if (typeof input !== "number" || !Number.isFinite(input) || input < min || input > max) {
28
+ fail(`${field} must be a number from ${min} to ${max}, or null for no rule.`);
29
+ }
30
+ return input;
31
+ }
32
+ function oneOf(input, field, options) {
33
+ if (input === null || input === void 0) return null;
34
+ if (!options.includes(input)) {
35
+ fail(`${field} must be one of ${options.join(", ")}, or null for no rule.`);
36
+ }
37
+ return input;
38
+ }
39
+ function list(input, field) {
40
+ if (!Array.isArray(input)) fail(`${field} must be a list.`);
41
+ if (input.length > MAX_ENTRIES) fail(`${field} has more than ${MAX_ENTRIES} entries.`);
42
+ return input;
43
+ }
44
+ function name(input, where) {
45
+ if (typeof input !== "string") fail(`${where} must be a string.`);
46
+ const trimmed = input.trim();
47
+ if (!trimmed || trimmed.length > MAX_LEN) fail(`${where} must be 1 to ${MAX_LEN} characters.`);
48
+ return trimmed;
49
+ }
50
+ function reason(input, where) {
51
+ if (input === null || input === void 0) return void 0;
52
+ if (typeof input !== "string" || input.length > MAX_LEN) {
53
+ fail(`${where}.reason must be text of at most ${MAX_LEN} characters.`);
54
+ }
55
+ return input.trim() || void 0;
56
+ }
57
+ function entry(input, where) {
58
+ if (!input || typeof input !== "object" || Array.isArray(input)) {
59
+ fail(`${where} must be an object with a name.`);
60
+ }
61
+ return input;
62
+ }
63
+ function denyRules(input) {
64
+ return list(input, "deny").map((item, i) => {
65
+ const raw = entry(item, `deny[${i}]`);
66
+ const rule = { name: name(raw.name, `deny[${i}].name`) };
67
+ const why = reason(raw.reason, `deny[${i}]`);
68
+ return why ? { ...rule, reason: why } : rule;
69
+ });
70
+ }
71
+ function allowRules(input) {
72
+ return list(input, "allow").map((item, i) => {
73
+ const where = `allow[${i}]`;
74
+ if (typeof item === "string") return { name: name(item, where) };
75
+ const raw = entry(item, where);
76
+ const rule = { name: name(raw.name, `${where}.name`) };
77
+ const why = reason(raw.reason, where);
78
+ if (why) rule.reason = why;
79
+ if (raw.expires !== null && raw.expires !== void 0) {
80
+ const valid = typeof raw.expires === "string" && /^\d{4}-\d{2}-\d{2}$/.test(raw.expires) && !Number.isNaN(Date.parse(raw.expires)) && new Date(raw.expires).toISOString().slice(0, 10) === raw.expires;
81
+ if (!valid) fail(`${where}.expires must be a date like 2026-12-31.`);
82
+ rule.expires = raw.expires;
83
+ }
84
+ return rule;
85
+ });
86
+ }
87
+ function build(input) {
88
+ if (!input || typeof input !== "object" || Array.isArray(input))
89
+ fail("policy must be an object.");
90
+ const raw = input;
91
+ const allow = allowRules(raw.allow);
92
+ const deny = denyRules(raw.deny);
93
+ if (typeof raw.blockDeprecated !== "boolean") fail("blockDeprecated must be true or false.");
94
+ const licenses = raw.licenses === null || raw.licenses === void 0 ? null : list(raw.licenses, "licenses").map((item, i) => name(item, `licenses[${i}]`));
95
+ if (raw.blockArchived !== void 0 && typeof raw.blockArchived !== "boolean") {
96
+ fail("blockArchived must be true or false.");
97
+ }
98
+ if (raw.mode !== void 0 && !MODES.includes(raw.mode)) {
99
+ fail(`mode must be ${MODES.join(" or ")}.`);
100
+ }
101
+ return {
102
+ mode: raw.mode ?? "enforce",
103
+ allow,
104
+ deny,
105
+ minConfidence: oneOf(raw.minConfidence, "minConfidence", CONFIDENCES),
106
+ licenses,
107
+ blockDeprecated: raw.blockDeprecated,
108
+ blockArchived: raw.blockArchived === true,
109
+ maxAdvisorySeverity: oneOf(raw.maxAdvisorySeverity, "maxAdvisorySeverity", SEVERITIES),
110
+ minWeeklyDownloads: bounded(raw.minWeeklyDownloads, "minWeeklyDownloads"),
111
+ maxStaleMonths: bounded(raw.maxStaleMonths, "maxStaleMonths"),
112
+ maxBundleKb: bounded(raw.maxBundleKb, "maxBundleKb"),
113
+ minPackageAgeDays: bounded(raw.minPackageAgeDays, "minPackageAgeDays")
114
+ };
115
+ }
116
+ function validateSelectionPolicy(input) {
117
+ try {
118
+ return { policy: build(input) };
119
+ } catch (err) {
120
+ if (err instanceof Invalid) return { error: err.message };
121
+ throw err;
122
+ }
123
+ }
124
+
125
+ export {
126
+ validateSelectionPolicy
127
+ };
@@ -0,0 +1,340 @@
1
+ import { createRequire as __lurqCreateRequire } from 'node:module'; const require = __lurqCreateRequire(import.meta.url);
2
+ import {
3
+ init_esm_shims
4
+ } from "./chunk-I3M2KJXB.js";
5
+
6
+ // src/core/capabilities.ts
7
+ init_esm_shims();
8
+ var CAPABILITIES = [
9
+ {
10
+ id: "verify",
11
+ title: "Check a package is real",
12
+ question: "Does this package actually exist, and is it safe to install?",
13
+ answer: "Checks the live registry for existence, deprecation, archival and advisories \u2014 the guard against a hallucinated or typosquatted name reaching package.json.",
14
+ mcp: "verify",
15
+ cli: "lurq verify <package>",
16
+ aliases: ["hallucinated", "typosquat", "slopsquat", "fake", "malicious", "safe", "exists"]
17
+ },
18
+ {
19
+ id: "evaluate",
20
+ title: "Read the evidence on a package",
21
+ question: "Is this package healthy, maintained, and worth adopting?",
22
+ answer: "The full evidence read for one package: health and quality scores with the signals behind them, release cadence, advisories, and how to wire it up.",
23
+ mcp: "evaluate",
24
+ cli: "lurq evaluate <package>",
25
+ aliases: ["health", "maintained", "abandoned", "deprecated", "score", "quality"]
26
+ },
27
+ {
28
+ id: "compare",
29
+ title: "Compare packages side by side",
30
+ question: "Which of these two should I go with?",
31
+ answer: "Ranks 2\u20135 named packages against each other on the same scored evidence.",
32
+ mcp: "compare",
33
+ cli: "lurq compare <a> <b>",
34
+ aliases: ["versus", "vs", "shootout", "better"]
35
+ },
36
+ {
37
+ id: "compat",
38
+ title: "Check a stack fits together",
39
+ question: "Will these packages work together?",
40
+ answer: "Peer-dependency and engine checks across the whole set at once, plus any sandbox-verified conflicts on record. Returns the exact clashing constraints.",
41
+ mcp: "compat",
42
+ cli: "lurq compat <a> <b> <c>",
43
+ aliases: ["peer", "conflict", "incompatible", "engines", "node version", "together"]
44
+ },
45
+ {
46
+ id: "usage",
47
+ title: "See a version-exact API",
48
+ question: "What does this package actually export at this version?",
49
+ answer: "The exported symbols and signatures for one exact version, read from the shipped JS \u2014 plus the delta from a version you already know.",
50
+ mcp: "usage",
51
+ cli: "lurq usage <package> --target <version>",
52
+ aliases: ["exports", "symbols", "api", "signature", "surface"]
53
+ },
54
+ {
55
+ id: "diff-surface",
56
+ title: "See what an upgrade removes",
57
+ question: "What breaks between these two versions?",
58
+ answer: "The symbol-level diff between two published versions: what was removed, what changed arity, what is new. Runtime breakage is reported separately from type-only breakage.",
59
+ mcp: "diff_surface",
60
+ cli: "lurq usage <package> --known <old> --target <new>",
61
+ aliases: ["breaking", "removed", "changed", "migration", "major"]
62
+ },
63
+ {
64
+ id: "audit",
65
+ title: "Audit this whole project",
66
+ question: "What in this project is outdated, vulnerable, or drifting?",
67
+ answer: "Reads your manifests and agent configs locally, then assesses every dependency and MCP server in one call: outdated versions, deprecations, advisories against the exact installed version, and servers that need credentials or cannot be observed. Always prints how many items it actually answered for \u2014 anything it could not assess is reported as unassessed, never as clean.",
68
+ mcp: "audit",
69
+ cli: "lurq audit",
70
+ aliases: [
71
+ "scan",
72
+ "outdated",
73
+ "vulnerable",
74
+ "audit",
75
+ "health",
76
+ "project",
77
+ "dependencies",
78
+ "cve"
79
+ ]
80
+ },
81
+ {
82
+ id: "mcp-stack",
83
+ title: "Check your MCP servers coexist",
84
+ question: "Do the MCP servers I have wired up conflict with each other?",
85
+ answer: "Tool-name collisions across the servers configured into one agent. They are separate processes, so nothing resolves between them \u2014 they clash in the flat tool namespace the agent assembles, where two servers exposing the same name leave it unable to say which it means. Also reports the standing context cost of the whole set.",
86
+ mcp: "mcp_stack",
87
+ cli: "lurq mcp-stack",
88
+ aliases: ["collision", "conflict", "namespace", "mcp", "tools", "shadow", "coexist"]
89
+ },
90
+ {
91
+ id: "mcp-surface",
92
+ title: "Read an MCP server's tools",
93
+ question: "What tools does this MCP server actually expose, and what do they take?",
94
+ answer: "Every tool from a live `tools/list` handshake in a sandbox: required and optional parameters, and the behaviour annotations that say whether a tool writes, destroys, or reaches outside. Also returns the API keys and settings the server declares it needs, with a request you can put in front of the user. Declared contract only \u2014 that a tool is listed is not evidence that calling it succeeds.",
95
+ mcp: "mcp_surface",
96
+ cli: "lurq mcp-surface <server>",
97
+ aliases: ["mcp", "tools", "tool schema", "inputSchema", "agent tools", "server"]
98
+ },
99
+ {
100
+ id: "mcp-drift",
101
+ title: "See what an MCP upgrade changes",
102
+ question: "Did this MCP server change its tool contract?",
103
+ answer: "The contract diff between two versions of an MCP server: tools removed, parameters that became required, types narrowed, and annotation flips. Reports silent drift (schema moved, description unchanged) and privilege widening (a tool that stopped being read-only) separately, because neither shows up in a changelog.",
104
+ mcp: "mcp_drift",
105
+ cli: "lurq mcp-drift <server> --from <old> --to <new>",
106
+ aliases: ["mcp", "schema drift", "tool drift", "readOnlyHint", "breaking", "agent"]
107
+ },
108
+ {
109
+ id: "connect-check",
110
+ title: "Check a server works in your client",
111
+ question: "Will this MCP server work in Claude, ChatGPT, Cursor or VS Code, and what does it need?",
112
+ answer: "Per-client verdicts (works, needs setup, blocked, unknown) for one MCP server, from a credential-free probe of its endpoint and each client\u2019s documented constraints: how it authenticates and whether that client can sign in to it, spec deviations strict clients refuse, tool names and schemas the client rejects. Returns the setup steps and ready-to-paste config in each client\u2019s own format.",
113
+ mcp: "connect_check",
114
+ cli: "lurq connect-check <server> --client <client>",
115
+ aliases: [
116
+ "connect",
117
+ "oauth",
118
+ "auth",
119
+ "connector",
120
+ "setup",
121
+ "install",
122
+ "config",
123
+ "claude.ai",
124
+ "chatgpt",
125
+ "cursor",
126
+ "vscode",
127
+ "remote server",
128
+ "401"
129
+ ]
130
+ },
131
+ {
132
+ id: "mcp-pin",
133
+ title: "Get alerted when a server changes",
134
+ question: "How do I know if a remote MCP server I approved changes its tools or sign-in?",
135
+ answer: "Pin the server as it is now. lurq re-reads every remote endpoint in the official registry on a schedule and alerts the account (email, Slack, the agent itself) when a pinned server\u2019s tools, sign-in path or availability change \u2014 approved keeps meaning what was approved. Servers in your uploaded mcp-scan results are watched the same way without pinning.",
136
+ cli: "lurq mcp-pin <url-or-registry-name>",
137
+ aliases: [
138
+ "pin",
139
+ "approve",
140
+ "watch",
141
+ "alert",
142
+ "rug pull",
143
+ "drift",
144
+ "changed",
145
+ "monitor",
146
+ "remote"
147
+ ]
148
+ },
149
+ {
150
+ id: "diagram",
151
+ title: "Draw the stack",
152
+ question: "Can I see this as a diagram?",
153
+ answer: "Renders an existing stack as a Mermaid graph other tools can parse.",
154
+ mcp: "diagram",
155
+ aliases: ["mermaid", "visual", "chart", "architecture", "graph"]
156
+ },
157
+ {
158
+ id: "upgrade-plan",
159
+ title: "Find what is behind",
160
+ question: "How far out of date is this project?",
161
+ answer: "Reads the local manifests and reports the gap between what is resolved and what is current, with what each upgrade removes from its API. Sends dependency ranges only, never source.",
162
+ cli: "lurq upgrade-plan",
163
+ page: "/dashboard/repos",
164
+ aliases: ["drift", "outdated", "behind", "stale", "update", "upgrade", "bump", "dependencies"]
165
+ },
166
+ {
167
+ id: "check-upgrade",
168
+ title: "Check an upgrade against your code",
169
+ question: "Will this upgrade break my code specifically?",
170
+ answer: "Intersects what the upgrade removes with what your code actually references, and answers with file:line. Needs no test suite and no network to lurq.",
171
+ cli: "lurq check-upgrade --plan plan.json --exit-code",
172
+ aliases: ["breaking", "ci", "gate", "call sites", "references", "blocking"]
173
+ },
174
+ {
175
+ id: "check-release",
176
+ title: "Check your own version bump",
177
+ question: "Am I about to break my consumers?",
178
+ answer: "Compares the built working tree against your last published version and says whether the bump you are about to tag covers what actually changed. For package authors.",
179
+ cli: "lurq check-release --exit-code",
180
+ aliases: ["publish", "semver", "release", "major", "prepublish", "library author"]
181
+ },
182
+ {
183
+ id: "check-api",
184
+ title: "Check your own API for breaks",
185
+ question: "Will this change break the people calling my service?",
186
+ answer: "Diffs two git revisions of your OpenAPI document: removed operations, newly required parameters and body fields, removed response codes \u2014 and whether info.version was bumped enough. Reads git only, nothing leaves the machine.",
187
+ cli: "lurq check-api --against origin/main --exit-code",
188
+ aliases: [
189
+ "openapi",
190
+ "swagger",
191
+ "endpoint",
192
+ "consumers",
193
+ "callers",
194
+ "service",
195
+ "contract",
196
+ "rest"
197
+ ]
198
+ },
199
+ {
200
+ id: "autopilot",
201
+ title: "Keep a repo current automatically",
202
+ question: "Can lurq just do the upgrades for me?",
203
+ answer: "Connect a repo and lurq drafts the upgrade, names the call sites it breaks, and opens a PR from your own runner. lurq never gets write access to your code.",
204
+ page: "/dashboard/repos",
205
+ cli: "lurq upgrade-plan --json",
206
+ aliases: ["automate", "pr", "github", "renovate", "dependabot", "autopilot", "workflow"]
207
+ },
208
+ {
209
+ id: "policy",
210
+ title: "Set what agents may install",
211
+ question: "How do I stop agents adding packages we do not allow?",
212
+ answer: "An allow/deny list plus licence and confidence floors, enforced at the moment an agent reaches for a dependency \u2014 before it becomes a line in package.json.",
213
+ page: "/dashboard/policy",
214
+ aliases: [
215
+ "allowlist",
216
+ "whitelist",
217
+ "deny",
218
+ "blocklist",
219
+ "licence",
220
+ "license",
221
+ "govern",
222
+ "standard"
223
+ ]
224
+ },
225
+ {
226
+ id: "alerts",
227
+ title: "Watch repos for new risk",
228
+ question: "Tell me when something I depend on goes bad",
229
+ answer: "Per-repo advisories and deprecations across the resolved tree, with the direct dependency responsible for each transitive finding.",
230
+ page: "/dashboard/repos",
231
+ aliases: ["advisory", "cve", "vulnerability", "security", "transitive", "sbom", "alert"]
232
+ },
233
+ {
234
+ id: "usage-dashboard",
235
+ title: "See how lurq is being used",
236
+ question: "Who on my team is calling lurq, and how much?",
237
+ answer: "Call volume by tool and by day, plus the outcomes agents reported back about the packages they chose.",
238
+ page: "/dashboard/usage",
239
+ aliases: ["activity", "analytics", "volume", "calls", "team", "adoption", "heatmap"]
240
+ },
241
+ {
242
+ id: "setup",
243
+ title: "Connect lurq to an assistant",
244
+ question: "How do I get this into Claude Code / Cursor?",
245
+ answer: "One command detects every assistant on the machine and writes the MCP entry. No database credentials touch your machine.",
246
+ cli: "npx lurqrun",
247
+ page: "/dashboard/keys",
248
+ aliases: [
249
+ "install",
250
+ "mcp",
251
+ "cursor",
252
+ "claude code",
253
+ "windsurf",
254
+ "copilot",
255
+ "api key",
256
+ "onboard"
257
+ ]
258
+ },
259
+ {
260
+ id: "weights",
261
+ title: "Change how scoring works",
262
+ question: "I disagree with the ranking \u2014 can I tune it?",
263
+ answer: "Show the weight model, or layer your own overrides on top of the defaults, per user or per project.",
264
+ cli: "lurq edit-weights --set composite.lambda=0.5",
265
+ aliases: ["scoring", "ranking", "tune", "bias", "lambda", "override"]
266
+ },
267
+ {
268
+ id: "report-outcome",
269
+ title: "Tell lurq how it went",
270
+ question: "How does lurq learn from what I picked?",
271
+ answer: "An opt-in report of whether you went with a package lurq checked and whether it built. Coarse signal only \u2014 never source code.",
272
+ mcp: "report_outcome",
273
+ aliases: ["feedback", "outcome", "accepted", "flywheel", "learn"]
274
+ }
275
+ ];
276
+ var STOP = /* @__PURE__ */ new Set([
277
+ "a",
278
+ "an",
279
+ "the",
280
+ "is",
281
+ "it",
282
+ "i",
283
+ "my",
284
+ "me",
285
+ "do",
286
+ "does",
287
+ "how",
288
+ "what",
289
+ "which",
290
+ "to",
291
+ "for",
292
+ "of",
293
+ "in",
294
+ "on",
295
+ "and",
296
+ "or",
297
+ "can",
298
+ "with",
299
+ "this",
300
+ "that",
301
+ "be",
302
+ "am",
303
+ "are",
304
+ "should",
305
+ "lurq",
306
+ "package",
307
+ "packages"
308
+ ]);
309
+ var tokenize = (s) => s.toLowerCase().split(/[^a-z0-9.@/]+/).filter((t) => t.length > 1 && !STOP.has(t));
310
+ var STEM_LEN = 5;
311
+ var sameStem = (a, b) => a.length >= STEM_LEN && b.length >= STEM_LEN && a.slice(0, STEM_LEN) === b.slice(0, STEM_LEN);
312
+ var FIELDS = [
313
+ { of: (c) => c.title, weight: 5 },
314
+ { of: (c) => c.question, weight: 4 },
315
+ { of: (c) => (c.aliases ?? []).join(" "), weight: 3 },
316
+ { of: (c) => c.answer, weight: 1 },
317
+ { of: (c) => [c.mcp, c.cli, c.page].filter(Boolean).join(" "), weight: 4 }
318
+ ];
319
+ function searchCapabilities(query, limit = 5) {
320
+ const terms = tokenize(query ?? "");
321
+ if (!terms.length) return CAPABILITIES.slice(0, limit).map((c) => ({ ...c, score: 0 }));
322
+ const scored = CAPABILITIES.map((c) => {
323
+ let score = 0;
324
+ for (const { of, weight } of FIELDS) {
325
+ const haystack = tokenize(of(c));
326
+ for (const term of terms) {
327
+ if (haystack.includes(term)) score += weight;
328
+ else if (haystack.some((h) => sameStem(h, term))) score += weight / 2;
329
+ }
330
+ }
331
+ return { ...c, score };
332
+ }).filter((c) => c.score > 0);
333
+ if (!scored.length) return CAPABILITIES.slice(0, limit).map((c) => ({ ...c, score: 0 }));
334
+ return scored.sort((a, b) => b.score - a.score).slice(0, limit);
335
+ }
336
+
337
+ export {
338
+ CAPABILITIES,
339
+ searchCapabilities
340
+ };