UNPKG

aiwg

Version:

Deployment tool and support utility for AI context. Copies agents, skills, commands, rules, and behaviors into the paths each AI platform reads (Claude Code, Codex, Copilot, Cursor, Warp, OpenClaw, and 6 more) so one source of truth works across 10 platfo

120 lines 4.96 kB
/** Header name (case-insensitive). */ export declare const SIGNATURE_HEADER = "x-aiwg-signature"; /** Five minutes — RFC 8941 timestamp tolerance. */ export declare const DEFAULT_TIMESTAMP_TOLERANCE_SECONDS = 300; /** Idempotency cache size. Old entries are evicted FIFO. */ export declare const DEFAULT_IDEMPOTENCY_CAPACITY = 4096; export interface VerifyResult { ok: boolean; /** When `ok=false`, a stable machine code suitable for ProblemDetails. */ code?: 'signature_missing' | 'signature_malformed' | 'signature_mismatch' | 'timestamp_skew' | 'secret_unknown'; /** Human-readable detail. */ detail?: string; /** Parsed timestamp on success (epoch seconds). */ timestamp?: number; } export interface VerifyOptions { /** * Function to look up the per-config secret given the `configId` query * parameter on the webhook URL. Returning `null` causes a * `secret_unknown` failure. * * Implementations should fetch from a per-mission store populated when * createPushNotificationConfig is called. */ lookupSecret: (configId: string) => string | null | Promise<string | null>; /** Skew tolerance in seconds. Defaults to 5 minutes. */ toleranceSeconds?: number; /** Clock override for testing. */ now?: () => number; } /** * Verify the `X-AIWG-Signature` header against the raw request body. * * Stripe-style format: * `X-AIWG-Signature: t=1700000000,v1=<hex hmac-sha256>` * * The HMAC is computed over `t=<timestamp>.<raw body>` (the timestamp and * a literal dot, prepended to the body). Multiple `v1=` entries may be * present in a single header (during a key rotation); any match accepts. */ export declare function verifyWebhookSignature(signature: string | undefined, body: Buffer, configId: string, opts: VerifyOptions): Promise<VerifyResult>; /** Parse `t=...,v1=...,(v1=...,)*` into structured fields. */ export declare function parseSignatureHeader(header: string): { timestamp: number; v1: string[]; } | null; /** * Bounded FIFO event-id deduper. The first call with a given id returns * true; subsequent calls return false until the id falls out of the * window. * * Subscribers MUST dedupe per the spec — the executor's delivery worker * retries on non-2xx responses, so a flaky downstream handler will * receive the same event-id multiple times. */ export declare class IdempotencyCache { private readonly capacity; private readonly seen; private readonly order; constructor(capacity?: number); /** Returns true if `id` is new (and stores it); false if it's a duplicate. */ markFresh(id: string): boolean; size(): number; } /** * Maps `configId` to its symmetric HMAC secret + optional mission * routing metadata. Populated when the AIWG client calls * createPushNotificationConfig on mission start; torn down on mission * complete. * * Threading model: single-process in-memory map. For multi-replica * deployments swap in a shared backing store (Redis, etc.) with the * same interface. */ export interface PushSecretRegistryEntry { configId: string; secret: string; /** Mission this config belongs to — used for routing webhook payloads. */ missionId?: string; /** Task this config belongs to — used to scope StreamEvent application. */ taskId?: string; /** Free-form metadata returned alongside the entry on lookup. */ metadata?: Record<string, unknown>; } export declare class PushSecretRegistry { private readonly entries; register(entry: PushSecretRegistryEntry): void; lookup(configId: string): PushSecretRegistryEntry | null; unregister(configId: string): boolean; /** Test/debug helper. */ size(): number; } export interface WebhookHandlerOptions { registry: PushSecretRegistry; idempotency: IdempotencyCache; /** * Route the verified, deduplicated StreamEvent into the mission state * machine. Implementations typically forward to the same event handler * SSE uses. */ route: (entry: PushSecretRegistryEntry, event: unknown) => void | Promise<void>; toleranceSeconds?: number; now?: () => number; } export interface HandleResult { status: number; /** ProblemDetails-shape body for non-2xx; minimal `{ ok: true }` for 2xx. */ body: Record<string, unknown>; } /** * One-shot processor: takes a raw webhook request (configId + body + * signature header + event-id header), verifies, dedupes, routes. * * Returns the status code and body the HTTP layer should emit. The HTTP * adapter (serve.ts) is responsible for reading the raw body bytes * BEFORE any JSON parsing — the signature is computed over raw bytes * (whitespace-sensitive). */ export declare function handleWebhook(configId: string, body: Buffer, signature: string | undefined, eventId: string | undefined, opts: WebhookHandlerOptions): Promise<HandleResult>; //# sourceMappingURL=webhook.d.ts.map