evOo-local/packages/adapters/src/github-webhook-emulator.ts
cynarAI 6c7eed7580 Initial clean evOo-local from f00577b
Fresh history cut from cynarAI/evoo antigravity-handoff/project-timeline
(Voice First + project timeline). Brand docs/titles as evOo; keep @rakazo/*
packages as tech debt. Excludes node_modules, caches, secrets, voice blobs.
2026-09-20 17:15:16 +02:00

51 lines
1.7 KiB
TypeScript

import { createHmac } from "node:crypto";
export type GithubWebhookDeliveryInput = {
botId: string;
event: string;
deliveryId?: string;
payload?: unknown;
/** Override the signing secret (defaults to the emulator secret). */
secret?: string;
origin?: string;
};
/**
* Deterministic GitHub webhook boundary emulator: mints HMAC-signed delivery
* requests for bot GitHub trigger routes without talking to GitHub.
*/
export class GithubWebhookEmulator {
readonly signingSecret = "github-webhook-test-secret-32chars!";
private deliveryCounter = 0;
nextDeliveryId(): string {
this.deliveryCounter += 1;
return `00000000-0000-4000-8000-${String(this.deliveryCounter).padStart(12, "0")}`;
}
sign(raw: string, secret = this.signingSecret): string {
return `sha256=${createHmac("sha256", secret).update(raw).digest("hex")}`;
}
/** A webhook request exactly as GitHub would deliver it (HMAC over the raw body). */
buildDeliveryRequest(input: GithubWebhookDeliveryInput): Request {
const raw =
typeof input.payload === "string"
? input.payload
: JSON.stringify(input.payload ?? { zen: "emulated", hook_id: 1 });
const origin = input.origin ?? "https://rakazo.test";
const secret = input.secret ?? this.signingSecret;
return new Request(`${origin}/api/v1/bots/${input.botId}/github`, {
method: "POST",
headers: {
"content-type": "application/json",
"x-github-event": input.event,
"x-github-delivery": input.deliveryId ?? this.nextDeliveryId(),
"x-hub-signature-256": this.sign(raw, secret),
"user-agent": "GitHub-Hookshot/emulator",
},
body: raw,
});
}
}