chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
node_modules
|
||||
dist
|
||||
*.md
|
||||
.git
|
||||
@@ -0,0 +1,25 @@
|
||||
FROM node:22-slim AS builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY package.json ./
|
||||
RUN npm install
|
||||
|
||||
COPY tsconfig.json ./
|
||||
COPY src/ ./src/
|
||||
RUN npm run build
|
||||
|
||||
FROM node:22-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY package.json ./
|
||||
RUN npm install --omit=dev
|
||||
|
||||
COPY --from=builder /app/dist ./dist
|
||||
|
||||
USER node
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
CMD ["node", "dist/server.js"]
|
||||
Generated
+246
@@ -0,0 +1,246 @@
|
||||
{
|
||||
"name": "showcase-eval-webhook",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "showcase-eval-webhook",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"@hono/node-server": "^1.13.8",
|
||||
"@octokit/auth-app": "^7.1.4",
|
||||
"@octokit/rest": "^21.1.1",
|
||||
"hono": "^4.7.10"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^25.6.0",
|
||||
"tsx": "^4.19.4",
|
||||
"typescript": "^5.8.3"
|
||||
}
|
||||
},
|
||||
"../../node_modules/.pnpm/@octokit+auth-app@7.2.2/node_modules/@octokit/auth-app": {
|
||||
"version": "7.2.2",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/auth-oauth-app": "^8.1.4",
|
||||
"@octokit/auth-oauth-user": "^5.1.4",
|
||||
"@octokit/request": "^9.2.3",
|
||||
"@octokit/request-error": "^6.1.8",
|
||||
"@octokit/types": "^14.0.0",
|
||||
"toad-cache": "^3.7.0",
|
||||
"universal-github-app-jwt": "^2.2.0",
|
||||
"universal-user-agent": "^7.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@octokit/tsconfig": "^4.0.0",
|
||||
"@types/node": "^22.0.0",
|
||||
"@vitest/coverage-v8": "^3.0.0",
|
||||
"@vitest/ui": "^3.0.0",
|
||||
"esbuild": "^0.25.0",
|
||||
"fetch-mock": "^11.0.0",
|
||||
"glob": "^11.0.0",
|
||||
"prettier": "3.5.3",
|
||||
"semantic-release-plugin-update-version-in-files": "^2.0.0",
|
||||
"typescript": "^5.0.0",
|
||||
"vitest": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"../../node_modules/.pnpm/@octokit+rest@21.1.1/node_modules/@octokit/rest": {
|
||||
"version": "21.1.1",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/core": "^6.1.4",
|
||||
"@octokit/plugin-paginate-rest": "^11.4.2",
|
||||
"@octokit/plugin-request-log": "^5.3.1",
|
||||
"@octokit/plugin-rest-endpoint-methods": "^13.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@octokit/auth-action": "^5.1.1",
|
||||
"@octokit/auth-app": "^7.1.4",
|
||||
"@octokit/fixtures-server": "^8.1.0",
|
||||
"@octokit/request": "^9.2.1",
|
||||
"@octokit/tsconfig": "^4.0.0",
|
||||
"@types/node": "^22.0.0",
|
||||
"@vitest/coverage-v8": "^3.0.0",
|
||||
"esbuild": "^0.25.0",
|
||||
"fetch-mock": "^12.0.0",
|
||||
"glob": "^11.0.0",
|
||||
"nock": "^14.0.0-beta.8",
|
||||
"prettier": "^3.2.4",
|
||||
"semantic-release-plugin-update-version-in-files": "^1.1.0",
|
||||
"typescript": "^5.3.3",
|
||||
"undici": "^6.4.0",
|
||||
"vitest": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"../../node_modules/.pnpm/hono@4.12.15/node_modules/hono": {
|
||||
"version": "4.12.15",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@hono/eslint-config": "^2.1.0",
|
||||
"@hono/node-server": "^1.13.5",
|
||||
"@types/glob": "^9.0.0",
|
||||
"@types/jsdom": "^21.1.7",
|
||||
"@types/node": "^24.3.0",
|
||||
"@types/ws": "^8.18.1",
|
||||
"@typescript/native-preview": "7.0.0-dev.20260210.1",
|
||||
"@vitest/coverage-v8": "^3.2.4",
|
||||
"arg": "^5.0.2",
|
||||
"bun-types": "^1.2.20",
|
||||
"editorconfig-checker": "6.1.1",
|
||||
"esbuild": "^0.27.1",
|
||||
"eslint": "^9.39.3",
|
||||
"glob": "^11.0.0",
|
||||
"jsdom": "22.1.0",
|
||||
"msw": "^2.6.0",
|
||||
"np": "10.2.0",
|
||||
"oxc-parser": "^0.96.0",
|
||||
"pkg-pr-new": "^0.0.53",
|
||||
"prettier": "3.7.4",
|
||||
"publint": "0.3.15",
|
||||
"typescript": "^5.9.2",
|
||||
"undici": "^6.21.3",
|
||||
"vite-plugin-fastly-js-compute": "^0.4.2",
|
||||
"vitest": "^3.2.4",
|
||||
"wrangler": "4.12.0",
|
||||
"ws": "^8.18.0",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.9.0"
|
||||
}
|
||||
},
|
||||
"../../node_modules/.pnpm/tsx@4.21.0/node_modules/tsx": {
|
||||
"version": "4.21.0",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"esbuild": "~0.27.0",
|
||||
"get-tsconfig": "^4.7.5"
|
||||
},
|
||||
"bin": {
|
||||
"tsx": "dist/cli.mjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "~2.3.3"
|
||||
}
|
||||
},
|
||||
"../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript": {
|
||||
"version": "5.9.3",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@dprint/formatter": "^0.4.1",
|
||||
"@dprint/typescript": "0.93.4",
|
||||
"@esfx/canceltoken": "^1.0.0",
|
||||
"@eslint/js": "^9.20.0",
|
||||
"@octokit/rest": "^21.1.1",
|
||||
"@types/chai": "^4.3.20",
|
||||
"@types/diff": "^7.0.1",
|
||||
"@types/minimist": "^1.2.5",
|
||||
"@types/mocha": "^10.0.10",
|
||||
"@types/ms": "^0.7.34",
|
||||
"@types/node": "latest",
|
||||
"@types/source-map-support": "^0.5.10",
|
||||
"@types/which": "^3.0.4",
|
||||
"@typescript-eslint/rule-tester": "^8.24.1",
|
||||
"@typescript-eslint/type-utils": "^8.24.1",
|
||||
"@typescript-eslint/utils": "^8.24.1",
|
||||
"azure-devops-node-api": "^14.1.0",
|
||||
"c8": "^10.1.3",
|
||||
"chai": "^4.5.0",
|
||||
"chokidar": "^4.0.3",
|
||||
"diff": "^7.0.0",
|
||||
"dprint": "^0.49.0",
|
||||
"esbuild": "^0.25.0",
|
||||
"eslint": "^9.20.1",
|
||||
"eslint-formatter-autolinkable-stylish": "^1.4.0",
|
||||
"eslint-plugin-regexp": "^2.7.0",
|
||||
"fast-xml-parser": "^4.5.2",
|
||||
"glob": "^10.4.5",
|
||||
"globals": "^15.15.0",
|
||||
"hereby": "^1.10.0",
|
||||
"jsonc-parser": "^3.3.1",
|
||||
"knip": "^5.44.4",
|
||||
"minimist": "^1.2.8",
|
||||
"mocha": "^10.8.2",
|
||||
"mocha-fivemat-progress-reporter": "^0.1.0",
|
||||
"monocart-coverage-reports": "^2.12.1",
|
||||
"ms": "^2.1.3",
|
||||
"picocolors": "^1.1.1",
|
||||
"playwright": "^1.50.1",
|
||||
"source-map-support": "^0.5.21",
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.7.3",
|
||||
"typescript-eslint": "^8.24.1",
|
||||
"which": "^3.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/@hono/node-server": {
|
||||
"version": "1.19.14",
|
||||
"resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.14.tgz",
|
||||
"integrity": "sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18.14.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"hono": "^4"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/auth-app": {
|
||||
"resolved": "../../node_modules/.pnpm/@octokit+auth-app@7.2.2/node_modules/@octokit/auth-app",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@octokit/rest": {
|
||||
"resolved": "../../node_modules/.pnpm/@octokit+rest@21.1.1/node_modules/@octokit/rest",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "25.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.6.0.tgz",
|
||||
"integrity": "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~7.19.0"
|
||||
}
|
||||
},
|
||||
"node_modules/hono": {
|
||||
"resolved": "../../node_modules/.pnpm/hono@4.12.15/node_modules/hono",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/tsx": {
|
||||
"resolved": "../../node_modules/.pnpm/tsx@4.21.0/node_modules/tsx",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"resolved": "../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "7.19.2",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.19.2.tgz",
|
||||
"integrity": "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "showcase-eval-webhook",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "tsx watch src/server.ts",
|
||||
"start": "node dist/server.js",
|
||||
"build": "tsc"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hono/node-server": "^1.13.8",
|
||||
"@octokit/auth-app": "^7.1.4",
|
||||
"@octokit/rest": "^21.1.1",
|
||||
"hono": "^4.7.10"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^25.6.0",
|
||||
"tsx": "^4.19.4",
|
||||
"typescript": "^5.8.3"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,285 @@
|
||||
import { Hono } from "hono";
|
||||
import { serve } from "@hono/node-server";
|
||||
import { createAppAuth } from "@octokit/auth-app";
|
||||
import { Octokit } from "@octokit/rest";
|
||||
import crypto from "node:crypto";
|
||||
|
||||
const app = new Hono();
|
||||
|
||||
const WEBHOOK_SECRET = process.env.GITHUB_WEBHOOK_SECRET ?? "";
|
||||
const APP_ID = process.env.GITHUB_APP_ID ?? "1108748";
|
||||
const PRIVATE_KEY = (process.env.GITHUB_APP_PRIVATE_KEY ?? "").replace(
|
||||
/\\n/g,
|
||||
"\n",
|
||||
);
|
||||
const INSTALLATION_ID = process.env.GITHUB_APP_INSTALLATION_ID ?? "";
|
||||
|
||||
function verifySignature(payload: string, signature: string): boolean {
|
||||
const expected =
|
||||
"sha256=" +
|
||||
crypto.createHmac("sha256", WEBHOOK_SECRET).update(payload).digest("hex");
|
||||
const expectedBuf = Buffer.from(expected);
|
||||
const signatureBuf = Buffer.from(signature);
|
||||
if (expectedBuf.length !== signatureBuf.length) return false;
|
||||
return crypto.timingSafeEqual(expectedBuf, signatureBuf);
|
||||
}
|
||||
|
||||
function getOctokit(): Octokit {
|
||||
return new Octokit({
|
||||
authStrategy: createAppAuth,
|
||||
auth: {
|
||||
appId: APP_ID,
|
||||
privateKey: PRIVATE_KEY,
|
||||
installationId: Number(INSTALLATION_ID),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Health endpoints. The verify-deploy probe and every other API-shaped
|
||||
// showcase backend (agent, eval, pocketbase, plus the webhooks driver
|
||||
// in showcase/scripts/verify-deploy.drivers.*.ts) standardize on
|
||||
// `/api/health`. `/health` is retained for back-compat with any
|
||||
// pre-existing callers; both return the same body, so a flip between
|
||||
// paths is a no-op for clients.
|
||||
app.get("/health", (c) => c.json({ ok: true }));
|
||||
app.get("/api/health", (c) => c.json({ ok: true }));
|
||||
|
||||
app.post("/webhooks/github", async (c) => {
|
||||
const body = await c.req.text();
|
||||
const signature = c.req.header("x-hub-signature-256") ?? "";
|
||||
|
||||
if (!verifySignature(body, signature)) {
|
||||
return c.json({ error: "invalid signature" }, 401);
|
||||
}
|
||||
|
||||
const event = c.req.header("x-github-event");
|
||||
if (event !== "check_run") {
|
||||
return c.json({ ignored: true }, 200);
|
||||
}
|
||||
|
||||
const payload = JSON.parse(body);
|
||||
if (
|
||||
payload.action !== "requested_action" ||
|
||||
payload.requested_action?.identifier !== "run-eval"
|
||||
) {
|
||||
return c.json({ ignored: true }, 200);
|
||||
}
|
||||
|
||||
const checkRun = payload.check_run;
|
||||
const prNumber =
|
||||
checkRun.external_id || checkRun.pull_requests?.[0]?.number?.toString();
|
||||
|
||||
if (!prNumber) {
|
||||
return c.json({ error: "no PR number found" }, 422);
|
||||
}
|
||||
|
||||
const octokit = getOctokit();
|
||||
|
||||
await octokit.checks.update({
|
||||
owner: "CopilotKit",
|
||||
repo: "CopilotKit",
|
||||
check_run_id: checkRun.id,
|
||||
status: "in_progress",
|
||||
});
|
||||
|
||||
await octokit.actions.createWorkflowDispatch({
|
||||
owner: "CopilotKit",
|
||||
repo: "CopilotKit",
|
||||
workflow_id: "showcase_eval.yml",
|
||||
ref: "main",
|
||||
inputs: {
|
||||
pr_number: prNumber,
|
||||
check_run_id: String(checkRun.id),
|
||||
},
|
||||
});
|
||||
|
||||
console.log(
|
||||
`Dispatched eval for PR #${prNumber}, check_run_id=${checkRun.id}`,
|
||||
);
|
||||
return c.json({ dispatched: true, pr: prNumber }, 200);
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Signed trigger link — clicked from the bot comment on the PR
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const TRIGGER_SECRET = WEBHOOK_SECRET; // reuse the same secret for HMAC signing
|
||||
|
||||
export function signTriggerUrl(pr: string, checkRunId: string): string {
|
||||
const payload = `${pr}:${checkRunId}`;
|
||||
const sig = crypto
|
||||
.createHmac("sha256", TRIGGER_SECRET)
|
||||
.update(payload)
|
||||
.digest("hex");
|
||||
return sig;
|
||||
}
|
||||
|
||||
function verifyTriggerSig(
|
||||
pr: string,
|
||||
checkRunId: string,
|
||||
sig: string,
|
||||
): boolean {
|
||||
const expected = signTriggerUrl(pr, checkRunId);
|
||||
const expectedBuf = Buffer.from(expected);
|
||||
const sigBuf = Buffer.from(sig);
|
||||
if (expectedBuf.length !== sigBuf.length) return false;
|
||||
return crypto.timingSafeEqual(expectedBuf, sigBuf);
|
||||
}
|
||||
|
||||
function validateTriggerParams(
|
||||
pr: string,
|
||||
checkRunId: string,
|
||||
sig: string,
|
||||
): { valid: true } | { valid: false; status: 400 | 403; message: string } {
|
||||
if (!pr || !sig) {
|
||||
return { valid: false, status: 400, message: "Missing parameters." };
|
||||
}
|
||||
if (!verifyTriggerSig(pr, checkRunId, sig)) {
|
||||
return { valid: false, status: 403, message: "Invalid signature." };
|
||||
}
|
||||
return { valid: true };
|
||||
}
|
||||
|
||||
app.get("/trigger/eval", async (c) => {
|
||||
const pr = c.req.query("pr") ?? "";
|
||||
const checkRunId = c.req.query("check_run_id") ?? "";
|
||||
const sig = c.req.query("sig") ?? "";
|
||||
|
||||
const result = validateTriggerParams(pr, checkRunId, sig);
|
||||
if (result.valid !== true) {
|
||||
return c.html(
|
||||
`<h2>${result.status === 400 ? "Invalid request" : "Unauthorized"}</h2><p>${result.message}</p>`,
|
||||
result.status,
|
||||
);
|
||||
}
|
||||
|
||||
return c.html(`
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Showcase Eval — PR #${pr}</title>
|
||||
<style>
|
||||
body { font-family: system-ui; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; background: #0d1117; color: #e6edf3; }
|
||||
div { text-align: center; }
|
||||
h2 { color: #3fb950; }
|
||||
button { background: #238636; color: #fff; border: 1px solid #2ea043; border-radius: 6px; padding: 10px 24px; font-size: 16px; cursor: pointer; font-family: system-ui; }
|
||||
button:hover { background: #2ea043; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<h2>Showcase Eval</h2>
|
||||
<p>PR #${pr}</p>
|
||||
<form method="POST" action="/trigger/eval" target="_blank">
|
||||
<input type="hidden" name="pr" value="${pr}" />
|
||||
<input type="hidden" name="check_run_id" value="${checkRunId}" />
|
||||
<input type="hidden" name="sig" value="${sig}" />
|
||||
<button type="submit">Run Evaluation</button>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
`);
|
||||
});
|
||||
|
||||
app.post("/trigger/eval", async (c) => {
|
||||
const body = await c.req.parseBody();
|
||||
const pr = String(body["pr"] ?? "");
|
||||
const checkRunId = String(body["check_run_id"] ?? "");
|
||||
const sig = String(body["sig"] ?? "");
|
||||
|
||||
const result = validateTriggerParams(pr, checkRunId, sig);
|
||||
if (result.valid !== true) {
|
||||
return c.html(
|
||||
`<h2>${result.status === 400 ? "Invalid request" : "Unauthorized"}</h2><p>${result.message}</p>`,
|
||||
result.status,
|
||||
);
|
||||
}
|
||||
|
||||
const octokit = getOctokit();
|
||||
|
||||
if (checkRunId) {
|
||||
await octokit.checks.update({
|
||||
owner: "CopilotKit",
|
||||
repo: "CopilotKit",
|
||||
check_run_id: Number(checkRunId),
|
||||
status: "in_progress",
|
||||
output: {
|
||||
title: "Showcase Eval — running...",
|
||||
summary: "Evaluation in progress.",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
await octokit.actions.createWorkflowDispatch({
|
||||
owner: "CopilotKit",
|
||||
repo: "CopilotKit",
|
||||
workflow_id: "showcase_eval.yml",
|
||||
ref: "main",
|
||||
inputs: {
|
||||
pr_number: pr,
|
||||
check_run_id: checkRunId || "",
|
||||
},
|
||||
});
|
||||
|
||||
console.log(
|
||||
`Trigger link: dispatched eval for PR #${pr}, check_run_id=${checkRunId}`,
|
||||
);
|
||||
|
||||
// Poll for the Actions run URL (up to ~5 seconds)
|
||||
const fallbackUrl =
|
||||
"https://github.com/CopilotKit/CopilotKit/actions/workflows/showcase_eval.yml";
|
||||
let runUrl = fallbackUrl;
|
||||
const startTime = Date.now();
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
try {
|
||||
const runs = await octokit.actions.listWorkflowRuns({
|
||||
owner: "CopilotKit",
|
||||
repo: "CopilotKit",
|
||||
workflow_id: "showcase_eval.yml",
|
||||
event: "workflow_dispatch",
|
||||
per_page: 5,
|
||||
});
|
||||
const recent = runs.data.workflow_runs.find((run) => {
|
||||
const created = new Date(run.created_at).getTime();
|
||||
return Date.now() - created < 30_000;
|
||||
});
|
||||
if (recent) {
|
||||
runUrl = recent.html_url;
|
||||
break;
|
||||
}
|
||||
} catch {
|
||||
// ignore polling errors
|
||||
}
|
||||
if (Date.now() - startTime > 5000) break;
|
||||
}
|
||||
|
||||
return c.html(`
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Showcase Eval — dispatched</title>
|
||||
<meta http-equiv="refresh" content="0;url=${runUrl}">
|
||||
<style>
|
||||
body { font-family: system-ui; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; background: #0d1117; color: #e6edf3; }
|
||||
div { text-align: center; }
|
||||
h2 { color: #3fb950; }
|
||||
a { color: #58a6ff; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<h2>Showcase Eval triggered</h2>
|
||||
<p>PR #${pr} — evaluation dispatched.</p>
|
||||
<p>Redirecting to <a href="${runUrl}">the Actions run</a>...</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
`);
|
||||
});
|
||||
|
||||
const port = Number(process.env.PORT ?? 3000);
|
||||
console.log(`eval-webhook listening on :${port}`);
|
||||
serve({ fetch: app.fetch, port });
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "node",
|
||||
"strict": false,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"outDir": "dist",
|
||||
"rootDir": "src",
|
||||
"declaration": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
Reference in New Issue
Block a user