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.
- package/README.md +18 -10
- package/dist/auditHandler-JCOJSUYW.js +23 -0
- package/dist/bin/lurq.js +3090 -23306
- package/dist/browserAuth-B6G27USY.js +95 -0
- package/dist/cache-DKI34SVS.js +12 -0
- package/dist/check-2FDGUOV7.js +192 -0
- package/dist/checkUpgrade-F2WESBPM.js +103 -0
- package/dist/chunk-2VMOWSRY.js +300 -0
- package/dist/chunk-3GHJR7XR.js +2049 -0
- package/dist/chunk-3N3Q3DSW.js +214 -0
- package/dist/chunk-4ESO33J6.js +34 -0
- package/dist/chunk-4F7ZESPO.js +132 -0
- package/dist/chunk-4KCPOMG6.js +68 -0
- package/dist/chunk-4ZYCIMY4.js +155 -0
- package/dist/chunk-63MUJNZL.js +318 -0
- package/dist/chunk-6AAEVQ7I.js +6336 -0
- package/dist/chunk-6EQ3ARXA.js +304 -0
- package/dist/chunk-6L3U7DZ5.js +23 -0
- package/dist/chunk-AHBP533R.js +220 -0
- package/dist/chunk-B43LCXBM.js +223 -0
- package/dist/chunk-BCANRN5B.js +33 -0
- package/dist/chunk-BUALU27S.js +123 -0
- package/dist/chunk-BULZAX2U.js +1554 -0
- package/dist/chunk-BV32UNLL.js +154 -0
- package/dist/chunk-C6GLE626.js +160 -0
- package/dist/chunk-CIDWNUAP.js +145 -0
- package/dist/chunk-CUP36ND3.js +2126 -0
- package/dist/chunk-CW5HJ3LX.js +96 -0
- package/dist/chunk-DB2VF5UD.js +315 -0
- package/dist/chunk-DWAGCNEL.js +210 -0
- package/dist/chunk-EA3DP5HG.js +293 -0
- package/dist/chunk-ELGCAM7P.js +184 -0
- package/dist/chunk-FW6BD2NO.js +1915 -0
- package/dist/chunk-GE37NVLN.js +15 -0
- package/dist/chunk-GPTKSNR6.js +171 -0
- package/dist/chunk-GRJXQBQZ.js +793 -0
- package/dist/chunk-HWBBVLOT.js +92 -0
- package/dist/chunk-I2JUKN6Z.js +24 -0
- package/dist/chunk-I3M2KJXB.js +56 -0
- package/dist/chunk-IQ4SDKKY.js +733 -0
- package/dist/chunk-J5FZYN4A.js +116 -0
- package/dist/chunk-L6N4JFW2.js +263 -0
- package/dist/chunk-LBFGSSF2.js +126 -0
- package/dist/chunk-LEGPB7DO.js +81 -0
- package/dist/chunk-LO35HLQZ.js +219 -0
- package/dist/chunk-LRD4GBYE.js +73 -0
- package/dist/chunk-LUAKDHLZ.js +40 -0
- package/dist/chunk-LYMPYETA.js +965 -0
- package/dist/chunk-M6NYEXCW.js +38 -0
- package/dist/chunk-MHUEVBVO.js +35 -0
- package/dist/chunk-MYGSLK44.js +37 -0
- package/dist/chunk-NH5I4KHH.js +201 -0
- package/dist/chunk-OQS4GUSI.js +1391 -0
- package/dist/chunk-PSX3GIAN.js +176 -0
- package/dist/chunk-Q5VPF4WI.js +26 -0
- package/dist/chunk-QKGYDMWB.js +7409 -0
- package/dist/chunk-QSL6QN5X.js +115 -0
- package/dist/chunk-SHCEBP3J.js +107 -0
- package/dist/chunk-SJOVFF7B.js +201 -0
- package/dist/chunk-ST2X2GYN.js +373 -0
- package/dist/chunk-TNTXWSYA.js +19 -0
- package/dist/chunk-U5AT3TD4.js +2128 -0
- package/dist/chunk-UDVNA7EA.js +483 -0
- package/dist/chunk-VL2E5T2A.js +109 -0
- package/dist/chunk-VLRHFMGJ.js +58 -0
- package/dist/chunk-WHX2ZFCG.js +4048 -0
- package/dist/chunk-WQ23REYT.js +37 -0
- package/dist/chunk-XBIDMHSK.js +15425 -0
- package/dist/chunk-Y5GI7RL3.js +66 -0
- package/dist/client-IRKAMDYZ.js +15 -0
- package/dist/commands-FIL5ET6P.js +911 -0
- package/dist/compat-R3UTFQ5Q.js +201 -0
- package/dist/dashboardIssue-WYYMBFIB.js +192 -0
- package/dist/diff-GZA2774U.js +13 -0
- package/dist/dist-4XXIXHHZ.js +12945 -0
- package/dist/e2b-FVPHDULZ.js +172 -0
- package/dist/extract-NKGKLIVT.js +444 -0
- package/dist/fetch-QYTUJVG2.js +22 -0
- package/dist/firstTouch-QQTJH4QX.js +55 -0
- package/dist/handlers-GEJAVZDK.js +53 -0
- package/dist/http-ETPYMBGD.js +5360 -0
- package/dist/ingestQueue-VKN4KBIF.js +35 -0
- package/dist/install-EK3W2COX.js +20 -0
- package/dist/installSkill-VQJWJVCL.js +52 -0
- package/dist/inventory-5AWSLJYU.js +31 -0
- package/dist/local-XPZMRH5R.js +13 -0
- package/dist/mcp-3JHD5SUZ.js +472 -0
- package/dist/mcpHandlers-QFN4MCFL.js +22 -0
- package/dist/mcpScan-QZHWENIP.js +1871 -0
- package/dist/mcpScanWorkflow-JPYCBGC4.js +81 -0
- package/dist/mcpStack-BYJCZNIV.js +20 -0
- package/dist/packages-THVFMWFP.js +42 -0
- package/dist/plan-V4OEI3J4.js +463 -0
- package/dist/planView-JRXGCG2N.js +79 -0
- package/dist/policy-3P6BGRDN.js +115 -0
- package/dist/references-6N7VEPHT.js +14 -0
- package/dist/release-4U4ABNSJ.js +128 -0
- package/dist/remote-YGCXYI4V.js +32 -0
- package/dist/reportRuns-CKQQYLY4.js +64 -0
- package/dist/sandbox-MEZLAODF.js +39 -0
- package/dist/server-KFUD5X57.js +55 -0
- package/dist/surface-V62GE554.js +141 -0
- package/dist/surface-YL6O67W3.js +36 -0
- package/dist/surfaceHandlers-X2IZKA3E.js +20 -0
- package/dist/typecheck-X7U5ELMR.js +268 -0
- package/dist/types-M5JP2W3D.js +10 -0
- package/dist/uninstall-P7RGBX6G.js +174 -0
- package/dist/upgrade-NQ5IWFZB.js +596 -0
- package/dist/upgradePlan-A4LRKLEG.js +146 -0
- package/dist/userConfig-Z3GQE776.js +20 -0
- package/dist/watch-DGFMWUDL.js +214 -0
- package/package.json +26 -21
- package/templates/agent-rules.md +4 -4
- package/templates/skill-instructions.md +13 -14
- package/dist/bin/lurq.js.map +0 -1
- package/dist/index.d.ts +0 -805
- package/dist/index.js +0 -23570
- package/dist/index.js.map +0 -1
- package/drizzle/0000_confused_fabian_cortez.sql +0 -54
- package/drizzle/0001_aspiring_timeslip.sql +0 -15
- package/drizzle/0002_lyrical_lila_cheney.sql +0 -14
- package/drizzle/0003_violet_giant_girl.sql +0 -14
- package/drizzle/0004_slim_valeria_richards.sql +0 -15
- package/drizzle/0005_petite_luke_cage.sql +0 -12
- package/drizzle/0006_calm_nighthawk.sql +0 -3
- package/drizzle/0007_slimy_naoko.sql +0 -1
- package/drizzle/0008_peaceful_tigra.sql +0 -1
- package/drizzle/0009_smooth_harrier.sql +0 -10
- package/drizzle/0010_curved_amphibian.sql +0 -2
- package/drizzle/0011_compat_provenance_closures.sql +0 -11
- package/drizzle/0012_api_surfaces.sql +0 -9
- package/drizzle/0013_nice_thanos.sql +0 -1
- package/drizzle/0014_luxuriant_malcolm_colcord.sql +0 -10
- package/drizzle/0015_past_fat_cobra.sql +0 -11
- package/drizzle/0016_futuristic_junta.sql +0 -50
- package/drizzle/0017_absent_grandmaster.sql +0 -20
- package/drizzle/0018_rainy_proemial_gods.sql +0 -1
- package/drizzle/0019_dashing_korvac.sql +0 -11
- package/drizzle/0020_normal_vulture.sql +0 -3
- package/drizzle/0021_dazzling_blue_shield.sql +0 -17
- package/drizzle/0022_massive_ghost_rider.sql +0 -24
- package/drizzle/0023_next_shatterstar.sql +0 -1
- package/drizzle/0024_dusty_maelstrom.sql +0 -15
- package/drizzle/0025_gifted_harpoon.sql +0 -5
- package/drizzle/0026_awesome_mordo.sql +0 -1
- package/drizzle/0027_charming_ulik.sql +0 -2
- package/drizzle/0028_romantic_loners.sql +0 -2
- package/drizzle/0029_busy_korvac.sql +0 -16
- package/drizzle/0030_steady_mathemanic.sql +0 -6
- package/drizzle/0031_redundant_maginty.sql +0 -2
- package/drizzle/0032_stack_resolutions.sql +0 -14
- package/drizzle/0033_graceful_tyger_tiger.sql +0 -3
- package/drizzle/0034_uneven_thunderbird.sql +0 -1
- package/drizzle/0035_api_key_scopes.sql +0 -1
- package/drizzle/0036_policy_audit.sql +0 -21
- package/drizzle/0037_subscription_seats.sql +0 -1
- package/drizzle/0038_symbol_offsets.sql +0 -2
- package/drizzle/0039_mcp_runtime_scans.sql +0 -80
- package/drizzle/0040_subscription_overage_and_interval.sql +0 -4
- package/drizzle/0041_account_email.sql +0 -38
- package/drizzle/0042_alert_channels.sql +0 -21
- package/drizzle/meta/0000_snapshot.json +0 -370
- package/drizzle/meta/0001_snapshot.json +0 -482
- package/drizzle/meta/0002_snapshot.json +0 -575
- package/drizzle/meta/0003_snapshot.json +0 -667
- package/drizzle/meta/0004_snapshot.json +0 -769
- package/drizzle/meta/0005_snapshot.json +0 -864
- package/drizzle/meta/0006_snapshot.json +0 -882
- package/drizzle/meta/0007_snapshot.json +0 -876
- package/drizzle/meta/0008_snapshot.json +0 -882
- package/drizzle/meta/0009_snapshot.json +0 -948
- package/drizzle/meta/0010_snapshot.json +0 -969
- package/drizzle/meta/0011_snapshot.json +0 -1049
- package/drizzle/meta/0012_snapshot.json +0 -1115
- package/drizzle/meta/0013_snapshot.json +0 -1121
- package/drizzle/meta/0014_snapshot.json +0 -1190
- package/drizzle/meta/0015_snapshot.json +0 -1280
- package/drizzle/meta/0016_snapshot.json +0 -1683
- package/drizzle/meta/0017_snapshot.json +0 -1834
- package/drizzle/meta/0018_snapshot.json +0 -1834
- package/drizzle/meta/0019_snapshot.json +0 -1909
- package/drizzle/meta/0020_snapshot.json +0 -1921
- package/drizzle/meta/0021_snapshot.json +0 -2045
- package/drizzle/meta/0022_snapshot.json +0 -2246
- package/drizzle/meta/0023_snapshot.json +0 -2252
- package/drizzle/meta/0024_snapshot.json +0 -2375
- package/drizzle/meta/0025_snapshot.json +0 -2407
- package/drizzle/meta/0026_snapshot.json +0 -2413
- package/drizzle/meta/0027_snapshot.json +0 -2425
- package/drizzle/meta/0028_snapshot.json +0 -2438
- package/drizzle/meta/0029_snapshot.json +0 -2546
- package/drizzle/meta/0030_snapshot.json +0 -2586
- package/drizzle/meta/0031_snapshot.json +0 -2598
- package/drizzle/meta/0032_snapshot.json +0 -2699
- package/drizzle/meta/0033_snapshot.json +0 -2712
- package/drizzle/meta/0034_snapshot.json +0 -2719
- package/drizzle/meta/0035_snapshot.json +0 -2726
- package/drizzle/meta/0036_snapshot.json +0 -2876
- package/drizzle/meta/0037_snapshot.json +0 -2883
- package/drizzle/meta/0038_snapshot.json +0 -2895
- package/drizzle/meta/0039_snapshot.json +0 -3501
- package/drizzle/meta/0040_snapshot.json +0 -3527
- package/drizzle/meta/0041_snapshot.json +0 -3824
- package/drizzle/meta/0042_snapshot.json +0 -3960
- package/drizzle/meta/_journal.json +0 -307
- package/src/data/seed.json +0 -253
|
@@ -0,0 +1,911 @@
|
|
|
1
|
+
import { createRequire as __lurqCreateRequire } from 'node:module'; const require = __lurqCreateRequire(import.meta.url);
|
|
2
|
+
import {
|
|
3
|
+
searchCapabilities
|
|
4
|
+
} from "./chunk-L6N4JFW2.js";
|
|
5
|
+
import {
|
|
6
|
+
CONFIDENCE,
|
|
7
|
+
QUALITY_WEIGHTS,
|
|
8
|
+
WEIGHT_EXPLANATIONS,
|
|
9
|
+
activeWeightsPath,
|
|
10
|
+
applyOverrides,
|
|
11
|
+
loadWeights,
|
|
12
|
+
resetWeights,
|
|
13
|
+
saveWeights,
|
|
14
|
+
settableKeys,
|
|
15
|
+
validateWeights
|
|
16
|
+
} from "./chunk-2VMOWSRY.js";
|
|
17
|
+
import {
|
|
18
|
+
isCategory
|
|
19
|
+
} from "./chunk-VLRHFMGJ.js";
|
|
20
|
+
import {
|
|
21
|
+
require_semver
|
|
22
|
+
} from "./chunk-3GHJR7XR.js";
|
|
23
|
+
import {
|
|
24
|
+
loadEnv,
|
|
25
|
+
requireConfig
|
|
26
|
+
} from "./chunk-GRJXQBQZ.js";
|
|
27
|
+
import "./chunk-WHX2ZFCG.js";
|
|
28
|
+
import {
|
|
29
|
+
MissingKeyError
|
|
30
|
+
} from "./chunk-4ZYCIMY4.js";
|
|
31
|
+
import {
|
|
32
|
+
openInBrowser
|
|
33
|
+
} from "./chunk-TNTXWSYA.js";
|
|
34
|
+
import {
|
|
35
|
+
bold,
|
|
36
|
+
confidenceLabel,
|
|
37
|
+
detail,
|
|
38
|
+
dim,
|
|
39
|
+
formatDate,
|
|
40
|
+
formatNumber,
|
|
41
|
+
formatPercent,
|
|
42
|
+
green,
|
|
43
|
+
red,
|
|
44
|
+
table,
|
|
45
|
+
yellow
|
|
46
|
+
} from "./chunk-4KCPOMG6.js";
|
|
47
|
+
import "./chunk-LUAKDHLZ.js";
|
|
48
|
+
import {
|
|
49
|
+
resolveApiKey
|
|
50
|
+
} from "./chunk-Y5GI7RL3.js";
|
|
51
|
+
import "./chunk-GPTKSNR6.js";
|
|
52
|
+
import {
|
|
53
|
+
__toESM,
|
|
54
|
+
init_esm_shims
|
|
55
|
+
} from "./chunk-I3M2KJXB.js";
|
|
56
|
+
|
|
57
|
+
// src/cli/commands.ts
|
|
58
|
+
init_esm_shims();
|
|
59
|
+
var import_semver = __toESM(require_semver(), 1);
|
|
60
|
+
var handleRecommend = async (...a) => (await import("./handlers-GEJAVZDK.js")).handleRecommend(...a);
|
|
61
|
+
var handleEvaluate = async (...a) => (await import("./handlers-GEJAVZDK.js")).handleEvaluate(...a);
|
|
62
|
+
var handleCompare = async (...a) => (await import("./handlers-GEJAVZDK.js")).handleCompare(...a);
|
|
63
|
+
var handleVerify = async (...a) => (await import("./handlers-GEJAVZDK.js")).handleVerify(...a);
|
|
64
|
+
var getPackageVersions = async (...a) => (await import("./packages-THVFMWFP.js")).getPackageVersions(...a);
|
|
65
|
+
async function withDb(fn) {
|
|
66
|
+
requireConfig(["DATABASE_URL"]);
|
|
67
|
+
const { createDb } = await import("./client-IRKAMDYZ.js");
|
|
68
|
+
const handle = createDb();
|
|
69
|
+
try {
|
|
70
|
+
return await fn(handle.db);
|
|
71
|
+
} finally {
|
|
72
|
+
await handle.close();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function indexSource() {
|
|
76
|
+
loadEnv();
|
|
77
|
+
if (process.env.LURQ_LOCAL === "1") return "local";
|
|
78
|
+
if (resolveApiKey()) return "hosted";
|
|
79
|
+
if (process.env.DATABASE_URL) return "local";
|
|
80
|
+
throw new MissingKeyError();
|
|
81
|
+
}
|
|
82
|
+
async function fromIndex(tool, args, local) {
|
|
83
|
+
if (indexSource() === "local") return withDb(local);
|
|
84
|
+
const { callTool } = await import("./remote-YGCXYI4V.js");
|
|
85
|
+
return callTool(tool, args);
|
|
86
|
+
}
|
|
87
|
+
function candidateFlags(c) {
|
|
88
|
+
const flags = [
|
|
89
|
+
c.deprecated && red("deprecated"),
|
|
90
|
+
c.archived && yellow("archived"),
|
|
91
|
+
c.advisories ? yellow(`${c.advisories} advisory`) : null
|
|
92
|
+
].filter(Boolean);
|
|
93
|
+
return flags.length ? flags.join(" ") : dim("\u2014");
|
|
94
|
+
}
|
|
95
|
+
async function runRecommend(need, opts) {
|
|
96
|
+
if (opts.category && !isCategory(opts.category)) {
|
|
97
|
+
throw new Error(`Unknown category "${opts.category}".`);
|
|
98
|
+
}
|
|
99
|
+
const args = {
|
|
100
|
+
need,
|
|
101
|
+
category: opts.category,
|
|
102
|
+
constraints: opts.minConfidence ? { minConfidence: opts.minConfidence } : void 0
|
|
103
|
+
};
|
|
104
|
+
const res = await fromIndex("recommend", args, (db) => handleRecommend(db, args));
|
|
105
|
+
if (opts.json) return console.log(JSON.stringify(res, null, 2));
|
|
106
|
+
const candidates = res.candidates ?? [];
|
|
107
|
+
if (candidates.length === 0) {
|
|
108
|
+
console.log("No matching packages found.");
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
console.log(
|
|
112
|
+
table(
|
|
113
|
+
["Package", "Health", "Quality", "Confidence", "Weekly", "Latest", "Category", "Flags"],
|
|
114
|
+
candidates.map((c) => [
|
|
115
|
+
c.name,
|
|
116
|
+
String(c.healthScore),
|
|
117
|
+
c.qualityScore != null ? String(c.qualityScore) : "\u2014",
|
|
118
|
+
confidenceLabel(c.confidence),
|
|
119
|
+
formatNumber(c.weeklyDownloads),
|
|
120
|
+
c.latestVersion ?? "\u2014",
|
|
121
|
+
c.category ?? "\u2014",
|
|
122
|
+
// Last, and the only coloured cell: it is the column that can reverse
|
|
123
|
+
// the decision the other seven are supporting, so it reads as a verdict
|
|
124
|
+
// at the end of the row rather than as one more attribute among them.
|
|
125
|
+
candidateFlags(c)
|
|
126
|
+
])
|
|
127
|
+
)
|
|
128
|
+
);
|
|
129
|
+
printExclusions(res);
|
|
130
|
+
console.log(dim(`
|
|
131
|
+
data as of ${formatDate(res.dataAsOf)}`));
|
|
132
|
+
}
|
|
133
|
+
function printExclusions(res) {
|
|
134
|
+
if (!("excluded" in res)) return;
|
|
135
|
+
const excluded = res.excluded ?? [];
|
|
136
|
+
if (excluded.length === 0) return;
|
|
137
|
+
console.log(yellow(`
|
|
138
|
+
policy refused ${excluded.length}:`));
|
|
139
|
+
for (const e of excluded) console.log(` ${e.name} ${dim(`${e.rule}: ${e.reason}`)}`);
|
|
140
|
+
}
|
|
141
|
+
async function runEvaluate(pkg, opts) {
|
|
142
|
+
const res = await fromIndex(
|
|
143
|
+
"evaluate",
|
|
144
|
+
{ package: pkg },
|
|
145
|
+
(db) => handleEvaluate(db, { package: pkg })
|
|
146
|
+
);
|
|
147
|
+
if (opts.json) return console.log(JSON.stringify(res, null, 2));
|
|
148
|
+
if ("tracked" in res) {
|
|
149
|
+
console.log(res.suggestion);
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
console.log(bold(res.name) + (res.category ? dim(` (${res.category})`) : ""));
|
|
153
|
+
if (res.policy) {
|
|
154
|
+
console.log(
|
|
155
|
+
res.policy.allowed ? green("policy: allowed") : red(`policy: blocked (${res.policy.rule}): ${res.policy.reason}`)
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
const b = res.scoreBreakdown ?? {};
|
|
159
|
+
const advisories = res.advisories;
|
|
160
|
+
console.log(
|
|
161
|
+
detail([
|
|
162
|
+
["health", `${res.healthScore} ${confidenceLabel(res.confidence)}`],
|
|
163
|
+
["quality", res.qualityScore != null ? String(res.qualityScore) : "\u2014"],
|
|
164
|
+
[
|
|
165
|
+
"breakdown",
|
|
166
|
+
`maint ${b.maintenance ?? "\u2014"} \xB7 adopt ${b.adoption ?? "\u2014"} \xB7 rel ${b.reliability ?? "\u2014"} \xB7 eff ${b.efficiency ?? "\u2014"} \xB7 qual ${b.quality ?? "\u2014"}`
|
|
167
|
+
],
|
|
168
|
+
["version", res.latestVersion ?? "\u2014"],
|
|
169
|
+
[
|
|
170
|
+
"weekly dl",
|
|
171
|
+
`${formatNumber(res.weeklyDownloads)} (${formatPercent(res.downloadGrowth90d)} 90d)`
|
|
172
|
+
],
|
|
173
|
+
["scorecard", res.scorecard != null ? String(res.scorecard) : "\u2014"],
|
|
174
|
+
["bundle", res.bundleMinGzipKb != null ? `${res.bundleMinGzipKb} KB gzip` : "\u2014"],
|
|
175
|
+
["released", formatDate(res.lastReleaseAt)],
|
|
176
|
+
[
|
|
177
|
+
"flags",
|
|
178
|
+
[res.deprecated && "deprecated", res.archived && "archived"].filter(Boolean).join(", ") || "none"
|
|
179
|
+
],
|
|
180
|
+
[
|
|
181
|
+
"advisories",
|
|
182
|
+
advisories == null ? yellow("not checked yet") : advisories.length ? advisories.map((a) => `${a.severity}`).join(", ") : "none"
|
|
183
|
+
],
|
|
184
|
+
["repo", res.repoUrl ?? "\u2014"]
|
|
185
|
+
])
|
|
186
|
+
);
|
|
187
|
+
if (res.buildVerified) {
|
|
188
|
+
const bv = res.buildVerified;
|
|
189
|
+
const state = !bv.installed ? red("install failed") : bv.loaded === false ? yellow("installs, load failed") : green("installs and loads");
|
|
190
|
+
console.log(`
|
|
191
|
+
sandbox: ${state} ${dim(`${bv.version} \xB7 ${bv.driver}`)}`);
|
|
192
|
+
}
|
|
193
|
+
if (res.summary) console.log("\n" + res.summary);
|
|
194
|
+
if (res.usageGuide) {
|
|
195
|
+
const g = res.usageGuide;
|
|
196
|
+
console.log(
|
|
197
|
+
"\n" + detail(
|
|
198
|
+
[
|
|
199
|
+
["what", g.whatItIs],
|
|
200
|
+
["when", g.whenToUse],
|
|
201
|
+
g.whenNotToUse ? ["when not", g.whenNotToUse] : null,
|
|
202
|
+
["fits", g.whereItFits],
|
|
203
|
+
g.howToWireIn ? ["wire-in", g.howToWireIn] : null
|
|
204
|
+
].filter(Boolean)
|
|
205
|
+
)
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
console.log(dim(`
|
|
209
|
+
data as of ${formatDate(res.dataAsOf)}${res.stale ? " (stale)" : ""}`));
|
|
210
|
+
}
|
|
211
|
+
async function runCompare(pkgs, opts) {
|
|
212
|
+
const res = await fromIndex(
|
|
213
|
+
"compare",
|
|
214
|
+
{ packages: pkgs },
|
|
215
|
+
(db) => handleCompare(db, { packages: pkgs })
|
|
216
|
+
);
|
|
217
|
+
if (opts.json) return console.log(JSON.stringify(res, null, 2));
|
|
218
|
+
console.log(
|
|
219
|
+
table(
|
|
220
|
+
["Package", "Health", "Confidence", "Weekly", "90d", "Scorecard", "Released"],
|
|
221
|
+
(res.rows ?? []).map((r) => [
|
|
222
|
+
r.name,
|
|
223
|
+
String(r.healthScore),
|
|
224
|
+
confidenceLabel(r.confidence),
|
|
225
|
+
formatNumber(r.weeklyDownloads),
|
|
226
|
+
formatPercent(r.downloadGrowth90d),
|
|
227
|
+
r.scorecard != null ? String(r.scorecard) : "\u2014",
|
|
228
|
+
formatDate(r.lastReleaseAt)
|
|
229
|
+
])
|
|
230
|
+
)
|
|
231
|
+
);
|
|
232
|
+
const notFound = res.notFound ?? (res.pending ? [] : res.missing ?? []);
|
|
233
|
+
if (notFound.length) console.log(red(`
|
|
234
|
+
not on npm: ${notFound.join(", ")}`));
|
|
235
|
+
if (res.pending?.length) {
|
|
236
|
+
console.log(dim(`
|
|
237
|
+
being scored now (retry shortly): ${res.pending.join(", ")}`));
|
|
238
|
+
}
|
|
239
|
+
console.log(dim(`data as of ${formatDate(res.dataAsOf)}`));
|
|
240
|
+
}
|
|
241
|
+
function runCan(query, opts = {}) {
|
|
242
|
+
const matches = searchCapabilities(query, 5);
|
|
243
|
+
if (opts.json) {
|
|
244
|
+
console.log(JSON.stringify(matches, null, 2));
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
if (!query.trim()) console.log(dim("Showing everything lurq does. Add words to narrow it.\n"));
|
|
248
|
+
for (const m of matches) {
|
|
249
|
+
console.log(bold(m.title));
|
|
250
|
+
console.log(` ${m.answer}`);
|
|
251
|
+
if (m.cli) console.log(` ${green("$")} ${m.cli}`);
|
|
252
|
+
if (m.mcp) console.log(dim(` agents: call the \`${m.mcp}\` tool`));
|
|
253
|
+
if (m.page) console.log(dim(` dashboard: https://lurq.run${m.page}`));
|
|
254
|
+
console.log("");
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
function runWeights(opts = {}) {
|
|
258
|
+
const w = loadWeights();
|
|
259
|
+
const active = activeWeightsPath();
|
|
260
|
+
if (opts.json) {
|
|
261
|
+
console.log(JSON.stringify({ ...w, source: active?.source ?? "defaults" }, null, 2));
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
const pct = (n) => n.toFixed(2);
|
|
265
|
+
console.log(bold("Two axes, blended for default sort:"));
|
|
266
|
+
console.log(` composite = (1\u2212\u03BB)\xB7health + \u03BB\xB7quality \u03BB = ${pct(w.composite.lambda)}
|
|
267
|
+
`);
|
|
268
|
+
console.log(bold("Health (proven-ness) \u2014 weighted sum of 4 components:"));
|
|
269
|
+
console.log(
|
|
270
|
+
detail([
|
|
271
|
+
[
|
|
272
|
+
"maintenance",
|
|
273
|
+
`${pct(w.health.maintenance)} ${dim("\u2014 " + WEIGHT_EXPLANATIONS.maintenance)}`
|
|
274
|
+
],
|
|
275
|
+
["adoption", `${pct(w.health.adoption)} ${dim("\u2014 " + WEIGHT_EXPLANATIONS.adoption)}`],
|
|
276
|
+
[
|
|
277
|
+
"reliability",
|
|
278
|
+
`${pct(w.health.reliability)} ${dim("\u2014 " + WEIGHT_EXPLANATIONS.reliability)}`
|
|
279
|
+
],
|
|
280
|
+
["efficiency", `${pct(w.health.efficiency)} ${dim("\u2014 " + WEIGHT_EXPLANATIONS.efficiency)}`]
|
|
281
|
+
])
|
|
282
|
+
);
|
|
283
|
+
console.log("\n" + bold("Quality (intrinsic, adoption-independent):"));
|
|
284
|
+
console.log(" " + dim(Object.keys(QUALITY_WEIGHTS).join(", ")));
|
|
285
|
+
console.log("\n" + bold("Confidence thresholds:"));
|
|
286
|
+
console.log(
|
|
287
|
+
detail([
|
|
288
|
+
[
|
|
289
|
+
"proven",
|
|
290
|
+
`\u2265 ${formatNumber(CONFIDENCE.proven.minWeeklyDownloads)} weekly dl, \u2265 ${CONFIDENCE.proven.minAgeMonths}mo old`
|
|
291
|
+
],
|
|
292
|
+
[
|
|
293
|
+
"emerging",
|
|
294
|
+
`\u2265 ${formatNumber(CONFIDENCE.emerging.minWeeklyDownloads)} weekly dl OR \u2265 ${CONFIDENCE.emerging.strongGrowth * 100}% 90d growth`
|
|
295
|
+
],
|
|
296
|
+
["promising", `\u2265 ${CONFIDENCE.promising.minQuality} quality score (adoption-independent)`]
|
|
297
|
+
])
|
|
298
|
+
);
|
|
299
|
+
console.log(
|
|
300
|
+
dim(
|
|
301
|
+
`
|
|
302
|
+
Source: ${active ? `${active.source} (${active.path})` : "defaults (no user overrides)"}`
|
|
303
|
+
)
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
async function runEditWeights(opts) {
|
|
307
|
+
const { invalidateCache } = await import("./cache-DKI34SVS.js");
|
|
308
|
+
if (opts.reset) {
|
|
309
|
+
const removed = resetWeights();
|
|
310
|
+
console.log(
|
|
311
|
+
removed.length ? `Removed overrides:
|
|
312
|
+
${removed.join("\n ")}` : "No overrides to remove; already on defaults."
|
|
313
|
+
);
|
|
314
|
+
if (removed.length) await invalidateCache();
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
if (opts.explain) {
|
|
318
|
+
const key = opts.explain;
|
|
319
|
+
const text = WEIGHT_EXPLANATIONS[key];
|
|
320
|
+
if (!text) {
|
|
321
|
+
throw new Error(
|
|
322
|
+
`No explanation for "${key}". Known: ${Object.keys(WEIGHT_EXPLANATIONS).join(", ")}.`
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
console.log(`${bold(key)} \u2014 ${text}`);
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
if (opts.set && opts.set.length > 0) {
|
|
329
|
+
const next = applyOverrides(loadWeights(), opts.set);
|
|
330
|
+
const { weights, normalized } = validateWeights(next);
|
|
331
|
+
const path = saveWeights(weights, opts.project ? "project" : "user");
|
|
332
|
+
await invalidateCache();
|
|
333
|
+
console.log(`Saved overrides to ${path}`);
|
|
334
|
+
if (normalized) {
|
|
335
|
+
console.log(
|
|
336
|
+
yellow("Health weights did not sum to 1.0 \u2014 renormalized to: ") + `maint ${weights.health.maintenance.toFixed(3)}, adopt ${weights.health.adoption.toFixed(3)}, rel ${weights.health.reliability.toFixed(3)}, eff ${weights.health.efficiency.toFixed(3)}`
|
|
337
|
+
);
|
|
338
|
+
}
|
|
339
|
+
console.log(
|
|
340
|
+
dim(
|
|
341
|
+
"\nThese weights apply to a self-hosted index, on its next operator rescore.\nThe hosted service ranks with its published weights (`lurq weights`)."
|
|
342
|
+
)
|
|
343
|
+
);
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
console.log(dim(`No changes. Settable keys: ${settableKeys().join(", ")}.
|
|
347
|
+
`));
|
|
348
|
+
runWeights(opts);
|
|
349
|
+
}
|
|
350
|
+
async function runPlan(file, opts) {
|
|
351
|
+
const { readFileSync } = await import("fs");
|
|
352
|
+
let document;
|
|
353
|
+
try {
|
|
354
|
+
document = readFileSync(file, "utf8");
|
|
355
|
+
} catch {
|
|
356
|
+
throw new Error(`Could not read "${file}".`);
|
|
357
|
+
}
|
|
358
|
+
if (opts.optimize && opts.optimize !== "speed" && opts.optimize !== "balanced") {
|
|
359
|
+
throw new Error("--optimize must be 'speed' or 'balanced'.");
|
|
360
|
+
}
|
|
361
|
+
const args = { document, optimize: opts.optimize };
|
|
362
|
+
const res = await fromIndex("plan", args, async (db) => {
|
|
363
|
+
const { handlePlan } = await import("./plan-V4OEI3J4.js");
|
|
364
|
+
return handlePlan(db, args);
|
|
365
|
+
});
|
|
366
|
+
if (opts.json) return console.log(JSON.stringify(res, null, 2));
|
|
367
|
+
if (!("slots" in res)) {
|
|
368
|
+
console.log(res.note);
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
if (opts.html || opts.open) {
|
|
372
|
+
const { writeFileSync } = await import("fs");
|
|
373
|
+
const { tmpdir } = await import("os");
|
|
374
|
+
const { join } = await import("path");
|
|
375
|
+
const { renderPlanHtml } = await import("./planView-JRXGCG2N.js");
|
|
376
|
+
const out = opts.html ?? join(tmpdir(), `lurq-plan-${Date.now()}.html`);
|
|
377
|
+
writeFileSync(out, renderPlanHtml(res), "utf8");
|
|
378
|
+
console.log(`Roadmap written to ${out}`);
|
|
379
|
+
if (opts.open) openInBrowser(out);
|
|
380
|
+
}
|
|
381
|
+
console.log(
|
|
382
|
+
table(
|
|
383
|
+
["Component", "Layer", "Recommended", "Health", "Confidence", "Alternatives"],
|
|
384
|
+
(res.slots ?? []).map((s) => [
|
|
385
|
+
s.need.length > 32 ? s.need.slice(0, 31) + "\u2026" : s.need,
|
|
386
|
+
s.layer,
|
|
387
|
+
s.recommended ? `${s.recommended.name}@${s.recommended.latestVersion ?? "?"}` : dim("\u2014"),
|
|
388
|
+
s.recommended ? String(s.recommended.healthScore) : "\u2014",
|
|
389
|
+
s.recommended ? confidenceLabel(s.recommended.confidence) : "\u2014",
|
|
390
|
+
(s.alternatives ?? []).map((a) => a.name).join(", ") || "\u2014"
|
|
391
|
+
])
|
|
392
|
+
)
|
|
393
|
+
);
|
|
394
|
+
if ((res.unmatched ?? []).length)
|
|
395
|
+
console.log(yellow(`
|
|
396
|
+
No match for: ${(res.unmatched ?? []).join(", ")}`));
|
|
397
|
+
if (res.compatibility) {
|
|
398
|
+
const c = res.compatibility;
|
|
399
|
+
const col = c.overall === "compatible" ? green : c.overall === "conflict" ? red : dim;
|
|
400
|
+
console.log("\n" + bold("Compatibility: ") + col(c.overall));
|
|
401
|
+
for (const s of res.slots ?? []) {
|
|
402
|
+
if (s.swappedFrom && s.recommended) {
|
|
403
|
+
console.log(
|
|
404
|
+
green(` \u2713 swapped ${s.swappedFrom} \u2192 ${s.recommended.name} for compatibility`)
|
|
405
|
+
);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
for (const cf of c.conflicts ?? [])
|
|
409
|
+
console.log(red(` \u2717 ${cf.detail} (no compatible alternative)`));
|
|
410
|
+
if (c.unverified?.length) console.log(dim(` unverified: ${c.unverified.join(", ")}`));
|
|
411
|
+
}
|
|
412
|
+
console.log("\n" + bold("Roadmap (Mermaid):"));
|
|
413
|
+
console.log(res.mermaid);
|
|
414
|
+
console.log(dim(`
|
|
415
|
+
${res.note}`));
|
|
416
|
+
console.log(dim(`data as of ${formatDate(res.dataAsOf)}`));
|
|
417
|
+
}
|
|
418
|
+
async function runVerify(pkg, opts) {
|
|
419
|
+
const res = await fromIndex(
|
|
420
|
+
"verify",
|
|
421
|
+
{ package: pkg },
|
|
422
|
+
(db) => handleVerify(db, { package: pkg })
|
|
423
|
+
);
|
|
424
|
+
if (opts.json) return console.log(JSON.stringify(res, null, 2));
|
|
425
|
+
const v = res.verdict;
|
|
426
|
+
const headline = v.level === "invalid" ? red("\u2717 NOT A REAL PACKAGE") : v.level === "high" ? red(`\u2717 do not install ${v.scope} without review`) : v.level === "medium" ? yellow("\u26A0 usable, but read the findings") : v.reassuring ? green(`\u2713 no supply-chain problems found in ${v.scope}`) : yellow(`? ${v.scope} is not fully checked yet`);
|
|
427
|
+
console.log(`${bold(pkg)} ${headline}`);
|
|
428
|
+
for (const r of v.reasons ?? []) console.log(` ${red("\u2022")} ${r}`);
|
|
429
|
+
for (const u of v.unknowns ?? []) console.log(` ${yellow("?")} ${u}`);
|
|
430
|
+
if (v.level === "invalid") return;
|
|
431
|
+
const riskColor = v.level === "high" ? red : v.level === "medium" ? yellow : green;
|
|
432
|
+
const riskFlags = res.riskFlags ?? [];
|
|
433
|
+
console.log(
|
|
434
|
+
detail([
|
|
435
|
+
["version", res.latestVersion ?? "\u2014"],
|
|
436
|
+
["weekly dl", formatNumber(res.weeklyDownloads)],
|
|
437
|
+
["confidence", res.confidence ? confidenceLabel(res.confidence) : "\u2014"],
|
|
438
|
+
// Never "0" for something nobody has looked at.
|
|
439
|
+
[
|
|
440
|
+
"advisories",
|
|
441
|
+
res.advisoryCount == null ? yellow("not checked yet") : String(res.advisoryCount)
|
|
442
|
+
],
|
|
443
|
+
["risk", riskColor(v.level)],
|
|
444
|
+
["risk flags", riskFlags.length ? yellow(riskFlags.join(", ")) : "none"]
|
|
445
|
+
])
|
|
446
|
+
);
|
|
447
|
+
}
|
|
448
|
+
async function runVersions(pkg, opts) {
|
|
449
|
+
const limit = opts.limit ? Math.max(1, parseInt(opts.limit, 10) || 30) : 30;
|
|
450
|
+
loadEnv();
|
|
451
|
+
if (!process.env.DATABASE_URL) {
|
|
452
|
+
throw new Error(
|
|
453
|
+
"`lurq versions` reads a self-hosted index and is not available on the hosted service. To see what changed between two versions, run `lurq usage <pkg> --known <v>`."
|
|
454
|
+
);
|
|
455
|
+
}
|
|
456
|
+
await withDb(async (db) => {
|
|
457
|
+
const versions = await getPackageVersions(db, pkg, limit);
|
|
458
|
+
if (opts.json) return console.log(JSON.stringify(versions, null, 2));
|
|
459
|
+
if (versions.length === 0) {
|
|
460
|
+
console.log(`No stored versions for ${bold(pkg)}. Run \`lurq sync --package ${pkg}\` first.`);
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
console.log(bold(pkg));
|
|
464
|
+
console.log(
|
|
465
|
+
table(
|
|
466
|
+
["Version", "Published"],
|
|
467
|
+
versions.map((v) => [
|
|
468
|
+
v.version,
|
|
469
|
+
v.publishedAt ? v.publishedAt.toISOString().slice(0, 10) : "\u2014"
|
|
470
|
+
])
|
|
471
|
+
)
|
|
472
|
+
);
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
async function runWatch() {
|
|
476
|
+
const { watchNpmChanges } = await import("./watch-DGFMWUDL.js");
|
|
477
|
+
await withDb(async (db) => {
|
|
478
|
+
const controller = new AbortController();
|
|
479
|
+
const stop = () => controller.abort();
|
|
480
|
+
process.once("SIGINT", stop);
|
|
481
|
+
process.once("SIGTERM", stop);
|
|
482
|
+
console.log(dim("watching npm for releases of tracked packages \u2014 Ctrl-C to stop"));
|
|
483
|
+
try {
|
|
484
|
+
await watchNpmChanges(db, { signal: controller.signal });
|
|
485
|
+
} finally {
|
|
486
|
+
process.off("SIGINT", stop);
|
|
487
|
+
process.off("SIGTERM", stop);
|
|
488
|
+
}
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
async function runSandbox(pkg, version, opts) {
|
|
492
|
+
if (opts.allowScripts) {
|
|
493
|
+
console.error(
|
|
494
|
+
yellow("warning: running install scripts and loading the package locally without isolation")
|
|
495
|
+
);
|
|
496
|
+
}
|
|
497
|
+
const { verifyPackageInSandbox } = await import("./sandbox-MEZLAODF.js");
|
|
498
|
+
await withDb(async (db) => {
|
|
499
|
+
const result = await verifyPackageInSandbox(db, pkg, version ?? null, {
|
|
500
|
+
target: { node: "20", moduleSystem: opts.esm ? "esm" : "cjs" },
|
|
501
|
+
allowScripts: opts.allowScripts
|
|
502
|
+
});
|
|
503
|
+
if (opts.json) return console.log(JSON.stringify(result, null, 2));
|
|
504
|
+
const ok = result.installed && result.imported !== false;
|
|
505
|
+
const label = version ? `${pkg}@${version}` : pkg;
|
|
506
|
+
const verdict = ok ? green("\u2713 installs and loads") : red("\u2717 failed");
|
|
507
|
+
console.log(
|
|
508
|
+
`${bold(label)} ${verdict} ${dim(`(${result.durationMs}ms \xB7 ${result.driver})`)}`
|
|
509
|
+
);
|
|
510
|
+
console.log(
|
|
511
|
+
detail([
|
|
512
|
+
["installed", result.installed ? "yes" : "no"],
|
|
513
|
+
["loaded", result.imported === null ? "\u2014" : result.imported ? "yes" : "no"],
|
|
514
|
+
["module", result.moduleSystem],
|
|
515
|
+
["scripts", result.ranScripts ? "ran" : "skipped"],
|
|
516
|
+
["error", result.error ?? "none"]
|
|
517
|
+
])
|
|
518
|
+
);
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
async function runUsage(pkg, opts) {
|
|
522
|
+
const args = {
|
|
523
|
+
package: pkg,
|
|
524
|
+
version: opts.version,
|
|
525
|
+
knownVersion: opts.known,
|
|
526
|
+
query: opts.query,
|
|
527
|
+
offset: opts.offset
|
|
528
|
+
};
|
|
529
|
+
const res = await fromIndex("usage", args, async (db) => {
|
|
530
|
+
const { handleUsage } = await import("./handlers-GEJAVZDK.js");
|
|
531
|
+
return handleUsage(db, args);
|
|
532
|
+
});
|
|
533
|
+
if (opts.json) return console.log(JSON.stringify(res, null, 2));
|
|
534
|
+
console.log(`${bold(res.package)}${res.version ? `@${res.version}` : ""}`);
|
|
535
|
+
if (res.engines) {
|
|
536
|
+
const reqs = Object.entries(res.engines).map(([k, v]) => `${k} ${v}`).join(", ");
|
|
537
|
+
if (reqs) console.log(dim(`requires: ${reqs}`));
|
|
538
|
+
}
|
|
539
|
+
if (res.unpublishedVersion) return console.log(red(res.note ?? "that version does not exist"));
|
|
540
|
+
if (!res.available) return console.log(dim(res.note ?? "no API surface available"));
|
|
541
|
+
console.log(
|
|
542
|
+
table(
|
|
543
|
+
["Export", "Kind", "Signature"],
|
|
544
|
+
(res.surface ?? []).map((s) => [s.name, s.kind, s.signature ?? ""])
|
|
545
|
+
)
|
|
546
|
+
);
|
|
547
|
+
if (res.note) console.log(res.shallow ? yellow(res.note) : dim(res.note));
|
|
548
|
+
if (res.delta) {
|
|
549
|
+
const d = res.delta;
|
|
550
|
+
const removed = d.removed ?? [];
|
|
551
|
+
const added = d.added ?? [];
|
|
552
|
+
const renamed = d.renamed ?? [];
|
|
553
|
+
const changed = d.changed ?? [];
|
|
554
|
+
console.log(bold(`
|
|
555
|
+
\u0394 from ${res.package}@${d.fromVersion}:`));
|
|
556
|
+
for (const s of removed) console.log(red(` - ${s.name}`));
|
|
557
|
+
for (const s of added) console.log(green(` + ${s.name}`));
|
|
558
|
+
for (const r of renamed) console.log(yellow(` ~ ${r.from.name} \u2192 ${r.to.name}`));
|
|
559
|
+
for (const c of changed)
|
|
560
|
+
console.log(yellow(` ! ${c.name}: ${c.before ?? "?"} \u2192 ${c.after ?? "?"}`));
|
|
561
|
+
if (!removed.length && !added.length && !renamed.length && !changed.length) {
|
|
562
|
+
console.log(dim(" no API changes"));
|
|
563
|
+
}
|
|
564
|
+
} else if (res.deltaNote) {
|
|
565
|
+
console.log(yellow(`
|
|
566
|
+
${res.deltaNote}`));
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
async function runCompatBackfill(opts) {
|
|
570
|
+
await withDb(async (db) => {
|
|
571
|
+
const compat = await import("./compat-R3UTFQ5Q.js");
|
|
572
|
+
if (opts.resolve) {
|
|
573
|
+
console.error(yellow("resolve-only backfill (npm resolution, no install/VM)"));
|
|
574
|
+
const res2 = await compat.resolveBackfill(db, opts);
|
|
575
|
+
console.log(
|
|
576
|
+
`${green("resolve backfill done")}: ${res2.verified} edges across ${res2.batches} runs, ${res2.skipped} batches skipped`
|
|
577
|
+
);
|
|
578
|
+
return;
|
|
579
|
+
}
|
|
580
|
+
console.error(
|
|
581
|
+
yellow(
|
|
582
|
+
"co-installing top packages in the sandbox (loads package code locally without isolation unless E2B_API_KEY is set)"
|
|
583
|
+
)
|
|
584
|
+
);
|
|
585
|
+
const res = await compat.backfillVerify(db, opts);
|
|
586
|
+
console.log(
|
|
587
|
+
`${green("backfill done")}: ${res.verified} edges across ${res.batches} runs, ${res.skipped} batches skipped`
|
|
588
|
+
);
|
|
589
|
+
});
|
|
590
|
+
}
|
|
591
|
+
function parsePins(pins) {
|
|
592
|
+
if (!pins?.length) return void 0;
|
|
593
|
+
const out = {};
|
|
594
|
+
for (const raw of pins) {
|
|
595
|
+
const at = raw.indexOf("=");
|
|
596
|
+
const name = at === -1 ? "" : raw.slice(0, at).trim();
|
|
597
|
+
const version = at === -1 ? "" : raw.slice(at + 1).trim();
|
|
598
|
+
if (!name || !version) throw new Error(`--pin expects name=version, got "${raw}"`);
|
|
599
|
+
if (!import_semver.default.valid(version)) {
|
|
600
|
+
throw new Error(
|
|
601
|
+
`--pin needs an exact published version, got "${name}=${version}". A range resolves to latest instead of erroring, which would report the wrong version as pinned.`
|
|
602
|
+
);
|
|
603
|
+
}
|
|
604
|
+
out[name] = version;
|
|
605
|
+
}
|
|
606
|
+
return out;
|
|
607
|
+
}
|
|
608
|
+
async function runCompat(pkgs, opts) {
|
|
609
|
+
const versions = parsePins(opts.pin);
|
|
610
|
+
const args = { packages: pkgs, versions };
|
|
611
|
+
const read = async (db) => {
|
|
612
|
+
const { handleCompat } = await import("./handlers-GEJAVZDK.js");
|
|
613
|
+
return handleCompat(db, args);
|
|
614
|
+
};
|
|
615
|
+
const res = opts.run ? await withDb(async (db) => {
|
|
616
|
+
console.error(
|
|
617
|
+
yellow("co-installing in the sandbox (loads package code locally without isolation)")
|
|
618
|
+
);
|
|
619
|
+
const { verifyCompatibility } = await import("./compat-R3UTFQ5Q.js");
|
|
620
|
+
await verifyCompatibility(db, pkgs);
|
|
621
|
+
return read(db);
|
|
622
|
+
}) : await fromIndex("compat", args, read);
|
|
623
|
+
const conflicts = res.conflicts ?? [];
|
|
624
|
+
const unverified = res.unverified ?? [];
|
|
625
|
+
if (opts.json) return console.log(JSON.stringify(res, null, 2));
|
|
626
|
+
const color = res.overall === "compatible" ? green : res.overall === "conflict" ? red : dim;
|
|
627
|
+
console.log(`${bold((res.packages ?? pkgs).join(" + "))} ${color(res.overall)}`);
|
|
628
|
+
if (conflicts.length) {
|
|
629
|
+
console.log(
|
|
630
|
+
table(
|
|
631
|
+
["Source", "Detail"],
|
|
632
|
+
conflicts.map((c) => [c.source, c.detail])
|
|
633
|
+
)
|
|
634
|
+
);
|
|
635
|
+
} else if (res.overall === "compatible") {
|
|
636
|
+
console.log(dim("no peer-dependency or engine conflicts across the set"));
|
|
637
|
+
}
|
|
638
|
+
if (unverified.length) {
|
|
639
|
+
console.log(dim(`
|
|
640
|
+
unverified (no metadata): ${unverified.join(", ")}`));
|
|
641
|
+
}
|
|
642
|
+
const conflicted = new Set(
|
|
643
|
+
conflicts.flatMap((c) => c.packages.length >= 2 ? [[...c.packages].sort().join("\0")] : [])
|
|
644
|
+
);
|
|
645
|
+
const isOverruled = (e) => conflicted.has([...e.packages].sort().join("\0"));
|
|
646
|
+
const compatEvidence = (res.evidence ?? []).filter((e) => e.status === "compatible");
|
|
647
|
+
if (compatEvidence.length) {
|
|
648
|
+
console.log(
|
|
649
|
+
table(
|
|
650
|
+
["Pair", "Evidence", "Witnesses"],
|
|
651
|
+
compatEvidence.map((e) => [
|
|
652
|
+
`${e.packages[0]} + ${e.packages[1]}`,
|
|
653
|
+
isOverruled(e) ? `${e.provenance} (overruled)` : e.provenance,
|
|
654
|
+
e.provenance === "observed" ? String(e.witnessCount) : "\u2014"
|
|
655
|
+
])
|
|
656
|
+
)
|
|
657
|
+
);
|
|
658
|
+
if (compatEvidence.some(isOverruled)) {
|
|
659
|
+
console.log(
|
|
660
|
+
dim("overruled: a declared peer/engine constraint above beats the co-resolution witness")
|
|
661
|
+
);
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
async function runMcpSurface(server, opts) {
|
|
666
|
+
const args = { server, version: opts.version ?? null };
|
|
667
|
+
const res = await fromIndex(
|
|
668
|
+
"mcp_surface",
|
|
669
|
+
args,
|
|
670
|
+
(db) => import("./mcpHandlers-QFN4MCFL.js").then((m) => m.handleMcpSurface(db, args))
|
|
671
|
+
);
|
|
672
|
+
if (opts.json) return console.log(JSON.stringify(res, null, 2));
|
|
673
|
+
console.log(`${bold(server)}${res.version ? dim(`@${res.version}`) : ""}`);
|
|
674
|
+
const requires = res.requires ?? [];
|
|
675
|
+
if (requires.length) {
|
|
676
|
+
console.log(
|
|
677
|
+
table(
|
|
678
|
+
["Needs", "Required", "Secret", "What for"],
|
|
679
|
+
requires.map((r) => [
|
|
680
|
+
r.name,
|
|
681
|
+
r.required ? red("yes") : dim("no"),
|
|
682
|
+
r.secret ? yellow("yes") : dim("no"),
|
|
683
|
+
r.description ?? "\u2014"
|
|
684
|
+
])
|
|
685
|
+
)
|
|
686
|
+
);
|
|
687
|
+
}
|
|
688
|
+
if (res.configRequest) console.log(yellow(`
|
|
689
|
+
${res.configRequest}
|
|
690
|
+
`));
|
|
691
|
+
const tools = res.tools ?? [];
|
|
692
|
+
if (res.verdict !== "verified_true" || tools.length === 0) {
|
|
693
|
+
console.log(yellow(`${res.verdict}: ${res.coverageNote}`));
|
|
694
|
+
return;
|
|
695
|
+
}
|
|
696
|
+
console.log(
|
|
697
|
+
table(
|
|
698
|
+
["Tool", "Required", "Optional", "Behaviour"],
|
|
699
|
+
tools.map((t) => {
|
|
700
|
+
const required = t.required ?? [];
|
|
701
|
+
const optional = (t.params ?? []).filter((p) => !required.includes(p));
|
|
702
|
+
const a = t.annotations ?? {};
|
|
703
|
+
const behaviour = [
|
|
704
|
+
a.readOnlyHint ? green("read-only") : red("writes"),
|
|
705
|
+
a.destructiveHint ? red("destroys") : null,
|
|
706
|
+
a.openWorldHint ? yellow("external") : null,
|
|
707
|
+
t.deprecated ? yellow("deprecated") : null
|
|
708
|
+
].filter(Boolean).join(" ");
|
|
709
|
+
return [t.name, required.join(", ") || "\u2014", optional.join(", ") || "\u2014", behaviour];
|
|
710
|
+
})
|
|
711
|
+
)
|
|
712
|
+
);
|
|
713
|
+
console.log(dim(res.coverageNote));
|
|
714
|
+
}
|
|
715
|
+
async function runMcpDrift(server, opts) {
|
|
716
|
+
const args = { server, fromVersion: opts.from, toVersion: opts.to };
|
|
717
|
+
const res = await fromIndex(
|
|
718
|
+
"mcp_drift",
|
|
719
|
+
args,
|
|
720
|
+
(db) => import("./mcpHandlers-QFN4MCFL.js").then((m) => m.handleMcpDrift(db, args))
|
|
721
|
+
);
|
|
722
|
+
if (opts.json) return console.log(JSON.stringify(res, null, 2));
|
|
723
|
+
console.log(`${bold(server)} ${dim(`${opts.from} \u2192 ${opts.to}`)}`);
|
|
724
|
+
if (res.inconclusive) {
|
|
725
|
+
console.log(yellow(res.inconclusive));
|
|
726
|
+
return;
|
|
727
|
+
}
|
|
728
|
+
const {
|
|
729
|
+
annotationFlips = [],
|
|
730
|
+
silentDrift = [],
|
|
731
|
+
removedTools = [],
|
|
732
|
+
requiredAdded = [],
|
|
733
|
+
paramsRemoved = [],
|
|
734
|
+
typeChanged = [],
|
|
735
|
+
addedTools = [],
|
|
736
|
+
requiredRelaxed = [],
|
|
737
|
+
paramsAdded = [],
|
|
738
|
+
outputChanged = [],
|
|
739
|
+
deprecated = [],
|
|
740
|
+
prosePolished = []
|
|
741
|
+
} = res;
|
|
742
|
+
const widened = annotationFlips.filter((f) => f.widensPrivilege);
|
|
743
|
+
for (const f of widened) {
|
|
744
|
+
console.log(red(`\u26A0 privilege ${f.tool}.${f.hint}: ${f.from} \u2192 ${f.to}`));
|
|
745
|
+
}
|
|
746
|
+
for (const t of silentDrift) {
|
|
747
|
+
console.log(yellow(`\u26A0 silent ${t}: schema changed, description did not`));
|
|
748
|
+
}
|
|
749
|
+
for (const t of removedTools) console.log(red(`\u2717 removed ${t}`));
|
|
750
|
+
for (const c of requiredAdded) {
|
|
751
|
+
console.log(red(`\u2717 required ${c.tool}: ${c.params.join(", ")} now mandatory`));
|
|
752
|
+
}
|
|
753
|
+
for (const c of paramsRemoved) {
|
|
754
|
+
console.log(red(`\u2717 dropped ${c.tool}: ${c.params.join(", ")}`));
|
|
755
|
+
}
|
|
756
|
+
for (const c of typeChanged.filter((x) => !x.widened)) {
|
|
757
|
+
console.log(red(`\u2717 narrowed ${c.tool}.${c.param}`));
|
|
758
|
+
}
|
|
759
|
+
for (const t of addedTools) console.log(green(`+ added ${t}`));
|
|
760
|
+
for (const c of requiredRelaxed) {
|
|
761
|
+
console.log(green(`+ optional ${c.tool}: ${c.params.join(", ")}`));
|
|
762
|
+
}
|
|
763
|
+
for (const c of paramsAdded) {
|
|
764
|
+
console.log(green(`+ params ${c.tool}: ${c.params.join(", ")}`));
|
|
765
|
+
}
|
|
766
|
+
for (const c of typeChanged.filter((x) => x.widened)) {
|
|
767
|
+
console.log(green(`+ relaxed ${c.tool}.${c.param}`));
|
|
768
|
+
}
|
|
769
|
+
for (const t of outputChanged) console.log(dim(`\xB7 output ${t}`));
|
|
770
|
+
for (const f of annotationFlips.filter((x) => !x.widensPrivilege)) {
|
|
771
|
+
console.log(dim(`\xB7 safer ${f.tool}.${f.hint}: ${f.from} \u2192 ${f.to}`));
|
|
772
|
+
}
|
|
773
|
+
for (const t of deprecated) console.log(yellow(`\xB7 deprecated ${t}`));
|
|
774
|
+
for (const t of prosePolished) console.log(dim(`\xB7 docs only ${t}`));
|
|
775
|
+
console.log(
|
|
776
|
+
res.breaking ? red(`
|
|
777
|
+
${res.summary}`) : green(`
|
|
778
|
+
${res.summary || "no contract change"}`)
|
|
779
|
+
);
|
|
780
|
+
}
|
|
781
|
+
async function probeProjectServers(servers, budgetMs) {
|
|
782
|
+
const { createDb } = await import("./client-IRKAMDYZ.js");
|
|
783
|
+
const { extractAndStoreMcp } = await import("./mcp-3JHD5SUZ.js");
|
|
784
|
+
const { hasStoredSurface } = await import("./surface-YL6O67W3.js");
|
|
785
|
+
const { LocalSandbox } = await import("./local-XPZMRH5R.js");
|
|
786
|
+
const targets = servers.filter((s) => s.kind === "npm-stdio" && s.packageName);
|
|
787
|
+
if (targets.length === 0) return { probed: 0, skipped: 0 };
|
|
788
|
+
const handle = createDb({ max: 2 });
|
|
789
|
+
const sandbox = new LocalSandbox();
|
|
790
|
+
const deadline = Date.now() + budgetMs;
|
|
791
|
+
let probed = 0;
|
|
792
|
+
let skipped = 0;
|
|
793
|
+
try {
|
|
794
|
+
for (const s of targets) {
|
|
795
|
+
if (Date.now() > deadline) {
|
|
796
|
+
skipped++;
|
|
797
|
+
continue;
|
|
798
|
+
}
|
|
799
|
+
const name = s.packageName;
|
|
800
|
+
if (s.version && await hasStoredSurface(handle.db, name, s.version, "mcp_tools_list", 0, "mcp_server")) {
|
|
801
|
+
continue;
|
|
802
|
+
}
|
|
803
|
+
try {
|
|
804
|
+
const r = await extractAndStoreMcp(handle.db, name, s.version, { sandbox });
|
|
805
|
+
if (r.outcome === "stored" || r.outcome === "cached") probed++;
|
|
806
|
+
} catch {
|
|
807
|
+
skipped++;
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
} finally {
|
|
811
|
+
await handle.close();
|
|
812
|
+
}
|
|
813
|
+
return { probed, skipped };
|
|
814
|
+
}
|
|
815
|
+
async function runAudit(dir, opts) {
|
|
816
|
+
const { collectInventory } = await import("./inventory-5AWSLJYU.js");
|
|
817
|
+
const inv = collectInventory(dir ?? process.cwd(), { projectOnly: opts.projectOnly });
|
|
818
|
+
let probeNote = null;
|
|
819
|
+
if (opts.probe) {
|
|
820
|
+
if (indexSource() !== "local") {
|
|
821
|
+
probeNote = "--probe is for a self-hosted index; on the hosted service queued MCP servers are probed for you";
|
|
822
|
+
} else {
|
|
823
|
+
const budget = Math.max(5e3, Number(opts.probeBudget ?? 90) * 1e3);
|
|
824
|
+
const { probed, skipped } = await probeProjectServers(inv.mcpServers, budget);
|
|
825
|
+
probeNote = `probed ${probed} MCP server(s)${skipped ? `, ${skipped} left queued (budget)` : ""}`;
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
const payload = {
|
|
829
|
+
packages: inv.packages.map((p) => ({ name: p.name, range: p.range, installed: p.installed })),
|
|
830
|
+
transitives: inv.transitives.map((t) => ({ name: t.name, version: t.version, via: t.via })),
|
|
831
|
+
mcpServers: inv.mcpServers.map((s) => ({
|
|
832
|
+
alias: s.alias,
|
|
833
|
+
kind: s.kind,
|
|
834
|
+
packageName: s.packageName,
|
|
835
|
+
version: s.version,
|
|
836
|
+
endpoint: s.endpoint
|
|
837
|
+
})),
|
|
838
|
+
notes: inv.notes
|
|
839
|
+
};
|
|
840
|
+
const res = await fromIndex(
|
|
841
|
+
"audit",
|
|
842
|
+
payload,
|
|
843
|
+
(db) => import("./auditHandler-JCOJSUYW.js").then((m) => m.handleAudit(db, payload))
|
|
844
|
+
);
|
|
845
|
+
if (opts.json) return console.log(JSON.stringify({ ...res, filesRead: inv.filesRead }, null, 2));
|
|
846
|
+
const { SEVERITY_RANK, worstSeverity } = await import("./types-M5JP2W3D.js");
|
|
847
|
+
const colour = (s) => s === "critical" || s === "high" ? red : s === "moderate" ? yellow : dim;
|
|
848
|
+
console.log(bold(inv.root));
|
|
849
|
+
console.log(dim(`read ${inv.filesRead.length} file(s): ${inv.filesRead.join(", ") || "\u2014"}`));
|
|
850
|
+
if (probeNote) console.log(dim(probeNote));
|
|
851
|
+
const flagged = (res.items ?? []).map((i) => ({ ...i, findings: i.findings ?? [] })).filter((i) => i.findings.length > 0).sort(
|
|
852
|
+
(a, b) => SEVERITY_RANK[worstSeverity(a.findings)] - SEVERITY_RANK[worstSeverity(b.findings)]
|
|
853
|
+
);
|
|
854
|
+
if (flagged.length === 0) {
|
|
855
|
+
console.log(green("\nNothing flagged."));
|
|
856
|
+
} else {
|
|
857
|
+
console.log("");
|
|
858
|
+
for (const item of flagged) {
|
|
859
|
+
const sev = worstSeverity(item.findings);
|
|
860
|
+
const tag = item.unit === "mcp" ? dim(" [mcp]") : "";
|
|
861
|
+
const tag2 = item.unit === "transitive" ? dim(" [transitive]") : tag;
|
|
862
|
+
const ver = item.installed ? dim(`@${item.installed}`) : "";
|
|
863
|
+
console.log(`${colour(sev)(sev.padEnd(8))} ${bold(item.name)}${ver}${tag2}`);
|
|
864
|
+
for (const f of item.findings) console.log(` ${f.detail}`);
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
const c = res.coverage;
|
|
868
|
+
console.log(
|
|
869
|
+
`
|
|
870
|
+
${bold("coverage")} ${c.answered}/${c.discovered} declared dependencies answered` + (c.queued ? `, ${yellow(`${c.queued} queued`)}` : "") + (c.skipped ? `, ${dim(`${c.skipped} not assessable`)}` : "")
|
|
871
|
+
);
|
|
872
|
+
console.log(
|
|
873
|
+
c.treeRead ? dim(` ${c.transitivesChecked} transitive install(s) checked for vulnerabilities`) : red(" no lockfile \u2014 the dependency tree was NOT checked for vulnerabilities")
|
|
874
|
+
);
|
|
875
|
+
if (!c.vulnComplete) {
|
|
876
|
+
console.log(
|
|
877
|
+
red("vulnerability lookup incomplete \u2014 absence of a finding here is NOT an all-clear")
|
|
878
|
+
);
|
|
879
|
+
}
|
|
880
|
+
console.log(
|
|
881
|
+
dim(
|
|
882
|
+
" scope: installed packages and configured MCP servers. API versions pinned in your own source are not read."
|
|
883
|
+
)
|
|
884
|
+
);
|
|
885
|
+
for (const n of res.notes ?? []) console.log(dim(`\xB7 ${n}`));
|
|
886
|
+
}
|
|
887
|
+
async function runMcpStack(dir, opts) {
|
|
888
|
+
const { runMcpStackLive } = await import("./mcpScan-QZHWENIP.js");
|
|
889
|
+
await runMcpStackLive(dir, opts);
|
|
890
|
+
}
|
|
891
|
+
export {
|
|
892
|
+
indexSource,
|
|
893
|
+
runAudit,
|
|
894
|
+
runCan,
|
|
895
|
+
runCompare,
|
|
896
|
+
runCompat,
|
|
897
|
+
runCompatBackfill,
|
|
898
|
+
runEditWeights,
|
|
899
|
+
runEvaluate,
|
|
900
|
+
runMcpDrift,
|
|
901
|
+
runMcpStack,
|
|
902
|
+
runMcpSurface,
|
|
903
|
+
runPlan,
|
|
904
|
+
runRecommend,
|
|
905
|
+
runSandbox,
|
|
906
|
+
runUsage,
|
|
907
|
+
runVerify,
|
|
908
|
+
runVersions,
|
|
909
|
+
runWatch,
|
|
910
|
+
runWeights
|
|
911
|
+
};
|