@@ -61,7 +61,7 @@ import type { RoutineTrigger, RoutineVariable } from "@paperclipai/shared";
6161const concurrencyPolicies = [ "coalesce_if_active" , "always_enqueue" , "skip_if_active" ] ;
6262const catchUpPolicies = [ "skip_missed" , "enqueue_missed_with_cap" ] ;
6363const triggerKinds = [ "schedule" , "webhook" ] ;
64- const signingModes = [ "bearer" , "hmac_sha256" ] ;
64+ const signingModes = [ "bearer" , "hmac_sha256" , "github_hmac" , "none" ] ;
6565const routineTabs = [ "triggers" , "runs" , "activity" ] as const ;
6666const concurrencyPolicyDescriptions : Record < string , string > = {
6767 coalesce_if_active : "Keep one follow-up run queued while an active run is still working." ,
@@ -75,7 +75,10 @@ const catchUpPolicyDescriptions: Record<string, string> = {
7575const signingModeDescriptions : Record < string , string > = {
7676 bearer : "Expect a shared bearer token in the Authorization header." ,
7777 hmac_sha256 : "Expect an HMAC SHA-256 signature over the request using the shared secret." ,
78+ github_hmac : "Accept GitHub-style X-Hub-Signature-256 header (HMAC over raw body, no timestamp)." ,
79+ none : "No authentication — the webhook URL itself acts as a shared secret." ,
7880} ;
81+ const SIGNING_MODES_WITHOUT_REPLAY_WINDOW = new Set ( [ "github_hmac" , "none" ] ) ;
7982
8083type RoutineTab = ( typeof routineTabs ) [ number ] ;
8184
@@ -198,13 +201,15 @@ function TriggerEditor({
198201 </ SelectContent >
199202 </ Select >
200203 </ div >
201- < div className = "space-y-1.5" >
202- < Label className = "text-xs" > Replay window (seconds)</ Label >
203- < Input
204- value = { draft . replayWindowSec }
205- onChange = { ( event ) => setDraft ( ( current ) => ( { ...current , replayWindowSec : event . target . value } ) ) }
206- />
207- </ div >
204+ { ! SIGNING_MODES_WITHOUT_REPLAY_WINDOW . has ( draft . signingMode ) && (
205+ < div className = "space-y-1.5" >
206+ < Label className = "text-xs" > Replay window (seconds)</ Label >
207+ < Input
208+ value = { draft . replayWindowSec }
209+ onChange = { ( event ) => setDraft ( ( current ) => ( { ...current , replayWindowSec : event . target . value } ) ) }
210+ />
211+ </ div >
212+ ) }
208213 </ >
209214 ) }
210215 </ div >
@@ -987,10 +992,12 @@ export function RoutineDetail() {
987992 </ Select >
988993 < p className = "text-xs text-muted-foreground" > { signingModeDescriptions [ newTrigger . signingMode ] } </ p >
989994 </ div >
990- < div className = "space-y-1.5" >
991- < Label className = "text-xs" > Replay window (seconds)</ Label >
992- < Input value = { newTrigger . replayWindowSec } onChange = { ( event ) => setNewTrigger ( ( current ) => ( { ...current , replayWindowSec : event . target . value } ) ) } />
993- </ div >
995+ { ! SIGNING_MODES_WITHOUT_REPLAY_WINDOW . has ( newTrigger . signingMode ) && (
996+ < div className = "space-y-1.5" >
997+ < Label className = "text-xs" > Replay window (seconds)</ Label >
998+ < Input value = { newTrigger . replayWindowSec } onChange = { ( event ) => setNewTrigger ( ( current ) => ( { ...current , replayWindowSec : event . target . value } ) ) } />
999+ </ div >
1000+ ) }
9941001 </ >
9951002 ) }
9961003 </ div >
0 commit comments