Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 1 | /** |
| 2 | * A repository's or a workspace's webhooks: the addresses events are sent | |
| 3 | * to, adding one, and every delivery with what was sent and what came back. | |
| 4 | */ | |
| The webhooks empty state is a sentence, without an icon squeezed to a dot on phones | 5 | import { ChevronRight, Pause, Play, RotateCw, Send, Trash2 } from "lucide-react"; |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 6 | import { useState } from "react"; |
| Merge branch 'worktree-agent-a985c01f43538af01' | 7 | import { Form, Link } from "react-router"; |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 8 | |
| 9 | import { EVENT_TYPES, type Hook, type HookDelivery } from "@g1t/contracts"; | |
| 10 | ||
| 11 | import type { WebhooksAction, WebhooksData } from "../lib/webhooks.server"; | |
| Merge branch 'worktree-agent-a985c01f43538af01' | 12 | import { CopyLine, EmptyState, ErrorText, Field, Input, SubmitButton, TimeAgo } from "./ui"; |
| The site is built from its shared components: one Button with its variants and sizes, one Badge, Card for every panel, Alert for notices, Kbd for keys, Table for the security tables, and Avatar, Command, Progress, ScrollArea, ToggleGroup, Collapsible and Accordion in the kit; raw buttons, hand-drawn cards and pills and per-file button class lists are gone, outline buttons stand the same height as filled ones, and the phone's Ask g1t loses its tile. Every page was compared pixel by pixel before and after. | 13 | import { Card } from "./ui/card"; |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 14 | import { CheckboxOption } from "./ui/checkbox"; |
| Merge branch 'worktree-agent-ae1299e92e4462012' | 15 | import { Hint } from "./ui/hint"; |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 16 | import { RadioGroup, RadioOption } from "./ui/radio-group"; |
| 17 | ||
| 18 | /** The events, in groups people recognise. */ | |
| 19 | const GROUPS: { title: string; events: string[] }[] = [ | |
| 20 | { title: "Code", events: ["git.push", "repo.created", "repo.forked"] }, | |
| 21 | { | |
| 22 | title: "Issues", | |
| Merge Actions: cross-repo workflows and actions, release and deployment triggers, step timeouts | 23 | events: [ |
| 24 | "issue.opened", | |
| 25 | "issue.updated", | |
| 26 | "issue.assigned", | |
| 27 | "issue.closed", | |
| 28 | "issue.reopened", | |
| 29 | "comment.created", | |
| 30 | "comment.edited", | |
| 31 | "comment.deleted", | |
| 32 | ], | |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 33 | }, |
| 34 | { | |
| 35 | title: "Pull requests", | |
| Merge Actions: cross-repo workflows and actions, release and deployment triggers, step timeouts | 36 | events: [ |
| 37 | "pull.opened", | |
| 38 | "pull.ready", | |
| 39 | "pull.converted_to_draft", | |
| 40 | "pull.updated", | |
| 41 | "pull.merge_requested", | |
| 42 | "pull.merged", | |
| 43 | "pull.closed", | |
| 44 | "pull.reopened", | |
| 45 | ], | |
| 46 | }, | |
| 47 | { | |
| 48 | title: "Releases", | |
| 49 | events: [ | |
| 50 | "release.created", | |
| 51 | "release.published", | |
| 52 | "release.released", | |
| 53 | "release.prereleased", | |
| 54 | "release.edited", | |
| 55 | "release.unpublished", | |
| 56 | "release.deleted", | |
| 57 | ], | |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 58 | }, |
| 59 | { title: "Checks, reviews and the queue", events: ["checks.completed", "workflow.completed", "review.completed", "queue.changed"] }, | |
| Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97 | 60 | { |
| Merge checks: statuses and check runs on every commit | 61 | title: "Statuses and check runs", |
| 62 | events: [ | |
| 63 | "status.created", | |
| 64 | "check_run.created", | |
| 65 | "check_run.completed", | |
| 66 | "check_run.rerequested", | |
| 67 | "check_run.requested_action", | |
| 68 | "check_suite.completed", | |
| 69 | "check_suite.rerequested", | |
| 70 | ], | |
| 71 | }, | |
| 72 | { | |
| Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97 | 73 | title: "Deployments", |
| 74 | events: ["deployment.created", "deployment_status.created", "deployment.succeeded", "deployment.failed"], | |
| 75 | }, | |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 76 | { title: "Agents", events: ["session.appended", "agent.asked"] }, |
| @g1t welcomes you when you join a workspace, and that is the one introduction any agent makes. Identity now says when a person becomes a member, workspace.member_joined, by invitation, by an invite code, or by creating the workspace, and webhooks can hear it; the agents service hears it too, and for anyone who joined, never the creator, the workspace's @g1t opens your direct message and says in its own voice who it is, what to ask it, how agents answer, and where things are, once per person per workspace and charged to @g1t like any reply, with a fixed welcome when no model can be used. The agents, workspaces, authentication and webhooks guides say so. | 77 | { title: "Access", events: ["workspace.member_joined", "repo.collaborator_added", "repo.collaborator_removed", "repo.collaborator_role_changed"] }, |
| Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar | 78 | { |
| 79 | title: "Teams", | |
| 80 | events: [ | |
| 81 | "team.created", | |
| 82 | "team.edited", | |
| 83 | "team.deleted", | |
| 84 | "team.member_added", | |
| 85 | "team.member_role_changed", | |
| 86 | "team.member_removed", | |
| 87 | "team.repo_added", | |
| 88 | "team.repo_role_changed", | |
| 89 | "team.repo_removed", | |
| 90 | ], | |
| 91 | }, | |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 92 | ]; |
| 93 | ||
| 94 | function StatusDot({ status }: { status: string | null }) { | |
| 95 | const color = | |
| Merge branch 'worktree-agent-ad7c6d88d93adc817' | 96 | status === "delivered" ? "bg-success" : status === "failed" ? "bg-danger" : status === "pending" ? "bg-warn" : "bg-faint"; |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 97 | return <span aria-hidden="true" className={`size-2 shrink-0 rounded-full ${color}`} />; |
| 98 | } | |
| 99 | ||
| 100 | function describeEvents(events: string[]): string { | |
| 101 | if (events.includes("*")) return "Every event"; | |
| 102 | return events.length === 1 ? events[0] : `${events.length} events`; | |
| 103 | } | |
| 104 | ||
| 105 | /** JSON, indented for reading, or the text as it is. */ | |
| 106 | function pretty(text: string | null): string { | |
| 107 | if (!text) return ""; | |
| 108 | try { | |
| 109 | return JSON.stringify(JSON.parse(text), null, 2); | |
| 110 | } catch { | |
| 111 | return text; | |
| 112 | } | |
| 113 | } | |
| 114 | ||
| 115 | function DeliveryRow({ delivery, manage }: { delivery: HookDelivery; manage: boolean }) { | |
| 116 | return ( | |
| 117 | <details className="group border-t border-line first:border-t-0"> | |
| 118 | <summary className="flex cursor-pointer list-none items-center gap-3 px-4 py-2.5 text-sm hover:bg-raised/40"> | |
| 119 | <ChevronRight size={14} className="shrink-0 text-faint transition-transform group-open:rotate-90" /> | |
| 120 | <StatusDot status={delivery.status} /> | |
| 121 | <span className="font-mono text-[0.8125rem]">{delivery.event}</span> | |
| 122 | <span className="min-w-0 truncate text-xs text-muted"> | |
| 123 | {delivery.responseStatus != null | |
| 124 | ? `${delivery.responseStatus}` | |
| 125 | : delivery.error | |
| 126 | ? delivery.error | |
| 127 | : "Not yet sent"} | |
| 128 | {delivery.attempts > 1 && ` · ${delivery.attempts} attempts`} | |
| 129 | {delivery.status === "pending" && delivery.nextAttemptAt && " · will try again"} | |
| 130 | </span> | |
| 131 | <span className="ml-auto shrink-0 font-mono text-xs text-faint"> | |
| 132 | {delivery.durationMs != null && `${delivery.durationMs}ms · `} | |
| 133 | <TimeAgo at={delivery.createdAt} /> | |
| 134 | </span> | |
| 135 | </summary> | |
| 136 | <div className="grid gap-4 border-t border-line bg-bg/40 p-4 lg:grid-cols-2"> | |
| 137 | <div className="min-w-0"> | |
| 138 | <p className="mb-1.5 text-xs font-medium text-muted">Request</p> | |
| 139 | <pre className="max-h-80 overflow-auto rounded-lg bg-surface p-3 font-mono text-xs ring-1 ring-line"> | |
| 140 | <code>{pretty(delivery.payload)}</code> | |
| 141 | </pre> | |
| 142 | </div> | |
| 143 | <div className="min-w-0"> | |
| 144 | <p className="mb-1.5 text-xs font-medium text-muted"> | |
| 145 | Response{delivery.responseStatus != null && ` · ${delivery.responseStatus}`} | |
| 146 | </p> | |
| 147 | <pre className="max-h-80 overflow-auto rounded-lg bg-surface p-3 font-mono text-xs ring-1 ring-line"> | |
| 148 | <code>{delivery.error ?? (pretty(delivery.responseBody) || "No body.")}</code> | |
| 149 | </pre> | |
| 150 | {manage && ( | |
| 151 | <Form method="post" className="mt-3"> | |
| 152 | <input type="hidden" name="intent" value="redeliver" /> | |
| 153 | <input type="hidden" name="delivery" value={delivery.id} /> | |
| Merge branch 'worktree-agent-a985c01f43538af01' | 154 | <SubmitButton |
| The site is built from its shared components: one Button with its variants and sizes, one Badge, Card for every panel, Alert for notices, Kbd for keys, Table for the security tables, and Avatar, Command, Progress, ScrollArea, ToggleGroup, Collapsible and Accordion in the kit; raw buttons, hand-drawn cards and pills and per-file button class lists are gone, outline buttons stand the same height as filled ones, and the phone's Ask g1t loses its tile. Every page was compared pixel by pixel before and after. | 155 | variant="outline" |
| Merge branch 'worktree-agent-a985c01f43538af01' | 156 | match={{ intent: "redeliver", delivery: delivery.id }} |
| 157 | pending="Redelivering…" | |
| 158 | > | |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 159 | <RotateCw size={14} /> |
| 160 | Redeliver | |
| Merge branch 'worktree-agent-a985c01f43538af01' | 161 | </SubmitButton> |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 162 | </Form> |
| 163 | )} | |
| 164 | </div> | |
| 165 | </div> | |
| 166 | </details> | |
| 167 | ); | |
| 168 | } | |
| 169 | ||
| 170 | function HookRow({ hook, open, deliveries, manage }: { hook: Hook; open: boolean; deliveries: HookDelivery[]; manage: boolean }) { | |
| 171 | return ( | |
| 172 | <li className="border-t border-line first:border-t-0"> | |
| 173 | <div className="flex items-center gap-3 px-4 py-3"> | |
| 174 | <StatusDot status={hook.active ? hook.lastStatus : null} /> | |
| 175 | <div className="min-w-0 grow"> | |
| 176 | <p className="truncate font-mono text-[0.8125rem]">{hook.url}</p> | |
| 177 | <p className="truncate text-xs text-muted"> | |
| 178 | {describeEvents(hook.events)} | |
| 179 | {!hook.active && " · paused"} | |
| 180 | {hook.lastDeliveredAt && ( | |
| 181 | <> | |
| 182 | {" · last sent "} | |
| 183 | <TimeAgo at={hook.lastDeliveredAt} /> | |
| 184 | </> | |
| 185 | )} | |
| 186 | {` · secret ${hook.secretHint}`} | |
| 187 | </p> | |
| 188 | </div> | |
| 189 | <Link | |
| 190 | to={open ? "?" : `?hook=${hook.id}`} | |
| 191 | preventScrollReset | |
| 192 | className="shrink-0 rounded-md px-2.5 py-1.5 text-xs text-muted transition-colors hover:bg-raised hover:text-fg" | |
| 193 | > | |
| 194 | {open ? "Hide deliveries" : "Deliveries"} | |
| 195 | </Link> | |
| 196 | {manage && ( | |
| 197 | <Form method="post" className="flex shrink-0 gap-1"> | |
| 198 | <input type="hidden" name="id" value={hook.id} /> | |
| Merge branch 'worktree-agent-a985c01f43538af01' | 199 | <IconButton intent="ping" id={hook.id} label="Send a ping"> |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 200 | <Send size={14} /> |
| 201 | </IconButton> | |
| 202 | <input type="hidden" name="active" value={hook.active ? "false" : "true"} /> | |
| Merge branch 'worktree-agent-a985c01f43538af01' | 203 | <IconButton intent="toggle" id={hook.id} label={hook.active ? "Pause" : "Resume"}> |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 204 | {hook.active ? <Pause size={14} /> : <Play size={14} />} |
| 205 | </IconButton> | |
| Merge branch 'worktree-agent-a985c01f43538af01' | 206 | <IconButton intent="delete" id={hook.id} label="Delete"> |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 207 | <Trash2 size={14} /> |
| 208 | </IconButton> | |
| 209 | </Form> | |
| 210 | )} | |
| 211 | </div> | |
| 212 | {open && ( | |
| The site is built from its shared components: one Button with its variants and sizes, one Badge, Card for every panel, Alert for notices, Kbd for keys, Table for the security tables, and Avatar, Command, Progress, ScrollArea, ToggleGroup, Collapsible and Accordion in the kit; raw buttons, hand-drawn cards and pills and per-file button class lists are gone, outline buttons stand the same height as filled ones, and the phone's Ask g1t loses its tile. Every page was compared pixel by pixel before and after. | 213 | <Card tone="plain" radius="lg" className="mx-4 mb-4 overflow-hidden"> |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 214 | {deliveries.length === 0 ? ( |
| 215 | <p className="px-4 py-3 text-sm text-muted">Nothing sent yet.</p> | |
| 216 | ) : ( | |
| 217 | deliveries.map((delivery) => <DeliveryRow key={delivery.id} delivery={delivery} manage={manage} />) | |
| 218 | )} | |
| The site is built from its shared components: one Button with its variants and sizes, one Badge, Card for every panel, Alert for notices, Kbd for keys, Table for the security tables, and Avatar, Command, Progress, ScrollArea, ToggleGroup, Collapsible and Accordion in the kit; raw buttons, hand-drawn cards and pills and per-file button class lists are gone, outline buttons stand the same height as filled ones, and the phone's Ask g1t loses its tile. Every page was compared pixel by pixel before and after. | 219 | </Card> |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 220 | )} |
| 221 | </li> | |
| 222 | ); | |
| 223 | } | |
| 224 | ||
| Merge branch 'worktree-agent-a985c01f43538af01' | 225 | /** One webhook's action, as an icon: a spinner in its place while that action goes. */ |
| 226 | function IconButton({ intent, id, label, children }: { intent: string; id: string; label: string; children: React.ReactNode }) { | |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 227 | return ( |
| Merge branch 'worktree-agent-ae1299e92e4462012' | 228 | <Hint label={label}> |
| 229 | <SubmitButton | |
| 230 | icon | |
| 231 | name="intent" | |
| 232 | value={intent} | |
| 233 | match={{ id }} | |
| 234 | aria-label={label} | |
| The site is built from its shared components: one Button with its variants and sizes, one Badge, Card for every panel, Alert for notices, Kbd for keys, Table for the security tables, and Avatar, Command, Progress, ScrollArea, ToggleGroup, Collapsible and Accordion in the kit; raw buttons, hand-drawn cards and pills and per-file button class lists are gone, outline buttons stand the same height as filled ones, and the phone's Ask g1t loses its tile. Every page was compared pixel by pixel before and after. | 235 | variant="ghost" |
| 236 | size="inline" | |
| 237 | className="p-2" | |
| Merge branch 'worktree-agent-ae1299e92e4462012' | 238 | > |
| 239 | {children} | |
| 240 | </SubmitButton> | |
| 241 | </Hint> | |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 242 | ); |
| 243 | } | |
| 244 | ||
| 245 | function AddWebhook() { | |
| 246 | const [which, setWhich] = useState<"all" | "some">("all"); | |
| 247 | return ( | |
| The site is built from its shared components: one Button with its variants and sizes, one Badge, Card for every panel, Alert for notices, Kbd for keys, Table for the security tables, and Avatar, Command, Progress, ScrollArea, ToggleGroup, Collapsible and Accordion in the kit; raw buttons, hand-drawn cards and pills and per-file button class lists are gone, outline buttons stand the same height as filled ones, and the phone's Ask g1t loses its tile. Every page was compared pixel by pixel before and after. | 248 | <Card asChild className="space-y-4 p-5"> |
| 249 | <Form method="post"> | |
| 250 | <input type="hidden" name="intent" value="create" /> | |
| 251 | <p className="font-medium">Add a webhook</p> | |
| 252 | <Field label="Payload URL" hint="An HTTPS address on the public internet. g1t sends it a ping as soon as you add it."> | |
| 253 | <Input name="url" type="url" required placeholder="https://example.com/g1t/events" /> | |
| 254 | </Field> | |
| 255 | <fieldset> | |
| 256 | <legend className="mb-1.5 text-sm font-medium text-muted">Which events</legend> | |
| 257 | <RadioGroup | |
| 258 | name="which" | |
| 259 | value={which} | |
| 260 | onValueChange={(value) => setWhich(value as typeof which)} | |
| 261 | aria-label="Which events" | |
| 262 | className="flex flex-wrap gap-x-6 gap-y-2" | |
| 263 | > | |
| 264 | <RadioOption value="all" label="Everything, including events added later" /> | |
| 265 | <RadioOption value="some" label="Let me choose" /> | |
| 266 | </RadioGroup> | |
| 267 | {which === "some" && ( | |
| 268 | <div className="mt-3 grid gap-4 sm:grid-cols-2 lg:grid-cols-3"> | |
| 269 | {GROUPS.map((group) => ( | |
| 270 | <div key={group.title}> | |
| 271 | <p className="mb-1.5 text-xs font-medium text-faint">{group.title}</p> | |
| 272 | <div className="space-y-1"> | |
| 273 | {group.events | |
| 274 | .filter((event) => (EVENT_TYPES as readonly string[]).includes(event)) | |
| 275 | .map((event) => ( | |
| 276 | <CheckboxOption | |
| 277 | key={event} | |
| 278 | name="event" | |
| 279 | value={event} | |
| 280 | label={event} | |
| 281 | className="items-center" | |
| 282 | labelClassName="font-mono text-[0.8125rem]" | |
| 283 | /> | |
| 284 | ))} | |
| 285 | </div> | |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 286 | </div> |
| The site is built from its shared components: one Button with its variants and sizes, one Badge, Card for every panel, Alert for notices, Kbd for keys, Table for the security tables, and Avatar, Command, Progress, ScrollArea, ToggleGroup, Collapsible and Accordion in the kit; raw buttons, hand-drawn cards and pills and per-file button class lists are gone, outline buttons stand the same height as filled ones, and the phone's Ask g1t loses its tile. Every page was compared pixel by pixel before and after. | 287 | ))} |
| 288 | </div> | |
| 289 | )} | |
| 290 | </fieldset> | |
| 291 | <Field label="Secret" hint="Optional. Deliveries are signed with it. Leave it empty and g1t makes one, shown once."> | |
| 292 | <Input name="secret" type="password" autoComplete="off" /> | |
| 293 | </Field> | |
| 294 | <SubmitButton match={{ intent: "create" }} pending="Adding…"> | |
| 295 | Add webhook | |
| 296 | </SubmitButton> | |
| 297 | </Form> | |
| 298 | </Card> | |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 299 | ); |
| 300 | } | |
| 301 | ||
| 302 | export function WebhooksPanel({ | |
| 303 | data, | |
| 304 | action, | |
| 305 | manage, | |
| 306 | scope, | |
| 307 | }: { | |
| 308 | data: WebhooksData; | |
| 309 | action: WebhooksAction | undefined; | |
| 310 | manage: boolean; | |
| 311 | /** Says whose events these are. */ | |
| 312 | scope: string; | |
| 313 | }) { | |
| 314 | const created = action?.created; | |
| 315 | return ( | |
| 316 | <div className="max-w-4xl space-y-6"> | |
| 317 | {created && ( | |
| Merge branch 'worktree-agent-ad7c6d88d93adc817' | 318 | <div className="space-y-3 rounded-xl border border-success-dim/60 bg-success/5 p-5 text-sm"> |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 319 | <p className="font-medium">Added. g1t sent it a ping.</p> |
| 320 | {created.secret ? ( | |
| 321 | <> | |
| 322 | <p className="text-muted"> | |
| 323 | Every delivery is signed with this secret in <code>X-G1t-Signature-256</code>. It is shown this once. | |
| 324 | </p> | |
| 325 | <CopyLine text={created.secret} /> | |
| 326 | </> | |
| 327 | ) : ( | |
| 328 | <p className="text-muted">Deliveries are signed with the secret you gave.</p> | |
| 329 | )} | |
| 330 | </div> | |
| 331 | )} | |
| 332 | {action?.sent && ( | |
| 333 | <p className="text-sm text-muted"> | |
| 334 | Sent {action.sent.event}:{" "} | |
| 335 | {action.sent.status === "delivered" ? ( | |
| Merge branch 'worktree-agent-ad7c6d88d93adc817' | 336 | <span className="text-success">delivered, {action.sent.responseStatus}</span> |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 337 | ) : ( |
| 338 | <span className="text-danger">{action.sent.error ?? `answered ${action.sent.responseStatus}`}</span> | |
| 339 | )} | |
| 340 | . | |
| 341 | </p> | |
| 342 | )} | |
| 343 | <ErrorText>{action?.error}</ErrorText> | |
| 344 | ||
| 345 | {data.hooks.length === 0 ? ( | |
| 346 | <EmptyState title="No webhooks yet"> | |
| The webhooks empty state is a sentence, without an icon squeezed to a dot on phones | 347 | Add one and {scope} sends its events to your address as they happen, signed, and retried until it answers. |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 348 | </EmptyState> |
| 349 | ) : ( | |
| The site is built from its shared components: one Button with its variants and sizes, one Badge, Card for every panel, Alert for notices, Kbd for keys, Table for the security tables, and Avatar, Command, Progress, ScrollArea, ToggleGroup, Collapsible and Accordion in the kit; raw buttons, hand-drawn cards and pills and per-file button class lists are gone, outline buttons stand the same height as filled ones, and the phone's Ask g1t loses its tile. Every page was compared pixel by pixel before and after. | 350 | <Card asChild className="overflow-hidden"> |
| 351 | <ul> | |
| 352 | {data.hooks.map((hook) => ( | |
| 353 | <HookRow | |
| 354 | key={hook.id} | |
| 355 | hook={hook} | |
| 356 | open={data.open === hook.id} | |
| 357 | deliveries={data.open === hook.id ? data.deliveries : []} | |
| 358 | manage={manage} | |
| 359 | /> | |
| 360 | ))} | |
| 361 | </ul> | |
| 362 | </Card> | |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 363 | )} |
| 364 | ||
| Merge branch 'worktree-agent-a985c01f43538af01' | 365 | {/* Keyed to the webhook just added, so the form starts empty for the next. */} |
| 366 | {manage && <AddWebhook key={created?.hook.id ?? "new"} />} | |
| Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API | 367 | <p className="text-xs text-faint"> |
| 368 | Each delivery is an HTTPS POST of JSON with <code>X-G1t-Event</code>, <code>X-G1t-Delivery</code> and{" "} | |
| 369 | <code>X-G1t-Signature-256</code>. One that is not answered with a 2xx is tried again after 1 minute, 5 minutes, | |
| 370 | 30 minutes, 2 hours and 5 hours.{" "} | |
| 371 | <a href="https://docs.g1t.sh/guides/webhooks/" className="text-muted underline underline-offset-4"> | |
| 372 | How to check the signature | |
| 373 | </a> | |
| 374 | . | |
| 375 | </p> | |
| 376 | </div> | |
| 377 | ); | |
| 378 | } |
This file's history is long; its oldest lines are credited to the oldest commit read.