@viberaven/cli 0.1.0-beta.1 → 0.1.0-beta.2
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/AGENTS.md +114 -46
- package/README.md +22 -10
- package/assets/report/report-cli.css +602 -348
- package/assets/report/station.css +8008 -4625
- package/dist/cli.js +2477 -293
- package/dist/cli.js.map +4 -4
- package/dist/report/report-cli.css +602 -348
- package/dist/report/station.css +8008 -4625
- package/package.json +1 -1
- package/templates/AGENTS.snippet.md +24 -0
- package/assets/report/assets/raven-mark.svg +0 -7
- package/dist/report/assets/raven-mark.svg +0 -7
package/dist/cli.js
CHANGED
|
@@ -9,6 +9,10 @@ var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
|
9
9
|
var __commonJS = (cb, mod) => function __require() {
|
|
10
10
|
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
11
11
|
};
|
|
12
|
+
var __export = (target, all) => {
|
|
13
|
+
for (var name in all)
|
|
14
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
15
|
+
};
|
|
12
16
|
var __copyProps = (to, from, except, desc) => {
|
|
13
17
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
18
|
for (let key of __getOwnPropNames(from))
|
|
@@ -25,6 +29,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
25
29
|
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
30
|
mod
|
|
27
31
|
));
|
|
32
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
28
33
|
|
|
29
34
|
// node_modules/picocolors/picocolors.js
|
|
30
35
|
var require_picocolors = __commonJS({
|
|
@@ -155,6 +160,12 @@ var require_src = __commonJS({
|
|
|
155
160
|
});
|
|
156
161
|
|
|
157
162
|
// src/cli.ts
|
|
163
|
+
var cli_exports = {};
|
|
164
|
+
__export(cli_exports, {
|
|
165
|
+
formatScanJsonStdout: () => formatScanJsonStdout,
|
|
166
|
+
parseArgs: () => parseArgs
|
|
167
|
+
});
|
|
168
|
+
module.exports = __toCommonJS(cli_exports);
|
|
158
169
|
var import_node_path8 = require("node:path");
|
|
159
170
|
|
|
160
171
|
// src/config.ts
|
|
@@ -241,8 +252,18 @@ async function loadCredentials() {
|
|
|
241
252
|
email: typeof parsed.email === "string" ? parsed.email : void 0,
|
|
242
253
|
plan: typeof parsed.plan === "string" ? parsed.plan : void 0
|
|
243
254
|
};
|
|
244
|
-
} catch {
|
|
245
|
-
|
|
255
|
+
} catch (error) {
|
|
256
|
+
if (error.code !== "ENOENT") {
|
|
257
|
+
return void 0;
|
|
258
|
+
}
|
|
259
|
+
const accessToken = process.env.VIBERAVEN_ACCESS_TOKEN?.trim();
|
|
260
|
+
if (!accessToken) {
|
|
261
|
+
return void 0;
|
|
262
|
+
}
|
|
263
|
+
return {
|
|
264
|
+
accessToken,
|
|
265
|
+
apiBaseUrl: resolveApiBaseUrl()
|
|
266
|
+
};
|
|
246
267
|
}
|
|
247
268
|
}
|
|
248
269
|
async function saveCredentials(credentials) {
|
|
@@ -454,6 +475,16 @@ function isRecord(value) {
|
|
|
454
475
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
455
476
|
}
|
|
456
477
|
|
|
478
|
+
// src/statusLabels.ts
|
|
479
|
+
var READY = "READY";
|
|
480
|
+
var LOGIN_REQUIRED = "LOGIN_REQUIRED";
|
|
481
|
+
var UPGRADE_REQUIRED = "UPGRADE_REQUIRED";
|
|
482
|
+
var MANUAL_ACTION_REQUIRED = "MANUAL_ACTION_REQUIRED";
|
|
483
|
+
var ERROR = "ERROR";
|
|
484
|
+
function formatAgentStatus(label, message) {
|
|
485
|
+
return `${label}: ${message}`;
|
|
486
|
+
}
|
|
487
|
+
|
|
457
488
|
// src/account.ts
|
|
458
489
|
async function fetchAccountMe(apiBaseUrl, accessToken) {
|
|
459
490
|
const url = `${normalizeBaseUrl(apiBaseUrl)}/v1/account/me`;
|
|
@@ -510,8 +541,9 @@ function formatUsageLine(usage) {
|
|
|
510
541
|
function formatScanLimitMessage(upgradeUrl) {
|
|
511
542
|
return [
|
|
512
543
|
"",
|
|
513
|
-
"Free scan limit reached. Upgrade to Pro to continue.",
|
|
544
|
+
formatAgentStatus(UPGRADE_REQUIRED, "Free scan limit reached. Upgrade to Pro to continue."),
|
|
514
545
|
"Your last scan artifacts are unchanged if you already ran a scan in this repo.",
|
|
546
|
+
"Do not keep retrying this scan until the user upgrades or quota resets.",
|
|
515
547
|
"",
|
|
516
548
|
`Upgrade & account: ${upgradeUrl}`,
|
|
517
549
|
""
|
|
@@ -539,7 +571,7 @@ async function runDeviceLogin(apiBaseUrl) {
|
|
|
539
571
|
continue;
|
|
540
572
|
}
|
|
541
573
|
if (result.status === "expired") {
|
|
542
|
-
throw new Error("Sign-in expired.
|
|
574
|
+
throw new Error(formatAgentStatus(LOGIN_REQUIRED, "Sign-in expired. Ask the user to run `npx -y @viberaven/cli@beta login`, complete browser/device approval, then rerun `npx -y @viberaven/cli@beta scan`."));
|
|
543
575
|
}
|
|
544
576
|
if (result.status === "denied") {
|
|
545
577
|
throw new Error("Sign-in was denied.");
|
|
@@ -570,7 +602,7 @@ async function runDeviceLogin(apiBaseUrl) {
|
|
|
570
602
|
return;
|
|
571
603
|
}
|
|
572
604
|
}
|
|
573
|
-
throw new Error("Sign-in timed out.
|
|
605
|
+
throw new Error(formatAgentStatus(LOGIN_REQUIRED, "Sign-in timed out. Ask the user to run `npx -y @viberaven/cli@beta login`, complete browser/device approval, then rerun `npx -y @viberaven/cli@beta scan`."));
|
|
574
606
|
}
|
|
575
607
|
function buildVerificationUrl(verificationUrl, deviceCode) {
|
|
576
608
|
try {
|
|
@@ -586,7 +618,7 @@ async function requireCredentials(apiBaseUrl) {
|
|
|
586
618
|
const creds = await loadCredentials();
|
|
587
619
|
const base = apiBaseUrl ?? creds?.apiBaseUrl ?? resolveApiBaseUrl();
|
|
588
620
|
if (!creds?.accessToken) {
|
|
589
|
-
throw new Error("Not signed in.
|
|
621
|
+
throw new Error(formatAgentStatus(LOGIN_REQUIRED, "Not signed in. Ask the user to run `npx -y @viberaven/cli@beta login`, complete browser/device approval, then rerun `npx -y @viberaven/cli@beta scan`."));
|
|
590
622
|
}
|
|
591
623
|
return { accessToken: creds.accessToken, apiBaseUrl: base };
|
|
592
624
|
}
|
|
@@ -1442,6 +1474,17 @@ var PROVIDERS = [
|
|
|
1442
1474
|
provider("authjs", "Auth.js", ["authjs", "auth", "nextauth"], ["auth"], ["auth"], "authjs", {
|
|
1443
1475
|
docsUrl: "https://authjs.dev"
|
|
1444
1476
|
}),
|
|
1477
|
+
provider("auth0", "Auth0", ["auth0"], ["auth"], ["auth"], "auth0", {
|
|
1478
|
+
docsUrl: "https://auth0.com/docs",
|
|
1479
|
+
dashboardUrl: "https://manage.auth0.com"
|
|
1480
|
+
}),
|
|
1481
|
+
provider("better-auth", "Better Auth", ["betterauth", "better-auth"], ["auth"], ["auth"], "better-auth", {
|
|
1482
|
+
docsUrl: "https://www.better-auth.com/docs"
|
|
1483
|
+
}),
|
|
1484
|
+
provider("firebase", "Firebase", ["firebase", "firestore"], ["database"], ["database"], "firebase", {
|
|
1485
|
+
docsUrl: "https://firebase.google.com/docs",
|
|
1486
|
+
dashboardUrl: "https://console.firebase.google.com"
|
|
1487
|
+
}),
|
|
1445
1488
|
provider("neon", "Neon", ["neon"], ["database"], ["database"], "neon", {
|
|
1446
1489
|
docsUrl: "https://neon.tech/docs",
|
|
1447
1490
|
dashboardUrl: "https://console.neon.tech",
|
|
@@ -1511,6 +1554,10 @@ var PROVIDERS = [
|
|
|
1511
1554
|
dashboardUrl: "https://polar.sh/dashboard",
|
|
1512
1555
|
verification: { supportsReadOnly: false }
|
|
1513
1556
|
}),
|
|
1557
|
+
provider("lemon-squeezy", "Lemon Squeezy", ["lemonsqueezy", "lemon-squeezy", "lemon squeezy"], ["payments"], ["payments"], "lemon-squeezy", {
|
|
1558
|
+
docsUrl: "https://docs.lemonsqueezy.com",
|
|
1559
|
+
dashboardUrl: "https://app.lemonsqueezy.com"
|
|
1560
|
+
}),
|
|
1514
1561
|
provider("vercel", "Vercel", ["vercel"], ["deployment"], ["deployment"], "vercel", {
|
|
1515
1562
|
docsUrl: "https://vercel.com/docs",
|
|
1516
1563
|
dashboardUrl: "https://vercel.com/dashboard",
|
|
@@ -1523,7 +1570,7 @@ var PROVIDERS = [
|
|
|
1523
1570
|
},
|
|
1524
1571
|
verification: { supportsReadOnly: true }
|
|
1525
1572
|
}),
|
|
1526
|
-
provider("netlify", "Netlify", ["netlify"], ["deployment"], [], "netlify", {
|
|
1573
|
+
provider("netlify", "Netlify", ["netlify"], ["deployment"], ["deployment"], "netlify", {
|
|
1527
1574
|
docsUrl: "https://docs.netlify.com",
|
|
1528
1575
|
dashboardUrl: "https://app.netlify.com",
|
|
1529
1576
|
mcp: {
|
|
@@ -1535,7 +1582,27 @@ var PROVIDERS = [
|
|
|
1535
1582
|
},
|
|
1536
1583
|
verification: { supportsReadOnly: false }
|
|
1537
1584
|
}),
|
|
1538
|
-
provider("
|
|
1585
|
+
provider("render", "Render", ["render", "rendercom"], ["deployment"], ["deployment"], "render", {
|
|
1586
|
+
docsUrl: "https://render.com/docs",
|
|
1587
|
+
dashboardUrl: "https://dashboard.render.com"
|
|
1588
|
+
}),
|
|
1589
|
+
provider("railway", "Railway", ["railway", "railwayapp"], ["deployment"], ["deployment"], "railway", {
|
|
1590
|
+
docsUrl: "https://docs.railway.com",
|
|
1591
|
+
dashboardUrl: "https://railway.com"
|
|
1592
|
+
}),
|
|
1593
|
+
provider("cloudflare", "Cloudflare", ["cloudflare", "cloudflarepages", "workers"], ["deployment"], ["deployment"], "cloudflare", {
|
|
1594
|
+
docsUrl: "https://developers.cloudflare.com",
|
|
1595
|
+
dashboardUrl: "https://dash.cloudflare.com",
|
|
1596
|
+
mcp: {
|
|
1597
|
+
label: "Cloudflare",
|
|
1598
|
+
serverName: "cloudflare-api",
|
|
1599
|
+
vscodeServer: { type: "http", url: "https://mcp.cloudflare.com/mcp" },
|
|
1600
|
+
cursorServer: { url: "https://mcp.cloudflare.com/mcp" },
|
|
1601
|
+
keyInstructions: "Cloudflare MCP uses Cloudflare account authentication. Finish browser sign-in or provide an API token only when prompted."
|
|
1602
|
+
},
|
|
1603
|
+
verification: { supportsReadOnly: true }
|
|
1604
|
+
}),
|
|
1605
|
+
provider("aws", "AWS", ["aws"], ["deployment"], ["deployment"], "aws", {
|
|
1539
1606
|
docsUrl: "https://docs.aws.amazon.com",
|
|
1540
1607
|
dashboardUrl: "https://console.aws.amazon.com"
|
|
1541
1608
|
}),
|
|
@@ -1567,6 +1634,18 @@ var PROVIDERS = [
|
|
|
1567
1634
|
docsUrl: "https://docs.logrocket.com",
|
|
1568
1635
|
dashboardUrl: "https://app.logrocket.com"
|
|
1569
1636
|
}),
|
|
1637
|
+
provider("github", "GitHub Actions", ["github", "githubactions"], ["testing"], [], "github", {
|
|
1638
|
+
docsUrl: "https://docs.github.com/actions",
|
|
1639
|
+
dashboardUrl: "https://github.com",
|
|
1640
|
+
mcp: {
|
|
1641
|
+
label: "GitHub",
|
|
1642
|
+
serverName: "github",
|
|
1643
|
+
vscodeServer: { type: "http", url: "https://api.githubcopilot.com/mcp/" },
|
|
1644
|
+
cursorServer: { url: "https://api.githubcopilot.com/mcp/" },
|
|
1645
|
+
keyInstructions: "GitHub MCP should use IDE/GitHub authentication. Use read-only repository, Actions, and branch-protection queries for verification."
|
|
1646
|
+
},
|
|
1647
|
+
verification: { supportsReadOnly: true }
|
|
1648
|
+
}),
|
|
1570
1649
|
provider("playwright", "Playwright", ["playwright", "playwrighttest"], ["testing"], [], "playwright", {
|
|
1571
1650
|
docsUrl: "https://playwright.dev/docs/intro",
|
|
1572
1651
|
mcp: {
|
|
@@ -1590,7 +1669,7 @@ var PROVIDERS = [
|
|
|
1590
1669
|
},
|
|
1591
1670
|
verification: { supportsReadOnly: false }
|
|
1592
1671
|
}),
|
|
1593
|
-
provider("bot-protection", "Bot protection", ["botprotection", "cloudflareturnstile", "turnstile"
|
|
1672
|
+
provider("bot-protection", "Bot protection", ["botprotection", "cloudflareturnstile", "turnstile"], ["security"], ["security"], "bot-protection", {
|
|
1594
1673
|
docsUrl: "https://developers.cloudflare.com/turnstile",
|
|
1595
1674
|
dashboardUrl: "https://dash.cloudflare.com",
|
|
1596
1675
|
mcp: {
|
|
@@ -1773,6 +1852,269 @@ function titleizeProvider(provider2) {
|
|
|
1773
1852
|
);
|
|
1774
1853
|
}
|
|
1775
1854
|
|
|
1855
|
+
// ../../src/station/verificationLayer/types.ts
|
|
1856
|
+
function providerCheckId(provider2, checkKey) {
|
|
1857
|
+
return `${provider2}:${checkKey}`;
|
|
1858
|
+
}
|
|
1859
|
+
var PROVIDER_CONNECTION_BLOCKS_VERIFY = [
|
|
1860
|
+
"not_configured",
|
|
1861
|
+
"configured",
|
|
1862
|
+
"unknown_runtime",
|
|
1863
|
+
"unsupported"
|
|
1864
|
+
];
|
|
1865
|
+
function coerceProviderVerificationStatus(status, connectionState) {
|
|
1866
|
+
if (status !== "verified") {
|
|
1867
|
+
return status;
|
|
1868
|
+
}
|
|
1869
|
+
if (PROVIDER_CONNECTION_BLOCKS_VERIFY.includes(connectionState)) {
|
|
1870
|
+
return connectionState === "not_configured" || connectionState === "configured" ? "needs_mcp" : "unknown";
|
|
1871
|
+
}
|
|
1872
|
+
return status;
|
|
1873
|
+
}
|
|
1874
|
+
function mapVerificationStatusToMissionStatus(status) {
|
|
1875
|
+
switch (status) {
|
|
1876
|
+
case "verified":
|
|
1877
|
+
return "passed";
|
|
1878
|
+
case "missing":
|
|
1879
|
+
return "missing";
|
|
1880
|
+
case "unknown":
|
|
1881
|
+
return "unknown";
|
|
1882
|
+
case "needs_mcp":
|
|
1883
|
+
return "needs-connection";
|
|
1884
|
+
case "manual":
|
|
1885
|
+
return "manual-required";
|
|
1886
|
+
default:
|
|
1887
|
+
return "failed";
|
|
1888
|
+
}
|
|
1889
|
+
}
|
|
1890
|
+
function mapEvidenceSourceToLegacyEvidenceClass(source, status) {
|
|
1891
|
+
if (source === "repo") {
|
|
1892
|
+
return status === "verified" ? "repo-verified" : "missing-repo-fix";
|
|
1893
|
+
}
|
|
1894
|
+
if (source === "manual") {
|
|
1895
|
+
return "manual-dashboard";
|
|
1896
|
+
}
|
|
1897
|
+
if (source === "mcp") {
|
|
1898
|
+
return "mcp-verifier";
|
|
1899
|
+
}
|
|
1900
|
+
return "mcp-verifier";
|
|
1901
|
+
}
|
|
1902
|
+
function isProviderLayerCheck(check) {
|
|
1903
|
+
return check.evidenceSource === "provider" || check.evidenceSource === "mcp";
|
|
1904
|
+
}
|
|
1905
|
+
function isRepoLayerCheck(check) {
|
|
1906
|
+
return check.evidenceSource === "repo";
|
|
1907
|
+
}
|
|
1908
|
+
function computeLayerReadinessPercent(checks, layer) {
|
|
1909
|
+
const filtered = checks.filter(
|
|
1910
|
+
(check) => layer === "repo" ? isRepoLayerCheck(check) : isProviderLayerCheck(check)
|
|
1911
|
+
);
|
|
1912
|
+
if (filtered.length === 0) {
|
|
1913
|
+
return 100;
|
|
1914
|
+
}
|
|
1915
|
+
const verified = filtered.filter((check) => check.status === "verified").length;
|
|
1916
|
+
return Math.round(verified / filtered.length * 100);
|
|
1917
|
+
}
|
|
1918
|
+
function aggregateReadinessPercents(providerResults) {
|
|
1919
|
+
if (providerResults.length === 0) {
|
|
1920
|
+
return { repoReadinessPercent: 100, providerReadinessPercent: 100 };
|
|
1921
|
+
}
|
|
1922
|
+
const repoSum = providerResults.reduce((sum, r) => sum + r.repoReadinessPercent, 0);
|
|
1923
|
+
const providerSum = providerResults.reduce((sum, r) => sum + r.providerReadinessPercent, 0);
|
|
1924
|
+
return {
|
|
1925
|
+
repoReadinessPercent: Math.round(repoSum / providerResults.length),
|
|
1926
|
+
providerReadinessPercent: Math.round(providerSum / providerResults.length)
|
|
1927
|
+
};
|
|
1928
|
+
}
|
|
1929
|
+
|
|
1930
|
+
// ../../src/station/verificationLayer/mergeIntoMissionGraph.ts
|
|
1931
|
+
var PROVIDER_WIRING_KEY = {
|
|
1932
|
+
vercel: "vercel-deployment",
|
|
1933
|
+
supabase: "supabase-database",
|
|
1934
|
+
stripe: "stripe-payments"
|
|
1935
|
+
};
|
|
1936
|
+
function mergeVerificationIntoMissionGraph(graph, layer) {
|
|
1937
|
+
const providerMissions = graph.areas.flatMap((area) => area.providerMissions);
|
|
1938
|
+
const missionByKey = new Map(providerMissions.map((mission) => [mission.key, mission]));
|
|
1939
|
+
for (const layerCheck of layer.checks) {
|
|
1940
|
+
if (layerCheck.evidenceSource === "repo") {
|
|
1941
|
+
continue;
|
|
1942
|
+
}
|
|
1943
|
+
const mission = resolveTargetMission(graph, missionByKey, layerCheck);
|
|
1944
|
+
if (!mission) {
|
|
1945
|
+
continue;
|
|
1946
|
+
}
|
|
1947
|
+
if (mission.checks.some((check) => check.verificationCheckId === layerCheck.id || check.id === missionRowId(layerCheck.id))) {
|
|
1948
|
+
continue;
|
|
1949
|
+
}
|
|
1950
|
+
mission.checks.push(verificationCheckToMissionCheck(layerCheck, mission));
|
|
1951
|
+
}
|
|
1952
|
+
recomputeMissionReadiness(providerMissions);
|
|
1953
|
+
const areas = rebuildAreas(providerMissions);
|
|
1954
|
+
return {
|
|
1955
|
+
...graph,
|
|
1956
|
+
areas,
|
|
1957
|
+
byArea: areas.reduce((acc, area) => {
|
|
1958
|
+
acc[area.key] = area;
|
|
1959
|
+
return acc;
|
|
1960
|
+
}, {}),
|
|
1961
|
+
byProvider: providerMissions.reduce((acc, mission) => {
|
|
1962
|
+
acc[mission.key] = mission;
|
|
1963
|
+
return acc;
|
|
1964
|
+
}, {}),
|
|
1965
|
+
verificationLayer: layer
|
|
1966
|
+
};
|
|
1967
|
+
}
|
|
1968
|
+
function resolveTargetMission(graph, missionByKey, layerCheck) {
|
|
1969
|
+
const wiringKey = PROVIDER_WIRING_KEY[layerCheck.provider];
|
|
1970
|
+
if (wiringKey) {
|
|
1971
|
+
return missionByKey.get(wiringKey) ?? graph.byProvider[wiringKey];
|
|
1972
|
+
}
|
|
1973
|
+
if (layerCheck.provider === "github") {
|
|
1974
|
+
return missionByKey.get("vitest-testing") ?? missionByKey.get("playwright-testing") ?? ensureGitHubMission(graph, missionByKey, layerCheck.area);
|
|
1975
|
+
}
|
|
1976
|
+
return void 0;
|
|
1977
|
+
}
|
|
1978
|
+
function ensureGitHubMission(graph, missionByKey, area) {
|
|
1979
|
+
const key = "vitest-testing";
|
|
1980
|
+
const existing = missionByKey.get(key);
|
|
1981
|
+
if (existing) {
|
|
1982
|
+
return existing;
|
|
1983
|
+
}
|
|
1984
|
+
const mission = {
|
|
1985
|
+
key,
|
|
1986
|
+
provider: "vitest",
|
|
1987
|
+
providerLabel: "GitHub Actions",
|
|
1988
|
+
area,
|
|
1989
|
+
promptSubject: "GitHub Actions CI",
|
|
1990
|
+
readinessPercent: 0,
|
|
1991
|
+
repoReadinessPercent: 100,
|
|
1992
|
+
providerReadinessPercent: 0,
|
|
1993
|
+
checks: []
|
|
1994
|
+
};
|
|
1995
|
+
missionByKey.set(key, mission);
|
|
1996
|
+
const areaEntry = graph.areas.find((entry) => entry.key === area);
|
|
1997
|
+
if (areaEntry) {
|
|
1998
|
+
areaEntry.providerMissions.push(mission);
|
|
1999
|
+
}
|
|
2000
|
+
return mission;
|
|
2001
|
+
}
|
|
2002
|
+
function missionRowId(verificationCheckId) {
|
|
2003
|
+
return `vl-${verificationCheckId}`;
|
|
2004
|
+
}
|
|
2005
|
+
function verificationCheckToMissionCheck(layerCheck, mission) {
|
|
2006
|
+
const evidenceClass = legacyEvidenceClassFor(layerCheck);
|
|
2007
|
+
const status = mapVerificationStatusToMissionStatus(layerCheck.status);
|
|
2008
|
+
return {
|
|
2009
|
+
id: missionRowId(layerCheck.id),
|
|
2010
|
+
label: layerCheck.title,
|
|
2011
|
+
providerKey: mission.key,
|
|
2012
|
+
providerLabel: mission.providerLabel,
|
|
2013
|
+
area: layerCheck.area,
|
|
2014
|
+
evidenceClass,
|
|
2015
|
+
status,
|
|
2016
|
+
evidence: [...layerCheck.repoSignals, ...layerCheck.providerSignals, ...layerCheck.evidenceRefs],
|
|
2017
|
+
promptHint: layerCheck.manualAction ?? layerCheck.description,
|
|
2018
|
+
evidenceSource: layerCheck.evidenceSource,
|
|
2019
|
+
verificationStatus: layerCheck.status,
|
|
2020
|
+
verificationCheckId: layerCheck.id
|
|
2021
|
+
};
|
|
2022
|
+
}
|
|
2023
|
+
function legacyEvidenceClassFor(layerCheck) {
|
|
2024
|
+
return mapEvidenceSourceToLegacyEvidenceClass(layerCheck.evidenceSource, layerCheck.status);
|
|
2025
|
+
}
|
|
2026
|
+
function recomputeMissionReadiness(missions) {
|
|
2027
|
+
for (const mission of missions) {
|
|
2028
|
+
mission.repoReadinessPercent = readinessPercentForRepoChecks(mission.checks);
|
|
2029
|
+
mission.providerReadinessPercent = readinessPercentForProviderChecks(mission.checks);
|
|
2030
|
+
mission.readinessPercent = mission.repoReadinessPercent;
|
|
2031
|
+
}
|
|
2032
|
+
}
|
|
2033
|
+
function readinessPercentForRepoChecks(checks) {
|
|
2034
|
+
const repoChecks = checks.filter(isRepoLayerMissionCheck);
|
|
2035
|
+
if (repoChecks.length === 0) {
|
|
2036
|
+
return 100;
|
|
2037
|
+
}
|
|
2038
|
+
const verified = repoChecks.filter((check) => isVerifiedMissionCheck(check)).length;
|
|
2039
|
+
return Math.round(verified / repoChecks.length * 100);
|
|
2040
|
+
}
|
|
2041
|
+
function readinessPercentForProviderChecks(checks) {
|
|
2042
|
+
const providerChecks = checks.filter(isProviderLayerMissionCheck);
|
|
2043
|
+
if (providerChecks.length === 0) {
|
|
2044
|
+
return 100;
|
|
2045
|
+
}
|
|
2046
|
+
const verified = providerChecks.filter((check) => isVerifiedMissionCheck(check)).length;
|
|
2047
|
+
return Math.round(verified / providerChecks.length * 100);
|
|
2048
|
+
}
|
|
2049
|
+
function isRepoLayerMissionCheck(check) {
|
|
2050
|
+
if (check.evidenceSource === "provider" || check.evidenceSource === "mcp" || check.evidenceSource === "manual") {
|
|
2051
|
+
return false;
|
|
2052
|
+
}
|
|
2053
|
+
if (check.evidenceSource === "repo") {
|
|
2054
|
+
return true;
|
|
2055
|
+
}
|
|
2056
|
+
return check.evidenceClass === "repo-verified" || check.evidenceClass === "missing-repo-fix";
|
|
2057
|
+
}
|
|
2058
|
+
function isProviderLayerMissionCheck(check) {
|
|
2059
|
+
if (check.evidenceSource === "provider" || check.evidenceSource === "mcp") {
|
|
2060
|
+
return true;
|
|
2061
|
+
}
|
|
2062
|
+
return check.evidenceClass === "mcp-verifier";
|
|
2063
|
+
}
|
|
2064
|
+
function isVerifiedMissionCheck(check) {
|
|
2065
|
+
if (check.verificationStatus) {
|
|
2066
|
+
return check.verificationStatus === "verified";
|
|
2067
|
+
}
|
|
2068
|
+
return check.status === "passed" || check.status === "user-confirmed";
|
|
2069
|
+
}
|
|
2070
|
+
function rebuildAreas(providerMissions) {
|
|
2071
|
+
const byArea = /* @__PURE__ */ new Map();
|
|
2072
|
+
for (const mission of providerMissions) {
|
|
2073
|
+
const list = byArea.get(mission.area) ?? [];
|
|
2074
|
+
list.push(mission);
|
|
2075
|
+
byArea.set(mission.area, list);
|
|
2076
|
+
}
|
|
2077
|
+
const AREA_LABELS2 = {
|
|
2078
|
+
appFlow: "App Flow",
|
|
2079
|
+
frontend: "Frontend",
|
|
2080
|
+
backend: "Backend / API",
|
|
2081
|
+
database: "Database",
|
|
2082
|
+
auth: "Auth",
|
|
2083
|
+
payments: "Payments",
|
|
2084
|
+
deployment: "Deployment",
|
|
2085
|
+
monitoring: "Monitoring",
|
|
2086
|
+
security: "Security",
|
|
2087
|
+
testing: "Testing",
|
|
2088
|
+
landing: "Landing / Onboarding",
|
|
2089
|
+
errorHandling: "Error Handling"
|
|
2090
|
+
};
|
|
2091
|
+
return [...byArea.entries()].map(([key, missions]) => {
|
|
2092
|
+
const repoChecks = missions.flatMap((m2) => m2.checks).filter(isRepoLayerMissionCheck);
|
|
2093
|
+
const providerChecks = missions.flatMap((m2) => m2.checks).filter(isProviderLayerMissionCheck);
|
|
2094
|
+
const repoReadinessPercent = percentVerified(repoChecks);
|
|
2095
|
+
const providerReadinessPercent = percentVerified(providerChecks);
|
|
2096
|
+
const criticalCount = missions.flatMap((m2) => m2.checks).filter(
|
|
2097
|
+
(check) => isProviderLayerMissionCheck(check) && (check.verificationStatus === "missing" || check.status === "missing" || check.status === "failed")
|
|
2098
|
+
).length;
|
|
2099
|
+
return {
|
|
2100
|
+
key,
|
|
2101
|
+
label: AREA_LABELS2[key],
|
|
2102
|
+
readinessPercent: repoReadinessPercent,
|
|
2103
|
+
repoReadinessPercent,
|
|
2104
|
+
providerReadinessPercent,
|
|
2105
|
+
criticalCount,
|
|
2106
|
+
providerMissions: missions
|
|
2107
|
+
};
|
|
2108
|
+
});
|
|
2109
|
+
}
|
|
2110
|
+
function percentVerified(checks) {
|
|
2111
|
+
if (checks.length === 0) {
|
|
2112
|
+
return 100;
|
|
2113
|
+
}
|
|
2114
|
+
const verified = checks.filter((check) => isVerifiedMissionCheck(check)).length;
|
|
2115
|
+
return Math.round(verified / checks.length * 100);
|
|
2116
|
+
}
|
|
2117
|
+
|
|
1776
2118
|
// ../../src/station/missionGraph.ts
|
|
1777
2119
|
var AREA_LABELS = {
|
|
1778
2120
|
appFlow: "App Flow",
|
|
@@ -1802,13 +2144,14 @@ function buildMissionGraph(input) {
|
|
|
1802
2144
|
acc[area.key] = area;
|
|
1803
2145
|
return acc;
|
|
1804
2146
|
}, {});
|
|
1805
|
-
|
|
2147
|
+
const graph = {
|
|
1806
2148
|
areas,
|
|
1807
2149
|
byArea,
|
|
1808
2150
|
byProvider,
|
|
1809
2151
|
repositoryEvidence: input.repositoryEvidence,
|
|
1810
2152
|
...input.staticInfrastructureFlowGraph ? { staticInfrastructureFlowGraph: input.staticInfrastructureFlowGraph } : {}
|
|
1811
2153
|
};
|
|
2154
|
+
return input.verificationLayer ? mergeVerificationIntoMissionGraph(graph, input.verificationLayer) : graph;
|
|
1812
2155
|
}
|
|
1813
2156
|
function toProviderMission(summary) {
|
|
1814
2157
|
const checks = summary.items.map((item3) => toMissionCheck(summary, item3));
|
|
@@ -1832,6 +2175,8 @@ function toProviderMission(summary) {
|
|
|
1832
2175
|
area: summary.area,
|
|
1833
2176
|
promptSubject: summary.promptSubject,
|
|
1834
2177
|
readinessPercent: summary.readinessPercent,
|
|
2178
|
+
repoReadinessPercent: summary.readinessPercent,
|
|
2179
|
+
providerReadinessPercent: checks.some((check) => check.evidenceClass === "mcp-verifier") ? 0 : 100,
|
|
1835
2180
|
checks
|
|
1836
2181
|
};
|
|
1837
2182
|
}
|
|
@@ -1969,6 +2314,10 @@ function buildAreas(providerMissions) {
|
|
|
1969
2314
|
key,
|
|
1970
2315
|
label: AREA_LABELS[key],
|
|
1971
2316
|
readinessPercent: Math.round(passed / Math.max(actionableChecks.length, 1) * 100),
|
|
2317
|
+
repoReadinessPercent: Math.round(passed / Math.max(actionableChecks.length, 1) * 100),
|
|
2318
|
+
providerReadinessPercent: missions.some(
|
|
2319
|
+
(mission) => mission.checks.some((check) => check.evidenceClass === "mcp-verifier")
|
|
2320
|
+
) ? 0 : 100,
|
|
1972
2321
|
criticalCount: missing,
|
|
1973
2322
|
providerMissions: missions
|
|
1974
2323
|
};
|
|
@@ -2130,6 +2479,17 @@ var PROVIDER_RULES = [
|
|
|
2130
2479
|
imports: ["@supabase/supabase-js", "@supabase/ssr"],
|
|
2131
2480
|
docs: ["supabase"]
|
|
2132
2481
|
},
|
|
2482
|
+
{
|
|
2483
|
+
area: "database",
|
|
2484
|
+
provider: "firebase",
|
|
2485
|
+
label: "Firebase",
|
|
2486
|
+
packages: [/^firebase$/, /^firebase-admin$/, /@firebase\//],
|
|
2487
|
+
env: ["FIREBASE_PROJECT_ID", "NEXT_PUBLIC_FIREBASE_PROJECT_ID", "VITE_FIREBASE_PROJECT_ID", "GOOGLE_APPLICATION_CREDENTIALS"],
|
|
2488
|
+
paths: [/firebase/, /firestore/],
|
|
2489
|
+
imports: ["firebase/app", "firebase/firestore", "firebase-admin"],
|
|
2490
|
+
content: [{ pattern: /getFirestore\s*\(|collection\s*\(|firebase-admin/i, signal: "code: Firebase/Firestore usage" }],
|
|
2491
|
+
docs: ["firebase", "firestore"]
|
|
2492
|
+
},
|
|
2133
2493
|
{
|
|
2134
2494
|
area: "database",
|
|
2135
2495
|
provider: "neon",
|
|
@@ -2182,10 +2542,33 @@ var PROVIDER_RULES = [
|
|
|
2182
2542
|
label: "Auth.js",
|
|
2183
2543
|
packages: [/^next-auth$/, /^@auth\//],
|
|
2184
2544
|
env: ["AUTH_SECRET", "NEXTAUTH_SECRET", "NEXTAUTH_URL"],
|
|
2185
|
-
paths: [/
|
|
2545
|
+
paths: [/api\/auth\//],
|
|
2186
2546
|
imports: ["next-auth", "@auth/core", "@auth/nextjs"],
|
|
2547
|
+
content: [{ pattern: /NextAuth\s*\(|getServerSession|useSession\s*\(/i, signal: "code: Auth.js session or route handler" }],
|
|
2187
2548
|
docs: ["auth.js", "nextauth", "next-auth"]
|
|
2188
2549
|
},
|
|
2550
|
+
{
|
|
2551
|
+
area: "auth",
|
|
2552
|
+
provider: "auth0",
|
|
2553
|
+
label: "Auth0",
|
|
2554
|
+
packages: [/@auth0\//],
|
|
2555
|
+
env: ["AUTH0_SECRET", "AUTH0_ISSUER_BASE_URL", "AUTH0_CLIENT_ID", "AUTH0_CLIENT_SECRET", "AUTH0_DOMAIN"],
|
|
2556
|
+
paths: [/api\/auth/, /auth0/],
|
|
2557
|
+
imports: ["@auth0/nextjs-auth0", "@auth0/auth0-react", "express-openid-connect"],
|
|
2558
|
+
content: [{ pattern: /handleAuth|withPageAuthRequired|withApiAuthRequired|getSession/i, signal: "code: Auth0 session or route protection" }],
|
|
2559
|
+
docs: ["auth0"]
|
|
2560
|
+
},
|
|
2561
|
+
{
|
|
2562
|
+
area: "auth",
|
|
2563
|
+
provider: "better-auth",
|
|
2564
|
+
label: "Better Auth",
|
|
2565
|
+
packages: [/^better-auth$/],
|
|
2566
|
+
env: ["BETTER_AUTH_SECRET", "BETTER_AUTH_URL", "AUTH_SECRET", "DATABASE_URL"],
|
|
2567
|
+
paths: [/better-auth/, /auth\.[jt]s$/],
|
|
2568
|
+
imports: ["better-auth"],
|
|
2569
|
+
content: [{ pattern: /betterAuth\s*\(|auth\.api|auth\.handler/i, signal: "code: Better Auth config" }],
|
|
2570
|
+
docs: ["better auth", "better-auth"]
|
|
2571
|
+
},
|
|
2189
2572
|
{
|
|
2190
2573
|
area: "payments",
|
|
2191
2574
|
provider: "stripe",
|
|
@@ -2206,6 +2589,28 @@ var PROVIDER_RULES = [
|
|
|
2206
2589
|
imports: ["@paddle/paddle-js", "@paddle/paddle-node-sdk"],
|
|
2207
2590
|
docs: ["paddle"]
|
|
2208
2591
|
},
|
|
2592
|
+
{
|
|
2593
|
+
area: "payments",
|
|
2594
|
+
provider: "polar",
|
|
2595
|
+
label: "Polar",
|
|
2596
|
+
packages: [/@polar-sh\//],
|
|
2597
|
+
env: ["POLAR_ACCESS_TOKEN", "POLAR_WEBHOOK_SECRET", "POLAR_PRODUCT_ID", "POLAR_PRO_PRODUCT_ID"],
|
|
2598
|
+
paths: [/polar.*webhook/, /webhook.*polar/, /polar.*checkout/, /checkout.*polar/],
|
|
2599
|
+
imports: ["@polar-sh/sdk"],
|
|
2600
|
+
content: [{ pattern: /api\.polar\.sh|polar.*checkout|createCheckoutSession/i, signal: "code: Polar checkout or API usage" }],
|
|
2601
|
+
docs: ["polar"]
|
|
2602
|
+
},
|
|
2603
|
+
{
|
|
2604
|
+
area: "payments",
|
|
2605
|
+
provider: "lemon-squeezy",
|
|
2606
|
+
label: "Lemon Squeezy",
|
|
2607
|
+
packages: [/@lemonsqueezy\//, /^lemonsqueezy\.ts$/],
|
|
2608
|
+
env: ["LEMON_SQUEEZY_API_KEY", "LEMONSQUEEZY_API_KEY", "LEMON_SQUEEZY_WEBHOOK_SECRET", "LEMON_SQUEEZY_STORE_ID", "LEMON_SQUEEZY_VARIANT_ID"],
|
|
2609
|
+
paths: [/lemon.*webhook/, /webhook.*lemon/, /lemon.*checkout/, /checkout.*lemon/, /lemonsqueezy/],
|
|
2610
|
+
imports: ["@lemonsqueezy/lemonsqueezy.js", "lemonsqueezy.ts"],
|
|
2611
|
+
content: [{ pattern: /api\.lemonsqueezy\.com|lemon.*checkout|checkout_url|variant_id/i, signal: "code: Lemon Squeezy checkout or API usage" }],
|
|
2612
|
+
docs: ["lemon squeezy", "lemonsqueezy"]
|
|
2613
|
+
},
|
|
2209
2614
|
{
|
|
2210
2615
|
area: "deployment",
|
|
2211
2616
|
provider: "vercel",
|
|
@@ -2215,6 +2620,55 @@ var PROVIDER_RULES = [
|
|
|
2215
2620
|
paths: [/vercel\.json$/],
|
|
2216
2621
|
docs: ["vercel"]
|
|
2217
2622
|
},
|
|
2623
|
+
{
|
|
2624
|
+
area: "deployment",
|
|
2625
|
+
provider: "netlify",
|
|
2626
|
+
label: "Netlify",
|
|
2627
|
+
packages: [/@netlify\//],
|
|
2628
|
+
env: ["NETLIFY_AUTH_TOKEN", "NETLIFY_SITE_ID"],
|
|
2629
|
+
paths: [/netlify\.toml$/, /\.netlify\//, /(^|\/)_redirects$/],
|
|
2630
|
+
imports: ["@netlify/functions"],
|
|
2631
|
+
docs: ["netlify"]
|
|
2632
|
+
},
|
|
2633
|
+
{
|
|
2634
|
+
area: "deployment",
|
|
2635
|
+
provider: "render",
|
|
2636
|
+
label: "Render",
|
|
2637
|
+
env: ["RENDER_API_KEY", "RENDER_SERVICE_ID"],
|
|
2638
|
+
paths: [/render\.ya?ml$/, /(^|\/)\.render\//],
|
|
2639
|
+
content: [{ pattern: /render\.yaml|render\.yml|render deploy|render service/i, signal: "code: Render deployment config" }],
|
|
2640
|
+
docs: ["render.com", "render deployment"]
|
|
2641
|
+
},
|
|
2642
|
+
{
|
|
2643
|
+
area: "deployment",
|
|
2644
|
+
provider: "railway",
|
|
2645
|
+
label: "Railway",
|
|
2646
|
+
env: ["RAILWAY_TOKEN", "RAILWAY_PROJECT_ID", "RAILWAY_SERVICE_ID"],
|
|
2647
|
+
paths: [/railway\.json$/, /nixpacks\.toml$/, /(^|\/)Procfile$/],
|
|
2648
|
+
content: [{ pattern: /railway up|railway deploy|nixpacks|railway\.json/i, signal: "code: Railway deployment config" }],
|
|
2649
|
+
docs: ["railway"]
|
|
2650
|
+
},
|
|
2651
|
+
{
|
|
2652
|
+
area: "deployment",
|
|
2653
|
+
provider: "cloudflare",
|
|
2654
|
+
label: "Cloudflare",
|
|
2655
|
+
packages: [/^wrangler$/, /@cloudflare\//],
|
|
2656
|
+
env: ["CLOUDFLARE_API_TOKEN", "CLOUDFLARE_ACCOUNT_ID", "CF_PAGES"],
|
|
2657
|
+
paths: [/wrangler\.toml$/, /wrangler\.json$/, /(^|\/)_headers$/, /(^|\/)_redirects$/],
|
|
2658
|
+
imports: ["@cloudflare/workers-types"],
|
|
2659
|
+
content: [{ pattern: /compatibility_date|pages_build_output_dir|cloudflare pages|cloudflare workers/i, signal: "code: Cloudflare Pages/Workers config" }],
|
|
2660
|
+
docs: ["cloudflare", "workers", "pages"]
|
|
2661
|
+
},
|
|
2662
|
+
{
|
|
2663
|
+
area: "deployment",
|
|
2664
|
+
provider: "aws",
|
|
2665
|
+
label: "AWS",
|
|
2666
|
+
packages: [/@aws-sdk\//, /^aws-cdk-lib$/, /^serverless$/],
|
|
2667
|
+
env: ["AWS_REGION", "AWS_ACCESS_KEY_ID"],
|
|
2668
|
+
paths: [/serverless\.ya?ml$/, /template\.ya?ml$/, /cdk\.json$/, /amplify\//],
|
|
2669
|
+
imports: ["aws-sdk", "@aws-sdk/client"],
|
|
2670
|
+
docs: ["aws", "amplify", "serverless"]
|
|
2671
|
+
},
|
|
2218
2672
|
{
|
|
2219
2673
|
area: "monitoring",
|
|
2220
2674
|
provider: "sentry",
|
|
@@ -3894,8 +4348,11 @@ function analyzeStackWiring(scan) {
|
|
|
3894
4348
|
analyzeSecretsHygiene(ctx),
|
|
3895
4349
|
analyzeClerkAuth(ctx),
|
|
3896
4350
|
analyzeAuthJsAuth(ctx),
|
|
4351
|
+
analyzeAuth0Auth(ctx),
|
|
4352
|
+
analyzeBetterAuth(ctx),
|
|
3897
4353
|
analyzeSupabaseAuth(ctx),
|
|
3898
4354
|
supabaseDatabase,
|
|
4355
|
+
analyzeFirebaseDatabase(ctx),
|
|
3899
4356
|
analyzeNeonDatabase(ctx),
|
|
3900
4357
|
analyzeTursoDatabase(ctx),
|
|
3901
4358
|
analyzeMongoDatabase(ctx),
|
|
@@ -3903,8 +4360,12 @@ function analyzeStackWiring(scan) {
|
|
|
3903
4360
|
analyzeStripePayments(ctx),
|
|
3904
4361
|
analyzePaddlePayments(ctx),
|
|
3905
4362
|
analyzePolarPayments(ctx),
|
|
4363
|
+
analyzeLemonSqueezyPayments(ctx),
|
|
3906
4364
|
analyzeVercelDeployment(ctx),
|
|
3907
4365
|
analyzeNetlifyDeployment(ctx),
|
|
4366
|
+
analyzeRenderDeployment(ctx),
|
|
4367
|
+
analyzeRailwayDeployment(ctx),
|
|
4368
|
+
analyzeCloudflareDeployment(ctx),
|
|
3908
4369
|
analyzeAwsDeployment(ctx),
|
|
3909
4370
|
analyzeSupabaseLanding(ctx),
|
|
3910
4371
|
analyzePostHogMonitoring(ctx),
|
|
@@ -4175,6 +4636,44 @@ function analyzeAuthJsAuth(ctx) {
|
|
|
4175
4636
|
]
|
|
4176
4637
|
});
|
|
4177
4638
|
}
|
|
4639
|
+
function analyzeAuth0Auth(ctx) {
|
|
4640
|
+
return summarize({
|
|
4641
|
+
key: "auth0-auth",
|
|
4642
|
+
provider: "auth0",
|
|
4643
|
+
providerLabel: "Auth0",
|
|
4644
|
+
area: "auth",
|
|
4645
|
+
areaLabel: "Auth",
|
|
4646
|
+
promptSubject: "Auth0 auth",
|
|
4647
|
+
items: [
|
|
4648
|
+
item2("package-installed", "Auth0 package installed", hasPackage2(ctx, [/@auth0\//]), packageEvidence2(ctx, [/@auth0\//]), "Install the Auth0 package that matches this framework."),
|
|
4649
|
+
item2("env-names-documented", "Auth0 env names documented", /auth0_secret|auth0_issuer_base_url|auth0_client_id|auth0_client_secret|auth0_domain/i.test(ctx.contentBlob), envEvidence2(ctx, [/auth0_secret/i, /auth0_issuer_base_url/i, /auth0_client_id/i, /auth0_client_secret/i, /auth0_domain/i]), "Document Auth0 issuer/domain, client ID, client secret, and app secret env names in safe examples."),
|
|
4650
|
+
item2("auth-route-found", "Auth0 callback or auth route found", /api\/auth|auth0|handleauth|callback/i.test(ctx.pathBlob + "\n" + ctx.contentBlob), pathEvidence2(ctx, /api\/auth|callback/i).concat(fileEvidence(ctx, /auth0|handleauth|callback/i)), "Expose the Auth0 callback/login/logout routes required by this framework."),
|
|
4651
|
+
item2("session-usage-found", "Auth0 session usage found", /getsession|withapirequiredauth|withpagerequiredauth|useuser\s*\(/i.test(ctx.contentBlob), fileEvidence(ctx, /getsession|withapirequiredauth|withpagerequiredauth|useuser\s*\(/i), "Use Auth0 session checks around authenticated app routes and API handlers."),
|
|
4652
|
+
item2("route-protection-found", "Protected route evidence found", /withpagerequiredauth|withapirequiredauth|middleware|protected|requires?auth/i.test(ctx.contentBlob + "\n" + ctx.pathBlob), fileEvidence(ctx, /withpagerequiredauth|withapirequiredauth|middleware|protected|requires?auth/i), "Protect private routes with Auth0 middleware or server session guards."),
|
|
4653
|
+
secretSafetyItem(ctx, "secret-not-exposed", "Auth0 secrets not exposed to frontend", /auth0_client_secret|auth0_secret/i, "Keep Auth0 secrets in server-only env and never expose them through public env variables."),
|
|
4654
|
+
manualItem("production-dashboard-checked", "Production Auth0 app checked", "Confirm callback URLs, logout URLs, allowed origins, social connections, MFA, and production domain in Auth0.")
|
|
4655
|
+
]
|
|
4656
|
+
});
|
|
4657
|
+
}
|
|
4658
|
+
function analyzeBetterAuth(ctx) {
|
|
4659
|
+
return summarize({
|
|
4660
|
+
key: "better-auth-auth",
|
|
4661
|
+
provider: "better-auth",
|
|
4662
|
+
providerLabel: "Better Auth",
|
|
4663
|
+
area: "auth",
|
|
4664
|
+
areaLabel: "Auth",
|
|
4665
|
+
promptSubject: "Better Auth",
|
|
4666
|
+
items: [
|
|
4667
|
+
item2("package-installed", "Better Auth package installed", hasPackage2(ctx, [/^better-auth$/]), packageEvidence2(ctx, [/^better-auth$/]), "Install Better Auth for this app framework."),
|
|
4668
|
+
item2("env-names-documented", "Better Auth env names documented", /better_auth_secret|better_auth_url|auth_secret|database_url/i.test(ctx.contentBlob), envEvidence2(ctx, [/better_auth_secret/i, /better_auth_url/i, /auth_secret/i, /database_url/i]), "Document BETTER_AUTH_SECRET, BETTER_AUTH_URL, and database env names in safe examples."),
|
|
4669
|
+
item2("auth-config-found", "Better Auth config found", /betterauth\s*\(|better-auth|auth\.api|auth\.handler/i.test(ctx.contentBlob + "\n" + ctx.pathBlob), fileEvidence(ctx, /betterauth\s*\(|better-auth|auth\.api|auth\.handler/i), "Add a central Better Auth config and route handler."),
|
|
4670
|
+
item2("database-adapter-found", "Auth database adapter or schema found", /database|adapter|drizzle|prisma|schema|migration/i.test(ctx.contentBlob + "\n" + ctx.pathBlob), fileEvidence(ctx, /adapter|drizzle|prisma|schema|migration/i), "Persist Better Auth users/sessions through a real database adapter and migrations."),
|
|
4671
|
+
item2("session-usage-found", "Session usage found", /getsession|usesession|auth\.api\.getsession|session/i.test(ctx.contentBlob), fileEvidence(ctx, /getsession|usesession|auth\.api\.getsession|session/i), "Use Better Auth session reads around private product routes."),
|
|
4672
|
+
secretSafetyItem(ctx, "secret-not-exposed", "Better Auth secret not exposed to frontend", /better_auth_secret|auth_secret/i, "Keep Better Auth secrets in server-only env and never expose them through public env variables."),
|
|
4673
|
+
manualItem("production-auth-checked", "Production auth settings checked", "Confirm production base URL, trusted origins, OAuth apps, email settings, and session policy.")
|
|
4674
|
+
]
|
|
4675
|
+
});
|
|
4676
|
+
}
|
|
4178
4677
|
function analyzeSupabaseAuth(ctx) {
|
|
4179
4678
|
return summarize({
|
|
4180
4679
|
key: "supabase-auth",
|
|
@@ -4252,6 +4751,38 @@ function analyzePolarPayments(ctx) {
|
|
|
4252
4751
|
]
|
|
4253
4752
|
});
|
|
4254
4753
|
}
|
|
4754
|
+
function analyzeLemonSqueezyPayments(ctx) {
|
|
4755
|
+
return summarize({
|
|
4756
|
+
key: "lemon-squeezy-payments",
|
|
4757
|
+
provider: "lemon-squeezy",
|
|
4758
|
+
providerLabel: "Lemon Squeezy",
|
|
4759
|
+
area: "payments",
|
|
4760
|
+
areaLabel: "Payments",
|
|
4761
|
+
promptSubject: "Lemon Squeezy payments",
|
|
4762
|
+
items: [
|
|
4763
|
+
item2("api-client-found", "Lemon Squeezy SDK or API client found", hasPackage2(ctx, [/@lemonsqueezy\//, /^lemonsqueezy\.ts$/]) || /api\.lemonsqueezy\.com|lemonsqueezy|lemon_squeezy/i.test(ctx.contentBlob), packageEvidence2(ctx, [/@lemonsqueezy\//, /^lemonsqueezy\.ts$/]).concat(fileEvidence(ctx, /api\.lemonsqueezy\.com|lemonsqueezy|lemon_squeezy/i)), "Add a server-side Lemon Squeezy API client for checkout and subscription state."),
|
|
4764
|
+
item2("env-names-documented", "Lemon Squeezy env names documented", /lemon_squeezy_api_key|lemonsqueezy_api_key|lemon_squeezy_webhook_secret|lemon_squeezy_store_id|lemon_squeezy_variant_id/i.test(ctx.contentBlob), envEvidence2(ctx, [/lemon_squeezy_api_key/i, /lemonsqueezy_api_key/i, /lemon_squeezy_webhook_secret/i, /lemon_squeezy_store_id/i, /lemon_squeezy_variant_id/i]), "Document Lemon Squeezy API key, webhook secret, store ID, and variant/product ID env names."),
|
|
4765
|
+
item2("checkout-found", "Lemon Squeezy checkout flow found", /lemon.*checkout|checkout.*lemon|checkout_url|variant_id|store_id/i.test(ctx.contentBlob + "\n" + ctx.pathBlob), fileEvidence(ctx, /lemon.*checkout|checkout.*lemon|checkout_url|variant_id|store_id/i), "Create a checkout URL/session flow for paid plans."),
|
|
4766
|
+
item2("webhook-route-found", "Lemon Squeezy webhook route found", /lemon.*webhook|webhook.*lemon|lemonsqueezy.*webhook/i.test(ctx.pathBlob + "\n" + ctx.contentBlob), pathEvidence2(ctx, /lemon.*webhook|webhook.*lemon|lemonsqueezy.*webhook/i).concat(fileEvidence(ctx, /lemonsqueezy.*webhook|lemon.*webhook/i)), "Add a Lemon Squeezy webhook route for order/subscription lifecycle events."),
|
|
4767
|
+
item2("webhook-signature-found", "Lemon Squeezy webhook verification found", /x-signature|lemon_squeezy_webhook_secret|verify.*lemon|webhook.*signature/i.test(ctx.contentBlob), fileEvidence(ctx, /x-signature|lemon_squeezy_webhook_secret|verify.*lemon|webhook.*signature/i), "Verify Lemon Squeezy webhook signatures before processing billing events."),
|
|
4768
|
+
secretSafetyItem(ctx, "secret-not-exposed", "Lemon Squeezy secret not exposed to frontend", /lemon_squeezy_api_key|lemonsqueezy_api_key|lemon_squeezy_webhook_secret/i, "Move Lemon Squeezy API keys and webhook secrets to server-only code."),
|
|
4769
|
+
manualItem("production-dashboard-checked", "Production Lemon Squeezy store checked", "Confirm products, variants, tax/merchant settings, license keys if used, and webhook URL in Lemon Squeezy.")
|
|
4770
|
+
]
|
|
4771
|
+
});
|
|
4772
|
+
}
|
|
4773
|
+
function analyzeFirebaseDatabase(ctx) {
|
|
4774
|
+
return databaseSummary(ctx, {
|
|
4775
|
+
key: "firebase-database",
|
|
4776
|
+
provider: "firebase",
|
|
4777
|
+
providerLabel: "Firebase",
|
|
4778
|
+
promptSubject: "Firebase database",
|
|
4779
|
+
packagePatterns: [/^firebase$/, /^firebase-admin$/, /@firebase\//],
|
|
4780
|
+
envPatterns: [/firebase_project_id/i, /firestore_database_url/i, /next_public_firebase/i, /vite_firebase/i, /google_application_credentials/i],
|
|
4781
|
+
usagePatterns: [/firebase\/firestore/i, /getfirestore\s*\(|collection\s*\(|doc\s*\(|firebase-admin/i],
|
|
4782
|
+
manualLabel: "Production Firebase project checked",
|
|
4783
|
+
manualHint: "Confirm production project, Firestore rules, indexes, backups, service account scope, and billing limits in Firebase."
|
|
4784
|
+
});
|
|
4785
|
+
}
|
|
4255
4786
|
function analyzeNeonDatabase(ctx) {
|
|
4256
4787
|
return databaseSummary(ctx, {
|
|
4257
4788
|
key: "neon-database",
|
|
@@ -4344,6 +4875,72 @@ function analyzeNetlifyDeployment(ctx) {
|
|
|
4344
4875
|
]
|
|
4345
4876
|
});
|
|
4346
4877
|
}
|
|
4878
|
+
function analyzeRenderDeployment(ctx) {
|
|
4879
|
+
const base = deploymentSummary(ctx, {
|
|
4880
|
+
key: "render-deployment",
|
|
4881
|
+
provider: "render",
|
|
4882
|
+
providerLabel: "Render",
|
|
4883
|
+
promptSubject: "Render deployment",
|
|
4884
|
+
packagePatterns: [],
|
|
4885
|
+
configPatterns: [/render\.ya?ml/i, /(^|\n|\/)\.render\//i],
|
|
4886
|
+
envPatterns: [/render_api_key/i, /render_service_id/i],
|
|
4887
|
+
manualLabel: "Production Render service checked",
|
|
4888
|
+
manualHint: "Confirm service type, build/start commands, env groups, health checks, custom domain, autoscaling, and rollback strategy in Render."
|
|
4889
|
+
});
|
|
4890
|
+
const servicePattern = /render\.ya?ml|healthcheckpath|startcommand|buildcommand|envvars|services:\s*|type:\s*web/i;
|
|
4891
|
+
return summarize({
|
|
4892
|
+
...base,
|
|
4893
|
+
items: [
|
|
4894
|
+
...base.items.filter((entry) => entry.status !== "manual"),
|
|
4895
|
+
item2("service-config-found", "Render service config found", servicePattern.test(ctx.contentBlob + "\n" + ctx.pathBlob), fileEvidence(ctx, servicePattern).concat(pathEvidence2(ctx, servicePattern)), "Add render.yaml or deployment docs covering service type, commands, health checks, and env groups."),
|
|
4896
|
+
...base.items.filter((entry) => entry.status === "manual")
|
|
4897
|
+
]
|
|
4898
|
+
});
|
|
4899
|
+
}
|
|
4900
|
+
function analyzeRailwayDeployment(ctx) {
|
|
4901
|
+
const base = deploymentSummary(ctx, {
|
|
4902
|
+
key: "railway-deployment",
|
|
4903
|
+
provider: "railway",
|
|
4904
|
+
providerLabel: "Railway",
|
|
4905
|
+
promptSubject: "Railway deployment",
|
|
4906
|
+
packagePatterns: [],
|
|
4907
|
+
configPatterns: [/railway\.json/i, /nixpacks\.toml/i, /(^|\n|\/)Procfile\b/i],
|
|
4908
|
+
envPatterns: [/railway_token/i, /railway_project_id/i, /railway_service_id/i],
|
|
4909
|
+
manualLabel: "Production Railway service checked",
|
|
4910
|
+
manualHint: "Confirm service, variables, start command, volumes/databases, custom domain, deploy policy, and logs in Railway."
|
|
4911
|
+
});
|
|
4912
|
+
const servicePattern = /railway\.json|nixpacks\.toml|railway up|railway deploy|Procfile|startCommand|healthcheck/i;
|
|
4913
|
+
return summarize({
|
|
4914
|
+
...base,
|
|
4915
|
+
items: [
|
|
4916
|
+
...base.items.filter((entry) => entry.status !== "manual"),
|
|
4917
|
+
item2("service-config-found", "Railway service config found", servicePattern.test(ctx.contentBlob + "\n" + ctx.pathBlob), fileEvidence(ctx, servicePattern).concat(pathEvidence2(ctx, servicePattern)), "Add Railway/Nixpacks/Procfile config or deployment docs for commands, health checks, and service wiring."),
|
|
4918
|
+
...base.items.filter((entry) => entry.status === "manual")
|
|
4919
|
+
]
|
|
4920
|
+
});
|
|
4921
|
+
}
|
|
4922
|
+
function analyzeCloudflareDeployment(ctx) {
|
|
4923
|
+
const base = deploymentSummary(ctx, {
|
|
4924
|
+
key: "cloudflare-deployment",
|
|
4925
|
+
provider: "cloudflare",
|
|
4926
|
+
providerLabel: "Cloudflare",
|
|
4927
|
+
promptSubject: "Cloudflare deployment",
|
|
4928
|
+
packagePatterns: [/^wrangler$/, /@cloudflare\//],
|
|
4929
|
+
configPatterns: [/wrangler\.toml/i, /wrangler\.json/i, /_headers\b/i, /_redirects\b/i],
|
|
4930
|
+
envPatterns: [/cloudflare_api_token/i, /cloudflare_account_id/i, /cf_pages/i],
|
|
4931
|
+
manualLabel: "Production Cloudflare project checked",
|
|
4932
|
+
manualHint: "Confirm Pages/Workers project, DNS, routes, compatibility date, env vars/secrets, cache rules, and rollback settings in Cloudflare."
|
|
4933
|
+
});
|
|
4934
|
+
const edgePattern = /wrangler\.toml|wrangler\.json|compatibility_date|pages_build_output_dir|workers_dev|routes?\s*=|cloudflare pages|cloudflare workers/i;
|
|
4935
|
+
return summarize({
|
|
4936
|
+
...base,
|
|
4937
|
+
items: [
|
|
4938
|
+
...base.items.filter((entry) => entry.status !== "manual"),
|
|
4939
|
+
item2("edge-config-found", "Cloudflare Pages or Workers config found", edgePattern.test(ctx.contentBlob + "\n" + ctx.pathBlob), fileEvidence(ctx, edgePattern).concat(pathEvidence2(ctx, edgePattern)), "Add Wrangler/Pages config for build output, compatibility date, routes, and edge runtime behavior."),
|
|
4940
|
+
...base.items.filter((entry) => entry.status === "manual")
|
|
4941
|
+
]
|
|
4942
|
+
});
|
|
4943
|
+
}
|
|
4347
4944
|
function analyzeAwsDeployment(ctx) {
|
|
4348
4945
|
const base = deploymentSummary(ctx, {
|
|
4349
4946
|
key: "aws-deployment",
|
|
@@ -5189,48 +5786,758 @@ function providerLabel2(provider2) {
|
|
|
5189
5786
|
);
|
|
5190
5787
|
}
|
|
5191
5788
|
|
|
5192
|
-
// ../../src/station/
|
|
5193
|
-
|
|
5194
|
-
|
|
5789
|
+
// ../../src/station/verificationLayer/shared/connection.ts
|
|
5790
|
+
var MCP_PROVIDER_MAP = {
|
|
5791
|
+
vercel: "vercel",
|
|
5792
|
+
supabase: "supabase",
|
|
5793
|
+
stripe: "stripe",
|
|
5794
|
+
github: "github"
|
|
5795
|
+
};
|
|
5796
|
+
function resolveProviderConnectionState(provider2, mcpVerifierState) {
|
|
5797
|
+
const registryProvider = MCP_PROVIDER_MAP[provider2];
|
|
5798
|
+
if (!registryProvider || !mcpVerifierState) {
|
|
5799
|
+
return "unknown_runtime";
|
|
5800
|
+
}
|
|
5801
|
+
const record = mcpVerifierState.records.find((entry) => entry.provider === registryProvider);
|
|
5802
|
+
if (!record) {
|
|
5803
|
+
return "unknown_runtime";
|
|
5804
|
+
}
|
|
5805
|
+
switch (record.status) {
|
|
5806
|
+
case "configured":
|
|
5807
|
+
return "configured";
|
|
5808
|
+
case "missing":
|
|
5809
|
+
return "not_configured";
|
|
5810
|
+
case "unsupported":
|
|
5811
|
+
return "unsupported";
|
|
5812
|
+
case "stale":
|
|
5813
|
+
return "configured";
|
|
5814
|
+
default:
|
|
5815
|
+
return "unknown_runtime";
|
|
5816
|
+
}
|
|
5195
5817
|
}
|
|
5196
|
-
function
|
|
5197
|
-
|
|
5818
|
+
function providerResultStatus(input) {
|
|
5819
|
+
if (input.connectionState === "connected") {
|
|
5820
|
+
return input.providerObservationMet ? "verified" : "missing";
|
|
5821
|
+
}
|
|
5822
|
+
if (!input.repoExpectationMet) {
|
|
5823
|
+
return "missing";
|
|
5824
|
+
}
|
|
5825
|
+
if (input.connectionState === "not_configured" || input.connectionState === "configured") {
|
|
5826
|
+
return "needs_mcp";
|
|
5827
|
+
}
|
|
5828
|
+
if (input.connectionState === "unsupported") {
|
|
5829
|
+
return "manual";
|
|
5830
|
+
}
|
|
5831
|
+
return "unknown";
|
|
5198
5832
|
}
|
|
5199
|
-
|
|
5200
|
-
|
|
5833
|
+
|
|
5834
|
+
// ../../src/station/verificationLayer/shared/buildCheck.ts
|
|
5835
|
+
function buildVerificationCheck(input) {
|
|
5836
|
+
const status = input.evidenceSource === "repo" ? input.repoExpectationMet ? "verified" : "missing" : providerResultStatus({
|
|
5837
|
+
connectionState: input.connectionState,
|
|
5838
|
+
repoExpectationMet: input.repoExpectationMet,
|
|
5839
|
+
providerObservationMet: input.providerObservationMet
|
|
5840
|
+
});
|
|
5841
|
+
return {
|
|
5842
|
+
id: providerCheckId(input.provider, input.checkKey),
|
|
5843
|
+
provider: input.provider,
|
|
5844
|
+
area: input.area,
|
|
5845
|
+
title: input.title,
|
|
5846
|
+
description: input.description,
|
|
5847
|
+
requiredEvidence: input.requiredEvidence ?? [],
|
|
5848
|
+
repoSignals: input.repoSignals,
|
|
5849
|
+
providerSignals: input.providerSignals,
|
|
5850
|
+
evidenceSource: input.evidenceSource,
|
|
5851
|
+
status,
|
|
5852
|
+
fixType: input.fixType,
|
|
5853
|
+
severity: input.severity,
|
|
5854
|
+
evidenceRefs: input.evidenceRefs ?? [],
|
|
5855
|
+
manualAction: input.manualAction
|
|
5856
|
+
};
|
|
5201
5857
|
}
|
|
5202
|
-
|
|
5858
|
+
|
|
5859
|
+
// ../../src/station/verificationLayer/shared/repoSignals.ts
|
|
5860
|
+
var ENV_NAME_PATTERN = /\b(?:process\.env|import\.meta\.env)\.([A-Z][A-Z0-9_]{2,})\b/g;
|
|
5861
|
+
var ENV_ASSIGNMENT_PATTERN = /^[ \t]*(?:export[ \t]+)?([A-Z][A-Z0-9_]{2,})[ \t]*=/gm;
|
|
5862
|
+
var SUPABASE_TABLE_PATTERN = /\.from\s*\(\s*['"]([a-z0-9_]+)['"]\s*\)/gi;
|
|
5863
|
+
var STRIPE_WEBHOOK_PATH_PATTERN = /(^|\/)(api\/webhooks\/stripe|api\/stripe\/webhook|webhooks\/stripe)[^/\s]*/i;
|
|
5864
|
+
var GITHUB_WORKFLOW_PATTERN = /(^|\/)\.github\/workflows\/([^/\n]+\.ya?ml)/i;
|
|
5865
|
+
function buildRepoScanContext(scan) {
|
|
5866
|
+
const files = visibleFiles4(scan);
|
|
5203
5867
|
return {
|
|
5204
|
-
|
|
5205
|
-
|
|
5206
|
-
|
|
5207
|
-
|
|
5208
|
-
|
|
5209
|
-
|
|
5210
|
-
|
|
5211
|
-
|
|
5212
|
-
|
|
5213
|
-
|
|
5214
|
-
|
|
5215
|
-
|
|
5216
|
-
|
|
5217
|
-
|
|
5218
|
-
|
|
5219
|
-
|
|
5220
|
-
|
|
5221
|
-
|
|
5222
|
-
|
|
5223
|
-
|
|
5224
|
-
|
|
5225
|
-
|
|
5226
|
-
|
|
5227
|
-
|
|
5228
|
-
|
|
5229
|
-
|
|
5230
|
-
|
|
5231
|
-
|
|
5232
|
-
|
|
5233
|
-
|
|
5868
|
+
files,
|
|
5869
|
+
pathBlob: `${scan.fileTree}
|
|
5870
|
+
${scan.files.map((file) => file.path).join("\n")}`.replace(/\\/g, "/"),
|
|
5871
|
+
contentBlob: files.map((file) => file.lowerContent).join("\n"),
|
|
5872
|
+
deps: scan.packageDeps.map((dep) => dep.toLowerCase())
|
|
5873
|
+
};
|
|
5874
|
+
}
|
|
5875
|
+
function visibleFiles4(scan) {
|
|
5876
|
+
return scan.files.filter((file) => !file.isSecret && typeof file.content === "string").map((file) => ({
|
|
5877
|
+
path: file.path.replace(/\\/g, "/"),
|
|
5878
|
+
normalizedPath: file.path.replace(/\\/g, "/").toLowerCase(),
|
|
5879
|
+
content: file.content,
|
|
5880
|
+
lowerContent: file.content.toLowerCase()
|
|
5881
|
+
}));
|
|
5882
|
+
}
|
|
5883
|
+
function collectReferencedEnvNames(scan, repositoryEvidence) {
|
|
5884
|
+
const names = /* @__PURE__ */ new Set();
|
|
5885
|
+
for (const entry of repositoryEvidence.env) {
|
|
5886
|
+
if (entry.present || entry.evidence.length > 0) {
|
|
5887
|
+
names.add(entry.name);
|
|
5888
|
+
}
|
|
5889
|
+
}
|
|
5890
|
+
for (const file of visibleFiles4(scan)) {
|
|
5891
|
+
if (!/(^|\/)\.env(\.|$)|env\.example|readme/i.test(file.normalizedPath)) {
|
|
5892
|
+
ENV_NAME_PATTERN.lastIndex = 0;
|
|
5893
|
+
let match;
|
|
5894
|
+
while ((match = ENV_NAME_PATTERN.exec(file.content)) !== null) {
|
|
5895
|
+
names.add(match[1]);
|
|
5896
|
+
}
|
|
5897
|
+
}
|
|
5898
|
+
if (/\.env\.example|env\.example/i.test(file.normalizedPath)) {
|
|
5899
|
+
ENV_ASSIGNMENT_PATTERN.lastIndex = 0;
|
|
5900
|
+
let assignment;
|
|
5901
|
+
while ((assignment = ENV_ASSIGNMENT_PATTERN.exec(file.content)) !== null) {
|
|
5902
|
+
names.add(assignment[1]);
|
|
5903
|
+
}
|
|
5904
|
+
}
|
|
5905
|
+
}
|
|
5906
|
+
const supplemental = collectEnvVarEvidence(scan, [...names]);
|
|
5907
|
+
for (const entry of supplemental) {
|
|
5908
|
+
if (entry.present || entry.evidence.length > 0) {
|
|
5909
|
+
names.add(entry.name);
|
|
5910
|
+
}
|
|
5911
|
+
}
|
|
5912
|
+
return [...names].sort();
|
|
5913
|
+
}
|
|
5914
|
+
function collectEnvExampleNames(scan) {
|
|
5915
|
+
const names = /* @__PURE__ */ new Set();
|
|
5916
|
+
for (const file of visibleFiles4(scan)) {
|
|
5917
|
+
if (!/\.env\.example|env\.example/i.test(file.normalizedPath)) {
|
|
5918
|
+
continue;
|
|
5919
|
+
}
|
|
5920
|
+
ENV_ASSIGNMENT_PATTERN.lastIndex = 0;
|
|
5921
|
+
let assignment;
|
|
5922
|
+
while ((assignment = ENV_ASSIGNMENT_PATTERN.exec(file.content)) !== null) {
|
|
5923
|
+
names.add(assignment[1]);
|
|
5924
|
+
}
|
|
5925
|
+
}
|
|
5926
|
+
return [...names].sort();
|
|
5927
|
+
}
|
|
5928
|
+
function hasRlsMigrationEvidence(repo) {
|
|
5929
|
+
return /\/policies\/|_rls\.sql|\brls\b/i.test(repo.pathBlob) || repo.files.some(
|
|
5930
|
+
(file) => /enable\s+row\s+level\s+security|create\s+policy|alter\s+table[\s\S]{0,200}enable\s+row\s+level/i.test(
|
|
5931
|
+
file.content
|
|
5932
|
+
)
|
|
5933
|
+
);
|
|
5934
|
+
}
|
|
5935
|
+
function collectSupabaseReferencedTables(repo) {
|
|
5936
|
+
const tables = /* @__PURE__ */ new Set();
|
|
5937
|
+
for (const file of repo.files) {
|
|
5938
|
+
SUPABASE_TABLE_PATTERN.lastIndex = 0;
|
|
5939
|
+
let match;
|
|
5940
|
+
while ((match = SUPABASE_TABLE_PATTERN.exec(file.content)) !== null) {
|
|
5941
|
+
tables.add(match[1]);
|
|
5942
|
+
}
|
|
5943
|
+
}
|
|
5944
|
+
return [...tables].sort();
|
|
5945
|
+
}
|
|
5946
|
+
function findStripeWebhookRoute(repo) {
|
|
5947
|
+
const pathMatch = repo.pathBlob.match(STRIPE_WEBHOOK_PATH_PATTERN);
|
|
5948
|
+
if (pathMatch) {
|
|
5949
|
+
return pathMatch[0].replace(/^\//, "");
|
|
5950
|
+
}
|
|
5951
|
+
const file = repo.files.find((entry) => /stripe.*webhook|webhook.*stripe/i.test(entry.normalizedPath));
|
|
5952
|
+
return file ? file.path : null;
|
|
5953
|
+
}
|
|
5954
|
+
function hasStripeWebhookSignature(repo) {
|
|
5955
|
+
return /webhooks\.constructevent/i.test(repo.contentBlob);
|
|
5956
|
+
}
|
|
5957
|
+
function collectStripePriceEnvNames(repo, referencedEnv) {
|
|
5958
|
+
return referencedEnv.filter((name) => /^STRIPE_(PRICE_|PRODUCT_)/i.test(name) || /STRIPE.*PRICE/i.test(name));
|
|
5959
|
+
}
|
|
5960
|
+
function findGithubWorkflowPaths(repo) {
|
|
5961
|
+
const paths = /* @__PURE__ */ new Set();
|
|
5962
|
+
for (const line of repo.pathBlob.split(/\r?\n/)) {
|
|
5963
|
+
const match = line.match(GITHUB_WORKFLOW_PATTERN);
|
|
5964
|
+
if (match) {
|
|
5965
|
+
paths.add(line.trim());
|
|
5966
|
+
}
|
|
5967
|
+
}
|
|
5968
|
+
return [...paths].sort();
|
|
5969
|
+
}
|
|
5970
|
+
|
|
5971
|
+
// ../../src/station/verificationLayer/mock/mockGitHubVerifier.ts
|
|
5972
|
+
var mockGitHubVerifier = {
|
|
5973
|
+
provider: "github",
|
|
5974
|
+
detectConfig(ctx) {
|
|
5975
|
+
const repo = buildRepoScanContext(ctx.scan);
|
|
5976
|
+
const workflows = findGithubWorkflowPaths(repo);
|
|
5977
|
+
const signals = [];
|
|
5978
|
+
if (ctx.scan.stackSignals.hasCI) {
|
|
5979
|
+
signals.push("stack: hasCI");
|
|
5980
|
+
}
|
|
5981
|
+
for (const workflow of workflows) {
|
|
5982
|
+
signals.push(`workflow: ${workflow}`);
|
|
5983
|
+
}
|
|
5984
|
+
const detected = workflows.length > 0 || Boolean(ctx.scan.stackSignals.hasCI);
|
|
5985
|
+
return { provider: "github", detected, signals };
|
|
5986
|
+
},
|
|
5987
|
+
connectStatus(ctx) {
|
|
5988
|
+
return resolveProviderConnectionState("github", ctx.mcpVerifierState);
|
|
5989
|
+
},
|
|
5990
|
+
runChecks(ctx) {
|
|
5991
|
+
const connectionState = this.connectStatus(ctx);
|
|
5992
|
+
const repo = buildRepoScanContext(ctx.scan);
|
|
5993
|
+
const workflows = findGithubWorkflowPaths(repo);
|
|
5994
|
+
return [
|
|
5995
|
+
buildVerificationCheck({
|
|
5996
|
+
provider: "github",
|
|
5997
|
+
checkKey: "actions-run-status",
|
|
5998
|
+
area: "testing",
|
|
5999
|
+
title: "GitHub Actions run status",
|
|
6000
|
+
description: "Recent workflow run conclusions require live GitHub API or MCP access.",
|
|
6001
|
+
evidenceSource: "provider",
|
|
6002
|
+
connectionState,
|
|
6003
|
+
repoExpectationMet: workflows.length > 0,
|
|
6004
|
+
providerObservationMet: false,
|
|
6005
|
+
fixType: "mcp-connect",
|
|
6006
|
+
severity: "warning",
|
|
6007
|
+
repoSignals: workflows.map((path) => `workflow: ${path}`),
|
|
6008
|
+
providerSignals: ["GitHub Actions API or MCP"],
|
|
6009
|
+
requiredEvidence: ["Latest workflow run status on default branch"]
|
|
6010
|
+
}),
|
|
6011
|
+
buildVerificationCheck({
|
|
6012
|
+
provider: "github",
|
|
6013
|
+
checkKey: "required-checks",
|
|
6014
|
+
area: "testing",
|
|
6015
|
+
title: "Required GitHub checks",
|
|
6016
|
+
description: "Branch protection and required check contexts need live GitHub verification.",
|
|
6017
|
+
evidenceSource: "provider",
|
|
6018
|
+
connectionState,
|
|
6019
|
+
repoExpectationMet: workflows.length > 0,
|
|
6020
|
+
providerObservationMet: false,
|
|
6021
|
+
fixType: "mcp-connect",
|
|
6022
|
+
severity: "warning",
|
|
6023
|
+
repoSignals: workflows,
|
|
6024
|
+
providerSignals: ["GitHub branch protection API or MCP"],
|
|
6025
|
+
requiredEvidence: ["Required status checks configured for production branch"]
|
|
6026
|
+
}),
|
|
6027
|
+
buildVerificationCheck({
|
|
6028
|
+
provider: "github",
|
|
6029
|
+
checkKey: "failed-checks-recent",
|
|
6030
|
+
area: "testing",
|
|
6031
|
+
title: "Recent failed GitHub checks",
|
|
6032
|
+
description: "Failed workflow runs in the last 7 days require live GitHub verification.",
|
|
6033
|
+
evidenceSource: "provider",
|
|
6034
|
+
connectionState,
|
|
6035
|
+
repoExpectationMet: workflows.length > 0,
|
|
6036
|
+
providerObservationMet: false,
|
|
6037
|
+
fixType: "mcp-connect",
|
|
6038
|
+
severity: "critical",
|
|
6039
|
+
repoSignals: workflows,
|
|
6040
|
+
providerSignals: ["GitHub check run API or MCP"],
|
|
6041
|
+
requiredEvidence: ["No failing required checks on latest default-branch commit"]
|
|
6042
|
+
})
|
|
6043
|
+
];
|
|
6044
|
+
},
|
|
6045
|
+
buildDiffs(ctx) {
|
|
6046
|
+
const repo = buildRepoScanContext(ctx.scan);
|
|
6047
|
+
const workflows = findGithubWorkflowPaths(repo);
|
|
6048
|
+
if (workflows.length === 0) {
|
|
6049
|
+
return [];
|
|
6050
|
+
}
|
|
6051
|
+
return [
|
|
6052
|
+
{
|
|
6053
|
+
id: providerCheckId("github", "ci-status-unverified"),
|
|
6054
|
+
provider: "github",
|
|
6055
|
+
area: "testing",
|
|
6056
|
+
title: "CI status not verified",
|
|
6057
|
+
description: "GitHub workflow files exist in the repo, but recent Actions conclusions have not been fetched.",
|
|
6058
|
+
repoExpectation: `Workflows: ${workflows.join(", ")}`,
|
|
6059
|
+
providerActual: "Not verified (mock \u2014 connect GitHub MCP read-only)",
|
|
6060
|
+
severity: "warning",
|
|
6061
|
+
suggestedFix: "mcp-connect",
|
|
6062
|
+
evidenceRefs: workflows.map((path) => `workflow: ${path}`)
|
|
6063
|
+
}
|
|
6064
|
+
];
|
|
6065
|
+
}
|
|
6066
|
+
};
|
|
6067
|
+
|
|
6068
|
+
// ../../src/station/verificationLayer/mock/mockStripeVerifier.ts
|
|
6069
|
+
var REQUIRED_STRIPE_EVENTS = [
|
|
6070
|
+
"checkout.session.completed",
|
|
6071
|
+
"customer.subscription.updated",
|
|
6072
|
+
"customer.subscription.deleted",
|
|
6073
|
+
"invoice.payment_failed"
|
|
6074
|
+
];
|
|
6075
|
+
var MOCK_STRIPE_HAS_WEBHOOK_ENDPOINT = false;
|
|
6076
|
+
var MOCK_STRIPE_CONFIGURED_EVENTS = /* @__PURE__ */ new Set();
|
|
6077
|
+
var MOCK_STRIPE_LIVE_PRICE_IDS = /* @__PURE__ */ new Set();
|
|
6078
|
+
var mockStripeVerifier = {
|
|
6079
|
+
provider: "stripe",
|
|
6080
|
+
detectConfig(ctx) {
|
|
6081
|
+
const repo = buildRepoScanContext(ctx.scan);
|
|
6082
|
+
const signals = [];
|
|
6083
|
+
if (ctx.scan.stackSignals.hasStripe) {
|
|
6084
|
+
signals.push("stack: hasStripe");
|
|
6085
|
+
}
|
|
6086
|
+
if (repo.deps.some((dep) => dep === "stripe" || dep.startsWith("@stripe/"))) {
|
|
6087
|
+
signals.push("package: stripe");
|
|
6088
|
+
}
|
|
6089
|
+
if (findStripeWebhookRoute(repo)) {
|
|
6090
|
+
signals.push(`route: ${findStripeWebhookRoute(repo)}`);
|
|
6091
|
+
}
|
|
6092
|
+
const detected = signals.length > 0;
|
|
6093
|
+
return { provider: "stripe", detected, signals };
|
|
6094
|
+
},
|
|
6095
|
+
connectStatus(ctx) {
|
|
6096
|
+
return resolveProviderConnectionState("stripe", ctx.mcpVerifierState);
|
|
6097
|
+
},
|
|
6098
|
+
runChecks(ctx) {
|
|
6099
|
+
const connectionState = this.connectStatus(ctx);
|
|
6100
|
+
const repo = buildRepoScanContext(ctx.scan);
|
|
6101
|
+
const webhookRoute = findStripeWebhookRoute(repo);
|
|
6102
|
+
const referencedEnv = collectReferencedEnvNames(ctx.scan, ctx.repositoryEvidence);
|
|
6103
|
+
const priceEnvNames = collectStripePriceEnvNames(repo, referencedEnv);
|
|
6104
|
+
const missingEvents = REQUIRED_STRIPE_EVENTS.filter((event) => !MOCK_STRIPE_CONFIGURED_EVENTS.has(event));
|
|
6105
|
+
return [
|
|
6106
|
+
buildVerificationCheck({
|
|
6107
|
+
provider: "stripe",
|
|
6108
|
+
checkKey: "dashboard-webhook-endpoint",
|
|
6109
|
+
area: "payments",
|
|
6110
|
+
title: "Stripe dashboard webhook endpoint",
|
|
6111
|
+
description: webhookRoute ? "Repo defines a webhook route; Stripe dashboard endpoint must match and be reachable." : "No Stripe webhook route found in repo; dashboard endpoint check still applies when Stripe is used.",
|
|
6112
|
+
evidenceSource: "provider",
|
|
6113
|
+
connectionState,
|
|
6114
|
+
repoExpectationMet: Boolean(webhookRoute),
|
|
6115
|
+
providerObservationMet: MOCK_STRIPE_HAS_WEBHOOK_ENDPOINT,
|
|
6116
|
+
fixType: "provider-config",
|
|
6117
|
+
severity: "critical",
|
|
6118
|
+
repoSignals: webhookRoute ? [`route: ${webhookRoute}`] : [],
|
|
6119
|
+
providerSignals: ["Stripe webhooks API or MCP"],
|
|
6120
|
+
requiredEvidence: ["Webhook endpoint URL registered in Stripe"],
|
|
6121
|
+
manualAction: "Register the production webhook URL in Stripe Dashboard \u2192 Developers \u2192 Webhooks."
|
|
6122
|
+
}),
|
|
6123
|
+
buildVerificationCheck({
|
|
6124
|
+
provider: "stripe",
|
|
6125
|
+
checkKey: "webhook-events",
|
|
6126
|
+
area: "payments",
|
|
6127
|
+
title: "Stripe webhook event subscriptions",
|
|
6128
|
+
description: missingEvents.length > 0 ? `Missing required events: ${missingEvents.join(", ")}` : "Required subscription lifecycle events are configured (mock).",
|
|
6129
|
+
evidenceSource: "provider",
|
|
6130
|
+
connectionState,
|
|
6131
|
+
repoExpectationMet: Boolean(webhookRoute && hasStripeWebhookSignature(repo)),
|
|
6132
|
+
providerObservationMet: missingEvents.length === 0,
|
|
6133
|
+
fixType: "provider-config",
|
|
6134
|
+
severity: "critical",
|
|
6135
|
+
repoSignals: hasStripeWebhookSignature(repo) ? ["webhooks.constructEvent in repo"] : [],
|
|
6136
|
+
providerSignals: ["Stripe webhook endpoint event list"],
|
|
6137
|
+
requiredEvidence: REQUIRED_STRIPE_EVENTS.map((event) => `event: ${event}`)
|
|
6138
|
+
}),
|
|
6139
|
+
buildVerificationCheck({
|
|
6140
|
+
provider: "stripe",
|
|
6141
|
+
checkKey: "live-price-ids",
|
|
6142
|
+
area: "payments",
|
|
6143
|
+
title: "Stripe live price IDs",
|
|
6144
|
+
description: priceEnvNames.length > 0 ? "Price/product env vars are referenced in repo; live Stripe prices must match." : "No Stripe price env vars detected; confirm products/prices if billing is enabled.",
|
|
6145
|
+
evidenceSource: "provider",
|
|
6146
|
+
connectionState,
|
|
6147
|
+
repoExpectationMet: priceEnvNames.length > 0,
|
|
6148
|
+
providerObservationMet: priceEnvNames.length > 0 && priceEnvNames.every((name) => MOCK_STRIPE_LIVE_PRICE_IDS.has(name)),
|
|
6149
|
+
fixType: "provider-config",
|
|
6150
|
+
severity: priceEnvNames.length > 0 ? "warning" : "info",
|
|
6151
|
+
repoSignals: priceEnvNames.map((name) => `env: ${name}`),
|
|
6152
|
+
providerSignals: ["Stripe products/prices API or MCP"],
|
|
6153
|
+
requiredEvidence: ["Live price IDs match env configuration"]
|
|
6154
|
+
})
|
|
6155
|
+
];
|
|
6156
|
+
},
|
|
6157
|
+
buildDiffs(ctx) {
|
|
6158
|
+
const repo = buildRepoScanContext(ctx.scan);
|
|
6159
|
+
const webhookRoute = findStripeWebhookRoute(repo);
|
|
6160
|
+
const diffs = [];
|
|
6161
|
+
if (webhookRoute && !MOCK_STRIPE_HAS_WEBHOOK_ENDPOINT) {
|
|
6162
|
+
diffs.push({
|
|
6163
|
+
id: providerCheckId("stripe", "webhook-endpoint-mismatch"),
|
|
6164
|
+
provider: "stripe",
|
|
6165
|
+
area: "payments",
|
|
6166
|
+
title: "Stripe webhook endpoint not registered",
|
|
6167
|
+
description: "The repo implements a webhook handler, but no matching endpoint is registered in the mock Stripe account.",
|
|
6168
|
+
repoExpectation: `Webhook route: ${webhookRoute}`,
|
|
6169
|
+
providerActual: "No Stripe webhook endpoint (mock empty dashboard)",
|
|
6170
|
+
severity: "critical",
|
|
6171
|
+
suggestedFix: "provider-config",
|
|
6172
|
+
evidenceRefs: [`route: ${webhookRoute}`]
|
|
6173
|
+
});
|
|
6174
|
+
}
|
|
6175
|
+
const referencedEnv = collectReferencedEnvNames(ctx.scan, ctx.repositoryEvidence);
|
|
6176
|
+
if (referencedEnv.includes("STRIPE_WEBHOOK_SECRET") && !MOCK_STRIPE_HAS_WEBHOOK_ENDPOINT) {
|
|
6177
|
+
diffs.push({
|
|
6178
|
+
id: providerCheckId("stripe", "webhook-secret-without-endpoint"),
|
|
6179
|
+
provider: "stripe",
|
|
6180
|
+
area: "payments",
|
|
6181
|
+
title: "STRIPE_WEBHOOK_SECRET without dashboard endpoint",
|
|
6182
|
+
description: "Repo expects STRIPE_WEBHOOK_SECRET but Stripe dashboard webhook endpoint is not confirmed.",
|
|
6183
|
+
repoExpectation: "STRIPE_WEBHOOK_SECRET referenced in repo",
|
|
6184
|
+
providerActual: "No webhook endpoint to deliver signed events",
|
|
6185
|
+
severity: "critical",
|
|
6186
|
+
suggestedFix: "provider-config",
|
|
6187
|
+
evidenceRefs: ["env: STRIPE_WEBHOOK_SECRET"]
|
|
6188
|
+
});
|
|
6189
|
+
}
|
|
6190
|
+
return diffs;
|
|
6191
|
+
}
|
|
6192
|
+
};
|
|
6193
|
+
|
|
6194
|
+
// ../../src/station/verificationLayer/mock/mockSupabaseVerifier.ts
|
|
6195
|
+
var mockSupabaseVerifier = {
|
|
6196
|
+
provider: "supabase",
|
|
6197
|
+
detectConfig(ctx) {
|
|
6198
|
+
const repo = buildRepoScanContext(ctx.scan);
|
|
6199
|
+
const signals = [];
|
|
6200
|
+
if (ctx.scan.stackSignals.hasSupabase) {
|
|
6201
|
+
signals.push("stack: hasSupabase");
|
|
6202
|
+
}
|
|
6203
|
+
if (repo.deps.some((dep) => dep.startsWith("@supabase/"))) {
|
|
6204
|
+
signals.push(`package: ${repo.deps.find((dep) => dep.startsWith("@supabase/"))}`);
|
|
6205
|
+
}
|
|
6206
|
+
if (/\/supabase\/migrations\//i.test(repo.pathBlob)) {
|
|
6207
|
+
signals.push("path: supabase/migrations");
|
|
6208
|
+
}
|
|
6209
|
+
const detected = signals.length > 0;
|
|
6210
|
+
return { provider: "supabase", detected, signals };
|
|
6211
|
+
},
|
|
6212
|
+
connectStatus(ctx) {
|
|
6213
|
+
return resolveProviderConnectionState("supabase", ctx.mcpVerifierState);
|
|
6214
|
+
},
|
|
6215
|
+
runChecks(ctx) {
|
|
6216
|
+
const connectionState = this.connectStatus(ctx);
|
|
6217
|
+
const repo = buildRepoScanContext(ctx.scan);
|
|
6218
|
+
const tables = collectSupabaseReferencedTables(repo);
|
|
6219
|
+
const rlsInRepo = hasRlsMigrationEvidence(repo);
|
|
6220
|
+
return [
|
|
6221
|
+
buildVerificationCheck({
|
|
6222
|
+
provider: "supabase",
|
|
6223
|
+
checkKey: "live-rls-enabled",
|
|
6224
|
+
area: "database",
|
|
6225
|
+
title: "Live Supabase RLS enabled",
|
|
6226
|
+
description: rlsInRepo ? "Repo contains RLS migration/policy evidence; live project RLS must be confirmed via Supabase MCP or dashboard." : "No RLS migration evidence in repo; live project likely needs RLS before launch.",
|
|
6227
|
+
evidenceSource: "provider",
|
|
6228
|
+
connectionState,
|
|
6229
|
+
repoExpectationMet: rlsInRepo,
|
|
6230
|
+
providerObservationMet: false,
|
|
6231
|
+
fixType: rlsInRepo ? "mcp-connect" : "provider-config",
|
|
6232
|
+
severity: "critical",
|
|
6233
|
+
repoSignals: rlsInRepo ? ["migration/policy references RLS"] : ["no RLS migration detected"],
|
|
6234
|
+
providerSignals: ["Supabase project policy API or MCP"],
|
|
6235
|
+
requiredEvidence: ["RLS enabled on user-owned tables in live project"],
|
|
6236
|
+
manualAction: "Enable RLS and policies in Supabase Dashboard \u2192 Authentication \u2192 Policies."
|
|
6237
|
+
}),
|
|
6238
|
+
buildVerificationCheck({
|
|
6239
|
+
provider: "supabase",
|
|
6240
|
+
checkKey: "live-policies",
|
|
6241
|
+
area: "database",
|
|
6242
|
+
title: "Live Supabase policies",
|
|
6243
|
+
description: "Row policies on referenced tables require live Supabase verification.",
|
|
6244
|
+
evidenceSource: "provider",
|
|
6245
|
+
connectionState,
|
|
6246
|
+
repoExpectationMet: tables.length > 0,
|
|
6247
|
+
providerObservationMet: false,
|
|
6248
|
+
fixType: "mcp-connect",
|
|
6249
|
+
severity: tables.length > 0 ? "critical" : "info",
|
|
6250
|
+
repoSignals: tables.map((table) => `table referenced: ${table}`),
|
|
6251
|
+
providerSignals: ["Supabase policy list API or MCP"],
|
|
6252
|
+
requiredEvidence: ["Policies exist for tables used in application code"]
|
|
6253
|
+
}),
|
|
6254
|
+
buildVerificationCheck({
|
|
6255
|
+
provider: "supabase",
|
|
6256
|
+
checkKey: "referenced-tables-protected",
|
|
6257
|
+
area: "database",
|
|
6258
|
+
title: "Referenced tables protected in live project",
|
|
6259
|
+
description: tables.length > 0 ? `Application code references ${tables.length} Supabase table(s); confirm live RLS/policies cover them.` : "No Supabase table references detected in scanned code.",
|
|
6260
|
+
evidenceSource: "provider",
|
|
6261
|
+
connectionState,
|
|
6262
|
+
repoExpectationMet: tables.length > 0 && rlsInRepo,
|
|
6263
|
+
providerObservationMet: false,
|
|
6264
|
+
fixType: "provider-config",
|
|
6265
|
+
severity: "warning",
|
|
6266
|
+
repoSignals: tables.map((table) => `.from("${table}")`),
|
|
6267
|
+
providerSignals: ["Live table policy map"],
|
|
6268
|
+
requiredEvidence: ["Each referenced public table has RLS + policy in production"]
|
|
6269
|
+
})
|
|
6270
|
+
];
|
|
6271
|
+
},
|
|
6272
|
+
buildDiffs(ctx) {
|
|
6273
|
+
const repo = buildRepoScanContext(ctx.scan);
|
|
6274
|
+
const tables = collectSupabaseReferencedTables(repo);
|
|
6275
|
+
const rlsInRepo = hasRlsMigrationEvidence(repo);
|
|
6276
|
+
const diffs = [];
|
|
6277
|
+
if (!rlsInRepo && tables.length > 0) {
|
|
6278
|
+
diffs.push({
|
|
6279
|
+
id: providerCheckId("supabase", "rls-migration-gap"),
|
|
6280
|
+
provider: "supabase",
|
|
6281
|
+
area: "database",
|
|
6282
|
+
title: "RLS migration missing for referenced tables",
|
|
6283
|
+
description: "Code queries Supabase tables but no RLS migration/policy evidence was found in the repo.",
|
|
6284
|
+
repoExpectation: `Tables referenced: ${tables.join(", ")}`,
|
|
6285
|
+
providerActual: "Live RLS state unknown without Supabase MCP",
|
|
6286
|
+
severity: "critical",
|
|
6287
|
+
suggestedFix: "repo-fix",
|
|
6288
|
+
evidenceRefs: tables.map((table) => `table: ${table}`)
|
|
6289
|
+
});
|
|
6290
|
+
}
|
|
6291
|
+
if (rlsInRepo) {
|
|
6292
|
+
diffs.push({
|
|
6293
|
+
id: providerCheckId("supabase", "live-rls-unverified"),
|
|
6294
|
+
provider: "supabase",
|
|
6295
|
+
area: "database",
|
|
6296
|
+
title: "Live RLS not verified",
|
|
6297
|
+
description: "Repo includes RLS migration/policy files, but live Supabase project RLS has not been confirmed.",
|
|
6298
|
+
repoExpectation: "RLS policies defined in repo migrations",
|
|
6299
|
+
providerActual: "Not verified (mock \u2014 connect Supabase MCP read-only)",
|
|
6300
|
+
severity: "critical",
|
|
6301
|
+
suggestedFix: "mcp-connect",
|
|
6302
|
+
evidenceRefs: repo.files.filter((file) => /enable\s+row\s+level\s+security|create\s+policy/i.test(file.content)).slice(0, 3).map((file) => `file: ${file.path}`)
|
|
6303
|
+
});
|
|
6304
|
+
}
|
|
6305
|
+
return diffs;
|
|
6306
|
+
}
|
|
6307
|
+
};
|
|
6308
|
+
|
|
6309
|
+
// ../../src/station/verificationLayer/mock/mockVercelVerifier.ts
|
|
6310
|
+
var MOCK_VERCEL_PRODUCTION_ENV = /* @__PURE__ */ new Set();
|
|
6311
|
+
function deploymentEnvCandidates(ctx) {
|
|
6312
|
+
const referenced = collectReferencedEnvNames(ctx.scan, ctx.repositoryEvidence);
|
|
6313
|
+
const fromExample = collectEnvExampleNames(ctx.scan);
|
|
6314
|
+
const names = /* @__PURE__ */ new Set([...referenced, ...fromExample]);
|
|
6315
|
+
return [...names].filter((name) => {
|
|
6316
|
+
if (/^VERCEL_/i.test(name)) {
|
|
6317
|
+
return true;
|
|
6318
|
+
}
|
|
6319
|
+
if (/^(DATABASE_URL|NEXT_PUBLIC_|VITE_|SUPABASE_|STRIPE_|CLERK_|SENTRY_|POSTHOG_|PADDLE_|AUTH_|NEXTAUTH_)/i.test(name)) {
|
|
6320
|
+
return true;
|
|
6321
|
+
}
|
|
6322
|
+
return referenced.includes(name) && fromExample.includes(name);
|
|
6323
|
+
});
|
|
6324
|
+
}
|
|
6325
|
+
var mockVercelVerifier = {
|
|
6326
|
+
provider: "vercel",
|
|
6327
|
+
detectConfig(ctx) {
|
|
6328
|
+
const repo = buildRepoScanContext(ctx.scan);
|
|
6329
|
+
const signals = [];
|
|
6330
|
+
if (ctx.scan.stackSignals.hasVercel) {
|
|
6331
|
+
signals.push("stack: hasVercel");
|
|
6332
|
+
}
|
|
6333
|
+
if (/vercel\.json/i.test(repo.pathBlob)) {
|
|
6334
|
+
signals.push("path: vercel.json");
|
|
6335
|
+
}
|
|
6336
|
+
if (ctx.scan.stackSignals.hasNextJs || ctx.scan.stackSignals.hasVite) {
|
|
6337
|
+
signals.push("framework: Vercel-compatible");
|
|
6338
|
+
}
|
|
6339
|
+
const detected = signals.length > 0;
|
|
6340
|
+
return { provider: "vercel", detected, signals };
|
|
6341
|
+
},
|
|
6342
|
+
connectStatus(ctx) {
|
|
6343
|
+
return resolveProviderConnectionState("vercel", ctx.mcpVerifierState);
|
|
6344
|
+
},
|
|
6345
|
+
runChecks(ctx) {
|
|
6346
|
+
const connectionState = this.connectStatus(ctx);
|
|
6347
|
+
const candidates = deploymentEnvCandidates(ctx);
|
|
6348
|
+
const missingOnVercel = candidates.filter((name) => !MOCK_VERCEL_PRODUCTION_ENV.has(name));
|
|
6349
|
+
const repo = buildRepoScanContext(ctx.scan);
|
|
6350
|
+
const checks = [
|
|
6351
|
+
buildVerificationCheck({
|
|
6352
|
+
provider: "vercel",
|
|
6353
|
+
checkKey: "production-env-sync",
|
|
6354
|
+
area: "deployment",
|
|
6355
|
+
title: "Vercel production environment variables",
|
|
6356
|
+
description: missingOnVercel.length > 0 ? `${missingOnVercel.length} repo-referenced env var(s) are not confirmed in Vercel production.` : "Repo env names align with the mock Vercel production snapshot.",
|
|
6357
|
+
evidenceSource: "provider",
|
|
6358
|
+
connectionState,
|
|
6359
|
+
repoExpectationMet: candidates.length > 0,
|
|
6360
|
+
providerObservationMet: missingOnVercel.length === 0,
|
|
6361
|
+
fixType: "provider-config",
|
|
6362
|
+
severity: missingOnVercel.length > 0 ? "critical" : "info",
|
|
6363
|
+
repoSignals: missingOnVercel.map((name) => `env expected: ${name}`),
|
|
6364
|
+
providerSignals: connectionState === "connected" ? ["Vercel production env API"] : ["Awaiting Vercel MCP read-only env lookup"],
|
|
6365
|
+
requiredEvidence: ["Vercel production env var names and values (redacted)"],
|
|
6366
|
+
manualAction: "Add missing keys in Vercel Project Settings \u2192 Environment Variables (Production)."
|
|
6367
|
+
}),
|
|
6368
|
+
buildVerificationCheck({
|
|
6369
|
+
provider: "vercel",
|
|
6370
|
+
checkKey: "latest-deployment",
|
|
6371
|
+
area: "deployment",
|
|
6372
|
+
title: "Latest Vercel deployment status",
|
|
6373
|
+
description: "Production deployment health requires live Vercel project access.",
|
|
6374
|
+
evidenceSource: "provider",
|
|
6375
|
+
connectionState,
|
|
6376
|
+
repoExpectationMet: Boolean(ctx.scan.stackSignals.hasVercel || /vercel\.json/i.test(repo.pathBlob)),
|
|
6377
|
+
providerObservationMet: false,
|
|
6378
|
+
fixType: "mcp-connect",
|
|
6379
|
+
severity: "warning",
|
|
6380
|
+
repoSignals: repo.pathBlob.split(/\r?\n/).filter((p2) => /vercel\.json/i.test(p2)).slice(0, 3),
|
|
6381
|
+
providerSignals: ["Vercel deployments API or MCP"],
|
|
6382
|
+
requiredEvidence: ["Latest production deployment state"]
|
|
6383
|
+
}),
|
|
6384
|
+
buildVerificationCheck({
|
|
6385
|
+
provider: "vercel",
|
|
6386
|
+
checkKey: "production-domain",
|
|
6387
|
+
area: "deployment",
|
|
6388
|
+
title: "Vercel production domain",
|
|
6389
|
+
description: "Custom domain and DNS status require live Vercel project access.",
|
|
6390
|
+
evidenceSource: "provider",
|
|
6391
|
+
connectionState,
|
|
6392
|
+
repoExpectationMet: Boolean(ctx.scan.stackSignals.hasVercel || ctx.scan.stackSignals.hasNextJs),
|
|
6393
|
+
providerObservationMet: false,
|
|
6394
|
+
fixType: "mcp-connect",
|
|
6395
|
+
severity: "warning",
|
|
6396
|
+
repoSignals: [],
|
|
6397
|
+
providerSignals: ["Vercel domains API or MCP"],
|
|
6398
|
+
requiredEvidence: ["Production domain assignment and DNS verification"]
|
|
6399
|
+
})
|
|
6400
|
+
];
|
|
6401
|
+
return checks;
|
|
6402
|
+
},
|
|
6403
|
+
buildDiffs(ctx) {
|
|
6404
|
+
const candidates = deploymentEnvCandidates(ctx);
|
|
6405
|
+
return candidates.filter((name) => !MOCK_VERCEL_PRODUCTION_ENV.has(name)).map((name) => ({
|
|
6406
|
+
id: providerCheckId("vercel", `env-missing-${name.toLowerCase()}`),
|
|
6407
|
+
provider: "vercel",
|
|
6408
|
+
area: "deployment",
|
|
6409
|
+
title: `${name} missing in Vercel production`,
|
|
6410
|
+
description: `The repo references ${name}, but the mock Vercel production env snapshot does not include it.`,
|
|
6411
|
+
repoExpectation: `Referenced in repo (.env.example, code, or env evidence)`,
|
|
6412
|
+
providerActual: "Not present in Vercel production (mock empty snapshot)",
|
|
6413
|
+
severity: /SECRET|KEY|TOKEN|PASSWORD/i.test(name) ? "critical" : "warning",
|
|
6414
|
+
suggestedFix: "provider-config",
|
|
6415
|
+
evidenceRefs: collectReferencedEnvNames(ctx.scan, ctx.repositoryEvidence).filter((entry) => entry === name).map((entry) => `env: ${entry}`)
|
|
6416
|
+
}));
|
|
6417
|
+
}
|
|
6418
|
+
};
|
|
6419
|
+
|
|
6420
|
+
// ../../src/station/verificationLayer/registry.ts
|
|
6421
|
+
var verifiers = [];
|
|
6422
|
+
function registerProviderVerifier(verifier) {
|
|
6423
|
+
const existing = verifiers.findIndex((entry) => entry.provider === verifier.provider);
|
|
6424
|
+
if (existing >= 0) {
|
|
6425
|
+
verifiers[existing] = verifier;
|
|
6426
|
+
return;
|
|
6427
|
+
}
|
|
6428
|
+
verifiers.push(verifier);
|
|
6429
|
+
}
|
|
6430
|
+
function getRegisteredVerifiers() {
|
|
6431
|
+
return [...verifiers];
|
|
6432
|
+
}
|
|
6433
|
+
|
|
6434
|
+
// ../../src/station/verificationLayer/registerDefaults.ts
|
|
6435
|
+
var defaultsRegistered = false;
|
|
6436
|
+
function ensureDefaultVerifiersRegistered() {
|
|
6437
|
+
if (defaultsRegistered) {
|
|
6438
|
+
return;
|
|
6439
|
+
}
|
|
6440
|
+
registerProviderVerifier(mockVercelVerifier);
|
|
6441
|
+
registerProviderVerifier(mockSupabaseVerifier);
|
|
6442
|
+
registerProviderVerifier(mockStripeVerifier);
|
|
6443
|
+
registerProviderVerifier(mockGitHubVerifier);
|
|
6444
|
+
defaultsRegistered = true;
|
|
6445
|
+
}
|
|
6446
|
+
|
|
6447
|
+
// ../../src/station/verificationLayer/runVerificationLayer.ts
|
|
6448
|
+
function runVerifier(verifier, ctx) {
|
|
6449
|
+
const config = verifier.detectConfig(ctx);
|
|
6450
|
+
if (!config.detected) {
|
|
6451
|
+
return null;
|
|
6452
|
+
}
|
|
6453
|
+
const connectionState = verifier.connectStatus(ctx);
|
|
6454
|
+
const rawChecks = verifier.runChecks(ctx);
|
|
6455
|
+
const checks = rawChecks.map((check) => ({
|
|
6456
|
+
...check,
|
|
6457
|
+
status: coerceProviderVerificationStatus(check.status, connectionState)
|
|
6458
|
+
}));
|
|
6459
|
+
const diffs = verifier.buildDiffs(ctx);
|
|
6460
|
+
return {
|
|
6461
|
+
provider: verifier.provider,
|
|
6462
|
+
connectionState,
|
|
6463
|
+
config,
|
|
6464
|
+
checks,
|
|
6465
|
+
diffs,
|
|
6466
|
+
repoReadinessPercent: computeLayerReadinessPercent(checks, "repo"),
|
|
6467
|
+
providerReadinessPercent: computeLayerReadinessPercent(checks, "provider")
|
|
6468
|
+
};
|
|
6469
|
+
}
|
|
6470
|
+
function runVerificationLayer(ctx, options) {
|
|
6471
|
+
if (options?.registerDefaults !== false) {
|
|
6472
|
+
ensureDefaultVerifiersRegistered();
|
|
6473
|
+
}
|
|
6474
|
+
const providers = getRegisteredVerifiers().map((verifier) => runVerifier(verifier, ctx)).filter((result) => result !== null);
|
|
6475
|
+
const checks = providers.flatMap((result) => result.checks);
|
|
6476
|
+
const diffs = providers.flatMap((result) => result.diffs);
|
|
6477
|
+
const { repoReadinessPercent, providerReadinessPercent } = aggregateReadinessPercents(providers);
|
|
6478
|
+
return {
|
|
6479
|
+
version: 1,
|
|
6480
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6481
|
+
runtimeMode: "mock",
|
|
6482
|
+
providers,
|
|
6483
|
+
checks,
|
|
6484
|
+
diffs,
|
|
6485
|
+
repoReadinessPercent,
|
|
6486
|
+
providerReadinessPercent
|
|
6487
|
+
};
|
|
6488
|
+
}
|
|
6489
|
+
|
|
6490
|
+
// ../../src/station/orchestrator.ts
|
|
6491
|
+
function isScanLimitResult(value) {
|
|
6492
|
+
return value.kind === "scan_limit_reached";
|
|
6493
|
+
}
|
|
6494
|
+
function isManagedRequiredResult(value) {
|
|
6495
|
+
return value.kind === "managed_required";
|
|
6496
|
+
}
|
|
6497
|
+
function isManagedSessionInvalidResult(value) {
|
|
6498
|
+
return value.kind === "managed_session_invalid";
|
|
6499
|
+
}
|
|
6500
|
+
function createStationOrchestrator(deps) {
|
|
6501
|
+
return {
|
|
6502
|
+
async run(input) {
|
|
6503
|
+
const allowLocal = Boolean(await deps.isLocalStationFallbackAllowed?.());
|
|
6504
|
+
const scan = await deps.scanWorkspace(input.workspaceRoot);
|
|
6505
|
+
const productionConnectionChoices = await loadProductionConnectionChoices(deps);
|
|
6506
|
+
const productionConnectionEvidence = detectProductionConnectionEvidence(scan);
|
|
6507
|
+
const productionConnections = summarizeProductionConnections(
|
|
6508
|
+
productionConnectionChoices,
|
|
6509
|
+
productionConnectionEvidence
|
|
6510
|
+
);
|
|
6511
|
+
const productionConnectionContext = buildProductionConnectionContext(
|
|
6512
|
+
productionConnectionChoices,
|
|
6513
|
+
productionConnectionEvidence
|
|
6514
|
+
);
|
|
6515
|
+
const verificationSummary = buildVerificationSummary(scan, productionConnections);
|
|
6516
|
+
const verificationEvidenceContext = buildVerificationEvidenceContext(verificationSummary);
|
|
6517
|
+
const stackWiring = analyzeStackWiring(scan);
|
|
6518
|
+
const stackWiringContext = buildStackWiringContext(stackWiring);
|
|
6519
|
+
const repositoryEvidence = analyzeRepositoryEvidence(scan);
|
|
6520
|
+
const providerRegistry = buildProviderRegistrySnapshot();
|
|
6521
|
+
const staticInfrastructureFlowGraph = buildStaticInfrastructureFlowGraph(scan);
|
|
6522
|
+
const stackAutomation = buildStackAutomationSummary(stackWiring, { staticInfrastructureFlowGraph });
|
|
6523
|
+
const stackAutomationContext = buildStackAutomationContext(stackAutomation);
|
|
6524
|
+
const mcpVerifierState = await deps.getMcpVerifierState?.();
|
|
6525
|
+
const verificationLayer = runVerificationLayer({
|
|
6526
|
+
workspaceRoot: input.workspaceRoot,
|
|
6527
|
+
scan,
|
|
6528
|
+
stackWiring,
|
|
6529
|
+
repositoryEvidence,
|
|
6530
|
+
mcpVerifierState
|
|
6531
|
+
});
|
|
6532
|
+
const missionGraph = buildMissionGraph({
|
|
6533
|
+
stackWiring,
|
|
6534
|
+
repositoryEvidence,
|
|
6535
|
+
staticInfrastructureFlowGraph,
|
|
6536
|
+
verificationLayer
|
|
6537
|
+
});
|
|
6538
|
+
const specContent = await readSpec(input.workspaceRoot);
|
|
6539
|
+
const modelPrompt = buildAnalysisPrompt(
|
|
6540
|
+
scan,
|
|
5234
6541
|
specContent,
|
|
5235
6542
|
productionConnectionContext,
|
|
5236
6543
|
verificationEvidenceContext,
|
|
@@ -5568,25 +6875,49 @@ function generateAgentSummary(artifact) {
|
|
|
5568
6875
|
}
|
|
5569
6876
|
}
|
|
5570
6877
|
lines.push("");
|
|
5571
|
-
lines.push("##
|
|
6878
|
+
lines.push("## Agent-code actions");
|
|
5572
6879
|
if (topGaps.length === 0) {
|
|
5573
|
-
lines.push("_No model gaps returned
|
|
6880
|
+
lines.push("_No model gaps returned. Review mission map checks before changing code._");
|
|
5574
6881
|
} else {
|
|
5575
6882
|
topGaps.forEach((gap, index) => {
|
|
5576
6883
|
lines.push(
|
|
5577
6884
|
`${index + 1}. **${gap.title}** (\`${gap.id}\`, ${gap.severity}, map: \`${gap.primaryMapCategory}\`)`
|
|
5578
6885
|
);
|
|
5579
6886
|
lines.push(` - ${gap.detail}`);
|
|
5580
|
-
lines.push(` -
|
|
6887
|
+
lines.push(` - Command: \`viberaven prompt --gap ${gap.id}\``);
|
|
6888
|
+
if (gap.copyPrompt) {
|
|
6889
|
+
lines.push(` - Prompt: ${gap.copyPrompt}`);
|
|
6890
|
+
}
|
|
6891
|
+
});
|
|
6892
|
+
}
|
|
6893
|
+
const manualChecks = (artifact.missionGraph.areas ?? []).flatMap(
|
|
6894
|
+
(area) => area.providerMissions.flatMap(
|
|
6895
|
+
(mission) => mission.checks.filter(
|
|
6896
|
+
(check) => check.evidenceClass === "manual-dashboard" || check.evidenceClass === "mcp-verifier" || check.evidenceSource === "provider" || check.evidenceSource === "mcp" || check.status === "needs-connection" || check.status === "unknown"
|
|
6897
|
+
).map((check) => ({ area: area.label, provider: mission.providerLabel, check }))
|
|
6898
|
+
)
|
|
6899
|
+
);
|
|
6900
|
+
lines.push("");
|
|
6901
|
+
lines.push("## Human-provider actions");
|
|
6902
|
+
if (manualChecks.length === 0) {
|
|
6903
|
+
lines.push("_No manual provider actions were identified in this scan._");
|
|
6904
|
+
} else {
|
|
6905
|
+
manualChecks.slice(0, 8).forEach((item3, index) => {
|
|
6906
|
+
lines.push(`${index + 1}. **${item3.check.label}** (${item3.area} / ${item3.provider})`);
|
|
6907
|
+
lines.push(
|
|
6908
|
+
` - ${item3.check.promptHint || "Ask the user to confirm this in the provider dashboard or through read-only MCP."}`
|
|
6909
|
+
);
|
|
5581
6910
|
});
|
|
5582
6911
|
}
|
|
5583
6912
|
lines.push("");
|
|
6913
|
+
lines.push("Do not claim human-provider actions as repo-code fixes.");
|
|
6914
|
+
lines.push("");
|
|
5584
6915
|
lines.push("## Agent workflow");
|
|
5585
|
-
lines.push("1. Pick the highest-severity gap unless the user names an area or provider.");
|
|
5586
|
-
lines.push("2. Run `viberaven prompt --gap <id>`
|
|
5587
|
-
lines.push("3. Implement the fix
|
|
5588
|
-
lines.push("4. Run `npx -y @viberaven/cli@beta scan` again
|
|
5589
|
-
lines.push("5. Tell the user to
|
|
6916
|
+
lines.push("1. Pick the highest-severity agent-code gap unless the user names an area or provider.");
|
|
6917
|
+
lines.push("2. Run `viberaven prompt --gap <id>` or read `copyPrompt` from `last-scan.json`.");
|
|
6918
|
+
lines.push("3. Implement the repo-code fix.");
|
|
6919
|
+
lines.push("4. Run `npx -y @viberaven/cli@beta scan` again.");
|
|
6920
|
+
lines.push("5. Tell the user to review `.viberaven/report.html` for the visual mission map.");
|
|
5590
6921
|
lines.push("");
|
|
5591
6922
|
if (artifact.usage) {
|
|
5592
6923
|
lines.push("## Account usage");
|
|
@@ -5719,6 +7050,24 @@ var PANEL_CLIENT_SCRIPT = `
|
|
|
5719
7050
|
|
|
5720
7051
|
|
|
5721
7052
|
|
|
7053
|
+
function normalizeProviderToken(value) {
|
|
7054
|
+
|
|
7055
|
+
return String(value == null ? '' : value)
|
|
7056
|
+
.trim()
|
|
7057
|
+
.toLowerCase()
|
|
7058
|
+
.replace(/&/g, 'and')
|
|
7059
|
+
.replace(/[^a-z0-9]+/g, '');
|
|
7060
|
+
|
|
7061
|
+
}
|
|
7062
|
+
|
|
7063
|
+
function buildProviderStackCommand(areaKey, provider) {
|
|
7064
|
+
|
|
7065
|
+
return 'viberaven stack set ' + areaKey + ' ' + normalizeProviderToken(provider) + ' && viberaven scan';
|
|
7066
|
+
|
|
7067
|
+
}
|
|
7068
|
+
|
|
7069
|
+
|
|
7070
|
+
|
|
5722
7071
|
function normKey(p) {
|
|
5723
7072
|
|
|
5724
7073
|
if (!p) return '';
|
|
@@ -5727,6 +7076,8 @@ var PANEL_CLIENT_SCRIPT = `
|
|
|
5727
7076
|
|
|
5728
7077
|
var compact = raw.replace(/[^a-z0-9]+/g, '');
|
|
5729
7078
|
|
|
7079
|
+
if (!compact || compact === 'notselected' || raw === 'not selected' || raw === 'none') return '';
|
|
7080
|
+
|
|
5730
7081
|
if (logoPayload.aliases[raw]) return logoPayload.aliases[raw];
|
|
5731
7082
|
|
|
5732
7083
|
if (logoPayload.aliases[compact]) return logoPayload.aliases[compact];
|
|
@@ -5791,17 +7142,101 @@ var PANEL_CLIENT_SCRIPT = `
|
|
|
5791
7142
|
|
|
5792
7143
|
}
|
|
5793
7144
|
|
|
7145
|
+
if (key && logoPayload.logos[key]) return logoPayload.logos[key];
|
|
7146
|
+
|
|
5794
7147
|
if (key && logoPayload.iconUrls && logoPayload.iconUrls[key]) {
|
|
5795
7148
|
|
|
5796
7149
|
return '<img class="provider-logo__img" src="' + esc(logoPayload.iconUrls[key]) + '" alt="" decoding="async" data-provider-logo-key="' + esc(key) + '" />';
|
|
5797
7150
|
|
|
5798
7151
|
}
|
|
5799
7152
|
|
|
5800
|
-
|
|
7153
|
+
return logoPayload.fallbackSvg || '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 3 20 7.5v9L12 21 4 16.5v-9L12 3Zm0 3.3-5 2.8v5.8l5 2.8 5-2.8V9.1l-5-2.8Zm0 3.2a2.5 2.5 0 1 1 0 5 2.5 2.5 0 0 1 0-5Z"/></svg>';
|
|
7154
|
+
|
|
7155
|
+
}
|
|
7156
|
+
|
|
7157
|
+
|
|
7158
|
+
|
|
7159
|
+
function nodeTone(p) {
|
|
7160
|
+
|
|
7161
|
+
var key = normKey(p);
|
|
7162
|
+
|
|
7163
|
+
if (!key || !logoPayload.nodeTones || !logoPayload.nodeTones[key]) return null;
|
|
7164
|
+
|
|
7165
|
+
return logoPayload.nodeTones[key];
|
|
7166
|
+
|
|
7167
|
+
}
|
|
7168
|
+
|
|
7169
|
+
|
|
7170
|
+
|
|
7171
|
+
function syncMapNode(areaKey, providerOption) {
|
|
7172
|
+
|
|
7173
|
+
var node = document.querySelector('.studio-node[data-area-key="' + areaKey + '"]');
|
|
7174
|
+
|
|
7175
|
+
if (!node) return;
|
|
7176
|
+
|
|
7177
|
+
var area = areas.find(function (a) { return a.key === areaKey; });
|
|
7178
|
+
|
|
7179
|
+
var areaLabel = (area && area.label) || LABELS[areaKey] || areaKey;
|
|
7180
|
+
|
|
7181
|
+
var missions = (area && area.providerMissions) || [];
|
|
7182
|
+
|
|
7183
|
+
var mission = missionForProvider(missions, providerOption);
|
|
7184
|
+
|
|
7185
|
+
var providerLabel = providerLabelFor(areaKey, providerOption, mission, areaLabel);
|
|
7186
|
+
|
|
7187
|
+
var iconKey = normKey(providerOption);
|
|
7188
|
+
|
|
7189
|
+
var logoInner = logoHtml(iconKey || providerOption, providerLabel);
|
|
5801
7190
|
|
|
5802
|
-
var
|
|
7191
|
+
var cls = logoClass(iconKey || providerOption);
|
|
5803
7192
|
|
|
5804
|
-
|
|
7193
|
+
var logoEl = node.querySelector('.studio-node__logo');
|
|
7194
|
+
|
|
7195
|
+
if (logoEl) {
|
|
7196
|
+
|
|
7197
|
+
logoEl.className = 'studio-node__logo provider-logo' + cls;
|
|
7198
|
+
|
|
7199
|
+
logoEl.innerHTML = logoInner;
|
|
7200
|
+
|
|
7201
|
+
}
|
|
7202
|
+
|
|
7203
|
+
node.className = node.className
|
|
7204
|
+
|
|
7205
|
+
.replace(/\\sprovider-logo--[a-z0-9-]+/g, '')
|
|
7206
|
+
|
|
7207
|
+
.replace(/\\sprovider-logo--brand/g, '')
|
|
7208
|
+
|
|
7209
|
+
.trim();
|
|
7210
|
+
|
|
7211
|
+
if (!/\\bprovider-logo\\b/.test(node.className)) node.className += ' provider-logo';
|
|
7212
|
+
|
|
7213
|
+
node.className += cls;
|
|
7214
|
+
|
|
7215
|
+
var provSpan = node.querySelector('.studio-node__provider');
|
|
7216
|
+
|
|
7217
|
+
if (provSpan) provSpan.textContent = providerLabel;
|
|
7218
|
+
|
|
7219
|
+
var tone = nodeTone(providerOption);
|
|
7220
|
+
|
|
7221
|
+
if (tone) {
|
|
7222
|
+
|
|
7223
|
+
node.style.setProperty('--provider-color', tone[0]);
|
|
7224
|
+
|
|
7225
|
+
node.style.setProperty('--provider-glow', tone[1]);
|
|
7226
|
+
|
|
7227
|
+
} else {
|
|
7228
|
+
|
|
7229
|
+
node.style.removeProperty('--provider-color');
|
|
7230
|
+
|
|
7231
|
+
node.style.removeProperty('--provider-glow');
|
|
7232
|
+
|
|
7233
|
+
}
|
|
7234
|
+
|
|
7235
|
+
var metaSpan = node.querySelector('.studio-node__meta');
|
|
7236
|
+
|
|
7237
|
+
var metaText = metaSpan ? metaSpan.textContent : '';
|
|
7238
|
+
|
|
7239
|
+
node.setAttribute('aria-label', areaLabel + ', ' + providerLabel + ', ' + metaText);
|
|
5805
7240
|
|
|
5806
7241
|
}
|
|
5807
7242
|
|
|
@@ -5887,19 +7322,53 @@ var PANEL_CLIENT_SCRIPT = `
|
|
|
5887
7322
|
|
|
5888
7323
|
|
|
5889
7324
|
|
|
5890
|
-
function
|
|
7325
|
+
function firstProviderOption(areaKey) {
|
|
5891
7326
|
|
|
5892
|
-
|
|
7327
|
+
var options = providerOptions[areaKey] || [];
|
|
5893
7328
|
|
|
5894
|
-
return
|
|
7329
|
+
return options[0] || null;
|
|
5895
7330
|
|
|
5896
7331
|
}
|
|
5897
7332
|
|
|
5898
7333
|
|
|
5899
7334
|
|
|
5900
|
-
function
|
|
7335
|
+
function iconProviderKey(areaKey, currentProvider, mission, providerLabel) {
|
|
5901
7336
|
|
|
5902
|
-
|
|
7337
|
+
var option = optionFor(areaKey, currentProvider);
|
|
7338
|
+
|
|
7339
|
+
var firstOption = firstProviderOption(areaKey);
|
|
7340
|
+
|
|
7341
|
+
return currentProvider ||
|
|
7342
|
+
|
|
7343
|
+
(mission && (mission.provider || mission.providerLabel || mission.key)) ||
|
|
7344
|
+
|
|
7345
|
+
(option && (option.provider || option.label)) ||
|
|
7346
|
+
|
|
7347
|
+
(firstOption && (firstOption.provider || firstOption.label)) ||
|
|
7348
|
+
|
|
7349
|
+
providerLabel ||
|
|
7350
|
+
|
|
7351
|
+
LABELS[areaKey] ||
|
|
7352
|
+
|
|
7353
|
+
areaKey;
|
|
7354
|
+
|
|
7355
|
+
}
|
|
7356
|
+
|
|
7357
|
+
|
|
7358
|
+
|
|
7359
|
+
function sameProvider(a, b) {
|
|
7360
|
+
|
|
7361
|
+
if (!a || !b) return false;
|
|
7362
|
+
|
|
7363
|
+
return String(a).toLowerCase() === String(b).toLowerCase() || normKey(a) === normKey(b);
|
|
7364
|
+
|
|
7365
|
+
}
|
|
7366
|
+
|
|
7367
|
+
|
|
7368
|
+
|
|
7369
|
+
function projectProviderFor(areaKey, missions) {
|
|
7370
|
+
|
|
7371
|
+
if (projectProviders[areaKey]) return projectProviders[areaKey];
|
|
5903
7372
|
|
|
5904
7373
|
var mission = preferredMission(missions, '');
|
|
5905
7374
|
|
|
@@ -6037,210 +7506,672 @@ var PANEL_CLIENT_SCRIPT = `
|
|
|
6037
7506
|
|
|
6038
7507
|
return [
|
|
6039
7508
|
|
|
6040
|
-
'Wire ' + subject + ' for this app safely.',
|
|
7509
|
+
'Wire ' + subject + ' for this app safely.',
|
|
7510
|
+
|
|
7511
|
+
'',
|
|
7512
|
+
|
|
7513
|
+
'Current ' + subject + ' readiness: ' + passed.length + '/' + Math.max(total, 1) + ' repo checks passed (' + (mission.readinessPercent || 0) + '%).',
|
|
7514
|
+
|
|
7515
|
+
'',
|
|
7516
|
+
|
|
7517
|
+
'Repo evidence already found:',
|
|
7518
|
+
|
|
7519
|
+
itemLines(passed, '- No ' + subject + ' checks passed yet.', true),
|
|
7520
|
+
|
|
7521
|
+
'',
|
|
7522
|
+
|
|
7523
|
+
'Missing ' + subject + ' checks:',
|
|
7524
|
+
|
|
7525
|
+
itemLines(missing, '- No missing ' + subject + ' checks were found by VibeRaven.', false),
|
|
7526
|
+
|
|
7527
|
+
'',
|
|
7528
|
+
|
|
7529
|
+
'Manual checks that repo evidence cannot prove:',
|
|
7530
|
+
|
|
7531
|
+
itemLines(manual, '- No manual dashboard checks were listed.', false),
|
|
7532
|
+
|
|
7533
|
+
'',
|
|
7534
|
+
|
|
7535
|
+
'First inspect the existing package.json files, env examples, framework routes, provider helpers, and server/client boundaries before editing.',
|
|
7536
|
+
|
|
7537
|
+
'',
|
|
7538
|
+
|
|
7539
|
+
'Implement:',
|
|
7540
|
+
|
|
7541
|
+
'1. Close only the missing ' + subject + ' checks listed above.',
|
|
7542
|
+
|
|
7543
|
+
'2. Follow the existing file structure and naming patterns.',
|
|
7544
|
+
|
|
7545
|
+
'3. Keep provider secrets in server-only code and documented env templates.',
|
|
7546
|
+
|
|
7547
|
+
'4. Keep external dashboard work explicit instead of claiming it from repo evidence.',
|
|
7548
|
+
|
|
7549
|
+
'',
|
|
7550
|
+
|
|
7551
|
+
'Constraints:',
|
|
7552
|
+
|
|
7553
|
+
'- Do not rewrite unrelated auth, payments, UI, billing, deployment, or analytics code.',
|
|
7554
|
+
|
|
7555
|
+
'- Do not expose secret keys to browser code, public env variables, or client-executed files.',
|
|
7556
|
+
|
|
7557
|
+
'- Do not claim external provider dashboard setup is complete from repo evidence alone.',
|
|
7558
|
+
|
|
7559
|
+
'',
|
|
7560
|
+
|
|
7561
|
+
'Verification:',
|
|
7562
|
+
|
|
7563
|
+
'- Run the relevant TypeScript/build/test command for this repo.',
|
|
7564
|
+
|
|
7565
|
+
'- Confirm VibeRaven can rescan and move the missing checks to passed where repo evidence exists.',
|
|
7566
|
+
|
|
7567
|
+
'- Summarize what changed and what still requires manual provider dashboard verification.'
|
|
7568
|
+
|
|
7569
|
+
].join('\\n');
|
|
7570
|
+
|
|
7571
|
+
}
|
|
7572
|
+
|
|
7573
|
+
|
|
7574
|
+
|
|
7575
|
+
function choiceTilesHtml(areaKey, currentProvider, missions, evidenceMissions) {
|
|
7576
|
+
|
|
7577
|
+
var options = providerOptions[areaKey];
|
|
7578
|
+
|
|
7579
|
+
if (!options || !options.length) return '';
|
|
7580
|
+
|
|
7581
|
+
var projectProvider = projectProviderFor(areaKey, missions);
|
|
7582
|
+
|
|
7583
|
+
var tiles = options.map(function (opt) {
|
|
7584
|
+
|
|
7585
|
+
var p = opt.provider || opt.label;
|
|
7586
|
+
|
|
7587
|
+
var active = sameProvider(p, currentProvider);
|
|
7588
|
+
|
|
7589
|
+
var inProject = sameProvider(p, projectProvider);
|
|
7590
|
+
|
|
7591
|
+
var desc = opt.description || benefitText(p);
|
|
7592
|
+
|
|
7593
|
+
var providerOption = normKey(p) || normalizeProviderToken(p);
|
|
7594
|
+
|
|
7595
|
+
var status = inProject ? 'Using now' : active ? 'Added to setup' : 'Use this path';
|
|
7596
|
+
|
|
7597
|
+
return '<button type="button" class="studio-choice-tile' + (active ? ' studio-choice-tile--selected' : '') + (inProject ? ' studio-choice-tile--in-project' : '') +
|
|
7598
|
+
|
|
7599
|
+
'" data-provider-option="' + attrEsc(providerOption) + '" data-provider-label="' + attrEsc(p) + '" data-area-key="' + attrEsc(areaKey) + '" aria-pressed="' + (active ? 'true' : 'false') + '">' +
|
|
7600
|
+
|
|
7601
|
+
'<span class="studio-choice-tile__icon provider-logo' + logoClass(p) + '" aria-hidden="true">' + logoHtml(p, opt.label) + '</span>' +
|
|
7602
|
+
|
|
7603
|
+
'<span class="studio-choice-tile__name">' + esc(opt.label) + '</span>' +
|
|
7604
|
+
|
|
7605
|
+
'<span class="studio-choice-tile__desc">' + esc(desc) + '</span>' +
|
|
7606
|
+
|
|
7607
|
+
'<span class="studio-choice-tile__status">' + status + '</span>' +
|
|
7608
|
+
|
|
7609
|
+
'</button>';
|
|
7610
|
+
|
|
7611
|
+
}).join('');
|
|
7612
|
+
|
|
7613
|
+
var hintLabel = CHOICE_HINTS[areaKey] || ('Choose ' + (LABELS[areaKey] || areaKey).toLowerCase());
|
|
7614
|
+
|
|
7615
|
+
return '<div class="studio-setup-panel__hint"><span>' + esc(hintLabel) + '</span>' + evidenceBadgeHtml(evidenceMissions || missions) + '</div>' +
|
|
7616
|
+
|
|
7617
|
+
'<div class="studio-choice-list" role="group" aria-label="' + attrEsc(hintLabel) + '">' + tiles + '</div>';
|
|
7618
|
+
|
|
7619
|
+
}
|
|
7620
|
+
|
|
7621
|
+
|
|
7622
|
+
|
|
7623
|
+
function buildPrompt(areaKey, missions, providerLabel, automation) {
|
|
7624
|
+
|
|
7625
|
+
if (automation) {
|
|
7626
|
+
|
|
7627
|
+
if (automation.automationLevel === 'manual-only' && automation.verificationPrompt) return automation.verificationPrompt;
|
|
7628
|
+
|
|
7629
|
+
if (automation.repoPrompt) return automation.repoPrompt;
|
|
7630
|
+
|
|
7631
|
+
if (automation.promptRoutes && automation.promptRoutes['repo-fix'] && automation.promptRoutes['repo-fix'].body) {
|
|
7632
|
+
|
|
7633
|
+
return automation.promptRoutes['repo-fix'].body;
|
|
7634
|
+
|
|
7635
|
+
}
|
|
7636
|
+
|
|
7637
|
+
}
|
|
7638
|
+
|
|
7639
|
+
var areaGaps = missions[0] ? gaps.filter(function (g) { return g.primaryMapCategory === areaKey; }) : [];
|
|
7640
|
+
|
|
7641
|
+
if (areaGaps[0] && areaGaps[0].copyPrompt) return areaGaps[0].copyPrompt;
|
|
7642
|
+
|
|
7643
|
+
var missionPrompt = stackPromptFromMission(missions[0], providerLabel);
|
|
7644
|
+
|
|
7645
|
+
if (missionPrompt) return missionPrompt;
|
|
7646
|
+
|
|
7647
|
+
var missing = (missions[0] && missions[0].checks || []).filter(function (c) {
|
|
7648
|
+
|
|
7649
|
+
return c.evidenceClass === 'missing-repo-fix' || c.status === 'missing' || c.status === 'failed';
|
|
7650
|
+
|
|
7651
|
+
});
|
|
7652
|
+
|
|
7653
|
+
if (missing[0] && missing[0].promptHint) return missing[0].promptHint;
|
|
7654
|
+
|
|
7655
|
+
return setupPromptForProvider(areaKey, providerLabel);
|
|
7656
|
+
|
|
7657
|
+
}
|
|
7658
|
+
|
|
7659
|
+
|
|
7660
|
+
|
|
7661
|
+
function setupPromptForProvider(areaKey, providerLabel) {
|
|
7662
|
+
|
|
7663
|
+
var areaLabel = LABELS[areaKey] || areaKey;
|
|
7664
|
+
|
|
7665
|
+
var provider = providerLabel || areaLabel;
|
|
7666
|
+
|
|
7667
|
+
return [
|
|
7668
|
+
|
|
7669
|
+
'Set up ' + provider + ' for the ' + areaLabel + ' production section safely.',
|
|
7670
|
+
|
|
7671
|
+
'',
|
|
7672
|
+
|
|
7673
|
+
'Inspect first:',
|
|
7674
|
+
|
|
7675
|
+
'- Review package.json files, env examples, framework routes, provider helpers, server/client boundaries, and existing ' + areaLabel.toLowerCase() + ' patterns before editing.',
|
|
7676
|
+
|
|
7677
|
+
'- Identify the current framework, folder structure, naming style, validation style, and test/build commands.',
|
|
7678
|
+
|
|
7679
|
+
'- If VibeRaven SIFG leak context is present, treat its leak IDs and allowed files as the source of truth.',
|
|
7680
|
+
|
|
7681
|
+
'',
|
|
7682
|
+
|
|
7683
|
+
'Implement:',
|
|
7684
|
+
|
|
7685
|
+
'- Make the smallest repo-only changes needed to wire the ' + provider + ' path.',
|
|
7686
|
+
|
|
7687
|
+
'- Add the right package or SDK only if it is missing.',
|
|
7688
|
+
|
|
7689
|
+
'- Document required environment variable names in safe examples or setup docs without reading or exposing real secrets.',
|
|
7690
|
+
|
|
7691
|
+
'- Add server-side integration points, route handlers, webhooks, guards, or helpers only where this repo structure expects them.',
|
|
7692
|
+
|
|
7693
|
+
'',
|
|
7694
|
+
|
|
7695
|
+
'Provider constraints:',
|
|
7696
|
+
|
|
7697
|
+
'- Do not call provider APIs, mutate external projects, or edit VibeRaven dashboard state.',
|
|
7698
|
+
|
|
7699
|
+
'- Keep dashboard/provider setup as explicit manual steps.',
|
|
7700
|
+
|
|
7701
|
+
'- Do not claim live provider configuration is complete from repo changes alone.',
|
|
7702
|
+
|
|
7703
|
+
'- Keep secrets in server-only code and env examples. Use placeholder env names only.',
|
|
7704
|
+
|
|
7705
|
+
'',
|
|
7706
|
+
|
|
7707
|
+
'Verification:',
|
|
7708
|
+
|
|
7709
|
+
'- Run the closest relevant build, test, lint, or typecheck command.',
|
|
7710
|
+
|
|
7711
|
+
'- Confirm repo evidence exists for each implemented item.',
|
|
7712
|
+
|
|
7713
|
+
'- List provider dashboard checks separately as manual or read-only MCP verification.',
|
|
7714
|
+
|
|
7715
|
+
'- Rescan VibeRaven after editing so repo evidence can move to verified.'
|
|
7716
|
+
|
|
7717
|
+
].join('\\n');
|
|
7718
|
+
|
|
7719
|
+
|
|
7720
|
+
}
|
|
7721
|
+
|
|
7722
|
+
function evidenceSourceLabel(check) {
|
|
7723
|
+
|
|
7724
|
+
if (!check) return 'Unknown';
|
|
7725
|
+
|
|
7726
|
+
if (check.evidenceSource === 'provider') return 'Provider live';
|
|
7727
|
+
|
|
7728
|
+
if (check.evidenceSource === 'mcp' || check.evidenceClass === 'mcp-verifier') return 'MCP';
|
|
7729
|
+
|
|
7730
|
+
if (check.evidenceSource === 'manual' || check.evidenceClass === 'manual-dashboard' || check.status === 'user-confirmed') return 'Manual';
|
|
7731
|
+
|
|
7732
|
+
if (check.evidenceSource === 'repo' || check.evidenceClass === 'repo-verified' || check.evidenceClass === 'repo-file') return 'Repo files';
|
|
7733
|
+
|
|
7734
|
+
return 'Unknown';
|
|
7735
|
+
|
|
7736
|
+
}
|
|
7737
|
+
|
|
7738
|
+
|
|
7739
|
+
|
|
7740
|
+
function contractItem(label, detail, source, tone) {
|
|
7741
|
+
|
|
7742
|
+
return {
|
|
7743
|
+
|
|
7744
|
+
label: label || 'Unknown',
|
|
7745
|
+
|
|
7746
|
+
detail: detail || '',
|
|
7747
|
+
|
|
7748
|
+
source: source || 'Unknown',
|
|
7749
|
+
|
|
7750
|
+
tone: tone || 'neutral'
|
|
7751
|
+
|
|
7752
|
+
};
|
|
7753
|
+
|
|
7754
|
+
}
|
|
7755
|
+
|
|
7756
|
+
|
|
7757
|
+
|
|
7758
|
+
function checkDetail(check) {
|
|
7759
|
+
|
|
7760
|
+
if (!check) return '';
|
|
7761
|
+
|
|
7762
|
+
if (check.evidence && check.evidence[0]) return check.evidence[0];
|
|
7763
|
+
|
|
7764
|
+
if (check.promptHint) return check.promptHint;
|
|
7765
|
+
|
|
7766
|
+
if (check.status === 'unknown') return 'Not checked';
|
|
7767
|
+
|
|
7768
|
+
if (check.status === 'needs-connection') return 'Needs verification';
|
|
7769
|
+
|
|
7770
|
+
return '';
|
|
7771
|
+
|
|
7772
|
+
}
|
|
7773
|
+
|
|
7774
|
+
|
|
7775
|
+
|
|
7776
|
+
function buildCliSidebarContract(areaKey, label, mission, categoryGaps, providerLabel, automation, currentProvider) {
|
|
7777
|
+
|
|
7778
|
+
var connected = [];
|
|
7779
|
+
|
|
7780
|
+
var missing = [];
|
|
7781
|
+
|
|
7782
|
+
var manual = [];
|
|
7783
|
+
|
|
7784
|
+
var checks = mission && Array.isArray(mission.checks) ? mission.checks : [];
|
|
7785
|
+
|
|
7786
|
+
var mcpProvider = (automation && automation.mcpProvider) || '';
|
|
7787
|
+
|
|
7788
|
+
checks.forEach(function (check) {
|
|
7789
|
+
|
|
7790
|
+
var source = evidenceSourceLabel(check);
|
|
7791
|
+
|
|
7792
|
+
var item = contractItem(check.label, checkDetail(check), source, source === 'Repo files' ? 'repo' : source === 'MCP' ? 'mcp' : source === 'Manual' ? 'manual' : 'neutral');
|
|
7793
|
+
|
|
7794
|
+
if ((check.status === 'passed' || check.status === 'user-confirmed') && source !== 'Unknown') {
|
|
7795
|
+
|
|
7796
|
+
connected.push(item);
|
|
7797
|
+
|
|
7798
|
+
return;
|
|
7799
|
+
|
|
7800
|
+
}
|
|
7801
|
+
|
|
7802
|
+
if (check.evidenceClass === 'missing-repo-fix' || check.status === 'missing' || check.status === 'failed') {
|
|
7803
|
+
|
|
7804
|
+
missing.push(contractItem(check.label, checkDetail(check), 'Repo files', 'missing'));
|
|
7805
|
+
|
|
7806
|
+
return;
|
|
7807
|
+
|
|
7808
|
+
}
|
|
7809
|
+
|
|
7810
|
+
if (source === 'Manual' || source === 'MCP' || check.status === 'needs-connection' || check.status === 'unknown') {
|
|
7811
|
+
|
|
7812
|
+
manual.push(contractItem(check.label, checkDetail(check) || 'Not checked', source, 'manual'));
|
|
7813
|
+
|
|
7814
|
+
}
|
|
7815
|
+
|
|
7816
|
+
});
|
|
7817
|
+
|
|
7818
|
+
(Array.isArray(categoryGaps) ? categoryGaps : []).slice(0, 4).forEach(function (gap) {
|
|
7819
|
+
|
|
7820
|
+
missing.push(contractItem(gap.title || 'Product gap needs repo review', gap.detail || 'Agent can inspect and adjust repo implementation.', 'Repo files', 'missing'));
|
|
7821
|
+
|
|
7822
|
+
});
|
|
7823
|
+
|
|
7824
|
+
if (!checks.length) {
|
|
7825
|
+
|
|
7826
|
+
manual.push(contractItem('Provider live proof', 'Not checked', 'Unknown', 'manual'));
|
|
7827
|
+
|
|
7828
|
+
}
|
|
7829
|
+
|
|
7830
|
+
if (!manual.length) {
|
|
7831
|
+
|
|
7832
|
+
manual.push(contractItem('Provider live proof', 'Not checked', 'Unknown', 'manual'));
|
|
7833
|
+
|
|
7834
|
+
}
|
|
7835
|
+
|
|
7836
|
+
return {
|
|
7837
|
+
|
|
7838
|
+
areaKey: areaKey,
|
|
7839
|
+
|
|
7840
|
+
areaLabel: label || LABELS[areaKey] || areaKey,
|
|
7841
|
+
|
|
7842
|
+
providerKey: iconProviderKey(areaKey, currentProvider, mission, providerLabel),
|
|
7843
|
+
|
|
7844
|
+
providerLabel: providerLabel || 'Selected provider',
|
|
7845
|
+
|
|
7846
|
+
connected: connected,
|
|
7847
|
+
|
|
7848
|
+
missing: missing,
|
|
7849
|
+
|
|
7850
|
+
manual: manual,
|
|
7851
|
+
|
|
7852
|
+
repoPercent: typeof (mission && mission.repoReadinessPercent) === 'number'
|
|
7853
|
+
? Math.max(0, Math.min(100, Math.round(mission.repoReadinessPercent)))
|
|
7854
|
+
: typeof (mission && mission.readinessPercent) === 'number'
|
|
7855
|
+
? Math.max(0, Math.min(100, Math.round(mission.readinessPercent)))
|
|
7856
|
+
: 0,
|
|
7857
|
+
|
|
7858
|
+
providerPercent: typeof (mission && mission.providerReadinessPercent) === 'number'
|
|
7859
|
+
? Math.max(0, Math.min(100, Math.round(mission.providerReadinessPercent)))
|
|
7860
|
+
: 0,
|
|
7861
|
+
|
|
7862
|
+
hasMcpAccess: Boolean(providerLabel || mcpProvider) || checks.some(function (check) {
|
|
7863
|
+
return check.evidenceSource === 'mcp' || check.evidenceClass === 'mcp-verifier' || check.verificationStatus === 'needs_mcp';
|
|
7864
|
+
})
|
|
7865
|
+
|
|
7866
|
+
};
|
|
7867
|
+
|
|
7868
|
+
}
|
|
7869
|
+
|
|
7870
|
+
|
|
7871
|
+
|
|
7872
|
+
function contractLines(items) {
|
|
7873
|
+
|
|
7874
|
+
return (items && items.length ? items : [{ label: 'None', detail: 'No artifact-backed item.', source: 'Unknown' }]).map(function (item) {
|
|
7875
|
+
|
|
7876
|
+
return '- [' + (item.source || 'Unknown') + '] ' + item.label + (item.detail ? ': ' + item.detail : '');
|
|
7877
|
+
|
|
7878
|
+
}).join('\\n');
|
|
7879
|
+
|
|
7880
|
+
}
|
|
7881
|
+
|
|
7882
|
+
|
|
7883
|
+
|
|
7884
|
+
function focusedContractPrompt(contract) {
|
|
7885
|
+
|
|
7886
|
+
return [
|
|
7887
|
+
|
|
7888
|
+
'VibeRaven selected-node production checklist',
|
|
7889
|
+
|
|
7890
|
+
'',
|
|
7891
|
+
|
|
7892
|
+
'Node: ' + contract.areaLabel,
|
|
7893
|
+
|
|
7894
|
+
'Selected provider context: ' + contract.providerLabel,
|
|
7895
|
+
|
|
7896
|
+
'',
|
|
7897
|
+
|
|
7898
|
+
'Verified evidence:',
|
|
7899
|
+
|
|
7900
|
+
contractLines(contract.connected),
|
|
7901
|
+
|
|
7902
|
+
'',
|
|
7903
|
+
|
|
7904
|
+
'Agent-code actions:',
|
|
7905
|
+
|
|
7906
|
+
contractLines(contract.missing),
|
|
7907
|
+
|
|
7908
|
+
'',
|
|
7909
|
+
|
|
7910
|
+
'Human-provider actions:',
|
|
7911
|
+
|
|
7912
|
+
contractLines(contract.manual),
|
|
7913
|
+
|
|
7914
|
+
'',
|
|
7915
|
+
|
|
7916
|
+
'Rules:',
|
|
7917
|
+
|
|
7918
|
+
'- Selected provider is context only and does not mean connected.',
|
|
7919
|
+
|
|
7920
|
+
'- Connected evidence requires Repo files, Provider live, MCP, or Manual confirmation in the artifact.',
|
|
7921
|
+
|
|
7922
|
+
'- Do not represent human-provider actions as completed code fixes.'
|
|
7923
|
+
|
|
7924
|
+
].join('\\n');
|
|
7925
|
+
|
|
7926
|
+
}
|
|
7927
|
+
|
|
7928
|
+
|
|
7929
|
+
|
|
7930
|
+
function sidebarStatusBadge(contract) {
|
|
7931
|
+
|
|
7932
|
+
if ((contract.connected || []).some(function (item) { return item.source === 'Repo files'; })) return 'Repo evidence found';
|
|
7933
|
+
|
|
7934
|
+
if ((contract.missing || []).length) return 'Missing repo fixes';
|
|
7935
|
+
|
|
7936
|
+
if ((contract.manual || []).length) return 'Manual check';
|
|
7937
|
+
|
|
7938
|
+
return 'Selected only';
|
|
7939
|
+
|
|
7940
|
+
}
|
|
7941
|
+
|
|
7942
|
+
|
|
7943
|
+
|
|
7944
|
+
function sidebarReadinessSentence(contract) {
|
|
7945
|
+
|
|
7946
|
+
var hasRepo = (contract.connected || []).some(function (item) { return item.source === 'Repo files'; });
|
|
7947
|
+
|
|
7948
|
+
var hasLive = (contract.connected || []).some(function (item) { return item.source === 'Provider live' || item.source === 'MCP' || item.source === 'Manual'; });
|
|
7949
|
+
|
|
7950
|
+
if (hasRepo && !hasLive) return 'Repo found. Live not checked.';
|
|
7951
|
+
|
|
7952
|
+
if (hasLive) return 'Live proof exists in the scan artifact.';
|
|
7953
|
+
|
|
7954
|
+
if ((contract.missing || []).length) return 'Repo gaps found. Live not checked.';
|
|
7955
|
+
|
|
7956
|
+
return 'No verified evidence yet.';
|
|
7957
|
+
|
|
7958
|
+
}
|
|
7959
|
+
|
|
7960
|
+
|
|
7961
|
+
|
|
7962
|
+
function contractMetricHtml(label, percent, value, isLive) {
|
|
7963
|
+
|
|
7964
|
+
var safePercent = Math.max(0, Math.min(100, Math.round(percent || 0)));
|
|
7965
|
+
|
|
7966
|
+
return '<div class="studio-sidebar-contract__compact-metric' + (isLive ? ' studio-sidebar-contract__compact-metric--live' : '') + '">' +
|
|
7967
|
+
|
|
7968
|
+
'<span>' + esc(label) + '</span>' +
|
|
7969
|
+
|
|
7970
|
+
'<strong>' + esc(value || (safePercent + '%')) + '</strong>' +
|
|
7971
|
+
|
|
7972
|
+
'<i class="studio-sidebar-contract__compact-meter" aria-hidden="true"><b style="width:' + safePercent + '%"></b></i>' +
|
|
7973
|
+
|
|
7974
|
+
'</div>';
|
|
7975
|
+
|
|
7976
|
+
}
|
|
6041
7977
|
|
|
6042
|
-
'',
|
|
6043
7978
|
|
|
6044
|
-
'Current ' + subject + ' readiness: ' + passed.length + '/' + Math.max(total, 1) + ' repo checks passed (' + (mission.readinessPercent || 0) + '%).',
|
|
6045
7979
|
|
|
6046
|
-
|
|
7980
|
+
function contractReadinessHtml(contract) {
|
|
6047
7981
|
|
|
6048
|
-
|
|
7982
|
+
var providerKey = contract.providerKey || contract.providerLabel || contract.areaLabel;
|
|
6049
7983
|
|
|
6050
|
-
|
|
7984
|
+
var providerStatus = contract.providerPercent > 0 ? contract.providerPercent + '%' : 'Not checked';
|
|
6051
7985
|
|
|
6052
|
-
|
|
7986
|
+
return '<section class="studio-sidebar-contract__readiness" aria-label="' + attrEsc(contract.providerLabel + ' readiness') + '">' +
|
|
6053
7987
|
|
|
6054
|
-
'
|
|
7988
|
+
'<div class="studio-sidebar-contract__head">' +
|
|
6055
7989
|
|
|
6056
|
-
|
|
7990
|
+
'<span class="studio-sidebar-contract__logo provider-logo' + logoClass(providerKey) + '" title="' + attrEsc(contract.providerLabel) + '" aria-hidden="true">' + logoHtml(providerKey, contract.providerLabel) + '</span>' +
|
|
6057
7991
|
|
|
6058
|
-
''
|
|
7992
|
+
'<div class="studio-sidebar-contract__head-text"><strong>' + esc(contract.providerLabel) + '</strong><span>' + esc(contract.areaLabel) + '</span></div>' +
|
|
6059
7993
|
|
|
6060
|
-
'
|
|
7994
|
+
'<span class="studio-sidebar-contract__status">' + esc(sidebarStatusBadge(contract)) + '</span>' +
|
|
6061
7995
|
|
|
6062
|
-
|
|
7996
|
+
'</div>' +
|
|
6063
7997
|
|
|
6064
|
-
''
|
|
7998
|
+
'<div class="studio-sidebar-contract__compact-metrics">' +
|
|
6065
7999
|
|
|
6066
|
-
'
|
|
8000
|
+
contractMetricHtml('Repo files', contract.repoPercent, contract.repoPercent + '%', false) +
|
|
6067
8001
|
|
|
6068
|
-
'',
|
|
8002
|
+
contractMetricHtml('Provider live', contract.providerPercent, providerStatus, true) +
|
|
6069
8003
|
|
|
6070
|
-
'
|
|
8004
|
+
'</div>' +
|
|
6071
8005
|
|
|
6072
|
-
'
|
|
8006
|
+
'<p class="studio-sidebar-contract__summary">' + esc(sidebarReadinessSentence(contract)) + '</p>' +
|
|
6073
8007
|
|
|
6074
|
-
'
|
|
8008
|
+
'</section>';
|
|
6075
8009
|
|
|
6076
|
-
|
|
8010
|
+
}
|
|
6077
8011
|
|
|
6078
|
-
'4. Keep external dashboard work explicit instead of claiming it from repo evidence.',
|
|
6079
8012
|
|
|
6080
|
-
'',
|
|
6081
8013
|
|
|
6082
|
-
|
|
8014
|
+
function contractRepoSignalsHtml(contract) {
|
|
6083
8015
|
|
|
6084
|
-
|
|
8016
|
+
var repoItems = (contract.connected || []).filter(function (item) { return item.source === 'Repo files'; });
|
|
6085
8017
|
|
|
6086
|
-
|
|
8018
|
+
if (!repoItems.length) return '';
|
|
6087
8019
|
|
|
6088
|
-
|
|
8020
|
+
var rows = repoItems.slice(0, 6).map(function (item) {
|
|
6089
8021
|
|
|
6090
|
-
''
|
|
8022
|
+
return '<li><strong>' + esc(item.label || 'Repo evidence') + '</strong><span>' + esc(item.source || 'Repo files') + '</span></li>';
|
|
6091
8023
|
|
|
6092
|
-
|
|
8024
|
+
}).join('');
|
|
6093
8025
|
|
|
6094
|
-
|
|
8026
|
+
return '<section class="studio-sidebar-contract__repo-signals">' +
|
|
6095
8027
|
|
|
6096
|
-
'
|
|
8028
|
+
'<h4>' + repoItems.length + ' repo signal' + (repoItems.length === 1 ? '' : 's') + ' in the codebase.</h4>' +
|
|
6097
8029
|
|
|
6098
|
-
'-
|
|
8030
|
+
'<ul class="studio-sidebar-contract__signal-list">' + rows + '</ul>' +
|
|
6099
8031
|
|
|
6100
|
-
|
|
8032
|
+
'</section>';
|
|
6101
8033
|
|
|
6102
8034
|
}
|
|
6103
8035
|
|
|
6104
8036
|
|
|
6105
8037
|
|
|
6106
|
-
function
|
|
8038
|
+
function contractAttentionItems(contract) {
|
|
6107
8039
|
|
|
6108
|
-
var
|
|
8040
|
+
var items = [];
|
|
6109
8041
|
|
|
6110
|
-
|
|
8042
|
+
(contract.missing || []).slice(0, 3).forEach(function (item) {
|
|
6111
8043
|
|
|
6112
|
-
|
|
8044
|
+
items.push({
|
|
6113
8045
|
|
|
6114
|
-
|
|
8046
|
+
label: item.label,
|
|
6115
8047
|
|
|
6116
|
-
|
|
8048
|
+
detail: item.detail || 'Repo/config check needs attention.',
|
|
6117
8049
|
|
|
6118
|
-
|
|
8050
|
+
source: 'Agent-code',
|
|
6119
8051
|
|
|
6120
|
-
|
|
8052
|
+
next: 'Ask the agent to inspect related files and patch only repo code/config.'
|
|
6121
8053
|
|
|
6122
|
-
|
|
8054
|
+
});
|
|
6123
8055
|
|
|
6124
|
-
|
|
8056
|
+
});
|
|
6125
8057
|
|
|
6126
|
-
|
|
8058
|
+
(contract.manual || []).slice(0, 3).forEach(function (item) {
|
|
6127
8059
|
|
|
6128
|
-
|
|
8060
|
+
items.push({
|
|
6129
8061
|
|
|
6130
|
-
|
|
8062
|
+
label: item.label,
|
|
6131
8063
|
|
|
6132
|
-
|
|
8064
|
+
detail: item.detail || 'MCP/API/dashboard required.',
|
|
6133
8065
|
|
|
6134
|
-
|
|
8066
|
+
source: item.source === 'MCP' ? 'MCP' : 'Manual',
|
|
6135
8067
|
|
|
6136
|
-
'
|
|
8068
|
+
next: item.source === 'MCP' ? 'Use read-only MCP/API verification.' : 'Ask the user to confirm the provider dashboard.'
|
|
6137
8069
|
|
|
6138
|
-
|
|
8070
|
+
});
|
|
6139
8071
|
|
|
6140
|
-
})
|
|
8072
|
+
});
|
|
6141
8073
|
|
|
6142
|
-
|
|
8074
|
+
return items;
|
|
6143
8075
|
|
|
6144
|
-
|
|
8076
|
+
}
|
|
6145
8077
|
|
|
6146
|
-
'<div class="studio-choice-list" role="group" aria-label="' + esc(hintLabel) + '">' + tiles + '</div>';
|
|
6147
8078
|
|
|
6148
|
-
}
|
|
6149
8079
|
|
|
8080
|
+
function contractAttentionHtml(contract) {
|
|
6150
8081
|
|
|
8082
|
+
var items = contractAttentionItems(contract);
|
|
6151
8083
|
|
|
6152
|
-
|
|
8084
|
+
if (!items.length) return '';
|
|
6153
8085
|
|
|
6154
|
-
|
|
8086
|
+
function attentionRow(item) {
|
|
6155
8087
|
|
|
6156
|
-
|
|
8088
|
+
return '<li class="studio-sidebar-contract__attention-item">' +
|
|
6157
8089
|
|
|
6158
|
-
|
|
8090
|
+
'<div class="studio-sidebar-contract__attention-top"><strong>' + esc(item.label || 'Attention') + '</strong><em>' + esc(item.source || 'Verify') + '</em></div>' +
|
|
6159
8091
|
|
|
6160
|
-
|
|
8092
|
+
(item.detail ? '<span>' + esc(item.detail) + '</span>' : '') +
|
|
6161
8093
|
|
|
6162
|
-
|
|
8094
|
+
(item.next ? '<b>' + esc(item.next) + '</b>' : '') +
|
|
6163
8095
|
|
|
6164
|
-
|
|
8096
|
+
'</li>';
|
|
6165
8097
|
|
|
6166
8098
|
}
|
|
6167
8099
|
|
|
6168
|
-
var
|
|
8100
|
+
var first = attentionRow(items[0]);
|
|
6169
8101
|
|
|
6170
|
-
|
|
8102
|
+
var more = items.length > 1
|
|
6171
8103
|
|
|
6172
|
-
|
|
8104
|
+
? '<li class="studio-sidebar-contract__attention-more"><details class="studio-sidebar-contract__attention-details"><summary>+' + (items.length - 1) + ' more details</summary><ul class="studio-sidebar-contract__attention-detail-list">' +
|
|
6173
8105
|
|
|
6174
|
-
|
|
8106
|
+
items.slice(1).map(function (item) {
|
|
6175
8107
|
|
|
6176
|
-
|
|
8108
|
+
return '<li><strong>' + esc(item.label || 'Attention') + '</strong>' +
|
|
6177
8109
|
|
|
6178
|
-
|
|
8110
|
+
(item.detail ? '<span>' + esc(item.detail) + '</span>' : '') +
|
|
6179
8111
|
|
|
6180
|
-
|
|
8112
|
+
(item.next ? '<b>' + esc(item.next) + '</b>' : '') +
|
|
6181
8113
|
|
|
6182
|
-
|
|
8114
|
+
'</li>';
|
|
6183
8115
|
|
|
6184
|
-
|
|
8116
|
+
}).join('') +
|
|
6185
8117
|
|
|
6186
|
-
|
|
8118
|
+
'</ul></details></li>'
|
|
6187
8119
|
|
|
8120
|
+
: '';
|
|
6188
8121
|
|
|
8122
|
+
return '<section class="studio-sidebar-contract__attention"><h4>Attention</h4><ul class="studio-sidebar-contract__attention-list">' + first + more + '</ul></section>';
|
|
6189
8123
|
|
|
6190
|
-
|
|
8124
|
+
}
|
|
6191
8125
|
|
|
6192
|
-
var areaLabel = LABELS[areaKey] || areaKey;
|
|
6193
8126
|
|
|
6194
|
-
var provider = providerLabel || areaLabel;
|
|
6195
8127
|
|
|
6196
|
-
|
|
8128
|
+
function contractNextActionHtml(contract, prompt) {
|
|
6197
8129
|
|
|
6198
|
-
|
|
8130
|
+
var buttonText = (contract.missing || []).length ? 'Copy focused agent prompt' : (contract.manual || []).length ? 'Copy live check prompt' : 'Copy verification prompt';
|
|
6199
8131
|
|
|
6200
|
-
|
|
8132
|
+
var note = (contract.missing || []).length ? 'Inspect repo wiring, patch scoped gaps, then rescan.' : (contract.manual || []).length ? 'Confirm in dashboard or connect MCP.' : 'Inspect repo wiring, then verify live setup.';
|
|
6201
8133
|
|
|
6202
|
-
|
|
8134
|
+
var accessClass = contract.hasMcpAccess ? ' studio-sidebar-contract__access-state--confirmed' : ' studio-sidebar-contract__access-state--needs-connection';
|
|
6203
8135
|
|
|
6204
|
-
|
|
8136
|
+
var accessLabel = contract.hasMcpAccess ? 'Available' : 'Connect';
|
|
6205
8137
|
|
|
6206
|
-
|
|
8138
|
+
return '<div class="studio-sidebar-contract__next-action">' +
|
|
6207
8139
|
|
|
6208
|
-
'
|
|
8140
|
+
'<strong>Next best action</strong>' +
|
|
6209
8141
|
|
|
6210
|
-
'
|
|
8142
|
+
'<p>' + esc(note) + '</p>' +
|
|
6211
8143
|
|
|
6212
|
-
'
|
|
8144
|
+
'<div class="studio-sidebar-contract__access-state' + accessClass + '"><span>MCP/API access</span><b>' + esc(accessLabel) + '</b></div>' +
|
|
6213
8145
|
|
|
6214
|
-
'
|
|
8146
|
+
'<div class="studio-sidebar-contract__actions"><button type="button" class="studio-action-button studio-action-button--primary" data-copy-prompt="' + attrEsc(prompt) + '">' + esc(buttonText) + '</button></div>' +
|
|
6215
8147
|
|
|
6216
|
-
''
|
|
8148
|
+
'</div>';
|
|
6217
8149
|
|
|
6218
|
-
|
|
8150
|
+
}
|
|
6219
8151
|
|
|
6220
|
-
'- Do not rewrite unrelated product, auth, payment, database, deployment, or analytics code.',
|
|
6221
8152
|
|
|
6222
|
-
'- Do not put secret keys in client code, public env variables, or browser-executed files.',
|
|
6223
8153
|
|
|
6224
|
-
|
|
8154
|
+
function selectedNodeContractHtml(contract) {
|
|
6225
8155
|
|
|
6226
|
-
|
|
8156
|
+
var prompt = focusedContractPrompt(contract);
|
|
6227
8157
|
|
|
6228
|
-
|
|
8158
|
+
return '<section class="studio-sidebar-contract" aria-label="Selected node production checklist">' +
|
|
6229
8159
|
|
|
6230
|
-
|
|
8160
|
+
contractReadinessHtml(contract) +
|
|
6231
8161
|
|
|
6232
|
-
|
|
8162
|
+
contractRepoSignalsHtml(contract) +
|
|
6233
8163
|
|
|
6234
|
-
|
|
8164
|
+
contractAttentionHtml(contract) +
|
|
6235
8165
|
|
|
6236
|
-
|
|
8166
|
+
contractNextActionHtml(contract, prompt) +
|
|
6237
8167
|
|
|
8168
|
+
'</section>';
|
|
6238
8169
|
|
|
6239
8170
|
}
|
|
6240
8171
|
|
|
6241
8172
|
|
|
6242
8173
|
|
|
6243
|
-
function setupActionsHtml(areaKey, missions, providerLabel, automation) {
|
|
8174
|
+
function setupActionsHtml(areaKey, missions, providerLabel, automation, currentProvider) {
|
|
6244
8175
|
|
|
6245
8176
|
var prompt = buildPrompt(areaKey, missions, providerLabel, automation);
|
|
6246
8177
|
|
|
@@ -6264,6 +8195,8 @@ var PANEL_CLIENT_SCRIPT = `
|
|
|
6264
8195
|
|
|
6265
8196
|
var supportsMcp = Boolean(mcpProvider);
|
|
6266
8197
|
|
|
8198
|
+
var providerKey = iconProviderKey(areaKey, currentProvider, missions[0], providerLabel);
|
|
8199
|
+
|
|
6267
8200
|
var title = esc(providerLabel || LABELS[areaKey] || areaKey) + (isManualOnly ? ' manual check' : hasFixes ? ' fix prompt' : ' setup');
|
|
6268
8201
|
|
|
6269
8202
|
var meta = supportsMcp ? 'MCP verification available' : 'Prompt only';
|
|
@@ -6286,7 +8219,13 @@ var PANEL_CLIENT_SCRIPT = `
|
|
|
6286
8219
|
|
|
6287
8220
|
return '<section class="studio-setup-actions" aria-label="Setup actions">' +
|
|
6288
8221
|
|
|
6289
|
-
'<div class="studio-setup-actions__head"
|
|
8222
|
+
'<div class="studio-setup-actions__head">' +
|
|
8223
|
+
|
|
8224
|
+
'<span class="studio-setup-actions__logo provider-logo' + logoClass(providerKey) + '" title="' + attrEsc(providerLabel || providerKey) + '" aria-hidden="true">' + logoHtml(providerKey, providerLabel) + '</span>' +
|
|
8225
|
+
|
|
8226
|
+
'<div class="studio-setup-actions__title"><strong>' + title + '</strong><span>' + meta + '</span></div>' +
|
|
8227
|
+
|
|
8228
|
+
'</div>' +
|
|
6290
8229
|
|
|
6291
8230
|
'<p class="studio-setup-actions__copy">' + esc(copy) + '</p>' +
|
|
6292
8231
|
|
|
@@ -6342,39 +8281,40 @@ var PANEL_CLIENT_SCRIPT = `
|
|
|
6342
8281
|
|
|
6343
8282
|
var repoItems = [
|
|
6344
8283
|
|
|
6345
|
-
{ label: provider + '
|
|
8284
|
+
{ label: provider + ' repo wiring', detail: 'Not checked' },
|
|
6346
8285
|
|
|
6347
|
-
{ label: provider + ' env names
|
|
8286
|
+
{ label: provider + ' env names', detail: 'Unknown' },
|
|
6348
8287
|
|
|
6349
|
-
{ label: provider + ' server
|
|
8288
|
+
{ label: provider + ' server route', detail: 'Unknown' }
|
|
6350
8289
|
|
|
6351
8290
|
];
|
|
6352
8291
|
|
|
6353
8292
|
var manualItems = [
|
|
6354
8293
|
|
|
6355
|
-
{ label: 'Production
|
|
8294
|
+
{ label: 'Production account', detail: 'Not checked' },
|
|
6356
8295
|
|
|
6357
|
-
{ label: '
|
|
8296
|
+
{ label: 'Webhook or credentials', detail: 'Not checked' }
|
|
6358
8297
|
|
|
6359
8298
|
];
|
|
6360
8299
|
|
|
6361
8300
|
var external = automation && automation.mcpProvider
|
|
6362
8301
|
|
|
6363
|
-
? groupHtml('
|
|
8302
|
+
? groupHtml('Provider live check', [{ label: provider + ' read-only MCP check available', detail: 'Use read-only MCP verification when already configured by the IDE.' }], 'external')
|
|
6364
8303
|
|
|
6365
8304
|
: '';
|
|
6366
8305
|
|
|
6367
|
-
return '<section class="studio-verification studio-provider-readiness" aria-label="' +
|
|
8306
|
+
return '<section class="studio-verification studio-provider-readiness" aria-label="' + attrEsc(provider + ' setup readiness') + '">' +
|
|
6368
8307
|
|
|
6369
8308
|
'<h3 class="studio-verification__title">' + esc(provider) + '</h3>' +
|
|
6370
8309
|
|
|
6371
|
-
readinessMetersHtml(0, 0, 'Not checked') +
|
|
8310
|
+
readinessMetersHtml(0, 0, 'Not checked', provider) +
|
|
6372
8311
|
|
|
6373
|
-
'<p class="studio-provider-readiness__note">Provider live moves after MCP verification or manual dashboard confirmation. Repo scans cannot prove live provider state.</p>' +
|
|
6374
8312
|
|
|
6375
|
-
|
|
8313
|
+
repoEvidenceCardHtml(repoItems.map(function (item) {
|
|
6376
8314
|
|
|
6377
|
-
|
|
8315
|
+
return { label: item.label, detail: item.detail, tone: 'missing' };
|
|
8316
|
+
|
|
8317
|
+
}), repoItems.length) +
|
|
6378
8318
|
|
|
6379
8319
|
external +
|
|
6380
8320
|
|
|
@@ -6386,18 +8326,24 @@ var PANEL_CLIENT_SCRIPT = `
|
|
|
6386
8326
|
|
|
6387
8327
|
|
|
6388
8328
|
|
|
6389
|
-
function readinessMetersHtml(repoPercent, providerPercent, providerStatus) {
|
|
8329
|
+
function readinessMetersHtml(repoPercent, providerPercent, providerStatus, providerLabel) {
|
|
6390
8330
|
|
|
6391
8331
|
var repo = Math.max(0, Math.min(100, Math.round(repoPercent || 0)));
|
|
6392
8332
|
|
|
6393
8333
|
var provider = Math.max(0, Math.min(100, Math.round(providerPercent || 0)));
|
|
6394
8334
|
var providerValue = providerStatus || (provider + '%');
|
|
6395
8335
|
|
|
6396
|
-
return '<div class="studio-provider
|
|
8336
|
+
return '<div class="verification-card studio-mission-card studio-mission-card--provider" aria-label="Repo and live provider readiness">' +
|
|
8337
|
+
|
|
8338
|
+
'<span class="studio-mission-card__title">' + esc(providerLabel || 'Provider') + '</span>' +
|
|
8339
|
+
|
|
8340
|
+
'<div class="verification-meter studio-mission-card__meters">' +
|
|
8341
|
+
|
|
8342
|
+
'<b class="studio-mission-card__meter-row studio-mission-card__meter-row--repo"><span class="studio-mission-card__meter-label">Repo files</span><span class="verification-meter__bar studio-mission-card__meter" aria-hidden="true"><span class="verification-meter__fill studio-mission-card__meter-fill" style="width:' + repo + '%"></span></span><span class="studio-mission-card__meter-value">' + repo + '%</span></b>' +
|
|
6397
8343
|
|
|
6398
|
-
'<
|
|
8344
|
+
'<b class="studio-mission-card__meter-row studio-mission-card__meter-row--provider"><span class="studio-mission-card__meter-label">Provider live</span><span class="verification-meter__bar studio-mission-card__meter" aria-hidden="true"><span class="verification-meter__fill studio-mission-card__meter-fill" style="width:' + provider + '%"></span></span><span class="studio-mission-card__meter-value">' + esc(providerValue) + '</span></b>' +
|
|
6399
8345
|
|
|
6400
|
-
'
|
|
8346
|
+
'</div>' +
|
|
6401
8347
|
|
|
6402
8348
|
'</div>';
|
|
6403
8349
|
|
|
@@ -6413,6 +8359,90 @@ var PANEL_CLIENT_SCRIPT = `
|
|
|
6413
8359
|
|
|
6414
8360
|
}
|
|
6415
8361
|
|
|
8362
|
+
function checkGroupKey(check) {
|
|
8363
|
+
|
|
8364
|
+
if (check.evidenceClass === 'manual-dashboard') return 'manual-dashboard';
|
|
8365
|
+
|
|
8366
|
+
if (check.evidenceSource === 'provider' || check.evidenceSource === 'mcp' || check.evidenceClass === 'mcp-verifier' || check.status === 'needs-connection' || check.status === 'unknown') return 'mcp-verifier';
|
|
8367
|
+
|
|
8368
|
+
if (check.evidenceClass === 'repo-verified' || check.status === 'passed') return 'repo-verified';
|
|
8369
|
+
|
|
8370
|
+
return 'missing-repo-fix';
|
|
8371
|
+
|
|
8372
|
+
}
|
|
8373
|
+
|
|
8374
|
+
function providerChecksForMission(mission) {
|
|
8375
|
+
|
|
8376
|
+
return (mission.checks || []).filter(function (check) {
|
|
8377
|
+
|
|
8378
|
+
return check.evidenceSource === 'provider' || check.evidenceSource === 'mcp' || check.evidenceClass === 'mcp-verifier';
|
|
8379
|
+
|
|
8380
|
+
});
|
|
8381
|
+
|
|
8382
|
+
}
|
|
8383
|
+
|
|
8384
|
+
function providerStatusValue(mission, providerPercent) {
|
|
8385
|
+
|
|
8386
|
+
var checks = providerChecksForMission(mission);
|
|
8387
|
+
|
|
8388
|
+
if (!checks.length) return 'No live checks';
|
|
8389
|
+
|
|
8390
|
+
var needsMcp = checks.some(function (check) {
|
|
8391
|
+
|
|
8392
|
+
return check.status === 'needs-connection' || check.verificationStatus === 'needs_mcp';
|
|
8393
|
+
|
|
8394
|
+
});
|
|
8395
|
+
|
|
8396
|
+
if (needsMcp) return 'Needs MCP';
|
|
8397
|
+
|
|
8398
|
+
return providerPercent > 0 ? providerPercent + '%' : 'Not checked';
|
|
8399
|
+
|
|
8400
|
+
}
|
|
8401
|
+
|
|
8402
|
+
function diffHtml(mission) {
|
|
8403
|
+
|
|
8404
|
+
var layer = artifact && artifact.missionGraph && artifact.missionGraph.verificationLayer;
|
|
8405
|
+
|
|
8406
|
+
var diffs = layer && Array.isArray(layer.diffs) ? layer.diffs : [];
|
|
8407
|
+
|
|
8408
|
+
var provider = normalizeProviderToken(mission.provider || '');
|
|
8409
|
+
|
|
8410
|
+
var area = mission.area || '';
|
|
8411
|
+
|
|
8412
|
+
var matches = diffs.filter(function (diff) {
|
|
8413
|
+
|
|
8414
|
+
return normalizeProviderToken(diff.provider || '') === provider && (!area || diff.area === area);
|
|
8415
|
+
|
|
8416
|
+
});
|
|
8417
|
+
|
|
8418
|
+
if (!matches.length) return '';
|
|
8419
|
+
|
|
8420
|
+
var rows = matches.slice(0, 4).map(function (diff) {
|
|
8421
|
+
|
|
8422
|
+
return '<div class="studio-mismatch-card__row">' +
|
|
8423
|
+
|
|
8424
|
+
'<span class="studio-mismatch-card__title">' + esc(diff.title || 'Provider mismatch') + '</span>' +
|
|
8425
|
+
|
|
8426
|
+
'<div class="studio-mismatch-card__comparison">' +
|
|
8427
|
+
|
|
8428
|
+
'<span class="studio-mismatch-card__side studio-mismatch-card__side--repo"><b>Repo</b><em>' + esc(diff.repoExpectation || 'Expected setup evidence in repository') + '</em></span>' +
|
|
8429
|
+
|
|
8430
|
+
'<span class="studio-mismatch-card__side studio-mismatch-card__side--live"><b>Live</b><em>' + esc(diff.providerActual || 'Not verified') + '</em></span>' +
|
|
8431
|
+
|
|
8432
|
+
'</div></div>';
|
|
8433
|
+
|
|
8434
|
+
}).join('');
|
|
8435
|
+
|
|
8436
|
+
return '<div class="studio-mismatch-card">' +
|
|
8437
|
+
|
|
8438
|
+
'<div class="studio-mismatch-card__head"><strong>Mismatch</strong><span class="studio-mismatch-card__count">' + matches.length + '</span></div>' +
|
|
8439
|
+
|
|
8440
|
+
'<p class="studio-mismatch-card__note">Repo evidence is present, but the live provider still needs MCP or dashboard confirmation.</p>' +
|
|
8441
|
+
|
|
8442
|
+
'<div class="studio-mismatch-card__list">' + rows + '</div></div>';
|
|
8443
|
+
|
|
8444
|
+
}
|
|
8445
|
+
|
|
6416
8446
|
|
|
6417
8447
|
|
|
6418
8448
|
function groupHtml(label, items, tone) {
|
|
@@ -6423,7 +8453,7 @@ var PANEL_CLIENT_SCRIPT = `
|
|
|
6423
8453
|
|
|
6424
8454
|
var rows = slice.map(function (item) {
|
|
6425
8455
|
|
|
6426
|
-
var title = item.detail ? ' title="' +
|
|
8456
|
+
var title = item.detail ? ' title="' + attrEsc(item.detail) + '"' : '';
|
|
6427
8457
|
|
|
6428
8458
|
return '<li class="studio-verification__item' + (tone === 'manual' ? ' studio-verification__item--manual' : '') + '"' + title + '><span class="studio-verification__item-label">' + esc(item.label) + '</span></li>';
|
|
6429
8459
|
|
|
@@ -6445,6 +8475,44 @@ var PANEL_CLIENT_SCRIPT = `
|
|
|
6445
8475
|
|
|
6446
8476
|
}
|
|
6447
8477
|
|
|
8478
|
+
function repoEvidenceCardHtml(repoItems, missingCount) {
|
|
8479
|
+
|
|
8480
|
+
if (!repoItems.length) return '';
|
|
8481
|
+
|
|
8482
|
+
var rows = repoItems.slice(0, 7).map(function (item) {
|
|
8483
|
+
|
|
8484
|
+
var tone = item.tone || 'verified';
|
|
8485
|
+
var statusLabel = tone === 'missing' ? 'Fix needed' : 'Verified';
|
|
8486
|
+
var title = item.detail ? ' title="' + attrEsc(item.detail) + '"' : '';
|
|
8487
|
+
|
|
8488
|
+
return '<li class="studio-mission-card__check studio-mission-card__check--' + esc(tone) + '"' + title + '>' +
|
|
8489
|
+
|
|
8490
|
+
'<b class="studio-mission-card__check-label">' + esc(item.label) + '</b>' +
|
|
8491
|
+
|
|
8492
|
+
'<em class="studio-mission-card__check-status">' + esc(statusLabel) + '</em>' +
|
|
8493
|
+
|
|
8494
|
+
'</li>';
|
|
8495
|
+
|
|
8496
|
+
}).join('');
|
|
8497
|
+
|
|
8498
|
+
return '<div class="repo-evidence-card studio-mission-card studio-mission-card--repo">' +
|
|
8499
|
+
|
|
8500
|
+
'<span class="studio-mission-card__title">' +
|
|
8501
|
+
|
|
8502
|
+
(missingCount > 0
|
|
8503
|
+
|
|
8504
|
+
? missingCount + ' repo fix' + (missingCount === 1 ? '' : 'es') + ' in the codebase.'
|
|
8505
|
+
|
|
8506
|
+
: repoItems.length + ' repo check' + (repoItems.length === 1 ? '' : 's') + ' verified in the codebase.') +
|
|
8507
|
+
|
|
8508
|
+
'</span>' +
|
|
8509
|
+
|
|
8510
|
+
'<ul class="studio-mission-card__check-list">' + rows + '</ul>' +
|
|
8511
|
+
|
|
8512
|
+
'</div>';
|
|
8513
|
+
|
|
8514
|
+
}
|
|
8515
|
+
|
|
6448
8516
|
|
|
6449
8517
|
|
|
6450
8518
|
function missionBlockHtml(missions, providerLabel) {
|
|
@@ -6467,65 +8535,54 @@ var PANEL_CLIENT_SCRIPT = `
|
|
|
6467
8535
|
|
|
6468
8536
|
mission.checks.forEach(function (check) {
|
|
6469
8537
|
|
|
6470
|
-
var bucket =
|
|
8538
|
+
var bucket = checkGroupKey(check);
|
|
6471
8539
|
|
|
6472
8540
|
groups[bucket].push(checkToItem(check));
|
|
6473
8541
|
|
|
6474
8542
|
});
|
|
6475
8543
|
|
|
6476
8544
|
var actionable = groups['repo-verified'].length + groups['missing-repo-fix'].length;
|
|
6477
|
-
var
|
|
6478
|
-
var
|
|
6479
|
-
|
|
6480
|
-
|
|
6481
|
-
|
|
6482
|
-
|
|
6483
|
-
|
|
6484
|
-
|
|
6485
|
-
|
|
6486
|
-
var summary =
|
|
8545
|
+
var providerChecks = providerChecksForMission(mission);
|
|
8546
|
+
var providerPercent = typeof mission.providerReadinessPercent === 'number'
|
|
8547
|
+
? Math.max(0, Math.min(100, Math.round(mission.providerReadinessPercent)))
|
|
8548
|
+
: 0;
|
|
8549
|
+
var providerValue = providerStatusValue(mission, providerPercent);
|
|
8550
|
+
var repoPercent = typeof mission.repoReadinessPercent === 'number'
|
|
8551
|
+
? Math.max(0, Math.min(100, Math.round(mission.repoReadinessPercent)))
|
|
8552
|
+
: (mission.readinessPercent || 0);
|
|
8553
|
+
var repoItems = groups['missing-repo-fix'].map(function (item) {
|
|
6487
8554
|
|
|
6488
|
-
|
|
8555
|
+
return { label: item.label, detail: item.detail, tone: 'missing' };
|
|
6489
8556
|
|
|
6490
|
-
|
|
8557
|
+
}).concat(groups['repo-verified'].map(function (item) {
|
|
6491
8558
|
|
|
6492
|
-
|
|
8559
|
+
return { label: item.label, detail: item.detail, tone: 'verified' };
|
|
6493
8560
|
|
|
6494
|
-
|
|
6495
|
-
|
|
6496
|
-
(groups['missing-repo-fix'].length === 1 ? '' : 's') + ', then rescan.';
|
|
6497
|
-
|
|
6498
|
-
}
|
|
8561
|
+
}));
|
|
6499
8562
|
|
|
6500
8563
|
var body =
|
|
6501
8564
|
|
|
6502
|
-
|
|
8565
|
+
repoEvidenceCardHtml(repoItems, groups['missing-repo-fix'].length) +
|
|
6503
8566
|
|
|
6504
|
-
groupHtml('
|
|
8567
|
+
groupHtml('Provider live check', groups['mcp-verifier'], 'external') +
|
|
6505
8568
|
|
|
6506
|
-
groupHtml('
|
|
8569
|
+
groupHtml('Manual dashboard check', groups['manual-dashboard'], 'manual') +
|
|
6507
8570
|
|
|
6508
|
-
|
|
8571
|
+
diffHtml(mission);
|
|
6509
8572
|
|
|
6510
8573
|
if (!body) return '';
|
|
6511
8574
|
|
|
6512
8575
|
return '<section class="studio-verification studio-wiring studio-mission-graph" aria-label="Mission evidence">' +
|
|
6513
8576
|
|
|
6514
|
-
|
|
6515
|
-
|
|
6516
|
-
readinessMetersHtml(mission.readinessPercent || 0, providerPercent, providerValue) +
|
|
8577
|
+
readinessMetersHtml(repoPercent, providerPercent, providerValue, providerLabel || mission.providerLabel || 'Detected evidence') +
|
|
6517
8578
|
|
|
6518
|
-
|
|
6519
|
-
|
|
6520
|
-
'<p class="studio-wiring__summary">' + esc(summary) + '</p>' + body + '</section>';
|
|
8579
|
+
body + '</section>';
|
|
6521
8580
|
|
|
6522
8581
|
}
|
|
6523
8582
|
|
|
6524
8583
|
|
|
6525
8584
|
|
|
6526
|
-
function render(areaKey
|
|
6527
|
-
|
|
6528
|
-
if (providerOverride) selectedProviders[areaKey] = providerOverride;
|
|
8585
|
+
function render(areaKey) {
|
|
6529
8586
|
|
|
6530
8587
|
var area = areas.find(function (a) { return a.key === areaKey; });
|
|
6531
8588
|
|
|
@@ -6559,6 +8616,10 @@ var PANEL_CLIENT_SCRIPT = `
|
|
|
6559
8616
|
|
|
6560
8617
|
var automation = automationFor(areaKey, mission, current);
|
|
6561
8618
|
|
|
8619
|
+
var categoryGaps = gaps.filter(function (gap) { return gap.primaryMapCategory === areaKey; });
|
|
8620
|
+
|
|
8621
|
+
var contract = buildCliSidebarContract(areaKey, label, mission, categoryGaps, providerLabel, automation, current);
|
|
8622
|
+
|
|
6562
8623
|
|
|
6563
8624
|
|
|
6564
8625
|
panel.innerHTML =
|
|
@@ -6575,31 +8636,29 @@ var PANEL_CLIENT_SCRIPT = `
|
|
|
6575
8636
|
|
|
6576
8637
|
addedSetupPathHtml(areaKey, current, providerLabel, missions) +
|
|
6577
8638
|
|
|
6578
|
-
setupActionsHtml(areaKey, panelMissions, providerLabel, automation) +
|
|
8639
|
+
setupActionsHtml(areaKey, panelMissions, providerLabel, automation, current) +
|
|
6579
8640
|
|
|
6580
|
-
|
|
8641
|
+
setupReadinessHtml(areaKey, current, providerLabel, panelMissions, automation) +
|
|
6581
8642
|
|
|
6582
|
-
|
|
6583
|
-
|
|
6584
|
-
'</div>';
|
|
8643
|
+
selectedNodeContractHtml(contract) +
|
|
6585
8644
|
|
|
8645
|
+
'</div>';
|
|
6586
8646
|
|
|
6587
8647
|
|
|
6588
|
-
panel.querySelectorAll('[data-copy-prompt]').forEach(function (btn) {
|
|
6589
8648
|
|
|
6590
|
-
|
|
8649
|
+
panel.querySelectorAll('[data-provider-option]').forEach(function (tile) {
|
|
6591
8650
|
|
|
6592
|
-
|
|
8651
|
+
tile.addEventListener('click', function () {
|
|
6593
8652
|
|
|
6594
|
-
|
|
8653
|
+
var providerOption = tile.getAttribute('data-provider-option') || '';
|
|
6595
8654
|
|
|
6596
|
-
|
|
8655
|
+
if (!providerOption) return;
|
|
6597
8656
|
|
|
6598
|
-
|
|
8657
|
+
selectedProviders[areaKey] = providerOption;
|
|
6599
8658
|
|
|
6600
|
-
|
|
8659
|
+
syncMapNode(areaKey, providerOption);
|
|
6601
8660
|
|
|
6602
|
-
|
|
8661
|
+
render(areaKey);
|
|
6603
8662
|
|
|
6604
8663
|
});
|
|
6605
8664
|
|
|
@@ -6607,15 +8666,21 @@ var PANEL_CLIENT_SCRIPT = `
|
|
|
6607
8666
|
|
|
6608
8667
|
|
|
6609
8668
|
|
|
6610
|
-
panel.querySelectorAll('[data-
|
|
8669
|
+
panel.querySelectorAll('[data-copy-prompt]').forEach(function (btn) {
|
|
6611
8670
|
|
|
6612
8671
|
btn.addEventListener('click', function () {
|
|
6613
8672
|
|
|
6614
|
-
var
|
|
8673
|
+
var text = btn.getAttribute('data-copy-prompt') || '';
|
|
8674
|
+
|
|
8675
|
+
navigator.clipboard.writeText(text).then(function () {
|
|
8676
|
+
|
|
8677
|
+
var label = btn.textContent;
|
|
8678
|
+
|
|
8679
|
+
btn.textContent = 'Copied';
|
|
6615
8680
|
|
|
6616
|
-
|
|
8681
|
+
setTimeout(function () { btn.textContent = label; }, 1200);
|
|
6617
8682
|
|
|
6618
|
-
|
|
8683
|
+
});
|
|
6619
8684
|
|
|
6620
8685
|
});
|
|
6621
8686
|
|
|
@@ -6670,46 +8735,79 @@ var PANEL_CLIENT_SCRIPT = `
|
|
|
6670
8735
|
`;
|
|
6671
8736
|
|
|
6672
8737
|
// src/report/providerLogos.ts
|
|
8738
|
+
var PROVIDER_LOGO_FALLBACK_SVG = '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 3 20 7.5v9L12 21 4 16.5v-9L12 3Zm0 3.3-5 2.8v5.8l5 2.8 5-2.8V9.1l-5-2.8Zm0 3.2a2.5 2.5 0 1 1 0 5 2.5 2.5 0 0 1 0-5Z"/></svg>';
|
|
6673
8739
|
var PROVIDER_LOGOS = {
|
|
6674
|
-
supabase: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="
|
|
6675
|
-
clerk: '<svg viewBox="0 0 24 24" aria-hidden="true"><path
|
|
6676
|
-
authjs: '<svg viewBox="0 0 24 24"
|
|
6677
|
-
auth0: '<svg viewBox="0 0 24 24" aria-hidden="true"><
|
|
6678
|
-
"better-auth": '<svg viewBox="0 0 24 24" aria-hidden="true"><rect
|
|
8740
|
+
supabase: '<svg viewBox="0 0 24 24" aria-hidden="true"><path fill="#3fcf8e" d="M11.9 1.036c-.015-.986-1.26-1.41-1.874-.637L.764 12.05C-.33 13.427.65 15.455 2.409 15.455h9.579l.113 7.51c.014.985 1.259 1.408 1.873.636l9.262-11.653c1.093-1.375.113-3.403-1.645-3.403h-9.642z"/></svg>',
|
|
8741
|
+
clerk: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="m21.47 20.829-2.881-2.881a.572.572 0 0 0-.7-.084 6.854 6.854 0 0 1-7.081 0 .576.576 0 0 0-.7.084l-2.881 2.881a.576.576 0 0 0-.103.69.57.57 0 0 0 .166.186 12 12 0 0 0 14.113 0 .58.58 0 0 0 .239-.423.576.576 0 0 0-.172-.453Zm.002-17.668-2.88 2.88a.569.569 0 0 1-.701.084A6.857 6.857 0 0 0 8.724 8.08a6.862 6.862 0 0 0-1.222 3.692 6.86 6.86 0 0 0 .978 3.764.573.573 0 0 1-.083.699l-2.881 2.88a.567.567 0 0 1-.864-.063A11.993 11.993 0 0 1 6.771 2.7a11.99 11.99 0 0 1 14.637-.405.566.566 0 0 1 .232.418.57.57 0 0 1-.168.448Zm-7.118 12.261a3.427 3.427 0 1 0 0-6.854 3.427 3.427 0 0 0 0 6.854Z"/></svg>',
|
|
8742
|
+
authjs: '<svg viewBox="0 0 24 24" aria-hidden="true"><path fill="#18c6cf" d="M12 1.4 21 4.9v6.7c0 5.1-3.5 8.7-9 11-5.5-2.3-9-5.9-9-11V4.9l9-3.5Z"/><path fill="#8b2ff5" d="M12 1.4 21 4.9v6.7c0 5.1-3.5 8.7-9 11V1.4Z"/><path fill="#fff7df" d="M12 7a5.2 5.2 0 1 0 0 10.4A5.2 5.2 0 0 0 12 7Z"/><path fill="#ff8a00" d="M10.2 9.6a2.2 2.2 0 0 1 2.9 2.9l3.5 3.5h-2.2v1.5h-1.8V16h-1.4l-1.3-1.3a2.2 2.2 0 0 1 .3-5.1Zm.9 1.8a.7.7 0 1 0 0 1.4.7.7 0 0 0 0-1.4Z"/></svg>',
|
|
8743
|
+
auth0: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M21.98 7.448L19.62 0H4.347L2.02 7.448c-1.352 4.312.03 9.206 3.815 12.015L12.007 24l6.157-4.552c3.755-2.81 5.182-7.688 3.815-12.015l-6.16 4.58 2.343 7.45-6.157-4.597-6.158 4.58 2.358-7.433-6.188-4.55 7.63-.045L12.008 0l2.356 7.404 7.615.044z"/></svg>',
|
|
8744
|
+
"better-auth": '<svg viewBox="0 0 24 24" aria-hidden="true"><rect x="1.5" y="1.5" width="21" height="21" rx="3" fill="#111827"/><path fill="#fffdf7" d="M5.2 6.2h5.15v4.25H5.2V6.2Zm8.45 0h5.15v4.25h-5.15V6.2Zm-8.45 7.35h5.15v4.25H5.2v-4.25Zm8.45 0h5.15v4.25h-5.15v-4.25Z"/><path fill="#3b82f6" d="M10.35 10.45h3.3v3.1h-3.3v-3.1Z"/></svg>',
|
|
6679
8745
|
polar: '<svg viewBox="0 0 29 29" aria-hidden="true"><path fill="#0062FF" fill-rule="evenodd" clip-rule="evenodd" d="M9.077 23.057c4.801 3.25 11.328 1.992 14.577-2.808 3.25-4.801 1.993-11.328-2.808-14.578C16.045 2.422 9.519 3.679 6.269 8.48c-3.25 4.801-1.993 11.327 2.808 14.577Zm1.393.086c4.392 2.247 9.963.138 12.444-4.711 2.48-4.848.93-10.6-3.461-12.847-4.392-2.247-9.963-.138-12.443 4.711-2.481 4.848-.932 10.6 3.46 12.847Z"/><path fill="#0062FF" fill-rule="evenodd" clip-rule="evenodd" d="M11.722 24.29c3.965 1.29 8.628-2.118 10.417-7.613 1.788-5.495.024-10.996-3.94-12.286-3.964-1.29-8.628 2.118-10.416 7.613-1.789 5.495-.025 10.995 3.939 12.286Zm1.213-.418c3.355.716 6.982-2.961 8.102-8.212 1.12-5.252-.691-10.089-4.046-10.804-3.356-.716-6.983 2.96-8.103 8.212-1.12 5.251.692 10.088 4.047 10.804Z"/><path fill="#0062FF" fill-rule="evenodd" clip-rule="evenodd" d="M13.854 24.738c2.652.284 5.3-4.14 5.912-9.882.613-5.74-1.04-10.624-3.692-10.907-2.653-.283-5.3 4.141-5.913 9.882-.613 5.741 1.04 10.624 3.693 10.907Zm1.241-1.747c1.92-.031 3.415-3.917 3.34-8.68-.075-4.764-1.693-8.6-3.612-8.57-1.92.03-3.415 3.916-3.34 8.68.076 4.763 1.693 8.6 3.612 8.57Z"/></svg>',
|
|
6680
|
-
"lemon-squeezy": '<svg viewBox="0 0 24 24" aria-hidden="true"><path
|
|
6681
|
-
github: '<svg viewBox="0 0 24 24" aria-hidden="true"><path
|
|
6682
|
-
gitlab: '<svg viewBox="0 0 24 24" aria-hidden="true"><path fill="#FC6D26" d="
|
|
6683
|
-
neon: '<svg viewBox="0 0
|
|
6684
|
-
planetscale: '<svg viewBox="0 0 24 24" aria-hidden="true"><path
|
|
6685
|
-
mongodb: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="
|
|
6686
|
-
turso: '<svg viewBox="0 0
|
|
8746
|
+
"lemon-squeezy": '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="m7.4916 10.835 2.3748-6.5114a3.1497 3.1497 0 0 0-.065-2.3418C9.0315.183 6.9427-.398 5.2928.265 3.643.929 2.71 2.4348 3.512 4.3046l2.8197 6.5615c.219.509.97.489 1.16-.03m1.6798 1.0969 6.5334-2.7758c2.1699-.9219 2.7218-3.6907 1.022-5.2905l-.068-.063c-1.6669-1.5469-4.4217-1.002-5.3706 1.0359L8.3566 11.135c-.234.503.295 1.0199.8159.7979m.373.87 6.6454-2.5119c2.2078-.8349 4.6206.745 4.5886 3.0398l-.002.09c-.048 2.2358-2.3938 3.7376-4.5536 2.9467l-6.6724-2.4418a.595.595 0 0 1-.006-1.1229m-.386 1.9269 6.4375 2.9767a3.2997 3.2997 0 0 1 1.6658 1.6989c.769 1.7998-.283 3.6396-1.9328 4.3016-1.6499.662-3.4097.235-4.2097-1.6359l-2.8027-6.5694c-.217-.509.328-1.009.8419-.772"/></svg>',
|
|
8747
|
+
github: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg>',
|
|
8748
|
+
gitlab: '<svg viewBox="0 0 24 24" aria-hidden="true"><path fill="#FC6D26" d="m23.6004 9.5927-.0337-.0862L20.3.9814a.851.851 0 0 0-.3362-.405.8748.8748 0 0 0-.9997.0539.8748.8748 0 0 0-.29.4399l-2.2055 6.748H7.5375l-2.2057-6.748a.8573.8573 0 0 0-.29-.4412.8748.8748 0 0 0-.9997-.0537.8585.8585 0 0 0-.3362.4049L.4332 9.5015l-.0325.0862a6.0657 6.0657 0 0 0 2.0119 7.0105l.0113.0087.03.0213 4.976 3.7264 2.462 1.8633 1.4995 1.1321a1.0085 1.0085 0 0 0 1.2197 0l1.4995-1.1321 2.4619-1.8633 5.006-3.7489.0125-.01a6.0682 6.0682 0 0 0 2.0094-7.003z"/></svg>',
|
|
8749
|
+
neon: '<svg viewBox="0 0 64 64" aria-hidden="true"><path fill="#37c38f" d="M63 0.0177909V63.5526L38.4178 42.2501V63.5526H0V0L63 0.0177909ZM7.72251 55.8389H30.6953V25.3238L55.2779 47.0476V7.72922L7.72251 7.71559V55.8389Z"/></svg>',
|
|
8750
|
+
planetscale: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M0 12C0 5.373 5.373 0 12 0c4.873 0 9.067 2.904 10.947 7.077l-15.87 15.87a11.981 11.981 0 0 1-1.935-1.099L14.99 12H12l-8.485 8.485A11.962 11.962 0 0 1 0 12Zm12.004 12L24 12.004C23.998 18.628 18.628 23.998 12.004 24Z"/></svg>',
|
|
8751
|
+
mongodb: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M17.193 9.555c-1.264-5.58-4.252-7.414-4.573-8.115-.28-.394-.53-.954-.735-1.44-.036.495-.055.685-.523 1.184-.723.566-4.438 3.682-4.74 10.02-.282 5.912 4.27 9.435 4.888 9.884l.07.05A73.49 73.49 0 0 1 11.91 24h.481c.114-1.032.284-2.056.51-3.07.417-.296.604-.463.85-.693a11.342 11.342 0 0 0 3.639-8.464c.01-.814-.103-1.662-.197-2.218Zm-5.336 8.195s0-8.291.275-8.29c.213 0 .49 10.695.49 10.695-.381-.045-.765-1.76-.765-2.405Z"/></svg>',
|
|
8752
|
+
turso: '<svg viewBox="0 0 241 240" aria-hidden="true"><path fill="#4ff8d2" d="M220.035 83.61C215.365 55.67 190.875 35 190.875 35V65.78L176.335 69.53L167.225 58.56L162.415 68.02C152.495 65.32 138.835 63.58 120.045 63.58C101.255 63.58 87.5949 65.33 77.6749 68.02L72.8649 58.56L63.7549 69.53L49.2149 65.78V35C49.2149 35 24.7249 55.67 20.0549 83.61L52.1949 94.73C53.2449 114.16 61.9849 166.61 64.4849 171.37C67.1449 176.44 81.2649 190.93 92.3149 196.5C92.3149 196.5 96.3149 192.27 98.7549 188.54C101.855 192.19 117.865 204.99 120.055 204.99C122.245 204.99 138.255 192.2 141.355 188.54C143.795 192.27 147.795 196.5 147.795 196.5C158.845 190.93 172.965 176.44 175.625 171.37C178.125 166.61 186.865 114.16 187.915 94.73L220.055 83.61H220.035ZM173.845 128.35L152.095 130.29L154.005 156.96C154.005 156.96 140.775 167.91 120.045 167.91C99.3149 167.91 86.0849 156.96 86.0849 156.96L87.9949 130.29L66.2449 128.35L62.5249 98.31L98.5749 110.79L95.7749 148.18C102.475 149.88 109.525 151.57 120.055 151.57C130.585 151.57 137.625 149.88 144.325 148.18L141.525 110.79L177.575 98.31L173.855 128.35H173.845Z"/></svg>',
|
|
6687
8753
|
stripe: '<svg viewBox="0 0 24 24" preserveAspectRatio="xMidYMid meet" aria-hidden="true"><path fill="#635BFF" d="M4.5 15.4c1.5.9 3.5 1.4 5.4 1.4 1.6 0 2.5-.4 2.5-1.2 0-.7-.7-1-3-1.5-3-.7-4.7-1.8-4.7-4.1 0-2.6 2.1-4.4 5.8-4.4 2.1 0 3.9.4 5.3 1.1v3.4a10 10 0 0 0-5.1-1.4c-1.5 0-2.2.4-2.2 1.1 0 .7.8 1 3 1.5 3.1.7 4.8 1.8 4.8 4.1 0 2.7-2.2 4.5-6.2 4.5-2.2 0-4.3-.5-5.6-1.3v-3.2Z"/></svg>',
|
|
6688
8754
|
paddle: '<svg viewBox="0 0 90 90" aria-hidden="true"><rect x="11" y="11" width="68" height="68" rx="17" fill="#101318"/><rect x="11.5" y="11.5" width="67" height="67" rx="16.5" fill="none" stroke="#343942"/><path fill="#FFD21E" d="M8.49991 17C8.51217 21.6945 12.3128 25.5001 17 25.5001C12.3128 25.5001 8.51217 29.3055 8.49991 34C8.48783 29.3055 4.68717 25.5001 0 25.5001C4.68717 25.5001 8.48783 21.6945 8.49991 17Z" transform="translate(19.5 -31.5) scale(3)"/></svg>',
|
|
6689
8755
|
vercel: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 4 22 20H2L12 4Z"/></svg>',
|
|
6690
|
-
netlify: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="
|
|
8756
|
+
netlify: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M6.49 19.04h-.23L5.13 17.9v-.23l1.73-1.71h1.2l.15.15v1.2L6.5 19.04ZM5.13 6.31V6.1l1.13-1.13h.23L8.2 6.68v1.2l-.15.15h-1.2L5.13 6.31Zm9.96 9.09h-1.65l-.14-.13v-3.83c0-.68-.27-1.2-1.1-1.23-.42 0-.9 0-1.43.02l-.07.08v4.96l-.14.14H8.9l-.13-.14V8.73l.13-.14h3.7a2.6 2.6 0 0 1 2.61 2.6v4.08l-.13.14Zm-8.37-2.44H.14L0 12.82v-1.64l.14-.14h6.58l.14.14v1.64l-.14.14Zm17.14 0h-6.58l-.14-.14v-1.64l.14-.14h6.58l.14.14v1.64l-.14.14ZM11.05 6.55V1.64l.14-.14h1.65l.14.14v4.9l-.14.14h-1.65l-.14-.13Zm0 15.81v-4.9l.14-.14h1.65l.14.13v4.91l-.14.14h-1.65l-.14-.14Z"/></svg>',
|
|
6691
8757
|
aws: '<svg viewBox="0 0 24 24" preserveAspectRatio="xMidYMid meet" aria-hidden="true"><path fill="#FF9900" d="M6.763 10.582c4.95-2.283 10.563-2.283 15.475 0 .532.243.848.741.848 1.256 0 .532-.323 1.026-.85 1.256-4.912 2.283-10.525 2.283-15.474 0-.528-.23-.851-.724-.851-.279 0-.544.098-.754.243-4.95 2.283-10.563 2.283-15.475 0-.532-.243-.848-.741-.848-1.256 0-.532.323-1.026.85-1.256.228-.098.492-.196.754-.196.279 0 .544.098.754.243Z"/><path fill="#FF9900" d="M12 3.65c-2.772 0-5.027 1.147-5.027 2.553S9.228 8.756 12 8.756s5.027-1.147 5.027-2.553S14.772 3.65 12 3.65Z"/></svg>',
|
|
6692
|
-
sentry: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="
|
|
6693
|
-
posthog: '<svg viewBox="0 0
|
|
6694
|
-
playwright: '<svg viewBox="0 0
|
|
8758
|
+
sentry: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M13.91 2.505c-.873-1.448-2.972-1.448-3.844 0L6.904 7.92a15.478 15.478 0 0 1 8.53 12.811h-2.221A13.301 13.301 0 0 0 5.784 9.814l-2.926 5.06a7.65 7.65 0 0 1 4.435 5.848H2.194a.365.365 0 0 1-.298-.534l1.413-2.402a5.16 5.16 0 0 0-1.614-.913L.296 19.275a2.182 2.182 0 0 0 .812 2.999 2.24 2.24 0 0 0 1.086.288h6.983a9.322 9.322 0 0 0-3.845-8.318l1.11-1.922a11.47 11.47 0 0 1 4.95 10.24h5.915a17.242 17.242 0 0 0-7.885-15.28l2.244-3.845a.37.37 0 0 1 .504-.13c.255.14 9.75 16.708 9.928 16.9a.365.365 0 0 1-.327.543h-2.287c.029.612.029 1.223 0 1.831h2.297a2.206 2.206 0 0 0 1.922-3.31z"/></svg>',
|
|
8759
|
+
posthog: '<svg viewBox="0 0 156 90" aria-hidden="true"><path fill="#f9bd2b" d="M0 68.33c0-2.9 3.505-4.352 5.556-2.302l14.916 14.917c2.05 2.05.598 5.555-2.301 5.555H3.254A3.254 3.254 0 0 1 0 83.246zm0-15.712c0 .863.343 1.69.953 2.301l30.628 30.628c.61.61 1.438.953 2.301.953h16.823c2.9 0 4.351-3.505 2.301-5.555L5.556 33.494C3.506 31.444 0 32.896 0 35.795zm0-32.534c0 .863.343 1.69.953 2.3l63.162 63.163c.61.61 1.439.953 2.302.953h16.822c2.9 0 4.352-3.505 2.302-5.555L5.556.96C3.506-1.09 0 .362 0 3.26zm32.534 0c0 .863.343 1.69.954 2.3l58.56 58.56c2.05 2.05 5.555.599 5.555-2.3V61.82c0-.863-.343-1.691-.953-2.301L38.09.96c-2.05-2.05-5.556-.598-5.556 2.3zM70.624.96c-2.05-2.05-5.555-.598-5.555 2.3v16.824c0 .863.343 1.69.953 2.3L92.047 48.41c2.05 2.05 5.556.598 5.556-2.3V29.285c0-.863-.343-1.69-.953-2.3z"/><path fill="#000000" d="M138.393 68.729 107.76 38.095c-2.05-2.05-5.555-.598-5.555 2.302v42.849a3.254 3.254 0 0 0 3.254 3.254h47.451a3.254 3.254 0 0 0 3.255-3.254v-3.903c0-1.797-1.463-3.232-3.246-3.464a25.14 25.14 0 0 1-14.526-7.15m-20.572 7.36a5.207 5.207 0 0 1-5.205-5.205 5.207 5.207 0 0 1 5.205-5.206 5.21 5.21 0 0 1 5.206 5.206 5.207 5.207 0 0 1-5.206 5.205"/><path fill="#1d4aff" d="m5.28 65.78.276.248 14.916 14.916c1.96 1.961.718 5.254-1.932 5.536l-.37.02H3.255a3.255 3.255 0 0 1-3.232-2.875L0 83.245V68.33c0-2.773 3.207-4.222 5.28-2.549m0-32.533.276.247 26.978 26.98V86.5L.954 54.919a3.26 3.26 0 0 1-.926-1.873L0 52.618V35.796c0-2.774 3.207-4.223 5.28-2.55M0 3.26C0 .488 3.207-.961 5.28.712l.276.248 26.978 26.978v26.028L.954 22.385a3.25 3.25 0 0 1-.926-1.874L0 20.084z"/><path fill="#f54e00" d="m32.534 27.939 31.581 31.58c.51.51.832 1.17.925 1.874l.029.428v24.68L33.488 54.919a3.26 3.26 0 0 1-.925-1.873l-.029-.428zm0 32.533 20.472 20.472c1.961 1.961.718 5.254-1.931 5.536l-.37.02h-18.17zm5.28-59.76.276.248 26.025 26.025c.51.509.832 1.168.925 1.874l.029.427v24.68L33.488 22.385a3.26 3.26 0 0 1-.925-1.874l-.029-.427V3.26c0-2.773 3.208-4.222 5.28-2.549"/></svg>',
|
|
8760
|
+
playwright: '<svg viewBox="0 0 400 400" aria-hidden="true"><path fill="#2d4552" d="M136.444 221.556C123.558 225.213 115.104 231.625 109.535 238.032C114.869 233.364 122.014 229.08 131.652 226.348C141.51 223.554 149.92 223.574 156.869 224.915V219.481C150.941 218.939 144.145 219.371 136.444 221.556ZM108.946 175.876L61.0895 188.484C61.0895 188.484 61.9617 189.716 63.5767 191.36L104.153 180.668C104.153 180.668 103.578 188.077 98.5847 194.705C108.03 187.559 108.946 175.876 108.946 175.876ZM149.005 288.347C81.6582 306.486 46.0272 228.438 35.2396 187.928C30.2556 169.229 28.0799 155.067 27.5 145.928C27.4377 144.979 27.4665 144.179 27.5336 143.446C24.04 143.657 22.3674 145.473 22.7077 150.721C23.2876 159.855 25.4633 174.016 30.4473 192.721C41.2301 233.225 76.8659 311.273 144.213 293.134C158.872 289.185 169.885 281.992 178.152 272.81C170.532 279.692 160.995 285.112 149.005 288.347ZM161.661 128.11V132.903H188.077C187.535 131.206 186.989 129.677 186.447 128.11H161.661Z"/><path fill="#2d4552" d="M193.981 167.584C205.861 170.958 212.144 179.287 215.465 186.658L228.711 190.42C228.711 190.42 226.904 164.623 203.57 157.995C181.741 151.793 168.308 170.124 166.674 172.496C173.024 167.972 182.297 164.268 193.981 167.584ZM299.422 186.777C277.573 180.547 264.145 198.916 262.535 201.255C268.89 196.736 278.158 193.031 289.837 196.362C301.698 199.741 307.976 208.06 311.307 215.436L324.572 219.212C324.572 219.212 322.736 193.41 299.422 186.777ZM286.262 254.795L176.072 223.99C176.072 223.99 177.265 230.038 181.842 237.869L274.617 263.805C282.255 259.386 286.262 254.795 286.262 254.795ZM209.867 321.102C122.618 297.71 133.166 186.543 147.284 133.865C153.097 112.156 159.073 96.0203 164.029 85.204C161.072 84.5953 158.623 86.1529 156.203 91.0746C150.941 101.747 144.212 119.124 137.7 143.45C123.586 196.127 113.038 307.29 200.283 330.682C241.406 341.699 273.442 324.955 297.323 298.659C274.655 319.19 245.714 330.701 209.867 321.102Z"/><path fill="#e2574c" d="M161.661 262.296V239.863L99.3324 257.537C99.3324 257.537 103.938 230.777 136.444 221.556C146.302 218.762 154.713 218.781 161.661 220.123V128.11H192.869C189.471 117.61 186.184 109.526 183.423 103.909C178.856 94.612 174.174 100.775 163.545 109.665C156.059 115.919 137.139 129.261 108.668 136.933C80.1966 144.61 57.179 142.574 47.5752 140.911C33.9601 138.562 26.8387 135.572 27.5049 145.928C28.0847 155.062 30.2605 169.224 35.2445 187.928C46.0272 228.433 81.663 306.481 149.01 288.342C166.602 283.602 179.019 274.233 187.626 262.291H161.661V262.296ZM61.0848 188.484L108.946 175.876C108.946 175.876 107.551 194.288 89.6087 199.018C71.6614 203.743 61.0848 188.484 61.0848 188.484Z"/><path fill="#2ead33" d="M341.786 129.174C329.345 131.355 299.498 134.072 262.612 124.185C225.716 114.304 201.236 97.0224 191.537 88.8994C177.788 77.3834 171.74 69.3802 165.788 81.4857C160.526 92.163 153.797 109.54 147.284 133.866C133.171 186.543 122.623 297.706 209.867 321.098C297.093 344.47 343.53 242.92 357.644 190.238C364.157 165.917 367.013 147.5 367.799 135.625C368.695 122.173 359.455 126.078 341.786 129.174ZM166.497 172.756C166.497 172.756 180.246 151.372 203.565 158C226.899 164.628 228.706 190.425 228.706 190.425L166.497 172.756ZM223.42 268.713C182.403 256.698 176.077 223.99 176.077 223.99L286.262 254.796C286.262 254.791 264.021 280.578 223.42 268.713ZM262.377 201.495C262.377 201.495 276.107 180.126 299.422 186.773C322.736 193.411 324.572 219.208 324.572 219.208L262.377 201.495Z"/><path fill="#d65348" d="M139.88 246.04L99.3324 257.532C99.3324 257.532 103.737 232.44 133.607 222.496L110.647 136.33L108.663 136.933C80.1918 144.611 57.1742 142.574 47.5704 140.911C33.9554 138.563 26.834 135.572 27.5001 145.929C28.08 155.063 30.2557 169.224 35.2397 187.929C46.0225 228.433 81.6583 306.481 149.005 288.342L150.989 287.719L139.88 246.04ZM61.0848 188.485L108.946 175.876C108.946 175.876 107.551 194.288 89.6087 199.018C71.6615 203.743 61.0848 188.485 61.0848 188.485Z"/><path fill="#1d8d22" d="M225.27 269.163L223.415 268.712C182.398 256.698 176.072 223.99 176.072 223.99L232.89 239.872L262.971 124.281L262.607 124.185C225.711 114.304 201.232 97.0224 191.532 88.8994C177.783 77.3834 171.735 69.3802 165.783 81.4857C160.526 92.163 153.797 109.54 147.284 133.866C133.171 186.543 122.623 297.706 209.867 321.097L211.655 321.5L225.27 269.163ZM166.497 172.756C166.497 172.756 180.246 151.372 203.565 158C226.899 164.628 228.706 190.425 228.706 190.425L166.497 172.756Z"/></svg>',
|
|
6695
8761
|
logrocket: '<svg viewBox="0 0 24 24" preserveAspectRatio="xMidYMid meet" aria-hidden="true"><rect x="2" y="2" width="20" height="20" rx="5" fill="#764ABC"/><path fill="#FFFFFF" d="M12 7.4c2.7 0 4.9 2.2 4.9 4.9s-2.2 4.9-4.9 4.9-4.9-2.2-4.9-4.9 4.9-4.9 4.9-4.9Zm0 1.9a3 3 0 1 0 0 6 3 3 0 0 0 0-6Zm-3.2 7.4h6.4v1.5H8.8v-1.5Z"/></svg>',
|
|
6696
8762
|
figma: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M8.5 3h3.5v6H8.5a3 3 0 1 1 0-6Zm3.5 6h3.5a3 3 0 1 0 0-6H12v6Zm0 0h3.5a3 3 0 1 1 0 6H12V9Zm-3.5 0H12v6H8.5a3 3 0 1 1 0-6ZM8.5 15H12v2.5A3.5 3.5 0 1 1 8.5 15Z"/></svg>',
|
|
6697
8763
|
storybook: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M6.2 3.4 17.9 2l.7 18.1-12.4.7V3.4Zm8.7 3.2.3 2.4 1.7-1.3 1.7 1.1.3-3.4-4 .5v.7Zm-5.2 5.1c0 2 1.6 3.5 4.2 3.5 2.4 0 3.8-1.2 3.8-3 0-1.7-1.1-2.6-3.4-3l-1.2-.2c-.7-.1-1-.4-1-.8 0-.5.5-.8 1.3-.8.9 0 1.5.4 1.8 1.1l2.1-.8c-.5-1.4-1.8-2.2-3.8-2.2-2.3 0-3.8 1.2-3.8 2.9 0 1.6 1.1 2.5 3.3 2.9l1.2.2c.8.1 1.1.4 1.1.9 0 .6-.5.9-1.4.9-1.1 0-1.8-.5-2.1-1.3l-2.1.7Z"/></svg>',
|
|
6698
8764
|
"product-spec": '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M6 3.2h9.2L19 7v13.8H6V3.2Zm8.2 1.9v3h3l-3-3ZM8.4 10h7.2v1.7H8.4V10Zm0 3.2h7.2v1.7H8.4v-1.7Zm0 3.2h4.9v1.7H8.4v-1.7Z"/></svg>',
|
|
6699
8765
|
"route-map": '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M6.5 4.2a3 3 0 0 1 3 3c0 2-3 5.1-3 5.1s-3-3.1-3-5.1a3 3 0 0 1 3-3Zm0 1.8a1.2 1.2 0 1 0 0 2.4 1.2 1.2 0 0 0 0-2.4Zm11 5.8a3 3 0 0 1 3 3c0 2-3 5.1-3 5.1s-3-3.1-3-5.1a3 3 0 0 1 3-3Zm0 1.8a1.2 1.2 0 1 0 0 2.4 1.2 1.2 0 0 0 0-2.4ZM9.5 7h2.1c2.8 0 4.9 2 4.9 4.8h-2c0-1.7-1.2-2.8-2.9-2.8H9.5V7Zm4.9 10h-2.1c-2.8 0-4.9-2-4.9-4.8h2c0 1.7 1.2 2.8 2.9 2.8h2.1v2Z"/></svg>',
|
|
6700
8766
|
react: '<svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="2.2"/><ellipse cx="12" cy="12" rx="9" ry="3.6" fill="none" stroke="currentColor" stroke-width="1.6"/><ellipse cx="12" cy="12" rx="9" ry="3.6" fill="none" stroke="currentColor" stroke-width="1.6" transform="rotate(60 12 12)"/><ellipse cx="12" cy="12" rx="9" ry="3.6" fill="none" stroke="currentColor" stroke-width="1.6" transform="rotate(120 12 12)"/></svg>',
|
|
6701
|
-
vue: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="
|
|
6702
|
-
svelte: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="
|
|
6703
|
-
angular: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 2
|
|
6704
|
-
nodejs: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 2.
|
|
8767
|
+
vue: '<svg viewBox="0 0 24 24" aria-hidden="true"><path fill="#42b883" d="M24 1.61H14.06L12 5.16 9.94 1.61H0l12 20.78L24 1.61Z"/><path fill="#35495e" d="M12 14.08 5.16 2.23h4.43L12 6.41l2.41-4.18h4.43L12 14.08Z"/></svg>',
|
|
8768
|
+
svelte: '<svg viewBox="0 0 24 24" aria-hidden="true"><path fill="#ff3e00" d="M10.354 21.125a4.44 4.44 0 0 1-4.765-1.767 4.109 4.109 0 0 1-.703-3.107 3.898 3.898 0 0 1 .134-.522l.105-.321.287.21a7.21 7.21 0 0 0 2.187 1.099l.208.063-.02.208a1.173 1.173 0 0 0 .21.766 1.297 1.297 0 0 0 1.39.514 1.22 1.22 0 0 0 .54-.31l3.927-3.986a.927.927 0 0 0 .203-.42.908.908 0 0 0-.154-.7 1.01 1.01 0 0 0-1.08-.4.946.946 0 0 0-.42.242l-1.5 1.522a4.15 4.15 0 0 1-1.831 1.045 4.453 4.453 0 0 1-4.767-1.767 4.108 4.108 0 0 1-.702-3.107 3.855 3.855 0 0 1 1.066-2.018l3.927-3.986A4.205 4.205 0 0 1 10.65 3.34a4.442 4.442 0 0 1 4.766 1.767 4.109 4.109 0 0 1 .702 3.108 3.943 3.943 0 0 1-.133.521l-.106.321-.286-.21a7.206 7.206 0 0 0-2.188-1.098l-.208-.063.02-.208a1.18 1.18 0 0 0-.21-.767 1.297 1.297 0 0 0-1.39-.514 1.229 1.229 0 0 0-.54.31L7.15 10.493a.929.929 0 0 0-.203.42.909.909 0 0 0 .154.7 1.01 1.01 0 0 0 1.08.4.943.943 0 0 0 .42-.242l1.5-1.522a4.144 4.144 0 0 1 1.831-1.045 4.443 4.443 0 0 1 4.766 1.767 4.109 4.109 0 0 1 .702 3.107 3.857 3.857 0 0 1-1.066 2.018l-3.927 3.986a4.193 4.193 0 0 1-2.053 1.043Z"/></svg>',
|
|
8769
|
+
angular: '<svg viewBox="0 0 24 24" aria-hidden="true"><path fill="#dd0031" d="M12 2 3.5 5.1l1.3 11.2L12 22l7.2-5.7 1.3-11.2L12 2Z"/><path fill="#c3002f" d="M12 2v20l7.2-5.7 1.3-11.2L12 2Z"/><path fill="#fff" d="M12 5.6 6.7 17.6h2l1.1-2.7h4.4l1.1 2.7h2L12 5.6Zm0 3.8 1.5 3.7h-3L12 9.4Z"/></svg>',
|
|
8770
|
+
nodejs: '<svg viewBox="0 0 24 24" aria-hidden="true"><path fill="#5fa04e" d="M12 2.15 20.7 7.1v9.8L12 21.85 3.3 16.9V7.1L12 2.15Z"/><path fill="#ffffff" d="M8.1 9.15h2.15v4.55c0 1.35-.75 2.15-2.2 2.15-.78 0-1.37-.18-1.85-.48l.57-1.67c.28.17.6.28.95.28.27 0 .38-.15.38-.48V9.15Zm4.05 4.05c.42.5 1.02.82 1.72.82.5 0 .82-.18.82-.52 0-.38-.32-.48-1.08-.7l-.57-.17c-1.15-.35-1.88-.92-1.88-1.95 0-1.13.92-1.72 2.28-1.72 1.02 0 1.82.32 2.4.98l-1.1 1.28c-.35-.35-.78-.55-1.25-.55-.4 0-.65.15-.65.45 0 .32.28.43.93.63l.57.17c1.32.4 2.05.93 2.05 2.05 0 1.25-1.05 1.88-2.52 1.88-1.32 0-2.35-.5-2.95-1.28l1.23-1.37Z"/></svg>',
|
|
6705
8771
|
python: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M11.8 3c2.7 0 4.2.8 4.2 2.5V9H9.6A2.6 2.6 0 0 0 7 11.6V13H4.5C3.5 13 3 12.2 3 11c0-3.2 1.9-4.9 5.6-4.9h3.8V5H8.8V3.6A10 10 0 0 1 11.8 3Zm-1.4 1.5a.8.8 0 1 0 0 1.6.8.8 0 0 0 0-1.6ZM12.2 21c-2.7 0-4.2-.8-4.2-2.5V15h6.4a2.6 2.6 0 0 0 2.6-2.6V11h2.5c1 0 1.5.8 1.5 2 0 3.2-1.9 4.9-5.6 4.9h-3.8V19h3.6v1.4a10 10 0 0 1-3 .6Zm1.4-3.1a.8.8 0 1 0 0 1.6.8.8 0 0 0 0-1.6Z"/></svg>',
|
|
6706
8772
|
rails: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M3 17.7C5.8 9.5 11.7 5.2 21 4.8v3.1C13.5 8.3 8.7 11.6 6 18.5L3 17.7Zm4.6.7c2.1-5 5.9-7.5 11.4-7.9v2.4c-4.4.4-7.4 2.5-9 6.2l-2.4-.7Zm4.7.7c1.3-2.4 3.4-3.7 6.7-4.1v2.2c-2.2.4-3.6 1.3-4.5 2.6l-2.2-.7Z"/></svg>',
|
|
6707
8773
|
go: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M3 8.2h7.4v1.5H3V8.2Zm-1 3h7.4v1.5H2v-1.5Zm2 3h5.4v1.5H4v-1.5Zm10.5-6.1c3.1 0 5.5 2 5.5 4.6s-2.4 4.6-5.5 4.6-5.5-2-5.5-4.6 2.4-4.6 5.5-4.6Zm0 2.1c-1.7 0-3 1.1-3 2.5s1.3 2.5 3 2.5 3-1.1 3-2.5-1.3-2.5-3-2.5Zm5.1-2h2.4l-1.2 9h-2.4l1.2-9Z"/></svg>',
|
|
6708
8774
|
"testing-library": '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M7 3h10v4.2l-3.4 4.5 5.4 7.9c.5.8 0 1.9-1 1.9H6c-1 0-1.6-1.1-1-1.9l5.4-7.9L7 7.2V3Zm3 3.5 2 2.7 2-2.7H10Zm1.9 8.4-2.6 3.8h5.4l-2.8-3.8Z"/></svg>',
|
|
6709
|
-
vitest: '<svg viewBox="0 0 24 24"
|
|
8775
|
+
vitest: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M13.74024 1.05293a.49504.49504 0 0 0-.1569.02512.49338.49338 0 0 0-.25056.1876L7.59513 9.56159a.4895.4895 0 0 0-.08373.22327.48846.48846 0 0 0 .03163.23629.4893.4893 0 0 0 .13985.19319.4927.4927 0 0 0 .2149.10481l3.70685.78609-.22947 4.58007a.48834.48834 0 0 0 .08466.30017.49205.49205 0 0 0 .24931.18854c.10157.03398.21174.03444.3135.00064a.49387.49387 0 0 0 .25056-.18761l5.73735-8.29594a.4884.4884 0 0 0 .08404-.22327c.009-.08015-.0016-.16137-.03163-.23629a.48835.48835 0 0 0-.13985-.19319.49318.49318 0 0 0-.2149-.1048l-3.70686-.7861.22947-4.58008a.48802.48802 0 0 0-.08466-.30017.4913.4913 0 0 0-.24931-.18853.49439.49439 0 0 0-.1566-.02574zM1.15697 9.78795c-.30647.0012-.60009.12378-.81679.34048a1.16107 1.16107 0 0 0-.34017.81648 1.162 1.162 0 0 0 .33366.81957l10.84241 10.8421a1.15762 1.15762 0 0 0 .37677.25211 1.1583 1.1583 0 0 0 .44467.08838c.00084 0 .0016-.00031.0025-.00031.00073 0 .0014.00031.0022.00031a1.15827 1.15827 0 0 0 .44467-.08838 1.15731 1.15731 0 0 0 .37677-.2521l10.84236-10.8421a1.16272 1.16272 0 0 0 .33397-.81958c-.0013-.30647-.12376-.59976-.34048-.81648a1.1616 1.1616 0 0 0-.81679-.34048 1.16114 1.16114 0 0 0-.81926.33366l-5.4012 5.4009c-.0078.0074-.01718.01255-.02482.02015L12 20.14011l-4.59776-4.59745c-.0074-.0074-.01659-.01238-.02419-.01954l-5.4015-5.40151a1.162 1.162 0 0 0-.81958-.33366Z"/></svg>',
|
|
6710
8776
|
"rate-limit": '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 3a9 9 0 1 0 9 9h-3a6 6 0 1 1-1.8-4.3L13 11h8V3l-2.7 2.7A9 9 0 0 0 12 3Z"/></svg>',
|
|
6711
|
-
"bot-protection": '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 2 20
|
|
6712
|
-
"secrets-hygiene": '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M7 10V7a5 5 0 0 1 10 0v3h1.5v11h-13V10H7Zm3 0h4V7a2 2 0 0 0-4 0v3Z"/></svg>'
|
|
8777
|
+
"bot-protection": '<svg viewBox="0 0 24 24" aria-hidden="true"><path fill="#f38020" d="M12 2.4 20 5.5v6.2c0 4.75-3.12 8.1-8 10.1-4.88-2-8-5.35-8-10.1V5.5l8-3.1Z"/><path fill="#fff7df" d="M12 5.9 16.9 8v3.72c0 2.64-1.8 4.64-4.9 6.05-3.1-1.41-4.9-3.41-4.9-6.05V8L12 5.9Z"/><path fill="#f38020" d="M12 8.2a3.15 3.15 0 0 1 3.15 3.15A3.15 3.15 0 0 1 12 14.5a3.15 3.15 0 0 1-3.15-3.15A3.15 3.15 0 0 1 12 8.2Zm0 1.65a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3Z"/></svg>',
|
|
8778
|
+
"secrets-hygiene": '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M7 10V7a5 5 0 0 1 10 0v3h1.5v11h-13V10H7Zm3 0h4V7a2 2 0 0 0-4 0v3Z"/></svg>',
|
|
8779
|
+
firebase: '<svg viewBox="0 0 24 24" aria-hidden="true"><path fill="#ffa000" d="m3.9 18.7 2.4-15.2c.1-.6.9-.8 1.2-.3l2.6 4.9 1.1-2.1c.2-.4.8-.4 1 0l7.9 12.7-8.1 4.5-8.1-4.5Z"/><path fill="#f57c00" d="m12 23.2 8.1-4.5-2.4-14.8c-.1-.6-.9-.8-1.2-.3L12 12.1v11.1Z"/><path fill="#ffca28" d="m3.9 18.7 6.2-10.6 1.9 3.7-8.1 6.9Z"/><path fill="#fff3c4" d="m12 12.1 4.5-8.5c.3-.5 1.1-.3 1.2.3l2.4 14.8-8.1-6.6Z"/></svg>',
|
|
8780
|
+
cloudflare: '<svg viewBox="0 0 32 32" aria-hidden="true"><rect x="7" y="7" width="21" height="21" rx="1.4" fill="#111827"/><rect x="4" y="4" width="21" height="21" rx="1.4" fill="#f38020"/><path fill="#fff7df" d="M11.1 17.6h9.2c1.4 0 2.5-1 2.5-2.2 0-1.1-.9-2-2.2-2.2-.6-2-2.4-3.4-4.6-3.4-1.9 0-3.6 1.1-4.3 2.7-.2 0-.4-.1-.7-.1-1.7 0-3.1 1.2-3.4 2.7-1.1.2-1.9 1-1.9 1.9 0 .9.8 1.6 1.8 1.6h3.6Z"/><path fill="#f38020" d="M18.1 13.3c1.2.4 2.1 1.4 2.4 2.6h.2c.6 0 1.1-.4 1.1-.9 0-.5-.5-.9-1.1-1h-.7l-.2-.7c-.4-1.5-1.8-2.5-3.5-2.5-.7 0-1.3.2-1.9.5 1.7.2 3 .9 3.7 2Z"/></svg>'
|
|
8781
|
+
};
|
|
8782
|
+
var PROVIDER_NODE_TONES = {
|
|
8783
|
+
supabase: ["#3ecf8e", "rgba(62, 207, 142, 0.34)"],
|
|
8784
|
+
clerk: ["#6c47ff", "rgba(108, 71, 255, 0.34)"],
|
|
8785
|
+
authjs: ["#6c47ff", "rgba(108, 71, 255, 0.3)"],
|
|
8786
|
+
auth0: ["#eb5424", "rgba(235, 84, 36, 0.34)"],
|
|
8787
|
+
"better-auth": ["#111827", "rgba(17, 24, 39, 0.22)"],
|
|
8788
|
+
firebase: ["#ffa000", "rgba(255, 160, 0, 0.34)"],
|
|
8789
|
+
neon: ["#00e599", "rgba(0, 229, 153, 0.34)"],
|
|
8790
|
+
planetscale: ["#111827", "rgba(17, 24, 39, 0.22)"],
|
|
8791
|
+
mongodb: ["#47a248", "rgba(71, 162, 72, 0.34)"],
|
|
8792
|
+
turso: ["#4ff8d2", "rgba(79, 248, 210, 0.3)"],
|
|
8793
|
+
stripe: ["#635bff", "rgba(99, 91, 255, 0.34)"],
|
|
8794
|
+
paddle: ["#fddd35", "rgba(253, 221, 53, 0.34)"],
|
|
8795
|
+
polar: ["#1d4aff", "rgba(29, 74, 255, 0.3)"],
|
|
8796
|
+
"lemon-squeezy": ["#ffc233", "rgba(255, 194, 51, 0.34)"],
|
|
8797
|
+
vercel: ["#111827", "rgba(17, 24, 39, 0.22)"],
|
|
8798
|
+
netlify: ["#00c7b7", "rgba(0, 199, 183, 0.32)"],
|
|
8799
|
+
cloudflare: ["#f38020", "rgba(243, 128, 32, 0.34)"],
|
|
8800
|
+
sentry: ["#a855f7", "rgba(168, 85, 247, 0.34)"],
|
|
8801
|
+
posthog: ["#f54e00", "rgba(245, 78, 0, 0.34)"],
|
|
8802
|
+
playwright: ["#45ba4b", "rgba(69, 186, 75, 0.32)"],
|
|
8803
|
+
vitest: ["#fcc72b", "rgba(252, 199, 43, 0.34)"],
|
|
8804
|
+
figma: ["#ff7262", "rgba(255, 114, 98, 0.38)"],
|
|
8805
|
+
react: ["#61dafb", "rgba(97, 218, 251, 0.38)"],
|
|
8806
|
+
vue: ["#42b883", "rgba(66, 184, 131, 0.34)"],
|
|
8807
|
+
angular: ["#dd0031", "rgba(221, 0, 49, 0.34)"],
|
|
8808
|
+
nodejs: ["#83cd29", "rgba(131, 205, 41, 0.34)"],
|
|
8809
|
+
github: ["#111827", "rgba(17, 24, 39, 0.22)"],
|
|
8810
|
+
gitlab: ["#fc6d26", "rgba(252, 109, 38, 0.34)"]
|
|
6713
8811
|
};
|
|
6714
8812
|
var ALIASES = {
|
|
6715
8813
|
authjs: "authjs",
|
|
@@ -6742,6 +8840,8 @@ var ALIASES = {
|
|
|
6742
8840
|
auth0: "auth0",
|
|
6743
8841
|
"better-auth": "better-auth",
|
|
6744
8842
|
betterauth: "better-auth",
|
|
8843
|
+
firebase: "firebase",
|
|
8844
|
+
firestore: "firebase",
|
|
6745
8845
|
"mongodb atlas": "mongodb",
|
|
6746
8846
|
logrocket: "logrocket",
|
|
6747
8847
|
sentry: "sentry",
|
|
@@ -6777,7 +8877,23 @@ var ALIASES = {
|
|
|
6777
8877
|
routes: "route-map",
|
|
6778
8878
|
ratelimit: "rate-limit"
|
|
6779
8879
|
};
|
|
6780
|
-
var INLINE_ONLY_LOGO_KEYS = /* @__PURE__ */ new Set([
|
|
8880
|
+
var INLINE_ONLY_LOGO_KEYS = /* @__PURE__ */ new Set([
|
|
8881
|
+
"auth0",
|
|
8882
|
+
"authjs",
|
|
8883
|
+
"better-auth",
|
|
8884
|
+
"clerk",
|
|
8885
|
+
"cloudflare",
|
|
8886
|
+
"firebase",
|
|
8887
|
+
"lemon-squeezy",
|
|
8888
|
+
"netlify",
|
|
8889
|
+
"paddle",
|
|
8890
|
+
"playwright",
|
|
8891
|
+
"polar",
|
|
8892
|
+
"posthog",
|
|
8893
|
+
"sentry",
|
|
8894
|
+
"svelte",
|
|
8895
|
+
"vitest"
|
|
8896
|
+
]);
|
|
6781
8897
|
var PROVIDER_ASSET_FILES = {
|
|
6782
8898
|
authjs: "provider-authjs.svg",
|
|
6783
8899
|
logrocket: "provider-logrocket.svg",
|
|
@@ -6797,6 +8913,7 @@ var PROVIDER_ICON_URLS = {
|
|
|
6797
8913
|
supabase: "https://cdn.simpleicons.org/supabase/3ECF8E",
|
|
6798
8914
|
clerk: "https://cdn.simpleicons.org/clerk/6C47FF",
|
|
6799
8915
|
auth0: "https://cdn.simpleicons.org/auth0/EB5424",
|
|
8916
|
+
firebase: "https://cdn.simpleicons.org/firebase/FFCA28",
|
|
6800
8917
|
neon: "https://cdn.simpleicons.org/neon/00E599",
|
|
6801
8918
|
planetscale: "https://cdn.simpleicons.org/planetscale/000000",
|
|
6802
8919
|
mongodb: "https://cdn.simpleicons.org/mongodb/47A248",
|
|
@@ -6833,16 +8950,26 @@ var BRAND_LOGO_KEYS = /* @__PURE__ */ new Set([
|
|
|
6833
8950
|
"better-auth",
|
|
6834
8951
|
"clerk",
|
|
6835
8952
|
"gitlab",
|
|
8953
|
+
"lemon-squeezy",
|
|
6836
8954
|
"logrocket",
|
|
8955
|
+
"netlify",
|
|
6837
8956
|
"auth0",
|
|
6838
8957
|
"paddle",
|
|
6839
8958
|
"playwright",
|
|
6840
8959
|
"polar",
|
|
6841
8960
|
"posthog",
|
|
8961
|
+
"sentry",
|
|
6842
8962
|
"stripe",
|
|
8963
|
+
"svelte",
|
|
6843
8964
|
"vitest"
|
|
6844
8965
|
]);
|
|
8966
|
+
function isUnselectedProviderToken(raw, compact) {
|
|
8967
|
+
return !compact || compact === "notselected" || raw === "not selected" || raw === "none";
|
|
8968
|
+
}
|
|
6845
8969
|
function resolveLogoKey(raw, compact) {
|
|
8970
|
+
if (isUnselectedProviderToken(raw, compact)) {
|
|
8971
|
+
return "";
|
|
8972
|
+
}
|
|
6846
8973
|
if (ALIASES[raw]) {
|
|
6847
8974
|
return ALIASES[raw];
|
|
6848
8975
|
}
|
|
@@ -6880,13 +9007,13 @@ function normalizeProviderKey2(providerOrLabel) {
|
|
|
6880
9007
|
function resolveProviderLogoKey(...candidates) {
|
|
6881
9008
|
for (const candidate of candidates) {
|
|
6882
9009
|
const key = normalizeProviderKey2(candidate);
|
|
6883
|
-
if (key && PROVIDER_LOGOS[key]) {
|
|
9010
|
+
if (key && key !== "notselected" && PROVIDER_LOGOS[key]) {
|
|
6884
9011
|
return key;
|
|
6885
9012
|
}
|
|
6886
9013
|
}
|
|
6887
9014
|
for (const candidate of candidates) {
|
|
6888
9015
|
const key = normalizeProviderKey2(candidate);
|
|
6889
|
-
if (key) {
|
|
9016
|
+
if (key && key !== "notselected") {
|
|
6890
9017
|
return key;
|
|
6891
9018
|
}
|
|
6892
9019
|
}
|
|
@@ -6894,7 +9021,7 @@ function resolveProviderLogoKey(...candidates) {
|
|
|
6894
9021
|
}
|
|
6895
9022
|
function providerLogoClass(providerOrLabel, ...moreCandidates) {
|
|
6896
9023
|
const key = resolveProviderLogoKey(providerOrLabel, ...moreCandidates) || normalizeProviderKey2(providerOrLabel);
|
|
6897
|
-
if (!key) {
|
|
9024
|
+
if (!key || key === "notselected") {
|
|
6898
9025
|
return "";
|
|
6899
9026
|
}
|
|
6900
9027
|
return ` provider-logo--${key}${BRAND_LOGO_KEYS.has(key) ? " provider-logo--brand" : ""}`;
|
|
@@ -6912,16 +9039,14 @@ function providerLogoHtml(providerOrLabel, labelFallback, ...moreCandidates) {
|
|
|
6912
9039
|
if (assetUrl) {
|
|
6913
9040
|
return providerIconImgHtml(assetUrl, key);
|
|
6914
9041
|
}
|
|
9042
|
+
if (svg) {
|
|
9043
|
+
return svg;
|
|
9044
|
+
}
|
|
6915
9045
|
const iconUrl = key && PROVIDER_ICON_URLS[key];
|
|
6916
9046
|
if (iconUrl) {
|
|
6917
9047
|
return providerIconImgHtml(iconUrl, key);
|
|
6918
9048
|
}
|
|
6919
|
-
|
|
6920
|
-
return svg;
|
|
6921
|
-
}
|
|
6922
|
-
const label = (labelFallback ?? providerOrLabel ?? "?").trim();
|
|
6923
|
-
const initials = label ? label.slice(0, 2).toUpperCase() : "?";
|
|
6924
|
-
return `<span aria-hidden="true">${initials}</span>`;
|
|
9049
|
+
return PROVIDER_LOGO_FALLBACK_SVG;
|
|
6925
9050
|
}
|
|
6926
9051
|
var PROVIDER_BENEFITS = {
|
|
6927
9052
|
supabase: "Best when you want auth, data, and storage in one stack.",
|
|
@@ -6946,6 +9071,7 @@ var PROVIDER_BENEFITS = {
|
|
|
6946
9071
|
sentry: "Best first choice for production errors and traces.",
|
|
6947
9072
|
posthog: "Best for product analytics and funnel verification.",
|
|
6948
9073
|
auth0: "Enterprise identity with rules, MFA, and social login.",
|
|
9074
|
+
firebase: "Good fit when auth, Firestore, and hosting stay in one Google stack.",
|
|
6949
9075
|
"better-auth": "Type-safe auth you own in your codebase.",
|
|
6950
9076
|
playwright: "Best for browser-flow verification and UI regression checks.",
|
|
6951
9077
|
logrocket: "Best when session replay is the missing evidence.",
|
|
@@ -6973,7 +9099,9 @@ function providerLogosPayloadJson() {
|
|
|
6973
9099
|
inlineOnly: [...INLINE_ONLY_LOGO_KEYS],
|
|
6974
9100
|
aliases: ALIASES,
|
|
6975
9101
|
benefits: PROVIDER_BENEFITS,
|
|
6976
|
-
brandKeys: [...BRAND_LOGO_KEYS]
|
|
9102
|
+
brandKeys: [...BRAND_LOGO_KEYS],
|
|
9103
|
+
nodeTones: PROVIDER_NODE_TONES,
|
|
9104
|
+
fallbackSvg: PROVIDER_LOGO_FALLBACK_SVG
|
|
6977
9105
|
}).replace(/</g, "\\u003c");
|
|
6978
9106
|
}
|
|
6979
9107
|
|
|
@@ -7010,6 +9138,23 @@ function defaultAreaKey(artifact) {
|
|
|
7010
9138
|
}
|
|
7011
9139
|
return areas[0]?.key ?? "frontend";
|
|
7012
9140
|
}
|
|
9141
|
+
function providerLabelForArea(artifact, areaKey, mission, selectedOverride) {
|
|
9142
|
+
if (selectedOverride) {
|
|
9143
|
+
const token = normalizeProviderKey2(selectedOverride);
|
|
9144
|
+
const options = artifact.providerOptions?.[areaKey] ?? [];
|
|
9145
|
+
const match = options.find(
|
|
9146
|
+
(option) => normalizeProviderKey2(option.provider) === token || normalizeProviderKey2(option.label) === token
|
|
9147
|
+
);
|
|
9148
|
+
if (match?.label) {
|
|
9149
|
+
return match.label;
|
|
9150
|
+
}
|
|
9151
|
+
if (mission && missionMatchesProvider(mission, selectedOverride)) {
|
|
9152
|
+
return mission.providerLabel ?? selectedOverride;
|
|
9153
|
+
}
|
|
9154
|
+
return selectedOverride;
|
|
9155
|
+
}
|
|
9156
|
+
return mission?.providerLabel ?? "Not selected";
|
|
9157
|
+
}
|
|
7013
9158
|
function buildAccountStripHtml(artifact) {
|
|
7014
9159
|
if (!artifact.accountEmail) {
|
|
7015
9160
|
return "";
|
|
@@ -7036,21 +9181,22 @@ function buildNodeHtml(artifact, meta, selectedAreaKey) {
|
|
|
7036
9181
|
const area = (artifact.missionGraph.areas ?? []).find((a) => a.key === meta.key);
|
|
7037
9182
|
const selectedOverride = artifact.selectedProviders?.[meta.key] ?? "";
|
|
7038
9183
|
const mission = preferredMissionForArea(area, selectedOverride);
|
|
7039
|
-
const
|
|
7040
|
-
const
|
|
9184
|
+
const providerLabel3 = providerLabelForArea(artifact, meta.key, mission, selectedOverride);
|
|
9185
|
+
const logoProvider = selectedOverride || mission?.provider || mission?.providerLabel || "";
|
|
7041
9186
|
const readiness = mission?.readinessPercent ?? area?.readinessPercent ?? 0;
|
|
7042
9187
|
const openChecks = openChecksForMission(mission);
|
|
9188
|
+
const repoOpenChecks = mission?.checks.filter(
|
|
9189
|
+
(check) => check.evidenceClass === "missing-repo-fix" || check.status === "missing" || check.status === "failed"
|
|
9190
|
+
).length ?? 0;
|
|
9191
|
+
const providerOpenChecks = mission?.checks.filter(
|
|
9192
|
+
(check) => check.evidenceSource === "provider" || check.evidenceSource === "mcp" || check.evidenceClass === "mcp-verifier" || check.status === "needs-connection" || check.status === "unknown"
|
|
9193
|
+
).length ?? 0;
|
|
7043
9194
|
const modelGaps = gapCountForArea(artifact, meta.key);
|
|
7044
9195
|
const stateClass = modelGaps > 0 ? " studio-node--critical" : openChecks > 0 ? " studio-node--warning" : area ? " studio-node--in-project" : "";
|
|
7045
9196
|
const selectedClass = selectedAreaKey === meta.key ? " studio-node--selected" : "";
|
|
7046
|
-
const metaText = modelGaps > 0 ? `${modelGaps} stack fix${modelGaps === 1 ? "" : "es"}` :
|
|
7047
|
-
const logoClass = providerLogoClass(
|
|
7048
|
-
const logoInner = providerLogoHtml(
|
|
7049
|
-
mission?.provider,
|
|
7050
|
-
providerLabel3,
|
|
7051
|
-
mission?.key,
|
|
7052
|
-
String(selectedProvider)
|
|
7053
|
-
);
|
|
9197
|
+
const metaText = modelGaps > 0 ? `${modelGaps} stack fix${modelGaps === 1 ? "" : "es"}` : repoOpenChecks > 0 ? `${repoOpenChecks} stack fix${repoOpenChecks === 1 ? "" : "es"}` : providerOpenChecks > 0 ? "Live check" : `${readiness}% health`;
|
|
9198
|
+
const logoClass = providerLogoClass(logoProvider, mission?.key, mission?.providerLabel);
|
|
9199
|
+
const logoInner = providerLogoHtml(logoProvider, providerLabel3, mission?.key, mission?.providerLabel);
|
|
7054
9200
|
const gapBadge = modelGaps > 0 ? `<span class="studio-node__raven-badge" role="presentation">Gap ${modelGaps}</span>` : "";
|
|
7055
9201
|
return `<button type="button" class="studio-node studio-node--${escapeHtml(meta.key)} provider-logo${logoClass}${stateClass}${selectedClass}" data-area-key="${escapeHtml(meta.key)}" aria-label="${escapeHtml(meta.label)}, ${escapeHtml(providerLabel3)}, ${escapeHtml(metaText)}">
|
|
7056
9202
|
<span class="studio-node__logo provider-logo${logoClass}" aria-hidden="true">${logoInner}</span>
|
|
@@ -7067,7 +9213,6 @@ function generateReportHtml(artifact) {
|
|
|
7067
9213
|
const nodesHtml = CATEGORY_META.map((meta) => buildNodeHtml(hydrated, meta, defaultKey)).join("\n");
|
|
7068
9214
|
const accountStrip = buildAccountStripHtml(hydrated);
|
|
7069
9215
|
const logosJson = providerLogosPayloadJson();
|
|
7070
|
-
const scannedLabel = escapeHtml(hydrated.scannedAt);
|
|
7071
9216
|
return `<!DOCTYPE html>
|
|
7072
9217
|
<html lang="en" class="cli-mission-report" data-surface="panel" data-skin="editorial">
|
|
7073
9218
|
<head>
|
|
@@ -7087,8 +9232,10 @@ function generateReportHtml(artifact) {
|
|
|
7087
9232
|
<div class="studio-shell" aria-label="VibeRaven Studio cockpit">
|
|
7088
9233
|
<header class="studio-top-rail" aria-label="Studio status">
|
|
7089
9234
|
<img class="studio-top-rail__logo" src="report/assets/viberaven-logo.png" alt="" width="32" height="32" />
|
|
7090
|
-
<
|
|
7091
|
-
|
|
9235
|
+
<div class="studio-top-rail__brand">
|
|
9236
|
+
<span class="studio-top-rail__wordmark">VIBERAVEN</span>
|
|
9237
|
+
<span class="studio-top-rail__label">MISSION MAP</span>
|
|
9238
|
+
</div>
|
|
7092
9239
|
</header>
|
|
7093
9240
|
<div class="studio-workspace">
|
|
7094
9241
|
<main class="studio-map-canvas" aria-label="Interactive full-stack system map">
|
|
@@ -7097,7 +9244,7 @@ function generateReportHtml(artifact) {
|
|
|
7097
9244
|
<div class="studio-connector-layer" aria-hidden="true"></div>
|
|
7098
9245
|
<div class="studio-core-group">
|
|
7099
9246
|
<div class="studio-core-node" aria-label="Production core score">
|
|
7100
|
-
<
|
|
9247
|
+
<span>VIBERAVEN</span>
|
|
7101
9248
|
<strong>${hydrated.productionCorePercent}%</strong>
|
|
7102
9249
|
<small>Production core</small>
|
|
7103
9250
|
</div>
|
|
@@ -7149,16 +9296,22 @@ var REPORT_ASSET_FILES = [
|
|
|
7149
9296
|
var INLINE_SECRET_PATTERNS = [
|
|
7150
9297
|
/\b(sk_(?:live|test)_[A-Za-z0-9]{12,}|sk-proj-[A-Za-z0-9_-]{16,}|sk-[A-Za-z0-9_-]{20,})\b/g,
|
|
7151
9298
|
/\b(whsec_[A-Za-z0-9]{12,}|rk_(?:live|test)_[A-Za-z0-9]{12,})\b/g,
|
|
7152
|
-
/\
|
|
9299
|
+
/\bghp_[A-Za-z0-9]{36,}\b/g,
|
|
9300
|
+
/\bgithub_pat_[A-Za-z0-9_]{50,}\b/g,
|
|
9301
|
+
/\bxox[bp]-[A-Za-z0-9-]{20,}\b/g,
|
|
9302
|
+
/\bxapp-[A-Za-z0-9-]{20,}\b/g,
|
|
9303
|
+
/\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/g,
|
|
9304
|
+
/-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g
|
|
7153
9305
|
];
|
|
7154
|
-
var SENSITIVE_ENV_KEYS =
|
|
9306
|
+
var SENSITIVE_ENV_KEYS = /(?:^|_)(?:ACCESS_TOKEN|AUTHORIZATION|API_KEY|SECRET|SECRET_KEY|SERVICE_ROLE_KEY|TOKEN|PASSWORD|PRIVATE_KEY|CREDENTIALS?)(?:$|_)/i;
|
|
7155
9307
|
function redactString(value) {
|
|
7156
9308
|
let out = value;
|
|
7157
9309
|
for (const pattern of INLINE_SECRET_PATTERNS) {
|
|
7158
|
-
out = out.replace(pattern, "[REDACTED_SECRET]");
|
|
9310
|
+
out = out.replace(pattern, pattern.source.includes("PRIVATE KEY") ? "[REDACTED_PRIVATE_KEY]" : "[REDACTED_SECRET]");
|
|
7159
9311
|
}
|
|
9312
|
+
out = out.replace(/\bAuthorization\s*:\s*([A-Za-z][A-Za-z0-9._-]*)\s+[^\s;,]+/gi, "Authorization: $1 [REDACTED]");
|
|
7160
9313
|
return out.replace(
|
|
7161
|
-
/\b([A-Za-z0-9_]*(?:API_KEY|SECRET|TOKEN|PASSWORD|PRIVATE_KEY)[A-Za-z0-9_]*)\s*=\s*["']?[^"'\s;,]+["']?/gi,
|
|
9314
|
+
/\b([A-Za-z0-9_]*(?:ACCESS_TOKEN|AUTHORIZATION|API_KEY|SECRET|SECRET_KEY|SERVICE_ROLE_KEY|TOKEN|PASSWORD|PRIVATE_KEY|CREDENTIALS?)[A-Za-z0-9_]*)\s*=\s*["']?[^"'\s;,]+["']?/gi,
|
|
7162
9315
|
"$1=[REDACTED]"
|
|
7163
9316
|
);
|
|
7164
9317
|
}
|
|
@@ -7397,6 +9550,15 @@ function gapTagColor(modelGaps, open) {
|
|
|
7397
9550
|
}
|
|
7398
9551
|
return import_picocolors.default.dim;
|
|
7399
9552
|
}
|
|
9553
|
+
function manualActionCheckCount(artifact) {
|
|
9554
|
+
return (artifact.missionGraph.areas ?? []).reduce((areaTotal, area) => {
|
|
9555
|
+
return areaTotal + area.providerMissions.reduce((missionTotal, mission) => {
|
|
9556
|
+
return missionTotal + mission.checks.filter(
|
|
9557
|
+
(check) => check.evidenceClass === "manual-dashboard" || check.evidenceClass === "mcp-verifier" || check.evidenceSource === "provider" || check.evidenceSource === "mcp" || check.status === "needs-connection" || check.status === "unknown"
|
|
9558
|
+
).length;
|
|
9559
|
+
}, 0);
|
|
9560
|
+
}, 0);
|
|
9561
|
+
}
|
|
7400
9562
|
function printScanSummary(artifact, paths) {
|
|
7401
9563
|
const pct = artifact.productionCorePercent;
|
|
7402
9564
|
const gapCount = artifact.gaps.length;
|
|
@@ -7432,6 +9594,11 @@ function printScanSummary(artifact, paths) {
|
|
|
7432
9594
|
console.log("");
|
|
7433
9595
|
console.log(import_picocolors.default.dim("Press Enter in `viberaven` menu to rescan \xB7 `viberaven prompt` for top gap"));
|
|
7434
9596
|
console.log(import_picocolors.default.dim("Agents: read .viberaven/agent-summary.md"));
|
|
9597
|
+
console.log(formatAgentStatus(READY, `Scan complete. Read ${paths.summaryPath} before changing code.`));
|
|
9598
|
+
const manualCount = manualActionCheckCount(artifact);
|
|
9599
|
+
if (manualCount > 0) {
|
|
9600
|
+
console.log(formatAgentStatus(MANUAL_ACTION_REQUIRED, `${manualCount} provider dashboard or read-only MCP check${manualCount === 1 ? "" : "s"} require user/provider verification. Do not claim these as repo-code fixes.`));
|
|
9601
|
+
}
|
|
7435
9602
|
console.log("");
|
|
7436
9603
|
}
|
|
7437
9604
|
|
|
@@ -7972,7 +10139,7 @@ var Y2 = ({ indicator: t = "dots" } = {}) => {
|
|
|
7972
10139
|
var import_picocolors3 = __toESM(require_picocolors());
|
|
7973
10140
|
|
|
7974
10141
|
// src/version.ts
|
|
7975
|
-
var VERSION = "0.1.0-beta.
|
|
10142
|
+
var VERSION = "0.1.0-beta.2";
|
|
7976
10143
|
|
|
7977
10144
|
// src/tui/runInteractive.ts
|
|
7978
10145
|
async function formatStatusLine() {
|
|
@@ -8230,6 +10397,10 @@ function parseArgs(argv) {
|
|
|
8230
10397
|
flags.help = true;
|
|
8231
10398
|
continue;
|
|
8232
10399
|
}
|
|
10400
|
+
if (arg === "--version" || arg === "-v") {
|
|
10401
|
+
flags.version = true;
|
|
10402
|
+
continue;
|
|
10403
|
+
}
|
|
8233
10404
|
if (arg.startsWith("--")) {
|
|
8234
10405
|
const key = arg.slice(2);
|
|
8235
10406
|
const next = argv[i + 1];
|
|
@@ -8249,6 +10420,9 @@ function parseArgs(argv) {
|
|
|
8249
10420
|
}
|
|
8250
10421
|
return { command, flags, positional };
|
|
8251
10422
|
}
|
|
10423
|
+
function formatScanJsonStdout(artifact) {
|
|
10424
|
+
return JSON.stringify(sanitizeArtifactForDisk(artifact), null, 2);
|
|
10425
|
+
}
|
|
8252
10426
|
async function cmdLogin(flags) {
|
|
8253
10427
|
const apiBaseUrl = resolveApiBaseUrl(typeof flags["api-url"] === "string" ? flags["api-url"] : void 0);
|
|
8254
10428
|
await runDeviceLogin(apiBaseUrl);
|
|
@@ -8300,13 +10474,17 @@ async function cmdScan(flags, positional) {
|
|
|
8300
10474
|
}
|
|
8301
10475
|
return 2;
|
|
8302
10476
|
}
|
|
8303
|
-
|
|
10477
|
+
if (result.kind === "auth_required" || result.kind === "session_invalid") {
|
|
10478
|
+
console.error(formatAgentStatus(LOGIN_REQUIRED, result.message));
|
|
10479
|
+
return 1;
|
|
10480
|
+
}
|
|
10481
|
+
console.error(formatAgentStatus(ERROR, result.message));
|
|
8304
10482
|
return 1;
|
|
8305
10483
|
}
|
|
8306
10484
|
const artifact = await enrichArtifactWithAccount(result.artifact, apiBaseUrl, accessToken);
|
|
8307
10485
|
const paths = await writeScanArtifacts({ artifact, cwd: workspacePath });
|
|
8308
10486
|
if (flags.json) {
|
|
8309
|
-
console.log(
|
|
10487
|
+
console.log(formatScanJsonStdout(artifact));
|
|
8310
10488
|
return 0;
|
|
8311
10489
|
}
|
|
8312
10490
|
printScanSummary(artifact, paths);
|
|
@@ -8429,6 +10607,10 @@ async function main() {
|
|
|
8429
10607
|
printHelp();
|
|
8430
10608
|
return 0;
|
|
8431
10609
|
}
|
|
10610
|
+
if (flags.version || command === "version") {
|
|
10611
|
+
console.log(VERSION);
|
|
10612
|
+
return 0;
|
|
10613
|
+
}
|
|
8432
10614
|
if (!command) {
|
|
8433
10615
|
await runInteractiveSession();
|
|
8434
10616
|
return 0;
|
|
@@ -8454,23 +10636,25 @@ async function main() {
|
|
|
8454
10636
|
return cmdPrompt(flags, positional);
|
|
8455
10637
|
case "stack":
|
|
8456
10638
|
return cmdStack(positional);
|
|
8457
|
-
case "--version":
|
|
8458
|
-
case "-v":
|
|
8459
|
-
case "version":
|
|
8460
|
-
console.log(VERSION);
|
|
8461
|
-
return 0;
|
|
8462
10639
|
default:
|
|
8463
10640
|
console.error(`Unknown command: ${command}`);
|
|
8464
10641
|
printHelp();
|
|
8465
10642
|
return 1;
|
|
8466
10643
|
}
|
|
8467
10644
|
}
|
|
8468
|
-
|
|
8469
|
-
|
|
8470
|
-
|
|
8471
|
-
|
|
8472
|
-
}
|
|
8473
|
-
|
|
8474
|
-
|
|
10645
|
+
if (require.main === module) {
|
|
10646
|
+
main().then((code) => {
|
|
10647
|
+
if (code !== 0) {
|
|
10648
|
+
process.exitCode = code;
|
|
10649
|
+
}
|
|
10650
|
+
}).catch((error) => {
|
|
10651
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
10652
|
+
process.exitCode = 1;
|
|
10653
|
+
});
|
|
10654
|
+
}
|
|
10655
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
10656
|
+
0 && (module.exports = {
|
|
10657
|
+
formatScanJsonStdout,
|
|
10658
|
+
parseArgs
|
|
8475
10659
|
});
|
|
8476
10660
|
//# sourceMappingURL=cli.js.map
|