lurqrun 0.1.7 → 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 +312 -148
- package/dist/{auditHandler-JCOJSUYW.js → auditHandler-VWCXSDVO.js} +12 -11
- package/dist/autopilotInit-Q62MX5M6.js +178 -0
- package/dist/bin/lurq.js +235 -83
- package/dist/{browserAuth-B6G27USY.js → browserAuth-J7APSD4P.js} +4 -3
- package/dist/{cache-DKI34SVS.js → cache-AEAKCV4I.js} +2 -2
- package/dist/check-2JTQDQA6.js +2613 -0
- package/dist/{check-2FDGUOV7.js → check-PQSQ2MMA.js} +1 -1
- package/dist/checkUpgrade-5YW77OBL.js +24 -0
- package/dist/{chunk-BCANRN5B.js → chunk-2GAYNIHG.js} +8 -1
- package/dist/{chunk-EA3DP5HG.js → chunk-2O6Z37IT.js} +155 -25
- package/dist/{chunk-J5FZYN4A.js → chunk-2WD4QAH4.js} +1 -1
- package/dist/{chunk-CUP36ND3.js → chunk-3676XOBU.js} +145 -36
- package/dist/chunk-3D2MPGOQ.js +181 -0
- package/dist/{chunk-LYMPYETA.js → chunk-4JHDSVIN.js} +270 -6
- package/dist/chunk-53SG6XWA.js +141 -0
- package/dist/chunk-5ABNDB3I.js +37 -0
- package/dist/chunk-5ALIJFDA.js +114 -0
- package/dist/{chunk-BULZAX2U.js → chunk-64ZF7KO4.js} +55 -38
- package/dist/{chunk-2VMOWSRY.js → chunk-6DM6UQFD.js} +19 -1
- package/dist/{chunk-HWBBVLOT.js → chunk-6L67QMCP.js} +1 -1
- package/dist/{chunk-GPTKSNR6.js → chunk-72Y2FDVS.js} +2 -1
- package/dist/{chunk-B43LCXBM.js → chunk-A3TYI2TF.js} +81 -16
- package/dist/{chunk-IQ4SDKKY.js → chunk-B2UUYRBX.js} +31 -228
- package/dist/{chunk-DB2VF5UD.js → chunk-BJT2LHAR.js} +16 -11
- package/dist/chunk-BM3LF5T6.js +132 -0
- package/dist/{chunk-3N3Q3DSW.js → chunk-BN3NM47Q.js} +8 -8
- package/dist/chunk-D3K6IZVR.js +12845 -0
- package/dist/{chunk-AHBP533R.js → chunk-D3VTZS7V.js} +7 -5
- package/dist/{chunk-UDVNA7EA.js → chunk-DM37MTOO.js} +161 -8
- package/dist/chunk-DONTW3NE.js +171 -0
- package/dist/{chunk-M6NYEXCW.js → chunk-DSROTZ3Z.js} +1 -1
- package/dist/{chunk-4F7ZESPO.js → chunk-EQCXENKZ.js} +3 -5
- package/dist/{chunk-CW5HJ3LX.js → chunk-EYAKM6YN.js} +1 -1
- package/dist/{chunk-VL2E5T2A.js → chunk-FDFPMBCA.js} +6 -3
- package/dist/{chunk-BV32UNLL.js → chunk-FVYFV5HA.js} +2 -2
- package/dist/{chunk-DWAGCNEL.js → chunk-GM5Y555O.js} +36 -103
- package/dist/{chunk-PSX3GIAN.js → chunk-HWFX4BC4.js} +2 -1
- package/dist/chunk-IFNONGRQ.js +603 -0
- package/dist/chunk-J3U2CPSK.js +136 -0
- package/dist/{chunk-Y5GI7RL3.js → chunk-J6XQ7OPZ.js} +9 -1
- package/dist/{chunk-LBFGSSF2.js → chunk-KMV4Y6R3.js} +2 -1
- package/dist/{chunk-L6N4JFW2.js → chunk-KP5ZDDPE.js} +81 -4
- package/dist/{chunk-SJOVFF7B.js → chunk-KYKSZ3XZ.js} +3 -2
- package/dist/chunk-NEZNAPPN.js +41 -0
- package/dist/{chunk-NH5I4KHH.js → chunk-NPVN2THM.js} +1 -5
- package/dist/{chunk-6L3U7DZ5.js → chunk-NYZU5NCW.js} +1 -1
- package/dist/chunk-O4KHCV6I.js +320 -0
- package/dist/{chunk-LO35HLQZ.js → chunk-ONDVTXUO.js} +25 -4
- package/dist/{chunk-4ZYCIMY4.js → chunk-OTJFHESY.js} +25 -4
- package/dist/chunk-P6MIKSPY.js +243 -0
- package/dist/{checkUpgrade-F2WESBPM.js → chunk-QCRBX3LV.js} +72 -10
- package/dist/{chunk-LRD4GBYE.js → chunk-QUIMQ7YL.js} +1 -1
- package/dist/chunk-RG3YLU6E.js +49 -0
- package/dist/{chunk-ST2X2GYN.js → chunk-SHZFOSXC.js} +109 -20
- package/dist/{chunk-CIDWNUAP.js → chunk-SOXFMMXC.js} +8 -2
- package/dist/chunk-TDTYV6QI.js +119 -0
- package/dist/{chunk-FW6BD2NO.js → chunk-TUXQ32WE.js} +457 -121
- package/dist/{chunk-WQ23REYT.js → chunk-VGCDRK3S.js} +1 -1
- package/dist/{chunk-U5AT3TD4.js → chunk-X4W64MHU.js} +1735 -1858
- package/dist/chunk-XBQA4I36.js +92 -0
- package/dist/chunk-XGOV6KV4.js +413 -0
- package/dist/{chunk-OQS4GUSI.js → chunk-XIFQIFSZ.js} +210 -661
- package/dist/{chunk-XBIDMHSK.js → chunk-XLXPVN3Y.js} +2 -34
- package/dist/chunk-ZJMLQ37G.js +90 -0
- package/dist/{client-IRKAMDYZ.js → client-MU4EUDZV.js} +3 -3
- package/dist/{commands-FIL5ET6P.js → commands-UOOTMBKA.js} +145 -33
- package/dist/{compat-R3UTFQ5Q.js → compat-Q5DVPITN.js} +6 -6
- package/dist/constants-DJFOFZ44.js +24 -0
- package/dist/{dashboardIssue-WYYMBFIB.js → dashboardIssue-KBKLQMGC.js} +105 -23
- package/dist/{diff-GZA2774U.js → diff-I4SNUHU3.js} +1 -1
- package/dist/domains-VRT4BRVZ.js +14 -0
- package/dist/env-UN6KGZ7S.js +15 -0
- package/dist/envCheck-KUPTORFQ.js +71 -0
- package/dist/{extract-NKGKLIVT.js → extract-6WG265BL.js} +14 -3
- package/dist/{fetch-QYTUJVG2.js → fetch-PJ3SC32M.js} +4 -4
- package/dist/{firstTouch-QQTJH4QX.js → firstTouch-COJCMOB3.js} +1 -1
- package/dist/fix-3EDWNKW3.js +220 -0
- package/dist/{handlers-GEJAVZDK.js → handlers-4Q37WIFT.js} +19 -17
- package/dist/hook-NA66XJLC.js +412 -0
- package/dist/{http-ETPYMBGD.js → http-25GJ6SKK.js} +2078 -510
- package/dist/{ingestQueue-VKN4KBIF.js → ingestQueue-FSVJC3AA.js} +14 -12
- package/dist/{install-EK3W2COX.js → install-G2HHXYJ2.js} +6 -6
- package/dist/{installSkill-VQJWJVCL.js → installSkill-5TIMYIJZ.js} +21 -3
- package/dist/manifest-ENMIC54B.js +13 -0
- package/dist/{mcp-3JHD5SUZ.js → mcp-PENLL2PK.js} +60 -32
- package/dist/mcpConfig-3LYPRY5Q.js +80 -0
- package/dist/{mcpHandlers-QFN4MCFL.js → mcpHandlers-PO7GENA7.js} +7 -7
- package/dist/{mcpScan-QZHWENIP.js → mcpScan-ULP626TQ.js} +306 -442
- package/dist/{mcpScanWorkflow-JPYCBGC4.js → mcpScanWorkflow-27O44FOE.js} +29 -12
- package/dist/{mcpStack-BYJCZNIV.js → mcpStack-ZNYIFXZ5.js} +1 -1
- package/dist/model-ASMZWLM7.js +214 -0
- package/dist/{packages-THVFMWFP.js → packages-RWF2LXST.js} +3 -5
- package/dist/{plan-V4OEI3J4.js → plan-SDILIDZT.js} +23 -21
- package/dist/{planView-JRXGCG2N.js → planView-ZAKRTGVS.js} +4 -1
- package/dist/{policy-3P6BGRDN.js → policy-JAGNMQJI.js} +5 -5
- package/dist/probe-3IK646WN.js +610 -0
- package/dist/publicUpgrades-WTBGLDIK.js +42 -0
- package/dist/{references-6N7VEPHT.js → references-I6Y4BSRR.js} +3 -1
- package/dist/{release-4U4ABNSJ.js → release-7IU2R4YG.js} +5 -5
- package/dist/{remote-YGCXYI4V.js → remote-FN6225AH.js} +13 -3
- package/dist/rename-OS6JYBZQ.js +204 -0
- package/dist/{reportRuns-CKQQYLY4.js → reportRuns-O4X7ZFTX.js} +15 -2
- package/dist/safeFetch-COERXGFN.js +19 -0
- package/dist/{sandbox-MEZLAODF.js → sandbox-SAPSRKEI.js} +4 -4
- package/dist/sarif-WLVJFJ7U.js +125 -0
- package/dist/server-OLTY3NYT.js +59 -0
- package/dist/{surface-V62GE554.js → surface-7MHE3RL7.js} +18 -16
- package/dist/{surface-YL6O67W3.js → surface-JS2XU3U3.js} +5 -3
- package/dist/{surfaceHandlers-X2IZKA3E.js → surfaceHandlers-4B6TMZ34.js} +5 -5
- package/dist/{typecheck-X7U5ELMR.js → typecheck-NGOKNAJY.js} +23 -9
- package/dist/{uninstall-P7RGBX6G.js → uninstall-I2IJP5JZ.js} +30 -4
- package/dist/{upgrade-NQ5IWFZB.js → upgrade-FS26DJGG.js} +86 -32
- package/dist/{upgradePlan-A4LRKLEG.js → upgradePlan-ZS5IOVMZ.js} +46 -11
- package/dist/upkeepHandler-VFGYXJKB.js +13 -0
- package/dist/{userConfig-Z3GQE776.js → userConfig-ICASHI47.js} +1 -1
- package/dist/{watch-DGFMWUDL.js → watch-OCNEUXG6.js} +24 -34
- package/package.json +3 -2
- package/templates/skill-instructions.md +22 -3
- package/dist/chunk-6AAEVQ7I.js +0 -6336
- package/dist/chunk-I2JUKN6Z.js +0 -24
- package/dist/server-KFUD5X57.js +0 -55
|
@@ -0,0 +1,181 @@
|
|
|
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/notify/safeHttp.ts
|
|
7
|
+
init_esm_shims();
|
|
8
|
+
import { lookup as dnsLookup } from "dns";
|
|
9
|
+
import https from "https";
|
|
10
|
+
import { isIP } from "net";
|
|
11
|
+
var MAX_URL = 2e3;
|
|
12
|
+
var TIMEOUT_MS = 1e4;
|
|
13
|
+
var MAX_RESPONSE = 4e3;
|
|
14
|
+
function v4Private(ip) {
|
|
15
|
+
const [a = 0, b = 0] = ip.split(".").map(Number);
|
|
16
|
+
return a === 0 || a === 10 || a === 127 || a === 100 && b >= 64 && b <= 127 || // carrier-grade NAT
|
|
17
|
+
a === 169 && b === 254 || // link-local, cloud metadata
|
|
18
|
+
a === 172 && b >= 16 && b <= 31 || a === 192 && b === 168 || a === 192 && b === 0 || a === 198 && (b === 18 || b === 19) || // benchmarking
|
|
19
|
+
a >= 224;
|
|
20
|
+
}
|
|
21
|
+
function isPrivateAddress(ip) {
|
|
22
|
+
const kind = isIP(ip);
|
|
23
|
+
if (kind === 4) return v4Private(ip);
|
|
24
|
+
if (kind !== 6) return true;
|
|
25
|
+
const v = ip.toLowerCase();
|
|
26
|
+
if (v === "::" || v === "::1") return true;
|
|
27
|
+
const mapped = /^::ffff:(.+)$/.exec(v);
|
|
28
|
+
if (mapped) {
|
|
29
|
+
const rest = mapped[1];
|
|
30
|
+
if (isIP(rest) === 4) return v4Private(rest);
|
|
31
|
+
const [hi = "0", lo = "0"] = rest.split(":");
|
|
32
|
+
const n = parseInt(hi, 16) << 16 | parseInt(lo, 16);
|
|
33
|
+
return v4Private([n >>> 24 & 255, n >>> 16 & 255, n >>> 8 & 255, n & 255].join("."));
|
|
34
|
+
}
|
|
35
|
+
return /^f[cd]/.test(v) || /^fe[89ab]/.test(v) || v.startsWith("ff");
|
|
36
|
+
}
|
|
37
|
+
var SLACK_HOSTS = /* @__PURE__ */ new Set(["hooks.slack.com"]);
|
|
38
|
+
var DISCORD_HOSTS = /* @__PURE__ */ new Set([
|
|
39
|
+
"discord.com",
|
|
40
|
+
"discordapp.com",
|
|
41
|
+
"ptb.discord.com",
|
|
42
|
+
"canary.discord.com"
|
|
43
|
+
]);
|
|
44
|
+
var TEAMS_SUFFIXES = [".logic.azure.com", ".powerplatform.com", ".powerautomate.com"];
|
|
45
|
+
function validateChannelUrl(kind, raw) {
|
|
46
|
+
if (typeof raw !== "string" || !raw.trim() || raw.length > MAX_URL)
|
|
47
|
+
return { ok: false, error: "Paste the full webhook URL." };
|
|
48
|
+
let url;
|
|
49
|
+
try {
|
|
50
|
+
url = new URL(raw.trim());
|
|
51
|
+
} catch {
|
|
52
|
+
return { ok: false, error: "That is not a valid URL." };
|
|
53
|
+
}
|
|
54
|
+
if (url.protocol !== "https:") return { ok: false, error: "The URL must start with https://." };
|
|
55
|
+
if (url.username || url.password)
|
|
56
|
+
return { ok: false, error: "The URL must not contain a username or password." };
|
|
57
|
+
const host = url.hostname.toLowerCase();
|
|
58
|
+
if (kind === "slack") {
|
|
59
|
+
if (!SLACK_HOSTS.has(host) || !/^\/(services|triggers)\//.test(url.pathname)) {
|
|
60
|
+
return {
|
|
61
|
+
ok: false,
|
|
62
|
+
error: "A Slack incoming webhook URL starts with https://hooks.slack.com/services/."
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
} else if (kind === "discord") {
|
|
66
|
+
if (!DISCORD_HOSTS.has(host) || !url.pathname.startsWith("/api/webhooks/")) {
|
|
67
|
+
return {
|
|
68
|
+
ok: false,
|
|
69
|
+
error: "A Discord webhook URL starts with https://discord.com/api/webhooks/."
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
} else if (kind === "teams") {
|
|
73
|
+
if (host.endsWith(".webhook.office.com") || host === "outlook.office.com") {
|
|
74
|
+
return {
|
|
75
|
+
ok: false,
|
|
76
|
+
error: "Teams retired Office 365 connector webhooks in May 2026. Create one with the Workflows app instead."
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
if (!TEAMS_SUFFIXES.some((s) => host.endsWith(s))) {
|
|
80
|
+
return {
|
|
81
|
+
ok: false,
|
|
82
|
+
error: "Use the webhook URL a Teams Workflows flow gives you (a logic.azure.com or powerplatform.com address)."
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
} else {
|
|
86
|
+
if (host === "localhost" || host.endsWith(".localhost") || host.endsWith(".local") || host.endsWith(".internal")) {
|
|
87
|
+
return { ok: false, error: "The URL must be reachable on the public internet." };
|
|
88
|
+
}
|
|
89
|
+
if (isIP(host.replace(/^\[|\]$/g, "")) && isPrivateAddress(host.replace(/^\[|\]$/g, ""))) {
|
|
90
|
+
return { ok: false, error: "The URL must be reachable on the public internet." };
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return { ok: true, url };
|
|
94
|
+
}
|
|
95
|
+
function safeLookup(resolve = dnsLookup) {
|
|
96
|
+
return (hostname, options, callback) => {
|
|
97
|
+
resolve(hostname, { all: true }, (err, addresses) => {
|
|
98
|
+
if (err) return callback(err, []);
|
|
99
|
+
const list = addresses;
|
|
100
|
+
if (!list.length || list.some((a) => isPrivateAddress(a.address))) {
|
|
101
|
+
const e = Object.assign(new Error(`${hostname} resolves to a private network address`), {
|
|
102
|
+
code: "EPRIVATE"
|
|
103
|
+
});
|
|
104
|
+
return callback(e, []);
|
|
105
|
+
}
|
|
106
|
+
if (options?.all) return callback(null, list);
|
|
107
|
+
callback(null, list[0].address, list[0].family);
|
|
108
|
+
});
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function postJson(url, payload, headers, opts = {}) {
|
|
112
|
+
return new Promise((resolvePost) => {
|
|
113
|
+
let settled = false;
|
|
114
|
+
const done = (o) => {
|
|
115
|
+
if (!settled) {
|
|
116
|
+
settled = true;
|
|
117
|
+
resolvePost(o);
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
let req;
|
|
121
|
+
try {
|
|
122
|
+
req = https.request(
|
|
123
|
+
url,
|
|
124
|
+
{
|
|
125
|
+
method: "POST",
|
|
126
|
+
headers: {
|
|
127
|
+
"Content-Type": "application/json",
|
|
128
|
+
"User-Agent": "lurq-alerts",
|
|
129
|
+
"Content-Length": Buffer.byteLength(payload),
|
|
130
|
+
...headers
|
|
131
|
+
},
|
|
132
|
+
lookup: opts.lookup ?? safeLookup(),
|
|
133
|
+
timeout: opts.timeoutMs ?? TIMEOUT_MS
|
|
134
|
+
},
|
|
135
|
+
(res) => {
|
|
136
|
+
let body = "";
|
|
137
|
+
res.setEncoding("utf8");
|
|
138
|
+
res.on("data", (chunk) => {
|
|
139
|
+
if (body.length < MAX_RESPONSE) body += chunk;
|
|
140
|
+
});
|
|
141
|
+
res.on("end", () => {
|
|
142
|
+
const status = res.statusCode ?? 0;
|
|
143
|
+
done({
|
|
144
|
+
status,
|
|
145
|
+
error: status >= 200 && status < 300 ? null : `HTTP ${status}${body ? `: ${body.slice(0, 120).replace(/\s+/g, " ")}` : ""}`
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
res.on(
|
|
149
|
+
"error",
|
|
150
|
+
() => done({ status: res.statusCode ?? 0, error: "response interrupted" })
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
);
|
|
154
|
+
} catch (err) {
|
|
155
|
+
done({
|
|
156
|
+
status: 0,
|
|
157
|
+
error: err instanceof Error ? err.message : "request failed",
|
|
158
|
+
code: "EINVAL"
|
|
159
|
+
});
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
req.on("timeout", () => {
|
|
163
|
+
req.destroy(Object.assign(new Error("timed out"), { code: "ETIMEDOUT" }));
|
|
164
|
+
});
|
|
165
|
+
req.on("error", (err) => {
|
|
166
|
+
done({
|
|
167
|
+
status: 0,
|
|
168
|
+
error: err.code === "EPRIVATE" ? err.message : `could not connect (${err.code ?? err.message})`,
|
|
169
|
+
code: err.code ?? "ECONNECT"
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
req.end(payload);
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export {
|
|
177
|
+
isPrivateAddress,
|
|
178
|
+
validateChannelUrl,
|
|
179
|
+
safeLookup,
|
|
180
|
+
postJson
|
|
181
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createRequire as __lurqCreateRequire } from 'node:module'; const require = __lurqCreateRequire(import.meta.url);
|
|
2
2
|
import {
|
|
3
3
|
EMBEDDING_DIM
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-72Y2FDVS.js";
|
|
5
5
|
import {
|
|
6
6
|
__export,
|
|
7
7
|
init_esm_shims
|
|
@@ -14,6 +14,7 @@ __export(schema_exports, {
|
|
|
14
14
|
apiKeys: () => apiKeys,
|
|
15
15
|
apiSurfaces: () => apiSurfaces,
|
|
16
16
|
askSpendDaily: () => askSpendDaily,
|
|
17
|
+
builderScans: () => builderScans,
|
|
17
18
|
claims: () => claims,
|
|
18
19
|
compatEdges: () => compatEdges,
|
|
19
20
|
compatVerifyQueue: () => compatVerifyQueue,
|
|
@@ -23,8 +24,15 @@ __export(schema_exports, {
|
|
|
23
24
|
mcpChangeEvents: () => mcpChangeEvents,
|
|
24
25
|
mcpContracts: () => mcpContracts,
|
|
25
26
|
mcpDeployments: () => mcpDeployments,
|
|
27
|
+
mcpEndpointChangeAcks: () => mcpEndpointChangeAcks,
|
|
28
|
+
mcpEndpointChanges: () => mcpEndpointChanges,
|
|
29
|
+
mcpEndpointObservations: () => mcpEndpointObservations,
|
|
30
|
+
mcpEndpointServers: () => mcpEndpointServers,
|
|
26
31
|
mcpObservations: () => mcpObservations,
|
|
32
|
+
mcpPins: () => mcpPins,
|
|
27
33
|
mcpPublicReports: () => mcpPublicReports,
|
|
34
|
+
mcpRegistryServers: () => mcpRegistryServers,
|
|
35
|
+
mcpRemoteEndpoints: () => mcpRemoteEndpoints,
|
|
28
36
|
notificationChannels: () => notificationChannels,
|
|
29
37
|
notificationDeliveries: () => notificationDeliveries,
|
|
30
38
|
notificationItems: () => notificationItems,
|
|
@@ -36,6 +44,7 @@ __export(schema_exports, {
|
|
|
36
44
|
policyDecisions: () => policyDecisions,
|
|
37
45
|
recommendationOutcomes: () => recommendationOutcomes,
|
|
38
46
|
repoAlerts: () => repoAlerts,
|
|
47
|
+
repoPolicyDefaults: () => repoPolicyDefaults,
|
|
39
48
|
repos: () => repos,
|
|
40
49
|
resolvedClosures: () => resolvedClosures,
|
|
41
50
|
seedPackages: () => seedPackages,
|
|
@@ -161,7 +170,17 @@ var packages = pgTable(
|
|
|
161
170
|
* Null for crawler/_changes-created packages, or ones ingested before
|
|
162
171
|
* dashboard accounts existed. Stamped once via a standalone WHERE ... IS NULL
|
|
163
172
|
* update kept out of upsertPackage, so re-syncs can never clobber it. */
|
|
164
|
-
firstRequestedByOwnerId: text("first_requested_by_owner_id")
|
|
173
|
+
firstRequestedByOwnerId: text("first_requested_by_owner_id"),
|
|
174
|
+
/**
|
|
175
|
+
* Failed API-surface extractions for `surface_attempted_version`. The worker
|
|
176
|
+
* stops asking once a version has failed SURFACE_MAX_ATTEMPTS times; a new
|
|
177
|
+
* latest version starts the count again, since it may ship types the last
|
|
178
|
+
* one did not. Without this, a package with no extractable surface was
|
|
179
|
+
* re-attempted at random every hour, forever. Not in upsertPackage's row,
|
|
180
|
+
* so re-syncs never reset it.
|
|
181
|
+
*/
|
|
182
|
+
surfaceAttempts: integer("surface_attempts").notNull().default(0),
|
|
183
|
+
surfaceAttemptedVersion: text("surface_attempted_version")
|
|
165
184
|
},
|
|
166
185
|
(table) => [
|
|
167
186
|
uniqueIndex("packages_ecosystem_name_idx").on(table.ecosystem, table.name),
|
|
@@ -211,6 +230,13 @@ var discoveryQueue = pgTable(
|
|
|
211
230
|
* and never reaching the write that would take it off the queue. Same
|
|
212
231
|
* treatment `compat_verify_queue.attempts` already gives a failing set. */
|
|
213
232
|
attempts: integer("attempts").notNull().default(0),
|
|
233
|
+
/**
|
|
234
|
+
* The account whose query asked for this package (`reactive` rows only),
|
|
235
|
+
* credited as first requester when it is ingested. Stored here because the
|
|
236
|
+
* on-demand ingest used to live only in the API process's memory, and a
|
|
237
|
+
* deploy mid-backlog dropped both the work and the attribution.
|
|
238
|
+
*/
|
|
239
|
+
requestedByOwnerId: text("requested_by_owner_id"),
|
|
214
240
|
discoveredAt: ts("discovered_at").notNull().defaultNow()
|
|
215
241
|
},
|
|
216
242
|
(table) => [index("discovery_queue_status_idx").on(table.status)]
|
|
@@ -392,6 +418,11 @@ var ownerUsageDaily = pgTable(
|
|
|
392
418
|
index("owner_usage_daily_owner_date_idx").on(table.ownerId, table.date)
|
|
393
419
|
]
|
|
394
420
|
);
|
|
421
|
+
var repoPolicyDefaults = pgTable("repo_policy_defaults", {
|
|
422
|
+
ownerId: text("owner_id").primaryKey(),
|
|
423
|
+
policy: jsonb("policy").$type().notNull(),
|
|
424
|
+
updatedAt: ts("updated_at").notNull().defaultNow()
|
|
425
|
+
});
|
|
395
426
|
var repos = pgTable(
|
|
396
427
|
"repos",
|
|
397
428
|
{
|
|
@@ -458,6 +489,21 @@ var upgradeRuns = pgTable(
|
|
|
458
489
|
* row per package+target and updates in place.
|
|
459
490
|
*/
|
|
460
491
|
runUrl: text("run_url").notNull().default(""),
|
|
492
|
+
/**
|
|
493
|
+
* What started the run, from GITHUB_EVENT_NAME. See `RunTrigger`.
|
|
494
|
+
*
|
|
495
|
+
* NULLABLE, and deliberately NOT in the dedup index below. Every row
|
|
496
|
+
* written before this column existed has no trigger, and back-filling them
|
|
497
|
+
* with 'schedule' would invent evidence the log then presents as fact — the
|
|
498
|
+
* same rule `unverified` and `analysedOnly` follow. The dashboard renders
|
|
499
|
+
* null as "not recorded".
|
|
500
|
+
*
|
|
501
|
+
* Keeping it out of the dedup key is not a style choice: `runUrl` is in
|
|
502
|
+
* that key and is NOT NULL precisely because Postgres treats NULLs as
|
|
503
|
+
* distinct, so a nullable column there would let every re-post insert a
|
|
504
|
+
* duplicate instead of updating in place.
|
|
505
|
+
*/
|
|
506
|
+
trigger: text("trigger").$type(),
|
|
461
507
|
createdAt: ts("created_at").notNull().defaultNow()
|
|
462
508
|
},
|
|
463
509
|
(table) => [
|
|
@@ -480,7 +526,10 @@ var repoAlerts = pgTable(
|
|
|
480
526
|
id: serial("id").primaryKey(),
|
|
481
527
|
/** Clerk user id, copied from the repo — alerts are read owner-scoped. */
|
|
482
528
|
ownerId: text("owner_id").notNull(),
|
|
483
|
-
|
|
529
|
+
/** Null for an account that runs `check-upgrade` on a repo lurq has no
|
|
530
|
+
* GitHub App installation for: the alert is keyed by `repo_full_name` from
|
|
531
|
+
* its upgrade runs instead, and there is no repo page to link to. */
|
|
532
|
+
repoId: integer("repo_id"),
|
|
484
533
|
/** Denormalized so the feed renders without a join, as in `upgrade_runs`. */
|
|
485
534
|
repoFullName: text("repo_full_name").notNull(),
|
|
486
535
|
packageName: text("package_name").notNull(),
|
|
@@ -507,7 +556,11 @@ var repoAlerts = pgTable(
|
|
|
507
556
|
// One alert per repo per release. A re-sync of the same version — and the
|
|
508
557
|
// watcher re-syncs on every publish, including non-latest backports — must
|
|
509
558
|
// not re-notify.
|
|
510
|
-
uniqueIndex("repo_alerts_dedup_idx").on(table.repoId, table.packageName, table.toVersion)
|
|
559
|
+
uniqueIndex("repo_alerts_dedup_idx").on(table.repoId, table.packageName, table.toVersion),
|
|
560
|
+
// The same rule for alerts with no connected repo. Postgres treats NULLs as
|
|
561
|
+
// distinct, so the index above can never dedupe those rows; this one covers
|
|
562
|
+
// exactly them. Partial, so adding it cannot fail on existing rows.
|
|
563
|
+
uniqueIndex("repo_alerts_cli_dedup_idx").on(table.ownerId, table.repoFullName, table.packageName, table.toVersion).where(sql`repo_id is null`)
|
|
511
564
|
]
|
|
512
565
|
);
|
|
513
566
|
var surfaceQueue = pgTable(
|
|
@@ -532,6 +585,12 @@ var surfaceQueue = pgTable(
|
|
|
532
585
|
specKey: text("spec_key").notNull().unique(),
|
|
533
586
|
/** Failed drains bump this; the worker drops a spec that keeps failing. */
|
|
534
587
|
attempts: integer("attempts").notNull().default(0),
|
|
588
|
+
/**
|
|
589
|
+
* Not eligible before this. Set on every retry with an exponential delay, so
|
|
590
|
+
* a spec that just failed waits instead of being retried the next cycle and
|
|
591
|
+
* — oldest-first — blocking every newer spec behind it. Null = ready now.
|
|
592
|
+
*/
|
|
593
|
+
nextAttemptAt: ts("next_attempt_at"),
|
|
535
594
|
requestedAt: ts("requested_at").notNull().defaultNow()
|
|
536
595
|
},
|
|
537
596
|
(table) => [index("surface_queue_requested_idx").on(table.kind, table.requestedAt)]
|
|
@@ -801,7 +860,11 @@ var mcpDeployments = pgTable(
|
|
|
801
860
|
lastChangedAt: ts("last_changed_at")
|
|
802
861
|
},
|
|
803
862
|
(table) => [
|
|
804
|
-
uniqueIndex("mcp_deployments_identity_idx").on(
|
|
863
|
+
uniqueIndex("mcp_deployments_identity_idx").on(
|
|
864
|
+
table.ownerId,
|
|
865
|
+
table.serverKey,
|
|
866
|
+
table.configFingerprint
|
|
867
|
+
),
|
|
805
868
|
index("mcp_deployments_owner_idx").on(table.ownerId, table.lastScannedAt)
|
|
806
869
|
]
|
|
807
870
|
);
|
|
@@ -854,7 +917,9 @@ var mcpPublicReports = pgTable(
|
|
|
854
917
|
reportedAt: ts("reported_at").notNull().defaultNow()
|
|
855
918
|
},
|
|
856
919
|
(table) => [
|
|
857
|
-
primaryKey({
|
|
920
|
+
primaryKey({
|
|
921
|
+
columns: [table.registry, table.packageName, table.version, table.contentHash, table.ownerId]
|
|
922
|
+
}),
|
|
858
923
|
index("mcp_public_reports_version_idx").on(table.registry, table.packageName, table.version)
|
|
859
924
|
]
|
|
860
925
|
);
|
|
@@ -924,6 +989,196 @@ var notificationChannels = pgTable(
|
|
|
924
989
|
},
|
|
925
990
|
(table) => [index("notification_channels_owner_idx").on(table.ownerId)]
|
|
926
991
|
);
|
|
992
|
+
var builderScans = pgTable(
|
|
993
|
+
"builder_scans",
|
|
994
|
+
{
|
|
995
|
+
ownerId: text("owner_id").notNull(),
|
|
996
|
+
target: text("target").notNull(),
|
|
997
|
+
login: text("login").notNull(),
|
|
998
|
+
archetype: text("archetype").$type().notNull(),
|
|
999
|
+
avatarUrl: text("avatar_url").notNull(),
|
|
1000
|
+
// What builderStanding.ts ranks on, copied out of `profile` at save, so
|
|
1001
|
+
// ranking reads seven integers per builder instead of every whole report.
|
|
1002
|
+
repos: integer("repos").notNull(),
|
|
1003
|
+
active90: integer("active_90").notNull(),
|
|
1004
|
+
stars: integer("stars").notNull(),
|
|
1005
|
+
depsTracked: integer("deps_tracked").notNull(),
|
|
1006
|
+
depsBehind: integer("deps_behind").notNull(),
|
|
1007
|
+
depsMajor: integer("deps_major").notNull(),
|
|
1008
|
+
advisories: integer("advisories").notNull(),
|
|
1009
|
+
profile: jsonb("profile").$type().notNull(),
|
|
1010
|
+
scannedAt: ts("scanned_at").notNull().defaultNow(),
|
|
1011
|
+
createdAt: ts("created_at").notNull().defaultNow()
|
|
1012
|
+
},
|
|
1013
|
+
(table) => [
|
|
1014
|
+
primaryKey({ columns: [table.ownerId, table.target] }),
|
|
1015
|
+
index("builder_scans_owner_recent_idx").on(table.ownerId, table.scannedAt)
|
|
1016
|
+
]
|
|
1017
|
+
);
|
|
1018
|
+
var mcpRegistryServers = pgTable(
|
|
1019
|
+
"mcp_registry_servers",
|
|
1020
|
+
{
|
|
1021
|
+
name: text("name").notNull(),
|
|
1022
|
+
version: text("version").notNull(),
|
|
1023
|
+
title: text("title"),
|
|
1024
|
+
description: text("description"),
|
|
1025
|
+
websiteUrl: text("website_url"),
|
|
1026
|
+
repositoryUrl: text("repository_url"),
|
|
1027
|
+
/** `active`, `deprecated`, `deleted`, … as the registry reports it. */
|
|
1028
|
+
status: text("status").notNull(),
|
|
1029
|
+
isLatest: boolean("is_latest").notNull(),
|
|
1030
|
+
remotes: jsonb("remotes").$type().notNull(),
|
|
1031
|
+
packages: jsonb("packages").$type().notNull(),
|
|
1032
|
+
publishedAt: ts("published_at"),
|
|
1033
|
+
registryUpdatedAt: ts("registry_updated_at"),
|
|
1034
|
+
syncedAt: ts("synced_at").notNull().defaultNow()
|
|
1035
|
+
},
|
|
1036
|
+
(table) => [
|
|
1037
|
+
primaryKey({ columns: [table.name, table.version] }),
|
|
1038
|
+
index("mcp_registry_servers_latest_idx").on(table.name).where(sql`${table.isLatest}`),
|
|
1039
|
+
index("mcp_registry_servers_updated_idx").on(table.registryUpdatedAt)
|
|
1040
|
+
]
|
|
1041
|
+
);
|
|
1042
|
+
var mcpRemoteEndpoints = pgTable(
|
|
1043
|
+
"mcp_remote_endpoints",
|
|
1044
|
+
{
|
|
1045
|
+
id: serial("id").primaryKey(),
|
|
1046
|
+
url: text("url").notNull().unique(),
|
|
1047
|
+
host: text("host").notNull(),
|
|
1048
|
+
/** Transport the registry declares: `streamable-http` or `sse`. */
|
|
1049
|
+
transport: text("transport").notNull(),
|
|
1050
|
+
/** URL carries `{placeholders}`: never probed, answered as setup-dependent. */
|
|
1051
|
+
templated: boolean("templated").notNull().default(false),
|
|
1052
|
+
/** A maintainer asked not to be probed. Never scheduled again. */
|
|
1053
|
+
optedOut: boolean("opted_out").notNull().default(false),
|
|
1054
|
+
lastStatus: text("last_status").$type(),
|
|
1055
|
+
lastHttpStatus: integer("last_http_status"),
|
|
1056
|
+
/** Content hash of the last contract read (a key into `mcp_contracts`). */
|
|
1057
|
+
lastContentHash: text("last_content_hash"),
|
|
1058
|
+
/** Hash of the auth profile, so an auth change is detectable without a diff. */
|
|
1059
|
+
lastAuthHash: text("last_auth_hash"),
|
|
1060
|
+
auth: jsonb("auth").$type(),
|
|
1061
|
+
violations: jsonb("violations").$type().notNull().default([]),
|
|
1062
|
+
protocolMode: text("protocol_mode").$type(),
|
|
1063
|
+
protocolVersion: text("protocol_version"),
|
|
1064
|
+
serverName: text("server_name"),
|
|
1065
|
+
serverVersion: text("server_version"),
|
|
1066
|
+
finalUrl: text("final_url"),
|
|
1067
|
+
latencyMs: integer("latency_ms"),
|
|
1068
|
+
lastError: text("last_error"),
|
|
1069
|
+
consecutiveFailures: integer("consecutive_failures").notNull().default(0),
|
|
1070
|
+
probeCount: integer("probe_count").notNull().default(0),
|
|
1071
|
+
firstSeenAt: ts("first_seen_at").notNull().defaultNow(),
|
|
1072
|
+
lastProbedAt: ts("last_probed_at"),
|
|
1073
|
+
lastChangedAt: ts("last_changed_at"),
|
|
1074
|
+
nextProbeAt: ts("next_probe_at").notNull().defaultNow(),
|
|
1075
|
+
leaseUntil: ts("lease_until"),
|
|
1076
|
+
/** No live registry entry names this URL any more. Kept for history. */
|
|
1077
|
+
removedAt: ts("removed_at"),
|
|
1078
|
+
/**
|
|
1079
|
+
* The key an account's own `lurq mcp-scan` gives this server (`remote:<host><path>`,
|
|
1080
|
+
* query dropped). Joining on it is how a change the public probe sees reaches
|
|
1081
|
+
* every account already running the server, without anyone rescanning.
|
|
1082
|
+
*/
|
|
1083
|
+
scanKey: text("scan_key")
|
|
1084
|
+
},
|
|
1085
|
+
(table) => [
|
|
1086
|
+
index("mcp_remote_endpoints_scan_key_idx").on(table.scanKey),
|
|
1087
|
+
index("mcp_remote_endpoints_due_idx").on(table.nextProbeAt).where(
|
|
1088
|
+
sql`${table.optedOut} = false and ${table.templated} = false and ${table.removedAt} is null`
|
|
1089
|
+
),
|
|
1090
|
+
index("mcp_remote_endpoints_host_idx").on(table.host)
|
|
1091
|
+
]
|
|
1092
|
+
);
|
|
1093
|
+
var mcpEndpointServers = pgTable(
|
|
1094
|
+
"mcp_endpoint_servers",
|
|
1095
|
+
{
|
|
1096
|
+
endpointId: integer("endpoint_id").notNull().references(() => mcpRemoteEndpoints.id),
|
|
1097
|
+
serverName: text("server_name").notNull(),
|
|
1098
|
+
headers: jsonb("headers").$type().notNull().default([]),
|
|
1099
|
+
lastSeenAt: ts("last_seen_at").notNull().defaultNow(),
|
|
1100
|
+
/**
|
|
1101
|
+
* The server's latest registry version stopped naming this URL, or the
|
|
1102
|
+
* server was deleted. Marked, never deleted: rows in this schema are history.
|
|
1103
|
+
*/
|
|
1104
|
+
removedAt: ts("removed_at")
|
|
1105
|
+
},
|
|
1106
|
+
(table) => [
|
|
1107
|
+
primaryKey({ columns: [table.endpointId, table.serverName] }),
|
|
1108
|
+
index("mcp_endpoint_servers_name_idx").on(table.serverName)
|
|
1109
|
+
]
|
|
1110
|
+
);
|
|
1111
|
+
var mcpEndpointObservations = pgTable(
|
|
1112
|
+
"mcp_endpoint_observations",
|
|
1113
|
+
{
|
|
1114
|
+
id: serial("id").primaryKey(),
|
|
1115
|
+
endpointId: integer("endpoint_id").notNull().references(() => mcpRemoteEndpoints.id),
|
|
1116
|
+
status: text("status").$type().notNull(),
|
|
1117
|
+
httpStatus: integer("http_status"),
|
|
1118
|
+
contentHash: text("content_hash"),
|
|
1119
|
+
authHash: text("auth_hash"),
|
|
1120
|
+
violations: jsonb("violations").$type().notNull().default([]),
|
|
1121
|
+
protocolVersion: text("protocol_version"),
|
|
1122
|
+
error: text("error"),
|
|
1123
|
+
probeCount: integer("probe_count").notNull().default(1),
|
|
1124
|
+
firstSeenAt: ts("first_seen_at").notNull().defaultNow(),
|
|
1125
|
+
lastSeenAt: ts("last_seen_at").notNull().defaultNow()
|
|
1126
|
+
},
|
|
1127
|
+
(table) => [
|
|
1128
|
+
index("mcp_endpoint_observations_endpoint_idx").on(table.endpointId, table.firstSeenAt)
|
|
1129
|
+
]
|
|
1130
|
+
);
|
|
1131
|
+
var mcpEndpointChanges = pgTable(
|
|
1132
|
+
"mcp_endpoint_changes",
|
|
1133
|
+
{
|
|
1134
|
+
id: serial("id").primaryKey(),
|
|
1135
|
+
endpointId: integer("endpoint_id").notNull().references(() => mcpRemoteEndpoints.id),
|
|
1136
|
+
kind: text("kind").$type().notNull(),
|
|
1137
|
+
fromKey: text("from_key").notNull(),
|
|
1138
|
+
toKey: text("to_key").notNull(),
|
|
1139
|
+
severity: text("severity").$type().notNull(),
|
|
1140
|
+
summary: text("summary").notNull(),
|
|
1141
|
+
diff: jsonb("diff").$type(),
|
|
1142
|
+
createdAt: ts("created_at").notNull().defaultNow()
|
|
1143
|
+
},
|
|
1144
|
+
(table) => [
|
|
1145
|
+
uniqueIndex("mcp_endpoint_changes_pair_idx").on(
|
|
1146
|
+
table.endpointId,
|
|
1147
|
+
table.kind,
|
|
1148
|
+
table.fromKey,
|
|
1149
|
+
table.toKey
|
|
1150
|
+
),
|
|
1151
|
+
index("mcp_endpoint_changes_endpoint_idx").on(table.endpointId, table.createdAt),
|
|
1152
|
+
index("mcp_endpoint_changes_created_idx").on(table.createdAt)
|
|
1153
|
+
]
|
|
1154
|
+
);
|
|
1155
|
+
var mcpPins = pgTable(
|
|
1156
|
+
"mcp_pins",
|
|
1157
|
+
{
|
|
1158
|
+
id: serial("id").primaryKey(),
|
|
1159
|
+
ownerId: text("owner_id").notNull(),
|
|
1160
|
+
endpointId: integer("endpoint_id").notNull().references(() => mcpRemoteEndpoints.id),
|
|
1161
|
+
/** Null when the contract was not readable without credentials at pin time. */
|
|
1162
|
+
contentHash: text("content_hash"),
|
|
1163
|
+
authHash: text("auth_hash"),
|
|
1164
|
+
note: text("note"),
|
|
1165
|
+
pinnedAt: ts("pinned_at").notNull().defaultNow(),
|
|
1166
|
+
removedAt: ts("removed_at")
|
|
1167
|
+
},
|
|
1168
|
+
(table) => [
|
|
1169
|
+
uniqueIndex("mcp_pins_owner_endpoint_idx").on(table.ownerId, table.endpointId),
|
|
1170
|
+
index("mcp_pins_endpoint_idx").on(table.endpointId)
|
|
1171
|
+
]
|
|
1172
|
+
);
|
|
1173
|
+
var mcpEndpointChangeAcks = pgTable(
|
|
1174
|
+
"mcp_endpoint_change_acks",
|
|
1175
|
+
{
|
|
1176
|
+
ownerId: text("owner_id").notNull(),
|
|
1177
|
+
changeId: integer("change_id").notNull().references(() => mcpEndpointChanges.id),
|
|
1178
|
+
acknowledgedAt: ts("acknowledged_at").notNull().defaultNow()
|
|
1179
|
+
},
|
|
1180
|
+
(table) => [primaryKey({ columns: [table.ownerId, table.changeId] })]
|
|
1181
|
+
);
|
|
927
1182
|
|
|
928
1183
|
export {
|
|
929
1184
|
packages,
|
|
@@ -940,6 +1195,7 @@ export {
|
|
|
940
1195
|
recommendationOutcomes,
|
|
941
1196
|
askSpendDaily,
|
|
942
1197
|
ownerUsageDaily,
|
|
1198
|
+
repoPolicyDefaults,
|
|
943
1199
|
repos,
|
|
944
1200
|
upgradeRuns,
|
|
945
1201
|
repoAlerts,
|
|
@@ -961,5 +1217,13 @@ export {
|
|
|
961
1217
|
mcpPublicReports,
|
|
962
1218
|
notificationPreferences,
|
|
963
1219
|
notificationChannels,
|
|
1220
|
+
builderScans,
|
|
1221
|
+
mcpRegistryServers,
|
|
1222
|
+
mcpRemoteEndpoints,
|
|
1223
|
+
mcpEndpointServers,
|
|
1224
|
+
mcpEndpointObservations,
|
|
1225
|
+
mcpEndpointChanges,
|
|
1226
|
+
mcpPins,
|
|
1227
|
+
mcpEndpointChangeAcks,
|
|
964
1228
|
schema_exports
|
|
965
1229
|
};
|