144 lines · 6.2 KB
import { createServerFn } from "@tanstack/react-start";
import { z } from "zod";
import { requireViewer, viewerMiddleware } from "./middleware";
import { pullInput, requirePull, requirePullMutation } from "~/server/pull-access";
import { limitBrowse, limitWrite } from "~/server/limits";
import { currentBaseSha, loadPullRepos } from "~/server/pull-merge";
import { assertNotArchived, canAdministerAccount, requireRepoAccess } from "~/server/repos";
import { startMergeFix } from "~/server/mergefix/service";
import { applyProposal } from "~/server/mergefix/apply";
import { MERGEFIX_MODES } from "~/server/mergefix/proposal";
import { latestRuns, loadRepoSettings, saveRepoSettings } from "~/server/mergefix/store";
import { AI_ENDPOINT_KINDS, BYOK_PROVIDERS, endpointSummary } from "~/server/ai/endpoint";
import type { FixPlan } from "~/server/mergefix/plan";
import { usageSummary } from "~/server/metering/summary";
import { first } from "~/server/db";
import { forbidden } from "~/server/errors";
import { repoRef } from "~/lib/validation";

function aiUsage(summary: Awaited<ReturnType<typeof usageSummary>>) {
  const meter = (id: string) => summary.meters.find((entry) => entry.meter === id);
  const view = (id: string) => ({ used: meter(id)?.quantity ?? 0, cap: meter(id)?.hardCap ?? null });
  return {
    month: summary.period,
    calls: view("ai.calls"),
    tokens: view("ai.tokens"),
    byokCalls: view("ai.byok_calls").used,
    byokTokens: view("ai.byok_tokens").used,
  };
}

export const startPullMergeFix = createServerFn({ method: "POST" })
  .middleware([viewerMiddleware])
  .validator(pullInput.extend({ mode: z.enum(MERGEFIX_MODES).optional() }))
  .handler(async ({ data, context }) => {
    const viewer = requireViewer(context.viewer);
    await limitWrite(viewer);
    const { pull } = await requirePullMutation(data.owner, data.repo, data.number, viewer, "write");
    return { runId: await startMergeFix(pull, viewer, "button", null, data.mode) };
  });