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.
51 lines
1.7 KiB
TypeScript
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,
|
|
});
|
|
}
|
|
}
|