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
18 changes: 9 additions & 9 deletions app/api/accounts/[id]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { getAccountHandler } from "@/lib/accounts/getAccountHandler";
/**
* OPTIONS handler for CORS preflight requests.
*
* @returns A NextResponse with CORS headers.
* @returns A 200 NextResponse carrying the CORS headers.
*/
export async function OPTIONS() {
return new NextResponse(null, {
Expand All @@ -21,13 +21,13 @@ export async function OPTIONS() {
* Requires authentication via `x-api-key` or `Authorization: Bearer`; the caller must be
* allowed to access the requested account (same account, org delegation, or Recoup admin).
*
* Path parameters:
* - id (required): The unique identifier of the account (UUID)
*
* @param request - The request object
* @param params - Route params containing the account ID
* @returns A NextResponse with account data
* @param request - The incoming request. Authentication is read from the
* `x-api-key` or `Authorization: Bearer` header by `getAccountHandler`.
* @param context - Route context from Next.js.
* @param context.params - Promise resolving to `{ id }`, the account UUID from the URL path.
* @returns A 200 NextResponse with the account payload, 401/403 if the caller lacks access,
* or 404 when the account does not exist.
*/
export async function GET(request: NextRequest, { params }: { params: Promise<{ id: string }> }) {
return getAccountHandler(request, params);
export async function GET(request: NextRequest, context: { params: Promise<{ id: string }> }) {
return getAccountHandler(request, context.params);
}
11 changes: 9 additions & 2 deletions app/api/admins/coding/pr/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,20 @@ import { getPrStatusHandler } from "@/lib/admins/pr/getPrStatusHandler";
* Uses the GitHub REST API to check each PR's state.
* Requires admin authentication.
*
* @param request
* @param request - The incoming request; `pull_requests` is read from the query string
* and admin auth from the `x-api-key` / `Authorization: Bearer` header.
* @returns A 200 NextResponse with `{ results: Array<{ url, state }> }`, 400 when no
* `pull_requests` are supplied, or 401/403 for non-admin callers.
*/
export async function GET(request: NextRequest): Promise<NextResponse> {
return getPrStatusHandler(request);
}

/** CORS preflight handler. */
/**
* CORS preflight handler for /api/admins/coding/pr.
*
* @returns A 204 NextResponse carrying the CORS headers.
*/
export async function OPTIONS(): Promise<NextResponse> {
return new NextResponse(null, { status: 204, headers: getCorsHeaders() });
}
11 changes: 10 additions & 1 deletion app/api/admins/coding/slack/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,21 @@ import { getSlackTagsHandler } from "@/lib/admins/slack/getSlackTagsHandler";
* Pulls directly from the Slack API as the source of truth.
* Supports period filtering: all (default), daily, weekly, monthly.
* Requires admin authentication.
*
* @param request - The incoming request; `period` is read from the query string
* and admin auth from the `x-api-key` / `Authorization: Bearer` header.
* @returns A 200 NextResponse with the tagging analytics payload, 400 on invalid
* `period`, or 401/403 for non-admin callers.
*/
export async function GET(request: NextRequest): Promise<NextResponse> {
return getSlackTagsHandler(request);
}

/** CORS preflight handler. */
/**
* CORS preflight handler for /api/admins/coding/slack.
*
* @returns A 204 NextResponse carrying the CORS headers.
*/
export async function OPTIONS(): Promise<NextResponse> {
return new NextResponse(null, { status: 204, headers: getCorsHeaders() });
}
11 changes: 9 additions & 2 deletions app/api/admins/content/slack/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,20 @@ import { getContentSlackTagsHandler } from "@/lib/admins/content/getContentSlack
* Supports period filtering: all (default), daily, weekly, monthly.
* Requires admin authentication.
*
* @param request
* @param request - The incoming request; `period` is read from the query string
* and admin auth from the `x-api-key` / `Authorization: Bearer` header.
* @returns A 200 NextResponse with the tagging analytics payload, 400 on invalid
* `period`, or 401/403 for non-admin callers.
*/
export async function GET(request: NextRequest): Promise<NextResponse> {
return getContentSlackTagsHandler(request);
}

/** CORS preflight handler. */
/**
* CORS preflight handler for /api/admins/content/slack.
*
* @returns A 204 NextResponse carrying the CORS headers.
*/
export async function OPTIONS(): Promise<NextResponse> {
return new NextResponse(null, { status: 204, headers: getCorsHeaders() });
}
10 changes: 10 additions & 0 deletions app/api/admins/privy/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,21 @@ import { getPrivyLoginsHandler } from "@/lib/admins/privy/getPrivyLoginsHandler"
* Returns Privy login statistics for the requested time period.
* Supports daily (last 24h), weekly (last 7 days), and monthly (last 30 days) periods.
* Requires admin authentication.
*
* @param request - The incoming request; `period` is read from the query string
* and admin auth from the `x-api-key` / `Authorization: Bearer` header.
* @returns A 200 NextResponse with the login statistics payload, 400 on invalid
* `period`, or 401/403 for non-admin callers.
*/
export async function GET(request: NextRequest): Promise<NextResponse> {
return getPrivyLoginsHandler(request);
}

/**
* CORS preflight handler for /api/admins/privy.
*
* @returns A 204 NextResponse carrying the CORS headers.
*/
export async function OPTIONS(): Promise<NextResponse> {
return new NextResponse(null, { status: 204, headers: getCorsHeaders() });
}
19 changes: 16 additions & 3 deletions app/api/chats/[id]/messages/trailing/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { deleteTrailingChatMessagesHandler } from "@/lib/chats/deleteTrailingCha

/**
* OPTIONS handler for CORS preflight requests.
*
* @returns A 200 NextResponse carrying the CORS headers.
*/
export async function OPTIONS() {
return new NextResponse(null, {
Expand All @@ -16,12 +18,23 @@ export async function OPTIONS() {
/**
* DELETE /api/chats/[id]/messages/trailing
*
* Deletes all messages in chat `id` from `from_message_id` onward.
* Removes the selected message and every message that follows it in chat `id`, so the
* conversation can be resumed from an earlier turn. The caller must own the chat or
* have organization-level access; authentication uses `x-api-key` or
* `Authorization: Bearer`.
*
* @param request - The incoming request. `from_message_id` is read from the JSON body
* and identifies the oldest message to delete (that message and all newer ones go).
* @param context - Route context from Next.js.
* @param context.params - Promise resolving to `{ id }`, the chat UUID from the URL path.
* @returns A 200 NextResponse with the count of deleted messages, 400 on a missing
* `from_message_id`, 401/403 when the caller cannot access the chat, or 404 when the
* chat or message does not exist.
*/
export async function DELETE(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> },
context: { params: Promise<{ id: string }> },
): Promise<NextResponse> {
const { id } = await params;
const { id } = await context.params;
return deleteTrailingChatMessagesHandler(request, id);
}
12 changes: 9 additions & 3 deletions app/api/coding-agent/callback/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,16 @@ import { handleCodingAgentCallback } from "@/lib/coding-agent/handleCodingAgentC
/**
* POST /api/coding-agent/callback
*
* Callback endpoint for the coding agent Trigger.dev task.
* Receives task results and posts them back to the Slack thread.
* Callback endpoint for the coding agent Trigger.dev task. Receives the task result
* payload and posts it back to the originating Slack thread. The request is
* authenticated by the shared secret checked inside `handleCodingAgentCallback`
* (not by `x-api-key`), since it is called server-to-server from Trigger.dev.
*
* @param request - The incoming callback request
* @param request - The incoming callback request from the Trigger.dev task. The JSON
* body carries the task outcome plus `thread_ts` / `channel` so the bot knows where
* to reply in Slack.
* @returns A 200 NextResponse when the Slack post succeeds, 401 if the shared secret
* is missing or wrong, or 500 if Slack posting fails.
*/
export async function POST(request: NextRequest) {
await codingAgentBot.initialize();
Expand Down
14 changes: 11 additions & 3 deletions app/api/coding-agent/github/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,18 @@ import { handleGitHubWebhook } from "@/lib/coding-agent/handleGitHubWebhook";
/**
* POST /api/coding-agent/github
*
* Webhook endpoint for GitHub PR comment feedback.
* Receives issue_comment events and triggers update-pr when the bot is mentioned.
* Webhook endpoint for GitHub PR comment feedback. Receives `issue_comment` events
* and triggers the `update-pr` coding agent task when the bot is @-mentioned in a
* comment. Authentication relies on the GitHub `X-Hub-Signature-256` signature that
* `handleGitHubWebhook` validates against the webhook secret.
*
* @param request - The incoming GitHub webhook request
* @param request - The incoming GitHub webhook POST. Headers `X-GitHub-Event` and
* `X-Hub-Signature-256` are required; the JSON body is the raw GitHub event
* payload (typically `issue_comment`).
* @returns A NextResponse JSON payload with a `status` field indicating outcome
* (`update_triggered`, `ignored`, `busy`, `no_state`, or `updating`) on 200;
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Custom agent: Flag AI Slop and Fabricated Changes

The updated JSDoc documents updating as a 200 response status, but this endpoint never returns {"status":"updating"}. Remove it from the documented response outcomes to avoid publishing a fabricated API contract.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/api/coding-agent/github/route.ts, line 16:

<comment>The updated JSDoc documents `updating` as a 200 response status, but this endpoint never returns `{"status":"updating"}`. Remove it from the documented response outcomes to avoid publishing a fabricated API contract.</comment>

<file context>
@@ -12,8 +12,10 @@ import { handleGitHubWebhook } from "@/lib/coding-agent/handleGitHubWebhook";
- * @returns A 200 NextResponse with `{ ok: true }` when the event is accepted or
- *   ignored, 401 when the signature is missing or invalid, or 500 on internal error.
+ * @returns A NextResponse JSON payload with a `status` field indicating outcome
+ *   (`update_triggered`, `ignored`, `busy`, `no_state`, or `updating`) on 200;
+ *   `{ status: "error", error }` with 401 when signature validation fails; and
+ *   `{ status: "error", error }` with 500 on internal failure.
</file context>
Suggested change
* (`update_triggered`, `ignored`, `busy`, `no_state`, or `updating`) on 200;
* (`update_triggered`, `ignored`, `busy`, or `no_state`) on 200;
Fix with Cubic

* `{ status: "error", error }` with 401 when signature validation fails; and
* `{ status: "error", error }` with 500 on internal failure.
*/
export async function POST(request: NextRequest) {
return handleGitHubWebhook(request);
Expand Down
53 changes: 27 additions & 26 deletions app/api/connectors/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { disconnectConnectorHandler } from "@/lib/composio/connectors/disconnect

/**
* OPTIONS handler for CORS preflight requests.
*
* @returns A 200 NextResponse carrying the CORS headers.
*/
export async function OPTIONS() {
return new NextResponse(null, {
Expand All @@ -18,15 +20,15 @@ export async function OPTIONS() {
/**
* GET /api/connectors
*
* List all available connectors and their connection status.
*
* Query params:
* - account_id (optional): Entity ID for entity-specific connections (e.g., artist ID)
* Lists every available Composio connector with its connection status for the caller.
* When `account_id` is supplied, the statuses are scoped to that account (e.g. an
* artist) instead of the authenticated caller. Requires `x-api-key` or
* `Authorization: Bearer`.
*
* Authentication: x-api-key OR Authorization Bearer token required.
*
* @param request
* @returns List of connectors with connection status
* @param request - The incoming request. Optional query parameter: `account_id` — an
* account UUID (e.g. artist) to scope the connection status lookup to.
* @returns A 200 NextResponse with `{ connectors: Array<{ slug, connected, ... }> }`,
* 401 when unauthenticated, or 403 when the caller cannot access `account_id`.
*/
Comment on lines +24 to 32
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Remove account_id from this route contract and derive account from auth context.

These docs formalize account_id as client input across multiple methods, which conflicts with the authentication model and widens authorization risk. Please update the API contract (and corresponding validators/handlers if still present) so account scope is derived only from authenticated context.

Suggested doc-level correction
- * When `account_id` is supplied, the statuses are scoped to that account (e.g. an
- * artist) instead of the authenticated caller. Requires `x-api-key` or
+ * Statuses are scoped to the authenticated account context. Requires `x-api-key` or
  * `Authorization: Bearer`.
...
- * `@param` request - The incoming request. Optional query parameter: `account_id` — an
- *   account UUID (e.g. artist) to scope the connection status lookup to.
+ * `@param` request - The incoming request. No account selector is accepted; account scope
+ *   is derived from authenticated context.
...
- *   `account_id` (optional — the account to associate the connection with).
+ *   account scope is derived from authenticated context.
...
- *   unauthenticated, or 403 when the caller cannot access `account_id`.
+ *   unauthenticated, or 403 when the caller is not allowed to perform the action.
...
- *   — the Composio connected-account id to remove); `account_id` (optional — used to
- *   verify ownership of the connected account).
+ *   — the Composio connected-account id to remove).
As per coding guidelines "Never use `account_id` in request bodies or tool schemas; always derive the account ID from authentication".

Also applies to: 44-50, 61-66

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/api/connectors/route.ts` around lines 24 - 32, Update the route contract
and implementation so account_id is no longer accepted from the client: remove
`account_id` from the JSDoc and any query parsing/validation logic, update the
route handler (the GET/route handler in route.ts) to derive the account ID from
the authenticated context (e.g., the decoded bearer token or x-api-key auth
principal) and use that derived ID for scoping connector status, and remove or
adjust any validators/schemas that still expect `account_id` (and any
authorization checks that previously allowed client-supplied account_id) so
authorization always uses the authenticated principal.

export async function GET(request: NextRequest) {
return getConnectorsHandler(request);
Expand All @@ -35,17 +37,16 @@ export async function GET(request: NextRequest) {
/**
* POST /api/connectors
*
* Generate an OAuth authorization URL for a specific connector.
*
* Authentication: x-api-key OR Authorization Bearer token required.
* Generates a Composio OAuth authorization URL for a single connector. The caller
* completes OAuth in the browser and is redirected to `callback_url` (or a default).
* Requires `x-api-key` or `Authorization: Bearer`.
*
* Request body:
* - connector: The connector slug, e.g., "googlesheets" or "tiktok" (required)
* - callback_url: Optional custom callback URL after OAuth
* - account_id: Optional account ID for account-specific connections
*
* @param request
* @returns The redirect URL for OAuth authorization
* @param request - The incoming request. JSON body: `connector` (required slug, e.g.
* `"googlesheets"` or `"tiktok"`); `callback_url` (optional post-OAuth redirect);
* `account_id` (optional — the account to associate the connection with).
* @returns A 200 NextResponse with `{ redirectUrl }` pointing the caller at the
* provider's OAuth consent page, 400 on a missing/invalid `connector`, 401 when
* unauthenticated, or 403 when the caller cannot access `account_id`.
*/
export async function POST(request: NextRequest) {
return authorizeConnectorHandler(request);
Expand All @@ -54,15 +55,15 @@ export async function POST(request: NextRequest) {
/**
* DELETE /api/connectors
*
* Disconnect a connected account from Composio.
*
* Body:
* - connected_account_id (required): The connected account ID to disconnect
* - account_id (optional): Entity ID for ownership verification (e.g., artist ID)
*
* Authentication: x-api-key OR Authorization Bearer token required.
* Disconnects a previously-connected Composio account so it can no longer be used for
* tool calls. Requires `x-api-key` or `Authorization: Bearer`.
*
* @param request
* @param request - The incoming request. JSON body: `connected_account_id` (required
* — the Composio connected-account id to remove); `account_id` (optional — used to
* verify ownership of the connected account).
* @returns A 200 NextResponse on successful disconnect, 400 on a missing
* `connected_account_id`, 401 when unauthenticated, 403 when the caller does not
* own the connection, or 404 when the connected account does not exist.
*/
export async function DELETE(request: NextRequest) {
return disconnectConnectorHandler(request);
Expand Down
12 changes: 11 additions & 1 deletion app/api/content/analyze/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { createAnalyzeHandler } from "@/lib/content/analyze/createAnalyzeHandler

/**
* OPTIONS handler for CORS preflight requests.
*
* @returns A 204 NextResponse carrying the CORS headers.
*/
export async function OPTIONS() {
return new NextResponse(null, { status: 204, headers: getCorsHeaders() });
Expand All @@ -12,7 +14,15 @@ export async function OPTIONS() {
/**
* POST /api/content/analyze
*
* Analyze a video with AI — describe scenes, check quality, evaluate content.
* Runs a TwelveLabs analysis over a hosted video to describe scenes, check quality,
* or evaluate content against a caller-supplied prompt. Body is validated by
* `validateAnalyzeVideoBody` in `lib/content/analyze/`.
*
* @param request - The incoming request with JSON body `{ video_url, prompt, ... }`
* (see `validateAnalyzeVideoBody` for the full schema).
* @returns A 200 NextResponse with `{ text, finish_reason, usage }`, 400 on a bad
* body, 500 when TwelveLabs is not configured, or 502 when the upstream analysis
* request fails.
*/
export async function POST(request: NextRequest): Promise<NextResponse> {
return createAnalyzeHandler(request);
Expand Down
12 changes: 11 additions & 1 deletion app/api/content/caption/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { createTextHandler } from "@/lib/content/caption/createTextHandler";

/**
* OPTIONS handler for CORS preflight requests.
*
* @returns A 204 NextResponse carrying the CORS headers.
*/
export async function OPTIONS() {
return new NextResponse(null, { status: 204, headers: getCorsHeaders() });
Expand All @@ -12,7 +14,15 @@ export async function OPTIONS() {
/**
* POST /api/content/caption
*
* Generate on-screen caption text for a social video.
* Generates on-screen caption text for a social video using a lightweight LLM, with
* optional template-driven styling (tone, formats, example phrasing). Body is
* validated by `validateCreateCaptionBody` in `lib/content/caption/`.
*
* @param request - The incoming request with JSON body `{ topic, length, template? }`
* (see `validateCreateCaptionBody` for the full schema).
* @returns A 200 NextResponse with `{ content, font, color, borderColor, maxFontSize }`
* for the rendered caption, 400 on a bad body, 502 when the LLM returns empty text,
* or 500 on other generation errors.
*/
export async function POST(request: NextRequest): Promise<NextResponse> {
return createTextHandler(request);
Expand Down
10 changes: 9 additions & 1 deletion app/api/content/image/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { createImageHandler } from "@/lib/content/image/createImageHandler";

/**
* OPTIONS handler for CORS preflight requests.
*
* @returns A 204 NextResponse carrying the CORS headers.
*/
export async function OPTIONS() {
return new NextResponse(null, { status: 204, headers: getCorsHeaders() });
Expand All @@ -12,7 +14,13 @@ export async function OPTIONS() {
/**
* POST /api/content/image
*
* Generate an image from a prompt and optional reference image.
* Generates an image via Fal from a text prompt with optional reference images.
* Body is validated by `validateCreateImageBody` in `lib/content/image/`.
*
* @param request - The incoming request with JSON body `{ prompt, reference_images?,
* model?, ... }` (see `validateCreateImageBody` for the full schema).
* @returns A 200 NextResponse with `{ imageUrl, images: string[] }`, 400 on a bad
* body, 502 when Fal returns no image, or 500 on other generation errors.
*/
export async function POST(request: NextRequest): Promise<NextResponse> {
return createImageHandler(request);
Expand Down
13 changes: 12 additions & 1 deletion app/api/content/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { editHandler } from "@/lib/content/edit/editHandler";

/**
* OPTIONS handler for CORS preflight requests.
*
* @returns A 204 NextResponse carrying the CORS headers.
*/
export async function OPTIONS() {
return new NextResponse(null, { status: 204, headers: getCorsHeaders() });
Expand All @@ -12,7 +14,16 @@ export async function OPTIONS() {
/**
* PATCH /api/content
*
* Edit media with operations or a template preset.
* Edits a media asset by triggering the `ffmpeg-edit` Trigger.dev task with explicit
* operations or a template preset. Returns immediately with a run id; the actual
* edit happens asynchronously. Body is validated by `validateEditContentBody` in
* `lib/content/edit/`.
*
* @param request - The incoming request with JSON body `{ url, operations? | template? }`
* (see `validateEditContentBody` for the full schema). Exactly one of `operations`
* or `template` must be provided.
* @returns A 202 NextResponse with `{ runId, status: "triggered" }` when the task is
* queued, 400 on a bad body, or 500 when the task cannot be triggered.
*/
export async function PATCH(request: NextRequest): Promise<NextResponse> {
return editHandler(request);
Expand Down
Loading
Loading