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.
- package/README.md +311 -139
- package/dist/auditHandler-VWCXSDVO.js +24 -0
- package/dist/autopilotInit-Q62MX5M6.js +178 -0
- package/dist/bin/lurq.js +3255 -23319
- package/dist/browserAuth-J7APSD4P.js +96 -0
- package/dist/cache-AEAKCV4I.js +12 -0
- package/dist/check-2JTQDQA6.js +2613 -0
- package/dist/check-PQSQ2MMA.js +192 -0
- package/dist/checkUpgrade-5YW77OBL.js +24 -0
- package/dist/chunk-2GAYNIHG.js +40 -0
- package/dist/chunk-2O6Z37IT.js +423 -0
- package/dist/chunk-2WD4QAH4.js +116 -0
- package/dist/chunk-3676XOBU.js +2235 -0
- package/dist/chunk-3D2MPGOQ.js +181 -0
- package/dist/chunk-3GHJR7XR.js +2049 -0
- package/dist/chunk-4ESO33J6.js +34 -0
- package/dist/chunk-4JHDSVIN.js +1229 -0
- package/dist/chunk-4KCPOMG6.js +68 -0
- package/dist/chunk-53SG6XWA.js +141 -0
- package/dist/chunk-5ABNDB3I.js +37 -0
- package/dist/chunk-5ALIJFDA.js +114 -0
- package/dist/chunk-63MUJNZL.js +318 -0
- package/dist/chunk-64ZF7KO4.js +1571 -0
- package/dist/chunk-6DM6UQFD.js +318 -0
- package/dist/chunk-6EQ3ARXA.js +304 -0
- package/dist/chunk-6L67QMCP.js +92 -0
- package/dist/chunk-72Y2FDVS.js +172 -0
- package/dist/chunk-A3TYI2TF.js +288 -0
- package/dist/chunk-B2UUYRBX.js +536 -0
- package/dist/chunk-BJT2LHAR.js +320 -0
- package/dist/chunk-BM3LF5T6.js +132 -0
- package/dist/chunk-BN3NM47Q.js +214 -0
- package/dist/chunk-BUALU27S.js +123 -0
- package/dist/chunk-C6GLE626.js +160 -0
- package/dist/chunk-D3K6IZVR.js +12845 -0
- package/dist/chunk-D3VTZS7V.js +222 -0
- package/dist/chunk-DM37MTOO.js +636 -0
- package/dist/chunk-DONTW3NE.js +171 -0
- package/dist/chunk-DSROTZ3Z.js +38 -0
- package/dist/chunk-ELGCAM7P.js +184 -0
- package/dist/chunk-EQCXENKZ.js +130 -0
- package/dist/chunk-EYAKM6YN.js +96 -0
- package/dist/chunk-FDFPMBCA.js +112 -0
- package/dist/chunk-FVYFV5HA.js +154 -0
- package/dist/chunk-GE37NVLN.js +15 -0
- package/dist/chunk-GM5Y555O.js +143 -0
- package/dist/chunk-GRJXQBQZ.js +793 -0
- package/dist/chunk-HWFX4BC4.js +177 -0
- package/dist/chunk-I3M2KJXB.js +56 -0
- package/dist/chunk-IFNONGRQ.js +603 -0
- package/dist/chunk-J3U2CPSK.js +136 -0
- package/dist/chunk-J6XQ7OPZ.js +74 -0
- package/dist/chunk-KMV4Y6R3.js +127 -0
- package/dist/chunk-KP5ZDDPE.js +340 -0
- package/dist/chunk-KYKSZ3XZ.js +202 -0
- package/dist/chunk-LEGPB7DO.js +81 -0
- package/dist/chunk-LUAKDHLZ.js +40 -0
- package/dist/chunk-MHUEVBVO.js +35 -0
- package/dist/chunk-MYGSLK44.js +37 -0
- package/dist/chunk-NEZNAPPN.js +41 -0
- package/dist/chunk-NPVN2THM.js +197 -0
- package/dist/chunk-NYZU5NCW.js +23 -0
- package/dist/chunk-O4KHCV6I.js +320 -0
- package/dist/chunk-ONDVTXUO.js +240 -0
- package/dist/chunk-OTJFHESY.js +176 -0
- package/dist/chunk-P6MIKSPY.js +243 -0
- package/dist/chunk-Q5VPF4WI.js +26 -0
- package/dist/chunk-QCRBX3LV.js +165 -0
- package/dist/chunk-QKGYDMWB.js +7409 -0
- package/dist/chunk-QSL6QN5X.js +115 -0
- package/dist/chunk-QUIMQ7YL.js +73 -0
- package/dist/chunk-RG3YLU6E.js +49 -0
- package/dist/chunk-SHCEBP3J.js +107 -0
- package/dist/chunk-SHZFOSXC.js +462 -0
- package/dist/chunk-SOXFMMXC.js +151 -0
- package/dist/chunk-TDTYV6QI.js +119 -0
- package/dist/chunk-TNTXWSYA.js +19 -0
- package/dist/chunk-TUXQ32WE.js +2251 -0
- package/dist/chunk-VGCDRK3S.js +37 -0
- package/dist/chunk-VLRHFMGJ.js +58 -0
- package/dist/chunk-WHX2ZFCG.js +4048 -0
- package/dist/chunk-X4W64MHU.js +2005 -0
- package/dist/chunk-XBQA4I36.js +92 -0
- package/dist/chunk-XGOV6KV4.js +413 -0
- package/dist/chunk-XIFQIFSZ.js +940 -0
- package/dist/chunk-XLXPVN3Y.js +15393 -0
- package/dist/chunk-ZJMLQ37G.js +90 -0
- package/dist/client-MU4EUDZV.js +15 -0
- package/dist/commands-UOOTMBKA.js +1023 -0
- package/dist/compat-Q5DVPITN.js +201 -0
- package/dist/constants-DJFOFZ44.js +24 -0
- package/dist/dashboardIssue-KBKLQMGC.js +274 -0
- package/dist/diff-I4SNUHU3.js +13 -0
- package/dist/dist-4XXIXHHZ.js +12945 -0
- package/dist/domains-VRT4BRVZ.js +14 -0
- package/dist/e2b-FVPHDULZ.js +172 -0
- package/dist/env-UN6KGZ7S.js +15 -0
- package/dist/envCheck-KUPTORFQ.js +71 -0
- package/dist/extract-6WG265BL.js +455 -0
- package/dist/fetch-PJ3SC32M.js +22 -0
- package/dist/firstTouch-COJCMOB3.js +55 -0
- package/dist/fix-3EDWNKW3.js +220 -0
- package/dist/handlers-4Q37WIFT.js +55 -0
- package/dist/hook-NA66XJLC.js +412 -0
- package/dist/http-25GJ6SKK.js +6928 -0
- package/dist/ingestQueue-FSVJC3AA.js +37 -0
- package/dist/install-G2HHXYJ2.js +20 -0
- package/dist/installSkill-5TIMYIJZ.js +70 -0
- package/dist/inventory-5AWSLJYU.js +31 -0
- package/dist/local-XPZMRH5R.js +13 -0
- package/dist/manifest-ENMIC54B.js +13 -0
- package/dist/mcp-PENLL2PK.js +500 -0
- package/dist/mcpConfig-3LYPRY5Q.js +80 -0
- package/dist/mcpHandlers-PO7GENA7.js +22 -0
- package/dist/mcpScan-ULP626TQ.js +1735 -0
- package/dist/mcpScanWorkflow-27O44FOE.js +98 -0
- package/dist/mcpStack-ZNYIFXZ5.js +20 -0
- package/dist/model-ASMZWLM7.js +214 -0
- package/dist/packages-RWF2LXST.js +40 -0
- package/dist/plan-SDILIDZT.js +465 -0
- package/dist/planView-ZAKRTGVS.js +82 -0
- package/dist/policy-JAGNMQJI.js +115 -0
- package/dist/probe-3IK646WN.js +610 -0
- package/dist/publicUpgrades-WTBGLDIK.js +42 -0
- package/dist/references-I6Y4BSRR.js +16 -0
- package/dist/release-7IU2R4YG.js +128 -0
- package/dist/remote-FN6225AH.js +42 -0
- package/dist/rename-OS6JYBZQ.js +204 -0
- package/dist/reportRuns-O4X7ZFTX.js +77 -0
- package/dist/safeFetch-COERXGFN.js +19 -0
- package/dist/sandbox-SAPSRKEI.js +39 -0
- package/dist/sarif-WLVJFJ7U.js +125 -0
- package/dist/server-OLTY3NYT.js +59 -0
- package/dist/surface-7MHE3RL7.js +143 -0
- package/dist/surface-JS2XU3U3.js +38 -0
- package/dist/surfaceHandlers-4B6TMZ34.js +20 -0
- package/dist/typecheck-NGOKNAJY.js +282 -0
- package/dist/types-M5JP2W3D.js +10 -0
- package/dist/uninstall-I2IJP5JZ.js +200 -0
- package/dist/upgrade-FS26DJGG.js +650 -0
- package/dist/upgradePlan-ZS5IOVMZ.js +181 -0
- package/dist/upkeepHandler-VFGYXJKB.js +13 -0
- package/dist/userConfig-ICASHI47.js +20 -0
- package/dist/watch-OCNEUXG6.js +204 -0
- package/package.json +26 -20
- package/templates/agent-rules.md +4 -4
- package/templates/skill-instructions.md +35 -17
- 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,320 @@
|
|
|
1
|
+
import { createRequire as __lurqCreateRequire } from 'node:module'; const require = __lurqCreateRequire(import.meta.url);
|
|
2
|
+
import {
|
|
3
|
+
handleDiffSurface
|
|
4
|
+
} from "./chunk-BJT2LHAR.js";
|
|
5
|
+
import {
|
|
6
|
+
getPackageByName
|
|
7
|
+
} from "./chunk-NPVN2THM.js";
|
|
8
|
+
import {
|
|
9
|
+
enqueueSurface
|
|
10
|
+
} from "./chunk-ONDVTXUO.js";
|
|
11
|
+
import {
|
|
12
|
+
packages,
|
|
13
|
+
surfaceQueue
|
|
14
|
+
} from "./chunk-4JHDSVIN.js";
|
|
15
|
+
import {
|
|
16
|
+
require_semver
|
|
17
|
+
} from "./chunk-3GHJR7XR.js";
|
|
18
|
+
import {
|
|
19
|
+
logger
|
|
20
|
+
} from "./chunk-MYGSLK44.js";
|
|
21
|
+
import {
|
|
22
|
+
__toESM,
|
|
23
|
+
init_esm_shims
|
|
24
|
+
} from "./chunk-I3M2KJXB.js";
|
|
25
|
+
|
|
26
|
+
// src/mcp/publicUpgrades.ts
|
|
27
|
+
init_esm_shims();
|
|
28
|
+
var import_semver = __toESM(require_semver(), 1);
|
|
29
|
+
import { and as and2, count, desc as desc2, gte, isNotNull as isNotNull2, sql } from "drizzle-orm";
|
|
30
|
+
|
|
31
|
+
// src/mcp/publicSet.ts
|
|
32
|
+
init_esm_shims();
|
|
33
|
+
import { and, desc, isNotNull } from "drizzle-orm";
|
|
34
|
+
var PUBLIC_TOP_LIMIT = 5e3;
|
|
35
|
+
var NPM_NAME = /^(?:@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/i;
|
|
36
|
+
function validPackageName(name) {
|
|
37
|
+
return name.length > 0 && name.length <= 214 && NPM_NAME.test(name);
|
|
38
|
+
}
|
|
39
|
+
var floor = null;
|
|
40
|
+
async function publicDownloadFloor(db) {
|
|
41
|
+
if (floor && Date.now() - floor.at < 36e5) return floor.value;
|
|
42
|
+
const rows = await db.select({ downloads: packages.weeklyDownloads }).from(packages).where(and(isNotNull(packages.weeklyDownloads), isNotNull(packages.healthScore))).orderBy(desc(packages.weeklyDownloads)).offset(PUBLIC_TOP_LIMIT - 1).limit(1);
|
|
43
|
+
floor = { at: Date.now(), value: Number(rows[0]?.downloads ?? 0) };
|
|
44
|
+
return floor.value;
|
|
45
|
+
}
|
|
46
|
+
async function publicPackageRow(db, name) {
|
|
47
|
+
const [row, min] = await Promise.all([getPackageByName(db, name), publicDownloadFloor(db)]);
|
|
48
|
+
if (!row || row.healthScore === null || (row.weeklyDownloads ?? 0) < min) return null;
|
|
49
|
+
return row;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// src/mcp/publicUpgrades.ts
|
|
53
|
+
var PAIRS_PER_PACKAGE = 3;
|
|
54
|
+
var LIST_CAP = 500;
|
|
55
|
+
function majorPairs(latest, keep = PAIRS_PER_PACKAGE) {
|
|
56
|
+
const sorted = [...latest].sort((a, b) => a.major - b.major);
|
|
57
|
+
const pairs = [];
|
|
58
|
+
for (let i = 1; i < sorted.length; i++) {
|
|
59
|
+
const from = sorted[i - 1];
|
|
60
|
+
const to = sorted[i];
|
|
61
|
+
pairs.push({
|
|
62
|
+
fromMajor: from.major,
|
|
63
|
+
toMajor: to.major,
|
|
64
|
+
fromVersion: from.version,
|
|
65
|
+
toVersion: to.version,
|
|
66
|
+
ready: from.stored && to.stored
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
return pairs.slice(-keep);
|
|
70
|
+
}
|
|
71
|
+
function unextractedSides(latest, keep = PAIRS_PER_PACKAGE) {
|
|
72
|
+
if (latest.length < 2) return [];
|
|
73
|
+
const involved = [...latest].sort((a, b) => a.major - b.major).slice(-(keep + 1));
|
|
74
|
+
return involved.reverse().filter((l) => !l.stored && !l.attempted).map((l) => l.version);
|
|
75
|
+
}
|
|
76
|
+
function upgradeVerdict(d) {
|
|
77
|
+
if (d.inconclusive) return "unknown";
|
|
78
|
+
if (d.removed.length > 0 || d.renamed.length > 0) return "removes-exports";
|
|
79
|
+
if (d.arityChanged.length > 0) return "arity-changed";
|
|
80
|
+
if (d.typeOnlyRemoved.length > 0) return "types-only";
|
|
81
|
+
return "clean";
|
|
82
|
+
}
|
|
83
|
+
function toPublicUpgrade(pkg, pair, diff) {
|
|
84
|
+
const renamedPaths = new Set(diff.renamed.map((r) => r.path));
|
|
85
|
+
const removed = diff.removed.filter((r) => !renamedPaths.has(r.path));
|
|
86
|
+
const lists = [
|
|
87
|
+
removed,
|
|
88
|
+
diff.renamed,
|
|
89
|
+
diff.arityChanged,
|
|
90
|
+
diff.typeOnlyRemoved,
|
|
91
|
+
diff.deprecated
|
|
92
|
+
];
|
|
93
|
+
const cap = (xs) => xs.slice(0, LIST_CAP);
|
|
94
|
+
return {
|
|
95
|
+
package: pkg,
|
|
96
|
+
pair,
|
|
97
|
+
status: "ready",
|
|
98
|
+
verdict: upgradeVerdict({ ...diff, removed }),
|
|
99
|
+
removed: cap(removed.map((r) => ({ path: r.path, kind: r.kind }))),
|
|
100
|
+
renamed: cap(diff.renamed),
|
|
101
|
+
arityChanged: cap(diff.arityChanged),
|
|
102
|
+
typeOnlyRemoved: cap(diff.typeOnlyRemoved),
|
|
103
|
+
deprecated: cap(diff.deprecated),
|
|
104
|
+
added: diff.added.length,
|
|
105
|
+
truncated: lists.some((l) => l.length > LIST_CAP),
|
|
106
|
+
tier: diff.tier ?? null,
|
|
107
|
+
inconclusive: diff.inconclusive ?? null,
|
|
108
|
+
observedAt: diff.observedAt ? new Date(diff.observedAt).toISOString() : null
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function pendingUpgrade(pkg, pair) {
|
|
112
|
+
return {
|
|
113
|
+
package: pkg,
|
|
114
|
+
pair,
|
|
115
|
+
status: "pending",
|
|
116
|
+
verdict: "unknown",
|
|
117
|
+
removed: [],
|
|
118
|
+
renamed: [],
|
|
119
|
+
arityChanged: [],
|
|
120
|
+
typeOnlyRemoved: [],
|
|
121
|
+
deprecated: [],
|
|
122
|
+
added: 0,
|
|
123
|
+
truncated: false,
|
|
124
|
+
tier: null,
|
|
125
|
+
inconclusive: "Both versions are queued for extraction. NOT evidence that nothing changed.",
|
|
126
|
+
observedAt: null
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
function rowsOf(result) {
|
|
130
|
+
return result.rows ?? result;
|
|
131
|
+
}
|
|
132
|
+
async function latestOfMajors(db, names) {
|
|
133
|
+
const out = /* @__PURE__ */ new Map();
|
|
134
|
+
if (names.length === 0) return out;
|
|
135
|
+
const result = await db.execute(sql`
|
|
136
|
+
with stable as (
|
|
137
|
+
select package_name, version, (regexp_match(version, '^([0-9]+)\\.'))[1]::int as major
|
|
138
|
+
from package_versions
|
|
139
|
+
where package_name in (${sql.join(
|
|
140
|
+
names.map((n) => sql`${n}`),
|
|
141
|
+
sql`, `
|
|
142
|
+
)})
|
|
143
|
+
-- Stable releases only: an upgrade does not land on a prerelease.
|
|
144
|
+
and version ~ '^[0-9]+\\.[0-9]+\\.[0-9]+$'
|
|
145
|
+
),
|
|
146
|
+
latest as (
|
|
147
|
+
select distinct on (package_name, major) package_name, major, version
|
|
148
|
+
from stable
|
|
149
|
+
-- Numeric, not lexical (16.10.0 is newer than 16.9.0), and numeric rather
|
|
150
|
+
-- than int so a date-stamped version cannot overflow the cast.
|
|
151
|
+
order by package_name, major, string_to_array(version, '.')::numeric[] desc
|
|
152
|
+
)
|
|
153
|
+
select
|
|
154
|
+
l.package_name,
|
|
155
|
+
l.major,
|
|
156
|
+
l.version,
|
|
157
|
+
exists (
|
|
158
|
+
select 1 from entities e
|
|
159
|
+
join symbols s on s.entity_id = e.id and s.tier = 'shipped_js_ast'
|
|
160
|
+
where e.canonical_key = 'package_surface:npm:' || l.package_name || ':' || l.version
|
|
161
|
+
and e.tenant_id = 0
|
|
162
|
+
) as stored,
|
|
163
|
+
exists (
|
|
164
|
+
select 1 from entities e
|
|
165
|
+
where e.canonical_key = 'package_surface:npm:' || l.package_name || ':' || l.version
|
|
166
|
+
and e.tenant_id = 0
|
|
167
|
+
) as attempted
|
|
168
|
+
from latest l
|
|
169
|
+
`);
|
|
170
|
+
for (const r of rowsOf(result)) {
|
|
171
|
+
const list = out.get(r.package_name) ?? [];
|
|
172
|
+
list.push({
|
|
173
|
+
major: Number(r.major),
|
|
174
|
+
version: r.version,
|
|
175
|
+
stored: Boolean(r.stored),
|
|
176
|
+
attempted: Boolean(r.attempted)
|
|
177
|
+
});
|
|
178
|
+
out.set(r.package_name, list);
|
|
179
|
+
}
|
|
180
|
+
return out;
|
|
181
|
+
}
|
|
182
|
+
async function upgradePairsFor(db, name) {
|
|
183
|
+
return majorPairs((await latestOfMajors(db, [name])).get(name) ?? []);
|
|
184
|
+
}
|
|
185
|
+
async function upgradeGuideFor(db, pkg, fromVersion, toVersion, webUrl) {
|
|
186
|
+
const from = import_semver.default.coerce(fromVersion)?.major;
|
|
187
|
+
const to = import_semver.default.coerce(toVersion)?.major;
|
|
188
|
+
if (from === void 0 || to === void 0 || to <= from) return null;
|
|
189
|
+
if (!await publicPackageRow(db, pkg)) return null;
|
|
190
|
+
const pair = (await upgradePairsFor(db, pkg)).find(
|
|
191
|
+
(p) => p.fromMajor === from && p.toMajor === to && p.ready
|
|
192
|
+
);
|
|
193
|
+
if (!pair) return null;
|
|
194
|
+
const path = `/npm/${pkg.split("/").map(encodeURIComponent).join("/")}/${from}-to-${to}`;
|
|
195
|
+
return {
|
|
196
|
+
url: `${webUrl.replace(/\/$/, "")}${path}`,
|
|
197
|
+
fromVersion: pair.fromVersion,
|
|
198
|
+
toVersion: pair.toVersion
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
async function publicNames(db, limit = PUBLIC_TOP_LIMIT) {
|
|
202
|
+
const min = await publicDownloadFloor(db);
|
|
203
|
+
const rows = await db.select({ name: packages.name }).from(packages).where(and2(isNotNull2(packages.healthScore), gte(packages.weeklyDownloads, min))).orderBy(desc2(packages.weeklyDownloads)).limit(limit);
|
|
204
|
+
return rows.map((r) => r.name);
|
|
205
|
+
}
|
|
206
|
+
var QUEUE_HEADROOM = 20;
|
|
207
|
+
async function enqueuePublicUpgrades(db, opts = {}) {
|
|
208
|
+
const [{ pending } = { pending: 0 }] = await db.select({ pending: count() }).from(surfaceQueue);
|
|
209
|
+
let budget = QUEUE_HEADROOM - Number(pending);
|
|
210
|
+
if (budget <= 0) return 0;
|
|
211
|
+
const names = await publicNames(db, opts.packages ?? 1e3);
|
|
212
|
+
let queued = 0;
|
|
213
|
+
for (let i = 0; i < names.length && budget > 0; i += 250) {
|
|
214
|
+
const chunk = names.slice(i, i + 250);
|
|
215
|
+
const latest = await latestOfMajors(db, chunk);
|
|
216
|
+
for (const name of chunk) {
|
|
217
|
+
for (const version of unextractedSides(latest.get(name) ?? [])) {
|
|
218
|
+
if (budget <= 0) break;
|
|
219
|
+
await enqueueSurface(db, name, version);
|
|
220
|
+
queued++;
|
|
221
|
+
budget--;
|
|
222
|
+
}
|
|
223
|
+
if (budget <= 0) break;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return queued;
|
|
227
|
+
}
|
|
228
|
+
function registerPublicUpgradeRoutes(app, db, limiter) {
|
|
229
|
+
const cacheable = (res) => res.setHeader("Cache-Control", "public, max-age=3600, s-maxage=86400");
|
|
230
|
+
app.get("/public/upgrade", limiter, async (req, res) => {
|
|
231
|
+
const name = typeof req.query.name === "string" ? req.query.name.trim() : "";
|
|
232
|
+
const from = Number(req.query.from);
|
|
233
|
+
const to = Number(req.query.to);
|
|
234
|
+
if (!validPackageName(name) || !Number.isInteger(from) || !Number.isInteger(to) || from < 0 || to <= from) {
|
|
235
|
+
res.status(400).json({ error: "Give an npm package name and two majors, from < to." });
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
try {
|
|
239
|
+
if (!await publicPackageRow(db, name)) {
|
|
240
|
+
res.status(404).json({ error: "No public upgrade pages for that package." });
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
const pairs = await upgradePairsFor(db, name);
|
|
244
|
+
const pair = pairs.find((p) => p.fromMajor === from && p.toMajor === to);
|
|
245
|
+
if (!pair) {
|
|
246
|
+
res.status(404).json({ error: "No upgrade page for that version jump.", pairs });
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
if (!pair.ready) {
|
|
250
|
+
await Promise.all([
|
|
251
|
+
enqueueSurface(db, name, pair.fromVersion),
|
|
252
|
+
enqueueSurface(db, name, pair.toVersion)
|
|
253
|
+
]).catch(() => {
|
|
254
|
+
});
|
|
255
|
+
res.setHeader("Cache-Control", "public, max-age=300, s-maxage=900");
|
|
256
|
+
res.json(pendingUpgrade(name, pair));
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
const diff = await handleDiffSurface(db, {
|
|
260
|
+
package: name,
|
|
261
|
+
fromVersion: pair.fromVersion,
|
|
262
|
+
toVersion: pair.toVersion
|
|
263
|
+
});
|
|
264
|
+
const body = toPublicUpgrade(name, pair, diff);
|
|
265
|
+
if (body.verdict === "unknown")
|
|
266
|
+
res.setHeader("Cache-Control", "public, max-age=300, s-maxage=900");
|
|
267
|
+
else cacheable(res);
|
|
268
|
+
res.json(body);
|
|
269
|
+
} catch (err) {
|
|
270
|
+
logger.error("public upgrade read failed:", err instanceof Error ? err.message : String(err));
|
|
271
|
+
res.status(500).json({ error: "Could not read that upgrade." });
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
let index = null;
|
|
275
|
+
app.get("/public/upgrades", limiter, async (_req, res) => {
|
|
276
|
+
try {
|
|
277
|
+
if (!index || Date.now() - index.at > 36e5) {
|
|
278
|
+
const names = await publicNames(db);
|
|
279
|
+
const value = [];
|
|
280
|
+
for (let i = 0; i < names.length; i += 500) {
|
|
281
|
+
const chunk = names.slice(i, i + 500);
|
|
282
|
+
const latest = await latestOfMajors(db, chunk);
|
|
283
|
+
for (const name of chunk) {
|
|
284
|
+
const ready = majorPairs(latest.get(name) ?? []).filter((p) => p.ready);
|
|
285
|
+
if (ready.length) value.push({ package: name, pairs: ready });
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
index = { at: Date.now(), value };
|
|
289
|
+
}
|
|
290
|
+
cacheable(res);
|
|
291
|
+
res.json({ upgrades: index.value });
|
|
292
|
+
} catch (err) {
|
|
293
|
+
logger.error(
|
|
294
|
+
"public upgrade index failed:",
|
|
295
|
+
err instanceof Error ? err.message : String(err)
|
|
296
|
+
);
|
|
297
|
+
res.status(500).json({ error: "Could not list upgrade pages." });
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
export {
|
|
303
|
+
PUBLIC_TOP_LIMIT,
|
|
304
|
+
validPackageName,
|
|
305
|
+
publicDownloadFloor,
|
|
306
|
+
publicPackageRow,
|
|
307
|
+
PAIRS_PER_PACKAGE,
|
|
308
|
+
LIST_CAP,
|
|
309
|
+
majorPairs,
|
|
310
|
+
unextractedSides,
|
|
311
|
+
upgradeVerdict,
|
|
312
|
+
toPublicUpgrade,
|
|
313
|
+
pendingUpgrade,
|
|
314
|
+
latestOfMajors,
|
|
315
|
+
upgradePairsFor,
|
|
316
|
+
upgradeGuideFor,
|
|
317
|
+
QUEUE_HEADROOM,
|
|
318
|
+
enqueuePublicUpgrades,
|
|
319
|
+
registerPublicUpgradeRoutes
|
|
320
|
+
};
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import { createRequire as __lurqCreateRequire } from 'node:module'; const require = __lurqCreateRequire(import.meta.url);
|
|
2
|
+
import {
|
|
3
|
+
UNBOUNDED_ARITY,
|
|
4
|
+
claims,
|
|
5
|
+
entities,
|
|
6
|
+
observations,
|
|
7
|
+
packages,
|
|
8
|
+
surfaceQueue,
|
|
9
|
+
symbols
|
|
10
|
+
} from "./chunk-4JHDSVIN.js";
|
|
11
|
+
import {
|
|
12
|
+
init_esm_shims
|
|
13
|
+
} from "./chunk-I3M2KJXB.js";
|
|
14
|
+
|
|
15
|
+
// src/db/surface.ts
|
|
16
|
+
init_esm_shims();
|
|
17
|
+
import { and as and2, desc, eq as eq2, isNotNull, isNull as isNull2, sql } from "drizzle-orm";
|
|
18
|
+
|
|
19
|
+
// src/db/graph.ts
|
|
20
|
+
init_esm_shims();
|
|
21
|
+
import { and, eq, isNull } from "drizzle-orm";
|
|
22
|
+
import { createHash } from "crypto";
|
|
23
|
+
|
|
24
|
+
// src/graph/types.ts
|
|
25
|
+
init_esm_shims();
|
|
26
|
+
function canonicalKey(ref) {
|
|
27
|
+
return `${ref.kind}:${ref.namespace}:${ref.name}:${ref.version ?? ""}`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// src/db/graph.ts
|
|
31
|
+
async function upsertEntity(db, ref, tenantId = 0) {
|
|
32
|
+
const key = canonicalKey(ref);
|
|
33
|
+
const [row] = await db.insert(entities).values({
|
|
34
|
+
kind: ref.kind,
|
|
35
|
+
namespace: ref.namespace,
|
|
36
|
+
name: ref.name,
|
|
37
|
+
version: ref.version ?? null,
|
|
38
|
+
canonicalKey: key,
|
|
39
|
+
tenantId
|
|
40
|
+
}).onConflictDoUpdate({
|
|
41
|
+
target: [entities.canonicalKey, entities.tenantId],
|
|
42
|
+
set: { canonicalKey: key }
|
|
43
|
+
}).returning();
|
|
44
|
+
return row;
|
|
45
|
+
}
|
|
46
|
+
async function upsertClaim(db, args) {
|
|
47
|
+
const objectId = args.objectId ?? null;
|
|
48
|
+
const envId = args.environmentId ?? null;
|
|
49
|
+
const tenantId = args.tenantId ?? 0;
|
|
50
|
+
const existing = await db.select().from(claims).where(
|
|
51
|
+
and(
|
|
52
|
+
eq(claims.subjectId, args.subjectId),
|
|
53
|
+
objectId === null ? isNull(claims.objectId) : eq(claims.objectId, objectId),
|
|
54
|
+
eq(claims.relation, args.relation),
|
|
55
|
+
envId === null ? isNull(claims.environmentId) : eq(claims.environmentId, envId),
|
|
56
|
+
eq(claims.tenantId, tenantId)
|
|
57
|
+
)
|
|
58
|
+
).limit(1);
|
|
59
|
+
if (existing[0]) return existing[0];
|
|
60
|
+
const [row] = await db.insert(claims).values({
|
|
61
|
+
subjectId: args.subjectId,
|
|
62
|
+
objectId,
|
|
63
|
+
relation: args.relation,
|
|
64
|
+
environmentId: envId,
|
|
65
|
+
tenantId
|
|
66
|
+
}).returning();
|
|
67
|
+
return row;
|
|
68
|
+
}
|
|
69
|
+
async function recordObservation(db, args) {
|
|
70
|
+
await db.insert(observations).values({
|
|
71
|
+
claimId: args.claimId,
|
|
72
|
+
verdict: args.verdict,
|
|
73
|
+
class: args.class ?? "executed",
|
|
74
|
+
tier: args.tier ?? null,
|
|
75
|
+
evidence: args.evidence ?? null,
|
|
76
|
+
oracleId: args.oracleId,
|
|
77
|
+
oracleVer: args.oracleVer,
|
|
78
|
+
costMillis: args.costMillis ?? null
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// src/db/surface.ts
|
|
83
|
+
function surfaceRef(pkg, version) {
|
|
84
|
+
return { kind: "package_surface", namespace: "npm", name: pkg, version };
|
|
85
|
+
}
|
|
86
|
+
function mcpSurfaceRef(server, version) {
|
|
87
|
+
return { kind: "mcp_server", namespace: "npm", name: server, version };
|
|
88
|
+
}
|
|
89
|
+
async function isExtractionCached(db, ref, artifactHash, extractorVersion, tier = "shipped_js_ast", tenantId = 0) {
|
|
90
|
+
const row = await upsertEntity(db, ref, tenantId);
|
|
91
|
+
if (row.artifactHash !== artifactHash) return false;
|
|
92
|
+
const existing = await db.select({ id: symbols.id }).from(symbols).where(and2(eq2(symbols.entityId, row.id), eq2(symbols.tier, tier))).limit(1);
|
|
93
|
+
if (existing.length === 0) return false;
|
|
94
|
+
const seen = await db.select({ oracleVer: observations.oracleVer }).from(observations).innerJoin(claims, eq2(claims.id, observations.claimId)).where(
|
|
95
|
+
and2(
|
|
96
|
+
eq2(claims.subjectId, row.id),
|
|
97
|
+
eq2(claims.relation, "exposes"),
|
|
98
|
+
eq2(observations.tier, tier)
|
|
99
|
+
)
|
|
100
|
+
).orderBy(desc(observations.observedAt)).limit(1);
|
|
101
|
+
return seen[0]?.oracleVer === extractorVersion;
|
|
102
|
+
}
|
|
103
|
+
async function storeSurface(db, surface, opts = {
|
|
104
|
+
extractorVersion: "1"
|
|
105
|
+
}) {
|
|
106
|
+
const tenantId = opts.tenantId ?? 0;
|
|
107
|
+
const ref = opts.ref ?? surfaceRef(surface.package, surface.version);
|
|
108
|
+
const entity = await upsertEntity(db, ref, tenantId);
|
|
109
|
+
if (opts.artifactHash) {
|
|
110
|
+
await db.update(entities).set({ artifactHash: opts.artifactHash }).where(eq2(entities.id, entity.id));
|
|
111
|
+
}
|
|
112
|
+
const claim = await upsertClaim(db, {
|
|
113
|
+
subjectId: entity.id,
|
|
114
|
+
relation: "exposes",
|
|
115
|
+
// Declared claims are environment-independent: a shipped-JS surface reads
|
|
116
|
+
// identically on every machine, so no fingerprint is attached (§5).
|
|
117
|
+
environmentId: null,
|
|
118
|
+
tenantId
|
|
119
|
+
});
|
|
120
|
+
const undeclared = surface.symbols.length === 0;
|
|
121
|
+
await recordObservation(db, {
|
|
122
|
+
claimId: claim.id,
|
|
123
|
+
verdict: undeclared ? "undeclared" : "verified_true",
|
|
124
|
+
class: "declared",
|
|
125
|
+
tier: surface.tier,
|
|
126
|
+
evidence: undeclared ? surface.undeclaredReason ?? "no symbols extracted" : `${surface.symbols.length} symbols from ${surface.filesWalked} file(s), entry ${surface.entry}`,
|
|
127
|
+
oracleId: opts.oracleId ?? "surface.tier_a",
|
|
128
|
+
oracleVer: opts.extractorVersion
|
|
129
|
+
});
|
|
130
|
+
if (undeclared) return { entityId: entity.id, symbolsWritten: 0, verdict: "undeclared" };
|
|
131
|
+
await db.delete(symbols).where(and2(eq2(symbols.entityId, entity.id), eq2(symbols.tier, surface.tier)));
|
|
132
|
+
const rows = surface.symbols.map((s) => ({
|
|
133
|
+
entityId: entity.id,
|
|
134
|
+
path: s.path,
|
|
135
|
+
kind: s.kind,
|
|
136
|
+
arity: s.arity,
|
|
137
|
+
origin: s.origin,
|
|
138
|
+
deprecated: s.deprecated,
|
|
139
|
+
tier: s.tier,
|
|
140
|
+
signature: s.signature ?? null,
|
|
141
|
+
sourceFile: s.sourceRef?.file ?? null,
|
|
142
|
+
sourceLine: s.sourceRef?.line ?? null,
|
|
143
|
+
sourceOffset: s.sourceRef?.offset ?? null,
|
|
144
|
+
maxArity: s.maxArity === void 0 ? null : s.maxArity ?? UNBOUNDED_ARITY
|
|
145
|
+
}));
|
|
146
|
+
for (let i = 0; i < rows.length; i += 500) {
|
|
147
|
+
await db.insert(symbols).values(rows.slice(i, i + 500)).onConflictDoNothing();
|
|
148
|
+
}
|
|
149
|
+
return { entityId: entity.id, symbolsWritten: rows.length, verdict: "verified_true" };
|
|
150
|
+
}
|
|
151
|
+
function specKey(pkg, version, kind = "package_surface") {
|
|
152
|
+
const base = `${pkg}@${version ?? "latest"}`;
|
|
153
|
+
return kind === "package_surface" ? base : `${kind}:${base}`;
|
|
154
|
+
}
|
|
155
|
+
async function getPackagesMissingGraphSurface(db, limit, opts = {}) {
|
|
156
|
+
const order = opts.byDownloads ? sql`${packages.weeklyDownloads} desc nulls last` : sql`random()`;
|
|
157
|
+
const rows = await db.select({ name: packages.name, version: packages.latestVersion }).from(packages).leftJoin(
|
|
158
|
+
entities,
|
|
159
|
+
and2(
|
|
160
|
+
eq2(entities.kind, "package_surface"),
|
|
161
|
+
eq2(entities.name, packages.name),
|
|
162
|
+
eq2(entities.version, packages.latestVersion)
|
|
163
|
+
)
|
|
164
|
+
).where(and2(isNotNull(packages.latestVersion), isNull2(entities.id))).orderBy(order).limit(limit);
|
|
165
|
+
return rows.filter((r) => r.version !== null);
|
|
166
|
+
}
|
|
167
|
+
async function enqueueSurface(db, pkg, version, kind = "package_surface") {
|
|
168
|
+
await db.insert(surfaceQueue).values({ packageName: pkg, version, kind, specKey: specKey(pkg, version, kind) }).onConflictDoNothing();
|
|
169
|
+
}
|
|
170
|
+
async function getPendingSurfaces(db, limit = 10, kind = "package_surface") {
|
|
171
|
+
return db.select().from(surfaceQueue).where(
|
|
172
|
+
and2(
|
|
173
|
+
eq2(surfaceQueue.kind, kind),
|
|
174
|
+
// Specs backing off after a failure wait their turn instead of sitting
|
|
175
|
+
// at the head of an oldest-first queue every cycle.
|
|
176
|
+
sql`(${surfaceQueue.nextAttemptAt} is null or ${surfaceQueue.nextAttemptAt} <= now())`
|
|
177
|
+
)
|
|
178
|
+
).orderBy(surfaceQueue.requestedAt).limit(limit);
|
|
179
|
+
}
|
|
180
|
+
async function surfaceQueueDepth(db) {
|
|
181
|
+
const [row] = await db.select({ n: sql`count(*)::int` }).from(surfaceQueue);
|
|
182
|
+
return Number(row?.n ?? 0);
|
|
183
|
+
}
|
|
184
|
+
async function dropSurfaceQueue(db, id) {
|
|
185
|
+
await db.delete(surfaceQueue).where(eq2(surfaceQueue.id, id));
|
|
186
|
+
}
|
|
187
|
+
async function bumpSurfaceAttempt(db, id) {
|
|
188
|
+
await db.update(surfaceQueue).set({
|
|
189
|
+
attempts: sql`${surfaceQueue.attempts} + 1`,
|
|
190
|
+
nextAttemptAt: sql`now() + interval '15 minutes' * power(4, ${surfaceQueue.attempts})`
|
|
191
|
+
}).where(eq2(surfaceQueue.id, id));
|
|
192
|
+
}
|
|
193
|
+
async function hasStoredSurface(db, pkg, version, tier = "shipped_js_ast", tenantId = 0, kind = "package_surface") {
|
|
194
|
+
const key = canonicalKey(
|
|
195
|
+
kind === "mcp_server" ? mcpSurfaceRef(pkg, version) : surfaceRef(pkg, version)
|
|
196
|
+
);
|
|
197
|
+
const [row] = await db.select({ id: entities.id }).from(entities).where(and2(eq2(entities.canonicalKey, key), eq2(entities.tenantId, tenantId))).limit(1);
|
|
198
|
+
if (!row) return false;
|
|
199
|
+
const [sym] = await db.select({ id: symbols.id }).from(symbols).where(and2(eq2(symbols.entityId, row.id), eq2(symbols.tier, tier))).limit(1);
|
|
200
|
+
return sym !== void 0;
|
|
201
|
+
}
|
|
202
|
+
async function enqueuePreviousSurface(db, pkg, version, history, kind = "package_surface") {
|
|
203
|
+
const previous = previousVersion(history, version);
|
|
204
|
+
if (previous === null) return false;
|
|
205
|
+
const tier = kind === "mcp_server" ? "mcp_tools_list" : "shipped_js_ast";
|
|
206
|
+
if (await hasStoredSurface(db, pkg, previous, tier, 0, kind)) return false;
|
|
207
|
+
await enqueueSurface(db, pkg, previous, kind);
|
|
208
|
+
return true;
|
|
209
|
+
}
|
|
210
|
+
function previousVersion(history, version) {
|
|
211
|
+
const idx = history.findIndex((h) => h.version === version);
|
|
212
|
+
if (idx === -1 || idx + 1 >= history.length) return null;
|
|
213
|
+
return history[idx + 1].version ?? null;
|
|
214
|
+
}
|
|
215
|
+
async function getMcpServerNames(db, tenantId = 0) {
|
|
216
|
+
const rows = await db.selectDistinct({ name: entities.name }).from(entities).where(and2(eq2(entities.kind, "mcp_server"), eq2(entities.tenantId, tenantId)));
|
|
217
|
+
return rows.map((r) => r.name);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export {
|
|
221
|
+
canonicalKey,
|
|
222
|
+
upsertEntity,
|
|
223
|
+
upsertClaim,
|
|
224
|
+
recordObservation,
|
|
225
|
+
surfaceRef,
|
|
226
|
+
mcpSurfaceRef,
|
|
227
|
+
isExtractionCached,
|
|
228
|
+
storeSurface,
|
|
229
|
+
specKey,
|
|
230
|
+
getPackagesMissingGraphSurface,
|
|
231
|
+
enqueueSurface,
|
|
232
|
+
getPendingSurfaces,
|
|
233
|
+
surfaceQueueDepth,
|
|
234
|
+
dropSurfaceQueue,
|
|
235
|
+
bumpSurfaceAttempt,
|
|
236
|
+
hasStoredSurface,
|
|
237
|
+
enqueuePreviousSurface,
|
|
238
|
+
previousVersion,
|
|
239
|
+
getMcpServerNames
|
|
240
|
+
};
|