Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 59 additions & 27 deletions apps/loopover-ui/public/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -16718,8 +16718,8 @@
"OrbWebhookSignature": {
"type": "apiKey",
"in": "header",
"name": "x-loopover-signature",
"description": "HMAC signature over the raw request body, verified against the instance's shared secret. The webhook carries no bearer token."
"name": "x-hub-signature-256",
"description": "GitHub-style HMAC-SHA256 signature over the raw request body, verified against the receiving app's own webhook secret. A webhook delivery carries no bearer token."
}
}
},
Expand Down Expand Up @@ -20701,6 +20701,11 @@
"operationId": "postGithubWebhook",
"tags": [
"Webhooks"
],
"security": [
{
"OrbWebhookSignature": []
}
]
}
},
Expand Down Expand Up @@ -20977,7 +20982,8 @@
"operationId": "postAuthGithubDeviceStart",
"tags": [
"Auth"
]
],
"security": []
}
},
"/v1/auth/github/device/poll": {
Expand All @@ -21003,7 +21009,8 @@
"operationId": "postAuthGithubDevicePoll",
"tags": [
"Auth"
]
],
"security": []
}
},
"/v1/auth/github/session": {
Expand All @@ -21029,7 +21036,8 @@
"operationId": "postAuthGithubSession",
"tags": [
"Auth"
]
],
"security": []
}
},
"/v1/auth/logout": {
Expand All @@ -21055,7 +21063,8 @@
"operationId": "postAuthLogout",
"tags": [
"Auth"
]
],
"security": []
}
},
"/v1/auth/session": {
Expand Down Expand Up @@ -22206,7 +22215,7 @@
"summary": "Queue a contributor evidence build job",
"responses": {
"202": {
"description": "Internal job queued"
"description": "Job queued"
},
"401": {
"description": "Invalid internal token"
Expand All @@ -22215,6 +22224,11 @@
"operationId": "postInternalJobsBuildContributorEvidence",
"tags": [
"Internal"
],
"security": [
{
"LoopOverBearer": []
}
]
}
},
Expand Down Expand Up @@ -22246,7 +22260,7 @@
"summary": "Queue a burden forecast build job",
"responses": {
"202": {
"description": "Internal job queued"
"description": "Job queued"
},
"401": {
"description": "Invalid internal token"
Expand All @@ -22255,6 +22269,11 @@
"operationId": "postInternalJobsBuildBurdenForecasts",
"tags": [
"Internal"
],
"security": [
{
"LoopOverBearer": []
}
]
}
},
Expand Down Expand Up @@ -22309,7 +22328,7 @@
"summary": "Queue a data fidelity repair job",
"responses": {
"202": {
"description": "Internal job queued"
"description": "Job queued"
},
"401": {
"description": "Invalid internal token"
Expand All @@ -22318,6 +22337,11 @@
"operationId": "postInternalJobsRepairDataFidelity",
"tags": [
"Internal"
],
"security": [
{
"LoopOverBearer": []
}
]
}
},
Expand All @@ -22335,6 +22359,11 @@
"operationId": "postInternalBountiesImport",
"tags": [
"Internal"
],
"security": [
{
"LoopOverBearer": []
}
]
}
},
Expand Down Expand Up @@ -23549,6 +23578,11 @@
"operationId": "getInternalProviderCredentialsByProvider",
"tags": [
"Internal"
],
"security": [
{
"LoopOverBearer": []
}
]
},
"post": {
Expand All @@ -23567,23 +23601,6 @@
"in": "path"
}
],
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"credential": {
"type": "string"
}
},
"required": [
"credential"
]
}
}
}
},
"responses": {
"200": {
"description": "Credential stored. Returns the secret-free status.",
Expand Down Expand Up @@ -23611,6 +23628,11 @@
"operationId": "postInternalProviderCredentialsByProvider",
"tags": [
"Internal"
],
"security": [
{
"LoopOverBearer": []
}
]
},
"delete": {
Expand Down Expand Up @@ -23661,6 +23683,11 @@
"operationId": "deleteInternalProviderCredentialsByProvider",
"tags": [
"Internal"
],
"security": [
{
"LoopOverBearer": []
}
]
}
},
Expand Down Expand Up @@ -26297,7 +26324,12 @@
"422": {
"description": "Authenticated but unverifiable: unknown_key, bad_signature, row_not_found, or row_hash_mismatch — an `ok` report must verify against a published key AND match the live chain row"
}
}
},
"security": [
{
"OrbBearer": []
}
]
}
}
},
Expand Down
19 changes: 17 additions & 2 deletions src/openapi/define-route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,21 @@ export function defineRoute<
/** Spec-side view of a route definition: the same fields, with the request schemas widened, since
* emitting the document never needs the parsed types the handler does. */
export type RouteSpecOptions = Omit<DefineRouteOptions<z.ZodTypeAny | undefined, z.ZodObject | undefined>, "request"> & {
request?: { body?: z.ZodTypeAny | undefined; query?: z.ZodObject | undefined } | undefined;
request?:
| {
body?: z.ZodTypeAny | undefined;
query?: z.ZodObject | undefined;
/**
* Narrower path parameters than the derived `z.string()` ones (#9707).
*
* Spec-only, and rare on purpose: the whole point of deriving parameters from the path is that
* nobody has to declare them twice. This exists for the handful of routes whose segment is a
* closed set -- `/v1/internal/provider-credentials/:provider` is `claude-code | codex` -- where
* publishing a bare string would tell a generated client less than the route actually enforces.
*/
params?: z.ZodObject | undefined;
}
| undefined;
};

/** The spec half, exported separately so a route that cannot yet move its handler through the seam
Expand All @@ -167,7 +181,8 @@ export function registerRouteSpec(registry: OpenAPIRegistry, options: RouteSpecO
method: options.method,
path: toSpecPath(options.path),
request: {
...(pathParameters(options.path) ?? {}),
// A declared `params` wins over the derived one -- same parameters, a narrower schema.
...(options.request?.params ? { params: options.request.params } : (pathParameters(options.path) ?? {})),
...(options.request?.body ? { body: { content: { "application/json": { schema: options.request.body } } } } : {}),
...(options.request?.query ? { query: options.request.query } : {}),
},
Expand Down
Loading
Loading