@realiizlabs/admin 0.8.0 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/git/errors.ts","../../src/git/client.ts","../../src/git/branch.ts","../../src/git/files.ts","../../src/git/pulls.ts","../../src/git/status.ts","../../src/git/merge.ts","../../src/git/reads.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Typed errors — so ADMIN-04 can `instanceof` rather than string-match.\n *\n * Each carries enough to log usefully without ever including the token.\n */\n\n/** Any non-2xx from GitHub that is not one of the more specific cases below. */\nexport class GitHubError extends Error {\n readonly status: number;\n readonly path: string;\n readonly body: unknown;\n\n constructor(message: string, opts: { status: number; path: string; body: unknown }) {\n super(message);\n this.name = \"GitHubError\";\n this.status = opts.status;\n this.path = opts.path;\n this.body = opts.body;\n }\n}\n\n/**\n * The pull request cannot merge because `main` moved underneath it in a way\n * that conflicts — someone else edited the same file first.\n *\n * Observed in the probe: a stale base sha does NOT fail the trees write. Tree,\n * commit, ref and PR all succeed; the conflict only shows as\n * `mergeable_state: \"dirty\"` and then a 405 \"Pull Request has merge conflicts\"\n * on merge. So this is raised from mergePullRequest, never from writeFiles.\n * The recovery is the caller's: re-read main, rebuild the change, open a new PR.\n */\nexport class StaleBaseError extends GitHubError {\n readonly pullNumber: number;\n\n constructor(pullNumber: number, opts: { status: number; path: string; body: unknown }) {\n super(`Pull request #${pullNumber} conflicts with the base branch`, opts);\n this.name = \"StaleBaseError\";\n this.pullNumber = pullNumber;\n }\n}\n\n/**\n * The token cannot do what was asked. A 403 from GitHub with a fine-grained PAT\n * almost always means a permission was not granted when the token was made —\n * a setup bug at onboarding, not a runtime condition.\n */\nexport class TokenScopeError extends GitHubError {\n static readonly REQUIRED_PERMISSIONS =\n \"Contents: Read and write, Pull requests: Read and write, Commit statuses: Read-only\";\n\n constructor(opts: { status: number; path: string; body: unknown }) {\n super(\n `GitHub refused ${opts.path} (${opts.status}). The repo token needs these fine-grained permissions: ${TokenScopeError.REQUIRED_PERMISSIONS}`,\n opts,\n );\n this.name = \"TokenScopeError\";\n }\n}\n\n/** mergePullRequest was asked to merge while checks are red. */\nexport class ChecksFailedError extends Error {\n readonly pullNumber: number;\n readonly failingCheck: string | null;\n\n constructor(pullNumber: number, failingCheck: string | null) {\n super(\n `Pull request #${pullNumber} has failing checks${failingCheck ? ` (${failingCheck})` : \"\"}`,\n );\n this.name = \"ChecksFailedError\";\n this.pullNumber = pullNumber;\n this.failingCheck = failingCheck;\n }\n}\n","/**\n * The one place that talks to api.github.com.\n *\n * ── Every call is bounded ────────────────────────────────────────────────────\n * Same pattern as 5degrees-website's HighLevel client: a module constant plus\n * AbortSignal.timeout on every fetch. That fix exists because a slow upstream\n * ran past the serverless function limit and the process was killed before its\n * own error handler could run. A publish is ~10 sequential GitHub calls, so the\n * same failure is available here. Observed probe latency was 200–1400 ms; 10 s\n * is generous, and if GitHub is slower than that we want the error, not the wait.\n *\n * ── No module state ──────────────────────────────────────────────────────────\n * The token arrives in RepoContext on every call. Nothing is read from\n * process.env here or anywhere in src/git (BUILD-ENV law).\n */\n\nimport type { RepoContext } from \"./types\";\nimport { GitHubError, TokenScopeError } from \"./errors\";\n\nconst API = \"https://api.github.com\";\nexport const GITHUB_TIMEOUT_MS = 10_000;\n\nexport interface GitHubResponse<T = unknown> {\n ok: boolean;\n status: number;\n json: T;\n}\n\n/**\n * Low-level request. Returns instead of throwing on non-2xx so callers can\n * inspect the status when a \"failure\" is a meaningful answer (405 on merge,\n * 422 on a duplicate ref). Use `ghFetchOrThrow` when it is not.\n */\nexport async function ghFetch<T = unknown>(\n ctx: RepoContext,\n method: \"GET\" | \"POST\" | \"PATCH\" | \"PUT\" | \"DELETE\",\n path: string,\n body?: unknown,\n): Promise<GitHubResponse<T>> {\n const res = await fetch(API + path, {\n method,\n signal: AbortSignal.timeout(GITHUB_TIMEOUT_MS),\n headers: {\n Authorization: `Bearer ${ctx.token}`,\n Accept: \"application/vnd.github+json\",\n \"X-GitHub-Api-Version\": \"2022-11-28\",\n \"User-Agent\": \"realiizlabs-admin\",\n ...(body !== undefined ? { \"Content-Type\": \"application/json\" } : {}),\n },\n body: body !== undefined ? JSON.stringify(body) : undefined,\n });\n\n const text = await res.text();\n let json: unknown = null;\n if (text) {\n try {\n json = JSON.parse(text);\n } catch {\n json = { raw: text };\n }\n }\n return { ok: res.ok, status: res.status, json: json as T };\n}\n\n/** Same as ghFetch but a non-2xx becomes a typed error. */\nexport async function ghFetchOrThrow<T = unknown>(\n ctx: RepoContext,\n method: \"GET\" | \"POST\" | \"PATCH\" | \"PUT\" | \"DELETE\",\n path: string,\n body?: unknown,\n): Promise<T> {\n const res = await ghFetch<T>(ctx, method, path, body);\n if (!res.ok) throw toError(res, path);\n return res.json;\n}\n\nexport function toError(res: GitHubResponse, path: string): GitHubError {\n const opts = { status: res.status, path, body: res.json };\n if (res.status === 403) return new TokenScopeError(opts);\n const message =\n (res.json as { message?: string } | null)?.message ?? `GitHub ${res.status} on ${path}`;\n return new GitHubError(message, opts);\n}\n\n/** `/repos/{owner}/{repo}` prefix, built per call. */\nexport function repoPath(ctx: RepoContext): string {\n return `/repos/${encodeURIComponent(ctx.owner)}/${encodeURIComponent(ctx.repo)}`;\n}\n\nexport function baseBranch(ctx: RepoContext): string {\n return ctx.baseBranch ?? \"main\";\n}\n","/**\n * createBranch — a new branch off the base branch, as it is RIGHT NOW.\n *\n * The base sha is fetched from the refs API on every call and never stored.\n * This module is one of four writers to the same repo (github.com, GitHub\n * Desktop, Cowork are the others), so a ref that was current a minute ago is\n * routinely stale. Branching from a cached sha would silently build on old\n * content.\n */\n\nimport type { CreateBranchResult, RepoContext } from \"./types\";\nimport { baseBranch, ghFetchOrThrow, repoPath } from \"./client\";\n\ninterface RefResponse {\n ref: string;\n object: { sha: string; type: string };\n}\n\n/** Read the current tip of a branch. Always a live call. */\nexport async function getBranchSha(ctx: RepoContext, branch: string): Promise<string> {\n const ref = await ghFetchOrThrow<RefResponse>(\n ctx,\n \"GET\",\n `${repoPath(ctx)}/git/ref/heads/${encodeURIComponent(branch)}`,\n );\n return ref.object.sha;\n}\n\nexport async function createBranch(\n ctx: RepoContext,\n branch: string,\n): Promise<CreateBranchResult> {\n const baseSha = await getBranchSha(ctx, baseBranch(ctx));\n\n await ghFetchOrThrow<RefResponse>(ctx, \"POST\", `${repoPath(ctx)}/git/refs`, {\n ref: `refs/heads/${branch}`,\n sha: baseSha,\n });\n\n return { branch, baseSha };\n}\n\n/**\n * Remove a branch. 204 on success. Used after a squash merge so the client's\n * repo does not accumulate one dead branch per publish.\n */\nexport async function deleteBranch(ctx: RepoContext, branch: string): Promise<void> {\n await ghFetchOrThrow(\n ctx,\n \"DELETE\",\n `${repoPath(ctx)}/git/refs/heads/${encodeURIComponent(branch)}`,\n );\n}\n","/**\n * writeFiles — one or more files in ONE commit on an existing branch.\n *\n * Why the trees API and not the Contents API: a post is an MDX file plus a\n * hero image. Two Contents calls make two commits, and the first triggers a\n * build of a half-finished state. One tree, one commit.\n *\n * Encoding rule, observed in the probe: a tree entry can inline utf-8 text as\n * `content`, but has no `encoding` field — a binary must be uploaded first via\n * POST /git/blobs with `encoding: \"base64\"` and referenced in the tree by sha.\n * So a two-file publish with one image is six calls, not five.\n */\n\nimport type { FileInput, RepoContext, WriteFilesResult } from \"./types\";\nimport { getBranchSha } from \"./branch\";\nimport { ghFetchOrThrow, repoPath } from \"./client\";\n\ntype TreeEntry =\n | { path: string; mode: \"100644\"; type: \"blob\"; content: string }\n | { path: string; mode: \"100644\"; type: \"blob\"; sha: string };\n\nexport async function writeFiles(\n ctx: RepoContext,\n branch: string,\n files: FileInput[],\n message: string,\n): Promise<WriteFilesResult> {\n if (files.length === 0) throw new Error(\"writeFiles: no files given\");\n\n const base = repoPath(ctx);\n\n // The branch tip as of now — not the sha createBranch returned earlier.\n const parentSha = await getBranchSha(ctx, branch);\n\n // Binary files become blobs first; text is inlined.\n const tree: TreeEntry[] = [];\n for (const file of files) {\n if (file.encoding === \"base64\") {\n const blob = await ghFetchOrThrow<{ sha: string }>(ctx, \"POST\", `${base}/git/blobs`, {\n content: file.content,\n encoding: \"base64\",\n });\n tree.push({ path: file.path, mode: \"100644\", type: \"blob\", sha: blob.sha });\n } else {\n tree.push({ path: file.path, mode: \"100644\", type: \"blob\", content: file.content });\n }\n }\n\n const newTree = await ghFetchOrThrow<{ sha: string }>(ctx, \"POST\", `${base}/git/trees`, {\n base_tree: parentSha,\n tree,\n });\n\n const commit = await ghFetchOrThrow<{ sha: string }>(ctx, \"POST\", `${base}/git/commits`, {\n message,\n tree: newTree.sha,\n parents: [parentSha],\n });\n\n // Fast-forward the branch onto the new commit. `force: false` means a\n // concurrent write to this same branch surfaces as a 422 \"Update is not a\n // fast forward\" rather than being silently overwritten.\n await ghFetchOrThrow(\n ctx,\n \"PATCH\",\n `${base}/git/refs/heads/${encodeURIComponent(branch)}`,\n { sha: commit.sha, force: false },\n );\n\n return { branch, commitSha: commit.sha, treeSha: newTree.sha, fileCount: files.length };\n}\n","/**\n * openPullRequest — the PR that CI runs on and the client eventually publishes.\n *\n * PR Law (site-starter AGENTS.md): nothing lands on main except through a PR.\n * This module never pushes to the base branch directly.\n */\n\nimport type { PullRequest, RepoContext } from \"./types\";\nimport { baseBranch, ghFetchOrThrow, repoPath } from \"./client\";\n\n/** The subset of GitHub's PR payload this module reads. Observed in the probe. */\nexport interface PullPayload {\n number: number;\n html_url: string;\n state: \"open\" | \"closed\";\n head: { sha: string; ref: string };\n base: { sha: string; ref: string };\n /** null while GitHub is still computing — usually the first ~2 s after creation. */\n mergeable: boolean | null;\n mergeable_state: \"unknown\" | \"clean\" | \"dirty\" | \"blocked\" | \"unstable\" | \"behind\" | \"draft\" | \"has_hooks\";\n merged: boolean;\n}\n\nexport async function openPullRequest(\n ctx: RepoContext,\n branch: string,\n opts: { title: string; body?: string },\n): Promise<PullRequest> {\n const pr = await ghFetchOrThrow<PullPayload>(ctx, \"POST\", `${repoPath(ctx)}/pulls`, {\n title: opts.title,\n head: branch,\n base: baseBranch(ctx),\n body: opts.body ?? \"\",\n });\n\n return { number: pr.number, html_url: pr.html_url, headSha: pr.head.sha, branch };\n}\n\n/** Live read of a PR — the source of truth for mergeability. */\nexport async function getPullRequest(ctx: RepoContext, number: number): Promise<PullPayload> {\n return ghFetchOrThrow<PullPayload>(ctx, \"GET\", `${repoPath(ctx)}/pulls/${number}`);\n}\n","/**\n * getPullRequestStatus — green, red or pending. Nothing else.\n *\n * ── Why not the check-runs endpoint ──────────────────────────────────────────\n * A fine-grained PAT cannot read it. Observed 403 on every probe run, and the\n * token permission picker has no \"Checks\" entry to grant. So status is derived\n * from two things a PAT can always read:\n *\n * 1. the PR's `mergeable_state`, which folds in required checks when branch\n * protection is on;\n * 2. the combined commit status for the head sha, which is where Vercel and\n * any status-based CI report — and the only place a failing check has a\n * name.\n *\n * The accepted limitation: a GitHub Actions failure surfaces only as\n * `blocked`/`unstable`, not by job name. That is red without a name.\n *\n * ── Two traps, both observed ─────────────────────────────────────────────────\n * `mergeable` is null and `mergeable_state` is \"unknown\" for ~2 s after a PR is\n * created. That is pending, never red.\n *\n * The combined status of ZERO statuses has `state: \"pending\"`, not \"success\".\n * A no-CI repo read naively would be pending forever. So `total_count` is\n * checked before `state`.\n */\n\nimport type { PullRequestStatus, RepoContext } from \"./types\";\nimport { ghFetchOrThrow, repoPath } from \"./client\";\nimport { getPullRequest } from \"./pulls\";\n\n/** Observed shape of GET /commits/{sha}/status. */\nexport interface CombinedStatus {\n state: \"success\" | \"failure\" | \"pending\" | \"error\";\n total_count: number;\n statuses: Array<{ context: string; state: string; description: string | null }>;\n}\n\nexport async function getPullRequestStatus(\n ctx: RepoContext,\n number: number,\n log: Pick<Console, \"warn\"> = console,\n): Promise<PullRequestStatus> {\n const pr = await getPullRequest(ctx, number);\n\n if (pr.mergeable === null || pr.mergeable_state === \"unknown\") {\n return { state: \"pending\" };\n }\n\n const combined = await ghFetchOrThrow<CombinedStatus>(\n ctx,\n \"GET\",\n `${repoPath(ctx)}/commits/${pr.head.sha}/status`,\n );\n\n const failing = combined.statuses.find((s) => s.state === \"failure\" || s.state === \"error\");\n\n // dirty is a conflict, not a check failure — mergePullRequest turns it into\n // StaleBaseError. Report it as red here so a poller stops polling.\n if (pr.mergeable_state === \"dirty\") {\n return { state: \"red\", failingCheck: failing?.context ?? \"merge conflict\" };\n }\n\n if (failing || pr.mergeable_state === \"unstable\") {\n return { state: \"red\", failingCheck: failing?.context ?? null };\n }\n\n if (combined.total_count === 0) {\n if (pr.mergeable_state === \"clean\" || pr.mergeable_state === \"behind\") {\n const warning = `[admin/git] ${ctx.owner}/${ctx.repo} has no CI statuses on PR #${number} — treating as green. A client site with no CI is a setup bug.`;\n log.warn(warning);\n return { state: \"green\", warning };\n }\n // blocked with nothing reported yet: required checks have not started.\n return { state: \"pending\" };\n }\n\n if (combined.state === \"success\" && pr.mergeable_state !== \"blocked\") {\n return { state: \"green\" };\n }\n\n return { state: \"pending\" };\n}\n","/**\n * mergePullRequest — publish. Squash, then delete the branch.\n *\n * `requireGreen` defaults to true. The whole point of the PR flow is that CI\n * catches bad frontmatter before it freezes the site; merging red automates the\n * exact failure the flow exists to prevent.\n *\n * Pending is the common case (checks are always pending right after a push),\n * so it is returned for the caller to poll — never thrown.\n */\n\nimport type { MergeResult, RepoContext } from \"./types\";\nimport { deleteBranch } from \"./branch\";\nimport { ghFetch, repoPath, toError } from \"./client\";\nimport { ChecksFailedError, StaleBaseError } from \"./errors\";\nimport { getPullRequest } from \"./pulls\";\nimport { getPullRequestStatus } from \"./status\";\n\ninterface MergeResponse {\n sha: string;\n merged: boolean;\n message: string;\n}\n\nexport interface MergeOptions {\n /** Refuse to merge unless checks are green. Default true. */\n requireGreen?: boolean;\n /** Delete the head branch after a successful merge. Default true. */\n deleteBranch?: boolean;\n /** Squash commit title. Defaults to GitHub's (the PR title + number). */\n commitTitle?: string;\n}\n\nexport async function mergePullRequest(\n ctx: RepoContext,\n number: number,\n opts: MergeOptions = {},\n): Promise<MergeResult> {\n const requireGreen = opts.requireGreen ?? true;\n const path = `${repoPath(ctx)}/pulls/${number}/merge`;\n\n if (requireGreen) {\n const status = await getPullRequestStatus(ctx, number);\n if (status.state === \"pending\") return { merged: false, state: \"pending\" };\n if (status.state === \"red\") {\n // A conflict is not a failed check — it is a stale base. Distinguish it.\n const pr = await getPullRequest(ctx, number);\n if (pr.mergeable_state === \"dirty\") {\n throw new StaleBaseError(number, { status: 0, path, body: pr });\n }\n throw new ChecksFailedError(number, status.failingCheck);\n }\n }\n\n const res = await ghFetch<MergeResponse>(ctx, \"PUT\", path, {\n merge_method: \"squash\",\n ...(opts.commitTitle ? { commit_title: opts.commitTitle } : {}),\n });\n\n if (!res.ok) {\n // Observed: 405 { message: \"Pull Request has merge conflicts\" } for a\n // stale base. Also 405 for \"not mergeable\" generally, and 409 if the head\n // sha moved between read and merge — both are the same recovery for the\n // caller (re-read, rebuild), so both map to StaleBaseError.\n if (res.status === 405 || res.status === 409) {\n throw new StaleBaseError(number, { status: res.status, path, body: res.json });\n }\n throw toError(res, path);\n }\n\n if (opts.deleteBranch ?? true) {\n const pr = await getPullRequest(ctx, number);\n await deleteBranch(ctx, pr.head.ref);\n }\n\n return { merged: true, sha: res.json.sha };\n}\n","/**\n * Read helpers — list a folder, open a file — straight from the repo at call time.\n *\n * The dashboard lists and opens content from `main` via the Contents API, never\n * from the deployed filesystem: Vercel's bundle only contains what the tracer\n * saw, and it is a snapshot of the last deploy rather than of the repo. This\n * is also how hand-written files (github.com, Desktop, Cowork) show up — the\n * dashboard never assumes it authored anything.\n */\n\nimport type { RepoContext } from \"./types\";\nimport { baseBranch, ghFetch, repoPath, toError } from \"./client\";\nimport { GitHubError } from \"./errors\";\n\nexport interface RepoEntry {\n name: string;\n path: string;\n sha: string;\n size: number;\n type: \"file\";\n}\n\nexport interface RepoFile {\n path: string;\n sha: string;\n content: string;\n}\n\nexport class NotFoundError extends GitHubError {\n constructor(path: string) {\n super(`Not found in repo: ${path}`, { status: 404, path, body: null });\n this.name = \"NotFoundError\";\n }\n}\n\ninterface ContentsEntry {\n name: string;\n path: string;\n sha: string;\n size: number;\n type: \"file\" | \"dir\" | \"symlink\" | \"submodule\";\n content?: string;\n encoding?: string;\n}\n\nfunction contentsPath(ctx: RepoContext, path: string, ref?: string): string {\n const clean = path.replace(/^\\/+|\\/+$/g, \"\");\n const encoded = clean.split(\"/\").map(encodeURIComponent).join(\"/\");\n return `${repoPath(ctx)}/contents/${encoded}?ref=${encodeURIComponent(ref ?? baseBranch(ctx))}`;\n}\n\n/** Files directly inside `path`, sorted by name. Missing folder → []. */\nexport async function listDirectory(ctx: RepoContext, path: string, opts: { ref?: string } = {}): Promise<RepoEntry[]> {\n const url = contentsPath(ctx, path, opts.ref);\n const res = await ghFetch<ContentsEntry[] | ContentsEntry>(ctx, \"GET\", url);\n if (res.status === 404) return [];\n if (!res.ok) throw toError(res, url);\n const list = Array.isArray(res.json) ? res.json : [];\n return list\n .filter((e) => e.type === \"file\")\n .map((e) => ({ name: e.name, path: e.path, sha: e.sha, size: e.size, type: \"file\" as const }))\n .sort((a, b) => a.name.localeCompare(b.name));\n}\n\n/** One file, decoded to utf-8. */\nexport async function readFile(ctx: RepoContext, path: string, opts: { ref?: string } = {}): Promise<RepoFile> {\n const url = contentsPath(ctx, path, opts.ref);\n const res = await ghFetch<ContentsEntry>(ctx, \"GET\", url);\n if (res.status === 404) throw new NotFoundError(path);\n if (!res.ok) throw toError(res, url);\n const entry = res.json;\n if (Array.isArray(entry) || entry.type !== \"file\") throw new GitHubError(`${path} is not a file`, { status: res.status, path: url, body: entry });\n if (typeof entry.content !== \"string\") {\n // GitHub omits content for files over 1 MB; MDX never gets near that.\n throw new GitHubError(`${path} is too large to read through the Contents API`, { status: res.status, path: url, body: null });\n }\n const content = decodeBase64Utf8(entry.content);\n return { path: entry.path, sha: entry.sha, content };\n}\n\n/** The Contents API wraps base64 at 60 columns; strip whitespace before decoding. */\nfunction decodeBase64Utf8(b64: string): string {\n const clean = b64.replace(/\\s+/g, \"\");\n const bin = atob(clean);\n const bytes = Uint8Array.from(bin, (c) => c.charCodeAt(0));\n return new TextDecoder(\"utf-8\").decode(bytes);\n}\n"]}
1
+ {"version":3,"sources":["../../src/git/errors.ts","../../src/git/client.ts","../../src/git/branch.ts","../../src/git/files.ts","../../src/git/pulls.ts","../../src/git/status.ts","../../src/git/merge.ts","../../src/git/reads.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Typed errors — so ADMIN-04 can `instanceof` rather than string-match.\n *\n * Each carries enough to log usefully without ever including the token.\n */\n\n/** Any non-2xx from GitHub that is not one of the more specific cases below. */\nexport class GitHubError extends Error {\n readonly status: number;\n readonly path: string;\n readonly body: unknown;\n\n constructor(message: string, opts: { status: number; path: string; body: unknown }) {\n super(message);\n this.name = \"GitHubError\";\n this.status = opts.status;\n this.path = opts.path;\n this.body = opts.body;\n }\n}\n\n/**\n * The pull request cannot merge because `main` moved underneath it in a way\n * that conflicts — someone else edited the same file first.\n *\n * Observed in the probe: a stale base sha does NOT fail the trees write. Tree,\n * commit, ref and PR all succeed; the conflict only shows as\n * `mergeable_state: \"dirty\"` and then a 405 \"Pull Request has merge conflicts\"\n * on merge. So this is raised from mergePullRequest, never from writeFiles.\n * The recovery is the caller's: re-read main, rebuild the change, open a new PR.\n */\nexport class StaleBaseError extends GitHubError {\n readonly pullNumber: number;\n\n constructor(pullNumber: number, opts: { status: number; path: string; body: unknown }) {\n super(`Pull request #${pullNumber} conflicts with the base branch`, opts);\n this.name = \"StaleBaseError\";\n this.pullNumber = pullNumber;\n }\n}\n\n/**\n * The token cannot do what was asked. A 403 from GitHub with a fine-grained PAT\n * almost always means a permission was not granted when the token was made —\n * a setup bug at onboarding, not a runtime condition.\n */\nexport class TokenScopeError extends GitHubError {\n static readonly REQUIRED_PERMISSIONS =\n \"Contents: Read and write, Pull requests: Read and write, Commit statuses: Read-only\";\n\n constructor(opts: { status: number; path: string; body: unknown }) {\n super(\n `GitHub refused ${opts.path} (${opts.status}). The repo token needs these fine-grained permissions: ${TokenScopeError.REQUIRED_PERMISSIONS}`,\n opts,\n );\n this.name = \"TokenScopeError\";\n }\n}\n\n/** mergePullRequest was asked to merge while checks are red. */\nexport class ChecksFailedError extends Error {\n readonly pullNumber: number;\n readonly failingCheck: string | null;\n\n constructor(pullNumber: number, failingCheck: string | null) {\n super(\n `Pull request #${pullNumber} has failing checks${failingCheck ? ` (${failingCheck})` : \"\"}`,\n );\n this.name = \"ChecksFailedError\";\n this.pullNumber = pullNumber;\n this.failingCheck = failingCheck;\n }\n}\n","/**\n * The one place that talks to api.github.com.\n *\n * ── Every call is bounded ────────────────────────────────────────────────────\n * Same pattern as 5degrees-website's HighLevel client: a module constant plus\n * AbortSignal.timeout on every fetch. That fix exists because a slow upstream\n * ran past the serverless function limit and the process was killed before its\n * own error handler could run. A publish is ~10 sequential GitHub calls, so the\n * same failure is available here. Observed probe latency was 200–1400 ms; 10 s\n * is generous, and if GitHub is slower than that we want the error, not the wait.\n *\n * ── No module state ──────────────────────────────────────────────────────────\n * The token arrives in RepoContext on every call. Nothing is read from\n * process.env here or anywhere in src/git (BUILD-ENV law).\n */\n\nimport type { RepoContext } from \"./types\";\nimport { GitHubError, TokenScopeError } from \"./errors\";\n\nconst API = \"https://api.github.com\";\nexport const GITHUB_TIMEOUT_MS = 10_000;\n\nexport interface GitHubResponse<T = unknown> {\n ok: boolean;\n status: number;\n json: T;\n}\n\n/**\n * Low-level request. Returns instead of throwing on non-2xx so callers can\n * inspect the status when a \"failure\" is a meaningful answer (405 on merge,\n * 422 on a duplicate ref). Use `ghFetchOrThrow` when it is not.\n */\nexport async function ghFetch<T = unknown>(\n ctx: RepoContext,\n method: \"GET\" | \"POST\" | \"PATCH\" | \"PUT\" | \"DELETE\",\n path: string,\n body?: unknown,\n): Promise<GitHubResponse<T>> {\n const res = await fetch(API + path, {\n method,\n signal: AbortSignal.timeout(GITHUB_TIMEOUT_MS),\n headers: {\n Authorization: `Bearer ${ctx.token}`,\n Accept: \"application/vnd.github+json\",\n \"X-GitHub-Api-Version\": \"2022-11-28\",\n \"User-Agent\": \"realiizlabs-admin\",\n ...(body !== undefined ? { \"Content-Type\": \"application/json\" } : {}),\n },\n body: body !== undefined ? JSON.stringify(body) : undefined,\n });\n\n const text = await res.text();\n let json: unknown = null;\n if (text) {\n try {\n json = JSON.parse(text);\n } catch {\n json = { raw: text };\n }\n }\n return { ok: res.ok, status: res.status, json: json as T };\n}\n\n/** Same as ghFetch but a non-2xx becomes a typed error. */\nexport async function ghFetchOrThrow<T = unknown>(\n ctx: RepoContext,\n method: \"GET\" | \"POST\" | \"PATCH\" | \"PUT\" | \"DELETE\",\n path: string,\n body?: unknown,\n): Promise<T> {\n const res = await ghFetch<T>(ctx, method, path, body);\n if (!res.ok) throw toError(res, path);\n return res.json;\n}\n\nexport function toError(res: GitHubResponse, path: string): GitHubError {\n const opts = { status: res.status, path, body: res.json };\n if (res.status === 403) return new TokenScopeError(opts);\n const message =\n (res.json as { message?: string } | null)?.message ?? `GitHub ${res.status} on ${path}`;\n return new GitHubError(message, opts);\n}\n\n/** `/repos/{owner}/{repo}` prefix, built per call. */\nexport function repoPath(ctx: RepoContext): string {\n return `/repos/${encodeURIComponent(ctx.owner)}/${encodeURIComponent(ctx.repo)}`;\n}\n\nexport function baseBranch(ctx: RepoContext): string {\n return ctx.baseBranch ?? \"main\";\n}\n","/**\n * createBranch — a new branch off the base branch, as it is RIGHT NOW.\n *\n * The base sha is fetched from the refs API on every call and never stored.\n * This module is one of four writers to the same repo (github.com, GitHub\n * Desktop, Cowork are the others), so a ref that was current a minute ago is\n * routinely stale. Branching from a cached sha would silently build on old\n * content.\n */\n\nimport type { CreateBranchResult, RepoContext } from \"./types\";\nimport { baseBranch, ghFetchOrThrow, repoPath } from \"./client\";\n\ninterface RefResponse {\n ref: string;\n object: { sha: string; type: string };\n}\n\n/** Read the current tip of a branch. Always a live call. */\nexport async function getBranchSha(ctx: RepoContext, branch: string): Promise<string> {\n const ref = await ghFetchOrThrow<RefResponse>(\n ctx,\n \"GET\",\n `${repoPath(ctx)}/git/ref/heads/${encodeURIComponent(branch)}`,\n );\n return ref.object.sha;\n}\n\nexport async function createBranch(\n ctx: RepoContext,\n branch: string,\n): Promise<CreateBranchResult> {\n const baseSha = await getBranchSha(ctx, baseBranch(ctx));\n\n await ghFetchOrThrow<RefResponse>(ctx, \"POST\", `${repoPath(ctx)}/git/refs`, {\n ref: `refs/heads/${branch}`,\n sha: baseSha,\n });\n\n return { branch, baseSha };\n}\n\n/**\n * Remove a branch. 204 on success. Used after a squash merge so the client's\n * repo does not accumulate one dead branch per publish.\n */\nexport async function deleteBranch(ctx: RepoContext, branch: string): Promise<void> {\n await ghFetchOrThrow(\n ctx,\n \"DELETE\",\n `${repoPath(ctx)}/git/refs/heads/${encodeURIComponent(branch)}`,\n );\n}\n","/**\n * writeFiles — one or more files in ONE commit on an existing branch.\n *\n * Why the trees API and not the Contents API: a post is an MDX file plus a\n * hero image. Two Contents calls make two commits, and the first triggers a\n * build of a half-finished state. One tree, one commit.\n *\n * Encoding rule, observed in the probe: a tree entry can inline utf-8 text as\n * `content`, but has no `encoding` field — a binary must be uploaded first via\n * POST /git/blobs with `encoding: \"base64\"` and referenced in the tree by sha.\n * So a two-file publish with one image is six calls, not five.\n *\n * `opts.remove` deletes paths in the same commit (tree entry with `sha: null`) —\n * how the dashboard takes a post down: one PR that removes the file.\n */\n\nimport type { FileInput, RepoContext, WriteFilesResult } from \"./types\";\nimport { getBranchSha } from \"./branch\";\nimport { ghFetchOrThrow, repoPath } from \"./client\";\n\ntype TreeEntry =\n | { path: string; mode: \"100644\"; type: \"blob\"; content: string }\n | { path: string; mode: \"100644\"; type: \"blob\"; sha: string }\n /* sha: null in a tree entry deletes the path — GitHub's documented way to remove a file via the trees API. */\n | { path: string; mode: \"100644\"; type: \"blob\"; sha: null };\n\nexport async function writeFiles(\n ctx: RepoContext,\n branch: string,\n files: FileInput[],\n message: string,\n opts: { remove?: string[] } = {},\n): Promise<WriteFilesResult> {\n const remove = opts.remove ?? [];\n if (files.length === 0 && remove.length === 0) throw new Error(\"writeFiles: no files given\");\n\n const base = repoPath(ctx);\n\n // The branch tip as of now — not the sha createBranch returned earlier.\n const parentSha = await getBranchSha(ctx, branch);\n\n // Binary files become blobs first; text is inlined.\n const tree: TreeEntry[] = [];\n for (const file of files) {\n if (file.encoding === \"base64\") {\n const blob = await ghFetchOrThrow<{ sha: string }>(ctx, \"POST\", `${base}/git/blobs`, {\n content: file.content,\n encoding: \"base64\",\n });\n tree.push({ path: file.path, mode: \"100644\", type: \"blob\", sha: blob.sha });\n } else {\n tree.push({ path: file.path, mode: \"100644\", type: \"blob\", content: file.content });\n }\n }\n\n for (const path of remove) tree.push({ path, mode: \"100644\", type: \"blob\", sha: null });\n\n const newTree = await ghFetchOrThrow<{ sha: string }>(ctx, \"POST\", `${base}/git/trees`, {\n base_tree: parentSha,\n tree,\n });\n\n const commit = await ghFetchOrThrow<{ sha: string }>(ctx, \"POST\", `${base}/git/commits`, {\n message,\n tree: newTree.sha,\n parents: [parentSha],\n });\n\n // Fast-forward the branch onto the new commit. `force: false` means a\n // concurrent write to this same branch surfaces as a 422 \"Update is not a\n // fast forward\" rather than being silently overwritten.\n await ghFetchOrThrow(\n ctx,\n \"PATCH\",\n `${base}/git/refs/heads/${encodeURIComponent(branch)}`,\n { sha: commit.sha, force: false },\n );\n\n return { branch, commitSha: commit.sha, treeSha: newTree.sha, fileCount: files.length + remove.length };\n}\n","/**\n * openPullRequest — the PR that CI runs on and the client eventually publishes.\n *\n * PR Law (site-starter AGENTS.md): nothing lands on main except through a PR.\n * This module never pushes to the base branch directly.\n */\n\nimport type { PullRequest, RepoContext } from \"./types\";\nimport { baseBranch, ghFetchOrThrow, repoPath } from \"./client\";\n\n/** The subset of GitHub's PR payload this module reads. Observed in the probe. */\nexport interface PullPayload {\n number: number;\n title: string;\n html_url: string;\n state: \"open\" | \"closed\";\n head: { sha: string; ref: string };\n base: { sha: string; ref: string };\n /** null while GitHub is still computing — usually the first ~2 s after creation. */\n mergeable: boolean | null;\n mergeable_state: \"unknown\" | \"clean\" | \"dirty\" | \"blocked\" | \"unstable\" | \"behind\" | \"draft\" | \"has_hooks\";\n merged: boolean;\n}\n\nexport async function openPullRequest(\n ctx: RepoContext,\n branch: string,\n opts: { title: string; body?: string },\n): Promise<PullRequest> {\n const pr = await ghFetchOrThrow<PullPayload>(ctx, \"POST\", `${repoPath(ctx)}/pulls`, {\n title: opts.title,\n head: branch,\n base: baseBranch(ctx),\n body: opts.body ?? \"\",\n });\n\n return { number: pr.number, html_url: pr.html_url, headSha: pr.head.sha, branch };\n}\n\n/** Live read of a PR — the source of truth for mergeability. */\nexport async function getPullRequest(ctx: RepoContext, number: number): Promise<PullPayload> {\n return ghFetchOrThrow<PullPayload>(ctx, \"GET\", `${repoPath(ctx)}/pulls/${number}`);\n}\n\nexport interface OpenPullRequest extends PullRequest {\n title: string;\n}\n\n/**\n * Open PRs whose head branch starts with `headPrefix` — the dashboard's own\n * in-flight changes (04b names them `admin/{type}/{file}-{stamp}`). One page of\n * 100 is far more than a content site ever has open at once.\n */\nexport async function listOpenPullRequests(ctx: RepoContext, opts: { headPrefix: string }): Promise<OpenPullRequest[]> {\n const prs = await ghFetchOrThrow<PullPayload[]>(ctx, \"GET\", `${repoPath(ctx)}/pulls?state=open&per_page=100`);\n return prs\n .filter((p) => p.head.ref.startsWith(opts.headPrefix))\n .map((p) => ({ number: p.number, title: p.title, html_url: p.html_url, headSha: p.head.sha, branch: p.head.ref }));\n}\n\n/**\n * Close a PR without merging — the client changed their mind. The branch is\n * the caller's to delete (deleteBranch) once the close succeeds; closing first\n * means a failed delete leaves a closed PR, never an open one pointing at nothing.\n */\nexport async function closePullRequest(ctx: RepoContext, number: number): Promise<void> {\n await ghFetchOrThrow<PullPayload>(ctx, \"PATCH\", `${repoPath(ctx)}/pulls/${number}`, { state: \"closed\" });\n}\n","/**\n * getPullRequestStatus — green, red or pending. Nothing else.\n *\n * ── Why not the check-runs endpoint ──────────────────────────────────────────\n * A fine-grained PAT cannot read it. Observed 403 on every probe run, and the\n * token permission picker has no \"Checks\" entry to grant. So status is derived\n * from two things a PAT can always read:\n *\n * 1. the PR's `mergeable_state`, which folds in required checks when branch\n * protection is on;\n * 2. the combined commit status for the head sha, which is where Vercel and\n * any status-based CI report — and the only place a failing check has a\n * name.\n *\n * The accepted limitation: a GitHub Actions failure surfaces only as\n * `blocked`/`unstable`, not by job name. That is red without a name.\n *\n * ── Two traps, both observed ─────────────────────────────────────────────────\n * `mergeable` is null and `mergeable_state` is \"unknown\" for ~2 s after a PR is\n * created. That is pending, never red.\n *\n * The combined status of ZERO statuses has `state: \"pending\"`, not \"success\".\n * A no-CI repo read naively would be pending forever. So `total_count` is\n * checked before `state`.\n */\n\nimport type { PullRequestStatus, RepoContext } from \"./types\";\nimport { ghFetchOrThrow, repoPath } from \"./client\";\nimport { getPullRequest } from \"./pulls\";\n\n/** Observed shape of GET /commits/{sha}/status. */\nexport interface CombinedStatus {\n state: \"success\" | \"failure\" | \"pending\" | \"error\";\n total_count: number;\n statuses: Array<{ context: string; state: string; description: string | null; target_url?: string | null }>;\n}\n\nexport async function getPullRequestStatus(\n ctx: RepoContext,\n number: number,\n log: Pick<Console, \"warn\"> = console,\n): Promise<PullRequestStatus> {\n const pr = await getPullRequest(ctx, number);\n\n if (pr.mergeable === null || pr.mergeable_state === \"unknown\") {\n return { state: \"pending\" };\n }\n\n const combined = await ghFetchOrThrow<CombinedStatus>(\n ctx,\n \"GET\",\n `${repoPath(ctx)}/commits/${pr.head.sha}/status`,\n );\n\n const failing = combined.statuses.find((s) => s.state === \"failure\" || s.state === \"error\");\n\n // dirty is a conflict, not a check failure — mergePullRequest turns it into\n // StaleBaseError. Report it as red here so a poller stops polling.\n if (pr.mergeable_state === \"dirty\") {\n return { state: \"red\", failingCheck: failing?.context ?? \"merge conflict\" };\n }\n\n if (failing || pr.mergeable_state === \"unstable\") {\n return { state: \"red\", failingCheck: failing?.context ?? null };\n }\n\n if (combined.total_count === 0) {\n if (pr.mergeable_state === \"clean\" || pr.mergeable_state === \"behind\") {\n const warning = `[admin/git] ${ctx.owner}/${ctx.repo} has no CI statuses on PR #${number} — treating as green. A client site with no CI is a setup bug.`;\n log.warn(warning);\n return { state: \"green\", warning };\n }\n // blocked with nothing reported yet: required checks have not started.\n return { state: \"pending\" };\n }\n\n if (combined.state === \"success\" && pr.mergeable_state !== \"blocked\") {\n // A successful deploy-preview status carries the preview's URL — the client can look before publishing.\n const preview = combined.statuses.find((s) => s.state === \"success\" && /vercel|netlify|deploy|preview/i.test(s.context) && s.target_url);\n return preview?.target_url ? { state: \"green\", previewUrl: preview.target_url } : { state: \"green\" };\n }\n\n return { state: \"pending\" };\n}\n","/**\n * mergePullRequest — publish. Squash, then delete the branch.\n *\n * `requireGreen` defaults to true. The whole point of the PR flow is that CI\n * catches bad frontmatter before it freezes the site; merging red automates the\n * exact failure the flow exists to prevent.\n *\n * Pending is the common case (checks are always pending right after a push),\n * so it is returned for the caller to poll — never thrown.\n */\n\nimport type { MergeResult, RepoContext } from \"./types\";\nimport { deleteBranch } from \"./branch\";\nimport { ghFetch, repoPath, toError } from \"./client\";\nimport { ChecksFailedError, StaleBaseError } from \"./errors\";\nimport { getPullRequest } from \"./pulls\";\nimport { getPullRequestStatus } from \"./status\";\n\ninterface MergeResponse {\n sha: string;\n merged: boolean;\n message: string;\n}\n\nexport interface MergeOptions {\n /** Refuse to merge unless checks are green. Default true. */\n requireGreen?: boolean;\n /** Delete the head branch after a successful merge. Default true. */\n deleteBranch?: boolean;\n /** Squash commit title. Defaults to GitHub's (the PR title + number). */\n commitTitle?: string;\n}\n\nexport async function mergePullRequest(\n ctx: RepoContext,\n number: number,\n opts: MergeOptions = {},\n): Promise<MergeResult> {\n const requireGreen = opts.requireGreen ?? true;\n const path = `${repoPath(ctx)}/pulls/${number}/merge`;\n\n if (requireGreen) {\n const status = await getPullRequestStatus(ctx, number);\n if (status.state === \"pending\") return { merged: false, state: \"pending\" };\n if (status.state === \"red\") {\n // A conflict is not a failed check — it is a stale base. Distinguish it.\n const pr = await getPullRequest(ctx, number);\n if (pr.mergeable_state === \"dirty\") {\n throw new StaleBaseError(number, { status: 0, path, body: pr });\n }\n throw new ChecksFailedError(number, status.failingCheck);\n }\n }\n\n const res = await ghFetch<MergeResponse>(ctx, \"PUT\", path, {\n merge_method: \"squash\",\n ...(opts.commitTitle ? { commit_title: opts.commitTitle } : {}),\n });\n\n if (!res.ok) {\n // Observed: 405 { message: \"Pull Request has merge conflicts\" } for a\n // stale base. Also 405 for \"not mergeable\" generally, and 409 if the head\n // sha moved between read and merge — both are the same recovery for the\n // caller (re-read, rebuild), so both map to StaleBaseError.\n if (res.status === 405 || res.status === 409) {\n throw new StaleBaseError(number, { status: res.status, path, body: res.json });\n }\n throw toError(res, path);\n }\n\n if (opts.deleteBranch ?? true) {\n const pr = await getPullRequest(ctx, number);\n await deleteBranch(ctx, pr.head.ref);\n }\n\n return { merged: true, sha: res.json.sha };\n}\n","/**\n * Read helpers — list a folder, open a file — straight from the repo at call time.\n *\n * The dashboard lists and opens content from `main` via the Contents API, never\n * from the deployed filesystem: Vercel's bundle only contains what the tracer\n * saw, and it is a snapshot of the last deploy rather than of the repo. This\n * is also how hand-written files (github.com, Desktop, Cowork) show up — the\n * dashboard never assumes it authored anything.\n */\n\nimport type { RepoContext } from \"./types\";\nimport { baseBranch, ghFetch, repoPath, toError } from \"./client\";\nimport { GitHubError } from \"./errors\";\n\nexport interface RepoEntry {\n name: string;\n path: string;\n sha: string;\n size: number;\n type: \"file\";\n}\n\nexport interface RepoFile {\n path: string;\n sha: string;\n content: string;\n}\n\nexport class NotFoundError extends GitHubError {\n constructor(path: string) {\n super(`Not found in repo: ${path}`, { status: 404, path, body: null });\n this.name = \"NotFoundError\";\n }\n}\n\ninterface ContentsEntry {\n name: string;\n path: string;\n sha: string;\n size: number;\n type: \"file\" | \"dir\" | \"symlink\" | \"submodule\";\n content?: string;\n encoding?: string;\n}\n\nfunction contentsPath(ctx: RepoContext, path: string, ref?: string): string {\n const clean = path.replace(/^\\/+|\\/+$/g, \"\");\n const encoded = clean.split(\"/\").map(encodeURIComponent).join(\"/\");\n return `${repoPath(ctx)}/contents/${encoded}?ref=${encodeURIComponent(ref ?? baseBranch(ctx))}`;\n}\n\n/** Files directly inside `path`, sorted by name. Missing folder → []. */\nexport async function listDirectory(ctx: RepoContext, path: string, opts: { ref?: string } = {}): Promise<RepoEntry[]> {\n const url = contentsPath(ctx, path, opts.ref);\n const res = await ghFetch<ContentsEntry[] | ContentsEntry>(ctx, \"GET\", url);\n if (res.status === 404) return [];\n if (!res.ok) throw toError(res, url);\n const list = Array.isArray(res.json) ? res.json : [];\n return list\n .filter((e) => e.type === \"file\")\n .map((e) => ({ name: e.name, path: e.path, sha: e.sha, size: e.size, type: \"file\" as const }))\n .sort((a, b) => a.name.localeCompare(b.name));\n}\n\n/** One file, decoded to utf-8. */\nexport async function readFile(ctx: RepoContext, path: string, opts: { ref?: string } = {}): Promise<RepoFile> {\n const url = contentsPath(ctx, path, opts.ref);\n const res = await ghFetch<ContentsEntry>(ctx, \"GET\", url);\n if (res.status === 404) throw new NotFoundError(path);\n if (!res.ok) throw toError(res, url);\n const entry = res.json;\n if (Array.isArray(entry) || entry.type !== \"file\") throw new GitHubError(`${path} is not a file`, { status: res.status, path: url, body: entry });\n if (typeof entry.content !== \"string\") {\n // GitHub omits content for files over 1 MB; MDX never gets near that.\n throw new GitHubError(`${path} is too large to read through the Contents API`, { status: res.status, path: url, body: null });\n }\n const content = decodeBase64Utf8(entry.content);\n return { path: entry.path, sha: entry.sha, content };\n}\n\n/** The Contents API wraps base64 at 60 columns; strip whitespace before decoding. */\nfunction decodeBase64Utf8(b64: string): string {\n const clean = b64.replace(/\\s+/g, \"\");\n const bin = atob(clean);\n const bytes = Uint8Array.from(bin, (c) => c.charCodeAt(0));\n return new TextDecoder(\"utf-8\").decode(bytes);\n}\n"]}
@@ -60,6 +60,7 @@ interface PullRequest {
60
60
  type PullRequestStatus = {
61
61
  state: "green";
62
62
  warning?: string;
63
+ previewUrl?: string;
63
64
  } | {
64
65
  state: "red";
65
66
  failingCheck: string | null;
@@ -102,9 +103,14 @@ declare function deleteBranch(ctx: RepoContext, branch: string): Promise<void>;
102
103
  * `content`, but has no `encoding` field — a binary must be uploaded first via
103
104
  * POST /git/blobs with `encoding: "base64"` and referenced in the tree by sha.
104
105
  * So a two-file publish with one image is six calls, not five.
106
+ *
107
+ * `opts.remove` deletes paths in the same commit (tree entry with `sha: null`) —
108
+ * how the dashboard takes a post down: one PR that removes the file.
105
109
  */
106
110
 
107
- declare function writeFiles(ctx: RepoContext, branch: string, files: FileInput[], message: string): Promise<WriteFilesResult>;
111
+ declare function writeFiles(ctx: RepoContext, branch: string, files: FileInput[], message: string, opts?: {
112
+ remove?: string[];
113
+ }): Promise<WriteFilesResult>;
108
114
 
109
115
  /**
110
116
  * openPullRequest — the PR that CI runs on and the client eventually publishes.
@@ -117,6 +123,23 @@ declare function openPullRequest(ctx: RepoContext, branch: string, opts: {
117
123
  title: string;
118
124
  body?: string;
119
125
  }): Promise<PullRequest>;
126
+ interface OpenPullRequest extends PullRequest {
127
+ title: string;
128
+ }
129
+ /**
130
+ * Open PRs whose head branch starts with `headPrefix` — the dashboard's own
131
+ * in-flight changes (04b names them `admin/{type}/{file}-{stamp}`). One page of
132
+ * 100 is far more than a content site ever has open at once.
133
+ */
134
+ declare function listOpenPullRequests(ctx: RepoContext, opts: {
135
+ headPrefix: string;
136
+ }): Promise<OpenPullRequest[]>;
137
+ /**
138
+ * Close a PR without merging — the client changed their mind. The branch is
139
+ * the caller's to delete (deleteBranch) once the close succeeds; closing first
140
+ * means a failed delete leaves a closed PR, never an open one pointing at nothing.
141
+ */
142
+ declare function closePullRequest(ctx: RepoContext, number: number): Promise<void>;
120
143
 
121
144
  /**
122
145
  * getPullRequestStatus — green, red or pending. Nothing else.
@@ -255,4 +278,4 @@ declare function readFile(ctx: RepoContext, path: string, opts?: {
255
278
  ref?: string;
256
279
  }): Promise<RepoFile>;
257
280
 
258
- export { type BinaryFile, ChecksFailedError, type CreateBranchResult, type FileInput, GitHubError, type MergeOptions, type MergeResult, NotFoundError, type PullRequest, type PullRequestStatus, type RepoContext, type RepoEntry, type RepoFile, StaleBaseError, type TextFile, TokenScopeError, type WriteFilesResult, createBranch, deleteBranch, getPullRequestStatus, listDirectory, mergePullRequest, openPullRequest, readFile, writeFiles };
281
+ export { type BinaryFile, ChecksFailedError, type CreateBranchResult, type FileInput, GitHubError, type MergeOptions, type MergeResult, NotFoundError, type OpenPullRequest, type PullRequest, type PullRequestStatus, type RepoContext, type RepoEntry, type RepoFile, StaleBaseError, type TextFile, TokenScopeError, type WriteFilesResult, closePullRequest, createBranch, deleteBranch, getPullRequestStatus, listDirectory, listOpenPullRequests, mergePullRequest, openPullRequest, readFile, writeFiles };
@@ -60,6 +60,7 @@ interface PullRequest {
60
60
  type PullRequestStatus = {
61
61
  state: "green";
62
62
  warning?: string;
63
+ previewUrl?: string;
63
64
  } | {
64
65
  state: "red";
65
66
  failingCheck: string | null;
@@ -102,9 +103,14 @@ declare function deleteBranch(ctx: RepoContext, branch: string): Promise<void>;
102
103
  * `content`, but has no `encoding` field — a binary must be uploaded first via
103
104
  * POST /git/blobs with `encoding: "base64"` and referenced in the tree by sha.
104
105
  * So a two-file publish with one image is six calls, not five.
106
+ *
107
+ * `opts.remove` deletes paths in the same commit (tree entry with `sha: null`) —
108
+ * how the dashboard takes a post down: one PR that removes the file.
105
109
  */
106
110
 
107
- declare function writeFiles(ctx: RepoContext, branch: string, files: FileInput[], message: string): Promise<WriteFilesResult>;
111
+ declare function writeFiles(ctx: RepoContext, branch: string, files: FileInput[], message: string, opts?: {
112
+ remove?: string[];
113
+ }): Promise<WriteFilesResult>;
108
114
 
109
115
  /**
110
116
  * openPullRequest — the PR that CI runs on and the client eventually publishes.
@@ -117,6 +123,23 @@ declare function openPullRequest(ctx: RepoContext, branch: string, opts: {
117
123
  title: string;
118
124
  body?: string;
119
125
  }): Promise<PullRequest>;
126
+ interface OpenPullRequest extends PullRequest {
127
+ title: string;
128
+ }
129
+ /**
130
+ * Open PRs whose head branch starts with `headPrefix` — the dashboard's own
131
+ * in-flight changes (04b names them `admin/{type}/{file}-{stamp}`). One page of
132
+ * 100 is far more than a content site ever has open at once.
133
+ */
134
+ declare function listOpenPullRequests(ctx: RepoContext, opts: {
135
+ headPrefix: string;
136
+ }): Promise<OpenPullRequest[]>;
137
+ /**
138
+ * Close a PR without merging — the client changed their mind. The branch is
139
+ * the caller's to delete (deleteBranch) once the close succeeds; closing first
140
+ * means a failed delete leaves a closed PR, never an open one pointing at nothing.
141
+ */
142
+ declare function closePullRequest(ctx: RepoContext, number: number): Promise<void>;
120
143
 
121
144
  /**
122
145
  * getPullRequestStatus — green, red or pending. Nothing else.
@@ -255,4 +278,4 @@ declare function readFile(ctx: RepoContext, path: string, opts?: {
255
278
  ref?: string;
256
279
  }): Promise<RepoFile>;
257
280
 
258
- export { type BinaryFile, ChecksFailedError, type CreateBranchResult, type FileInput, GitHubError, type MergeOptions, type MergeResult, NotFoundError, type PullRequest, type PullRequestStatus, type RepoContext, type RepoEntry, type RepoFile, StaleBaseError, type TextFile, TokenScopeError, type WriteFilesResult, createBranch, deleteBranch, getPullRequestStatus, listDirectory, mergePullRequest, openPullRequest, readFile, writeFiles };
281
+ export { type BinaryFile, ChecksFailedError, type CreateBranchResult, type FileInput, GitHubError, type MergeOptions, type MergeResult, NotFoundError, type OpenPullRequest, type PullRequest, type PullRequestStatus, type RepoContext, type RepoEntry, type RepoFile, StaleBaseError, type TextFile, TokenScopeError, type WriteFilesResult, closePullRequest, createBranch, deleteBranch, getPullRequestStatus, listDirectory, listOpenPullRequests, mergePullRequest, openPullRequest, readFile, writeFiles };
package/dist/git/index.js CHANGED
@@ -112,8 +112,9 @@ async function deleteBranch(ctx, branch) {
112
112
  }
113
113
 
114
114
  // src/git/files.ts
115
- async function writeFiles(ctx, branch, files, message) {
116
- if (files.length === 0) throw new Error("writeFiles: no files given");
115
+ async function writeFiles(ctx, branch, files, message, opts = {}) {
116
+ const remove = opts.remove ?? [];
117
+ if (files.length === 0 && remove.length === 0) throw new Error("writeFiles: no files given");
117
118
  const base = repoPath(ctx);
118
119
  const parentSha = await getBranchSha(ctx, branch);
119
120
  const tree = [];
@@ -128,6 +129,7 @@ async function writeFiles(ctx, branch, files, message) {
128
129
  tree.push({ path: file.path, mode: "100644", type: "blob", content: file.content });
129
130
  }
130
131
  }
132
+ for (const path of remove) tree.push({ path, mode: "100644", type: "blob", sha: null });
131
133
  const newTree = await ghFetchOrThrow(ctx, "POST", `${base}/git/trees`, {
132
134
  base_tree: parentSha,
133
135
  tree
@@ -143,7 +145,7 @@ async function writeFiles(ctx, branch, files, message) {
143
145
  `${base}/git/refs/heads/${encodeURIComponent(branch)}`,
144
146
  { sha: commit.sha, force: false }
145
147
  );
146
- return { branch, commitSha: commit.sha, treeSha: newTree.sha, fileCount: files.length };
148
+ return { branch, commitSha: commit.sha, treeSha: newTree.sha, fileCount: files.length + remove.length };
147
149
  }
148
150
 
149
151
  // src/git/pulls.ts
@@ -159,6 +161,13 @@ async function openPullRequest(ctx, branch, opts) {
159
161
  async function getPullRequest(ctx, number) {
160
162
  return ghFetchOrThrow(ctx, "GET", `${repoPath(ctx)}/pulls/${number}`);
161
163
  }
164
+ async function listOpenPullRequests(ctx, opts) {
165
+ const prs = await ghFetchOrThrow(ctx, "GET", `${repoPath(ctx)}/pulls?state=open&per_page=100`);
166
+ return prs.filter((p) => p.head.ref.startsWith(opts.headPrefix)).map((p) => ({ number: p.number, title: p.title, html_url: p.html_url, headSha: p.head.sha, branch: p.head.ref }));
167
+ }
168
+ async function closePullRequest(ctx, number) {
169
+ await ghFetchOrThrow(ctx, "PATCH", `${repoPath(ctx)}/pulls/${number}`, { state: "closed" });
170
+ }
162
171
 
163
172
  // src/git/status.ts
164
173
  async function getPullRequestStatus(ctx, number, log = console) {
@@ -187,7 +196,8 @@ async function getPullRequestStatus(ctx, number, log = console) {
187
196
  return { state: "pending" };
188
197
  }
189
198
  if (combined.state === "success" && pr.mergeable_state !== "blocked") {
190
- return { state: "green" };
199
+ const preview = combined.statuses.find((s) => s.state === "success" && /vercel|netlify|deploy|preview/i.test(s.context) && s.target_url);
200
+ return preview?.target_url ? { state: "green", previewUrl: preview.target_url } : { state: "green" };
191
201
  }
192
202
  return { state: "pending" };
193
203
  }
@@ -264,6 +274,6 @@ function decodeBase64Utf8(b64) {
264
274
  return new TextDecoder("utf-8").decode(bytes);
265
275
  }
266
276
 
267
- export { ChecksFailedError, GitHubError, NotFoundError, StaleBaseError, TokenScopeError, createBranch, deleteBranch, getPullRequestStatus, listDirectory, mergePullRequest, openPullRequest, readFile, writeFiles };
277
+ export { ChecksFailedError, GitHubError, NotFoundError, StaleBaseError, TokenScopeError, closePullRequest, createBranch, deleteBranch, getPullRequestStatus, listDirectory, listOpenPullRequests, mergePullRequest, openPullRequest, readFile, writeFiles };
268
278
  //# sourceMappingURL=index.js.map
269
279
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/git/errors.ts","../../src/git/client.ts","../../src/git/branch.ts","../../src/git/files.ts","../../src/git/pulls.ts","../../src/git/status.ts","../../src/git/merge.ts","../../src/git/reads.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Typed errors — so ADMIN-04 can `instanceof` rather than string-match.\n *\n * Each carries enough to log usefully without ever including the token.\n */\n\n/** Any non-2xx from GitHub that is not one of the more specific cases below. */\nexport class GitHubError extends Error {\n readonly status: number;\n readonly path: string;\n readonly body: unknown;\n\n constructor(message: string, opts: { status: number; path: string; body: unknown }) {\n super(message);\n this.name = \"GitHubError\";\n this.status = opts.status;\n this.path = opts.path;\n this.body = opts.body;\n }\n}\n\n/**\n * The pull request cannot merge because `main` moved underneath it in a way\n * that conflicts — someone else edited the same file first.\n *\n * Observed in the probe: a stale base sha does NOT fail the trees write. Tree,\n * commit, ref and PR all succeed; the conflict only shows as\n * `mergeable_state: \"dirty\"` and then a 405 \"Pull Request has merge conflicts\"\n * on merge. So this is raised from mergePullRequest, never from writeFiles.\n * The recovery is the caller's: re-read main, rebuild the change, open a new PR.\n */\nexport class StaleBaseError extends GitHubError {\n readonly pullNumber: number;\n\n constructor(pullNumber: number, opts: { status: number; path: string; body: unknown }) {\n super(`Pull request #${pullNumber} conflicts with the base branch`, opts);\n this.name = \"StaleBaseError\";\n this.pullNumber = pullNumber;\n }\n}\n\n/**\n * The token cannot do what was asked. A 403 from GitHub with a fine-grained PAT\n * almost always means a permission was not granted when the token was made —\n * a setup bug at onboarding, not a runtime condition.\n */\nexport class TokenScopeError extends GitHubError {\n static readonly REQUIRED_PERMISSIONS =\n \"Contents: Read and write, Pull requests: Read and write, Commit statuses: Read-only\";\n\n constructor(opts: { status: number; path: string; body: unknown }) {\n super(\n `GitHub refused ${opts.path} (${opts.status}). The repo token needs these fine-grained permissions: ${TokenScopeError.REQUIRED_PERMISSIONS}`,\n opts,\n );\n this.name = \"TokenScopeError\";\n }\n}\n\n/** mergePullRequest was asked to merge while checks are red. */\nexport class ChecksFailedError extends Error {\n readonly pullNumber: number;\n readonly failingCheck: string | null;\n\n constructor(pullNumber: number, failingCheck: string | null) {\n super(\n `Pull request #${pullNumber} has failing checks${failingCheck ? ` (${failingCheck})` : \"\"}`,\n );\n this.name = \"ChecksFailedError\";\n this.pullNumber = pullNumber;\n this.failingCheck = failingCheck;\n }\n}\n","/**\n * The one place that talks to api.github.com.\n *\n * ── Every call is bounded ────────────────────────────────────────────────────\n * Same pattern as 5degrees-website's HighLevel client: a module constant plus\n * AbortSignal.timeout on every fetch. That fix exists because a slow upstream\n * ran past the serverless function limit and the process was killed before its\n * own error handler could run. A publish is ~10 sequential GitHub calls, so the\n * same failure is available here. Observed probe latency was 200–1400 ms; 10 s\n * is generous, and if GitHub is slower than that we want the error, not the wait.\n *\n * ── No module state ──────────────────────────────────────────────────────────\n * The token arrives in RepoContext on every call. Nothing is read from\n * process.env here or anywhere in src/git (BUILD-ENV law).\n */\n\nimport type { RepoContext } from \"./types\";\nimport { GitHubError, TokenScopeError } from \"./errors\";\n\nconst API = \"https://api.github.com\";\nexport const GITHUB_TIMEOUT_MS = 10_000;\n\nexport interface GitHubResponse<T = unknown> {\n ok: boolean;\n status: number;\n json: T;\n}\n\n/**\n * Low-level request. Returns instead of throwing on non-2xx so callers can\n * inspect the status when a \"failure\" is a meaningful answer (405 on merge,\n * 422 on a duplicate ref). Use `ghFetchOrThrow` when it is not.\n */\nexport async function ghFetch<T = unknown>(\n ctx: RepoContext,\n method: \"GET\" | \"POST\" | \"PATCH\" | \"PUT\" | \"DELETE\",\n path: string,\n body?: unknown,\n): Promise<GitHubResponse<T>> {\n const res = await fetch(API + path, {\n method,\n signal: AbortSignal.timeout(GITHUB_TIMEOUT_MS),\n headers: {\n Authorization: `Bearer ${ctx.token}`,\n Accept: \"application/vnd.github+json\",\n \"X-GitHub-Api-Version\": \"2022-11-28\",\n \"User-Agent\": \"realiizlabs-admin\",\n ...(body !== undefined ? { \"Content-Type\": \"application/json\" } : {}),\n },\n body: body !== undefined ? JSON.stringify(body) : undefined,\n });\n\n const text = await res.text();\n let json: unknown = null;\n if (text) {\n try {\n json = JSON.parse(text);\n } catch {\n json = { raw: text };\n }\n }\n return { ok: res.ok, status: res.status, json: json as T };\n}\n\n/** Same as ghFetch but a non-2xx becomes a typed error. */\nexport async function ghFetchOrThrow<T = unknown>(\n ctx: RepoContext,\n method: \"GET\" | \"POST\" | \"PATCH\" | \"PUT\" | \"DELETE\",\n path: string,\n body?: unknown,\n): Promise<T> {\n const res = await ghFetch<T>(ctx, method, path, body);\n if (!res.ok) throw toError(res, path);\n return res.json;\n}\n\nexport function toError(res: GitHubResponse, path: string): GitHubError {\n const opts = { status: res.status, path, body: res.json };\n if (res.status === 403) return new TokenScopeError(opts);\n const message =\n (res.json as { message?: string } | null)?.message ?? `GitHub ${res.status} on ${path}`;\n return new GitHubError(message, opts);\n}\n\n/** `/repos/{owner}/{repo}` prefix, built per call. */\nexport function repoPath(ctx: RepoContext): string {\n return `/repos/${encodeURIComponent(ctx.owner)}/${encodeURIComponent(ctx.repo)}`;\n}\n\nexport function baseBranch(ctx: RepoContext): string {\n return ctx.baseBranch ?? \"main\";\n}\n","/**\n * createBranch — a new branch off the base branch, as it is RIGHT NOW.\n *\n * The base sha is fetched from the refs API on every call and never stored.\n * This module is one of four writers to the same repo (github.com, GitHub\n * Desktop, Cowork are the others), so a ref that was current a minute ago is\n * routinely stale. Branching from a cached sha would silently build on old\n * content.\n */\n\nimport type { CreateBranchResult, RepoContext } from \"./types\";\nimport { baseBranch, ghFetchOrThrow, repoPath } from \"./client\";\n\ninterface RefResponse {\n ref: string;\n object: { sha: string; type: string };\n}\n\n/** Read the current tip of a branch. Always a live call. */\nexport async function getBranchSha(ctx: RepoContext, branch: string): Promise<string> {\n const ref = await ghFetchOrThrow<RefResponse>(\n ctx,\n \"GET\",\n `${repoPath(ctx)}/git/ref/heads/${encodeURIComponent(branch)}`,\n );\n return ref.object.sha;\n}\n\nexport async function createBranch(\n ctx: RepoContext,\n branch: string,\n): Promise<CreateBranchResult> {\n const baseSha = await getBranchSha(ctx, baseBranch(ctx));\n\n await ghFetchOrThrow<RefResponse>(ctx, \"POST\", `${repoPath(ctx)}/git/refs`, {\n ref: `refs/heads/${branch}`,\n sha: baseSha,\n });\n\n return { branch, baseSha };\n}\n\n/**\n * Remove a branch. 204 on success. Used after a squash merge so the client's\n * repo does not accumulate one dead branch per publish.\n */\nexport async function deleteBranch(ctx: RepoContext, branch: string): Promise<void> {\n await ghFetchOrThrow(\n ctx,\n \"DELETE\",\n `${repoPath(ctx)}/git/refs/heads/${encodeURIComponent(branch)}`,\n );\n}\n","/**\n * writeFiles — one or more files in ONE commit on an existing branch.\n *\n * Why the trees API and not the Contents API: a post is an MDX file plus a\n * hero image. Two Contents calls make two commits, and the first triggers a\n * build of a half-finished state. One tree, one commit.\n *\n * Encoding rule, observed in the probe: a tree entry can inline utf-8 text as\n * `content`, but has no `encoding` field — a binary must be uploaded first via\n * POST /git/blobs with `encoding: \"base64\"` and referenced in the tree by sha.\n * So a two-file publish with one image is six calls, not five.\n */\n\nimport type { FileInput, RepoContext, WriteFilesResult } from \"./types\";\nimport { getBranchSha } from \"./branch\";\nimport { ghFetchOrThrow, repoPath } from \"./client\";\n\ntype TreeEntry =\n | { path: string; mode: \"100644\"; type: \"blob\"; content: string }\n | { path: string; mode: \"100644\"; type: \"blob\"; sha: string };\n\nexport async function writeFiles(\n ctx: RepoContext,\n branch: string,\n files: FileInput[],\n message: string,\n): Promise<WriteFilesResult> {\n if (files.length === 0) throw new Error(\"writeFiles: no files given\");\n\n const base = repoPath(ctx);\n\n // The branch tip as of now — not the sha createBranch returned earlier.\n const parentSha = await getBranchSha(ctx, branch);\n\n // Binary files become blobs first; text is inlined.\n const tree: TreeEntry[] = [];\n for (const file of files) {\n if (file.encoding === \"base64\") {\n const blob = await ghFetchOrThrow<{ sha: string }>(ctx, \"POST\", `${base}/git/blobs`, {\n content: file.content,\n encoding: \"base64\",\n });\n tree.push({ path: file.path, mode: \"100644\", type: \"blob\", sha: blob.sha });\n } else {\n tree.push({ path: file.path, mode: \"100644\", type: \"blob\", content: file.content });\n }\n }\n\n const newTree = await ghFetchOrThrow<{ sha: string }>(ctx, \"POST\", `${base}/git/trees`, {\n base_tree: parentSha,\n tree,\n });\n\n const commit = await ghFetchOrThrow<{ sha: string }>(ctx, \"POST\", `${base}/git/commits`, {\n message,\n tree: newTree.sha,\n parents: [parentSha],\n });\n\n // Fast-forward the branch onto the new commit. `force: false` means a\n // concurrent write to this same branch surfaces as a 422 \"Update is not a\n // fast forward\" rather than being silently overwritten.\n await ghFetchOrThrow(\n ctx,\n \"PATCH\",\n `${base}/git/refs/heads/${encodeURIComponent(branch)}`,\n { sha: commit.sha, force: false },\n );\n\n return { branch, commitSha: commit.sha, treeSha: newTree.sha, fileCount: files.length };\n}\n","/**\n * openPullRequest — the PR that CI runs on and the client eventually publishes.\n *\n * PR Law (site-starter AGENTS.md): nothing lands on main except through a PR.\n * This module never pushes to the base branch directly.\n */\n\nimport type { PullRequest, RepoContext } from \"./types\";\nimport { baseBranch, ghFetchOrThrow, repoPath } from \"./client\";\n\n/** The subset of GitHub's PR payload this module reads. Observed in the probe. */\nexport interface PullPayload {\n number: number;\n html_url: string;\n state: \"open\" | \"closed\";\n head: { sha: string; ref: string };\n base: { sha: string; ref: string };\n /** null while GitHub is still computing — usually the first ~2 s after creation. */\n mergeable: boolean | null;\n mergeable_state: \"unknown\" | \"clean\" | \"dirty\" | \"blocked\" | \"unstable\" | \"behind\" | \"draft\" | \"has_hooks\";\n merged: boolean;\n}\n\nexport async function openPullRequest(\n ctx: RepoContext,\n branch: string,\n opts: { title: string; body?: string },\n): Promise<PullRequest> {\n const pr = await ghFetchOrThrow<PullPayload>(ctx, \"POST\", `${repoPath(ctx)}/pulls`, {\n title: opts.title,\n head: branch,\n base: baseBranch(ctx),\n body: opts.body ?? \"\",\n });\n\n return { number: pr.number, html_url: pr.html_url, headSha: pr.head.sha, branch };\n}\n\n/** Live read of a PR — the source of truth for mergeability. */\nexport async function getPullRequest(ctx: RepoContext, number: number): Promise<PullPayload> {\n return ghFetchOrThrow<PullPayload>(ctx, \"GET\", `${repoPath(ctx)}/pulls/${number}`);\n}\n","/**\n * getPullRequestStatus — green, red or pending. Nothing else.\n *\n * ── Why not the check-runs endpoint ──────────────────────────────────────────\n * A fine-grained PAT cannot read it. Observed 403 on every probe run, and the\n * token permission picker has no \"Checks\" entry to grant. So status is derived\n * from two things a PAT can always read:\n *\n * 1. the PR's `mergeable_state`, which folds in required checks when branch\n * protection is on;\n * 2. the combined commit status for the head sha, which is where Vercel and\n * any status-based CI report — and the only place a failing check has a\n * name.\n *\n * The accepted limitation: a GitHub Actions failure surfaces only as\n * `blocked`/`unstable`, not by job name. That is red without a name.\n *\n * ── Two traps, both observed ─────────────────────────────────────────────────\n * `mergeable` is null and `mergeable_state` is \"unknown\" for ~2 s after a PR is\n * created. That is pending, never red.\n *\n * The combined status of ZERO statuses has `state: \"pending\"`, not \"success\".\n * A no-CI repo read naively would be pending forever. So `total_count` is\n * checked before `state`.\n */\n\nimport type { PullRequestStatus, RepoContext } from \"./types\";\nimport { ghFetchOrThrow, repoPath } from \"./client\";\nimport { getPullRequest } from \"./pulls\";\n\n/** Observed shape of GET /commits/{sha}/status. */\nexport interface CombinedStatus {\n state: \"success\" | \"failure\" | \"pending\" | \"error\";\n total_count: number;\n statuses: Array<{ context: string; state: string; description: string | null }>;\n}\n\nexport async function getPullRequestStatus(\n ctx: RepoContext,\n number: number,\n log: Pick<Console, \"warn\"> = console,\n): Promise<PullRequestStatus> {\n const pr = await getPullRequest(ctx, number);\n\n if (pr.mergeable === null || pr.mergeable_state === \"unknown\") {\n return { state: \"pending\" };\n }\n\n const combined = await ghFetchOrThrow<CombinedStatus>(\n ctx,\n \"GET\",\n `${repoPath(ctx)}/commits/${pr.head.sha}/status`,\n );\n\n const failing = combined.statuses.find((s) => s.state === \"failure\" || s.state === \"error\");\n\n // dirty is a conflict, not a check failure — mergePullRequest turns it into\n // StaleBaseError. Report it as red here so a poller stops polling.\n if (pr.mergeable_state === \"dirty\") {\n return { state: \"red\", failingCheck: failing?.context ?? \"merge conflict\" };\n }\n\n if (failing || pr.mergeable_state === \"unstable\") {\n return { state: \"red\", failingCheck: failing?.context ?? null };\n }\n\n if (combined.total_count === 0) {\n if (pr.mergeable_state === \"clean\" || pr.mergeable_state === \"behind\") {\n const warning = `[admin/git] ${ctx.owner}/${ctx.repo} has no CI statuses on PR #${number} — treating as green. A client site with no CI is a setup bug.`;\n log.warn(warning);\n return { state: \"green\", warning };\n }\n // blocked with nothing reported yet: required checks have not started.\n return { state: \"pending\" };\n }\n\n if (combined.state === \"success\" && pr.mergeable_state !== \"blocked\") {\n return { state: \"green\" };\n }\n\n return { state: \"pending\" };\n}\n","/**\n * mergePullRequest — publish. Squash, then delete the branch.\n *\n * `requireGreen` defaults to true. The whole point of the PR flow is that CI\n * catches bad frontmatter before it freezes the site; merging red automates the\n * exact failure the flow exists to prevent.\n *\n * Pending is the common case (checks are always pending right after a push),\n * so it is returned for the caller to poll — never thrown.\n */\n\nimport type { MergeResult, RepoContext } from \"./types\";\nimport { deleteBranch } from \"./branch\";\nimport { ghFetch, repoPath, toError } from \"./client\";\nimport { ChecksFailedError, StaleBaseError } from \"./errors\";\nimport { getPullRequest } from \"./pulls\";\nimport { getPullRequestStatus } from \"./status\";\n\ninterface MergeResponse {\n sha: string;\n merged: boolean;\n message: string;\n}\n\nexport interface MergeOptions {\n /** Refuse to merge unless checks are green. Default true. */\n requireGreen?: boolean;\n /** Delete the head branch after a successful merge. Default true. */\n deleteBranch?: boolean;\n /** Squash commit title. Defaults to GitHub's (the PR title + number). */\n commitTitle?: string;\n}\n\nexport async function mergePullRequest(\n ctx: RepoContext,\n number: number,\n opts: MergeOptions = {},\n): Promise<MergeResult> {\n const requireGreen = opts.requireGreen ?? true;\n const path = `${repoPath(ctx)}/pulls/${number}/merge`;\n\n if (requireGreen) {\n const status = await getPullRequestStatus(ctx, number);\n if (status.state === \"pending\") return { merged: false, state: \"pending\" };\n if (status.state === \"red\") {\n // A conflict is not a failed check — it is a stale base. Distinguish it.\n const pr = await getPullRequest(ctx, number);\n if (pr.mergeable_state === \"dirty\") {\n throw new StaleBaseError(number, { status: 0, path, body: pr });\n }\n throw new ChecksFailedError(number, status.failingCheck);\n }\n }\n\n const res = await ghFetch<MergeResponse>(ctx, \"PUT\", path, {\n merge_method: \"squash\",\n ...(opts.commitTitle ? { commit_title: opts.commitTitle } : {}),\n });\n\n if (!res.ok) {\n // Observed: 405 { message: \"Pull Request has merge conflicts\" } for a\n // stale base. Also 405 for \"not mergeable\" generally, and 409 if the head\n // sha moved between read and merge — both are the same recovery for the\n // caller (re-read, rebuild), so both map to StaleBaseError.\n if (res.status === 405 || res.status === 409) {\n throw new StaleBaseError(number, { status: res.status, path, body: res.json });\n }\n throw toError(res, path);\n }\n\n if (opts.deleteBranch ?? true) {\n const pr = await getPullRequest(ctx, number);\n await deleteBranch(ctx, pr.head.ref);\n }\n\n return { merged: true, sha: res.json.sha };\n}\n","/**\n * Read helpers — list a folder, open a file — straight from the repo at call time.\n *\n * The dashboard lists and opens content from `main` via the Contents API, never\n * from the deployed filesystem: Vercel's bundle only contains what the tracer\n * saw, and it is a snapshot of the last deploy rather than of the repo. This\n * is also how hand-written files (github.com, Desktop, Cowork) show up — the\n * dashboard never assumes it authored anything.\n */\n\nimport type { RepoContext } from \"./types\";\nimport { baseBranch, ghFetch, repoPath, toError } from \"./client\";\nimport { GitHubError } from \"./errors\";\n\nexport interface RepoEntry {\n name: string;\n path: string;\n sha: string;\n size: number;\n type: \"file\";\n}\n\nexport interface RepoFile {\n path: string;\n sha: string;\n content: string;\n}\n\nexport class NotFoundError extends GitHubError {\n constructor(path: string) {\n super(`Not found in repo: ${path}`, { status: 404, path, body: null });\n this.name = \"NotFoundError\";\n }\n}\n\ninterface ContentsEntry {\n name: string;\n path: string;\n sha: string;\n size: number;\n type: \"file\" | \"dir\" | \"symlink\" | \"submodule\";\n content?: string;\n encoding?: string;\n}\n\nfunction contentsPath(ctx: RepoContext, path: string, ref?: string): string {\n const clean = path.replace(/^\\/+|\\/+$/g, \"\");\n const encoded = clean.split(\"/\").map(encodeURIComponent).join(\"/\");\n return `${repoPath(ctx)}/contents/${encoded}?ref=${encodeURIComponent(ref ?? baseBranch(ctx))}`;\n}\n\n/** Files directly inside `path`, sorted by name. Missing folder → []. */\nexport async function listDirectory(ctx: RepoContext, path: string, opts: { ref?: string } = {}): Promise<RepoEntry[]> {\n const url = contentsPath(ctx, path, opts.ref);\n const res = await ghFetch<ContentsEntry[] | ContentsEntry>(ctx, \"GET\", url);\n if (res.status === 404) return [];\n if (!res.ok) throw toError(res, url);\n const list = Array.isArray(res.json) ? res.json : [];\n return list\n .filter((e) => e.type === \"file\")\n .map((e) => ({ name: e.name, path: e.path, sha: e.sha, size: e.size, type: \"file\" as const }))\n .sort((a, b) => a.name.localeCompare(b.name));\n}\n\n/** One file, decoded to utf-8. */\nexport async function readFile(ctx: RepoContext, path: string, opts: { ref?: string } = {}): Promise<RepoFile> {\n const url = contentsPath(ctx, path, opts.ref);\n const res = await ghFetch<ContentsEntry>(ctx, \"GET\", url);\n if (res.status === 404) throw new NotFoundError(path);\n if (!res.ok) throw toError(res, url);\n const entry = res.json;\n if (Array.isArray(entry) || entry.type !== \"file\") throw new GitHubError(`${path} is not a file`, { status: res.status, path: url, body: entry });\n if (typeof entry.content !== \"string\") {\n // GitHub omits content for files over 1 MB; MDX never gets near that.\n throw new GitHubError(`${path} is too large to read through the Contents API`, { status: res.status, path: url, body: null });\n }\n const content = decodeBase64Utf8(entry.content);\n return { path: entry.path, sha: entry.sha, content };\n}\n\n/** The Contents API wraps base64 at 60 columns; strip whitespace before decoding. */\nfunction decodeBase64Utf8(b64: string): string {\n const clean = b64.replace(/\\s+/g, \"\");\n const bin = atob(clean);\n const bytes = Uint8Array.from(bin, (c) => c.charCodeAt(0));\n return new TextDecoder(\"utf-8\").decode(bytes);\n}\n"]}
1
+ {"version":3,"sources":["../../src/git/errors.ts","../../src/git/client.ts","../../src/git/branch.ts","../../src/git/files.ts","../../src/git/pulls.ts","../../src/git/status.ts","../../src/git/merge.ts","../../src/git/reads.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Typed errors — so ADMIN-04 can `instanceof` rather than string-match.\n *\n * Each carries enough to log usefully without ever including the token.\n */\n\n/** Any non-2xx from GitHub that is not one of the more specific cases below. */\nexport class GitHubError extends Error {\n readonly status: number;\n readonly path: string;\n readonly body: unknown;\n\n constructor(message: string, opts: { status: number; path: string; body: unknown }) {\n super(message);\n this.name = \"GitHubError\";\n this.status = opts.status;\n this.path = opts.path;\n this.body = opts.body;\n }\n}\n\n/**\n * The pull request cannot merge because `main` moved underneath it in a way\n * that conflicts — someone else edited the same file first.\n *\n * Observed in the probe: a stale base sha does NOT fail the trees write. Tree,\n * commit, ref and PR all succeed; the conflict only shows as\n * `mergeable_state: \"dirty\"` and then a 405 \"Pull Request has merge conflicts\"\n * on merge. So this is raised from mergePullRequest, never from writeFiles.\n * The recovery is the caller's: re-read main, rebuild the change, open a new PR.\n */\nexport class StaleBaseError extends GitHubError {\n readonly pullNumber: number;\n\n constructor(pullNumber: number, opts: { status: number; path: string; body: unknown }) {\n super(`Pull request #${pullNumber} conflicts with the base branch`, opts);\n this.name = \"StaleBaseError\";\n this.pullNumber = pullNumber;\n }\n}\n\n/**\n * The token cannot do what was asked. A 403 from GitHub with a fine-grained PAT\n * almost always means a permission was not granted when the token was made —\n * a setup bug at onboarding, not a runtime condition.\n */\nexport class TokenScopeError extends GitHubError {\n static readonly REQUIRED_PERMISSIONS =\n \"Contents: Read and write, Pull requests: Read and write, Commit statuses: Read-only\";\n\n constructor(opts: { status: number; path: string; body: unknown }) {\n super(\n `GitHub refused ${opts.path} (${opts.status}). The repo token needs these fine-grained permissions: ${TokenScopeError.REQUIRED_PERMISSIONS}`,\n opts,\n );\n this.name = \"TokenScopeError\";\n }\n}\n\n/** mergePullRequest was asked to merge while checks are red. */\nexport class ChecksFailedError extends Error {\n readonly pullNumber: number;\n readonly failingCheck: string | null;\n\n constructor(pullNumber: number, failingCheck: string | null) {\n super(\n `Pull request #${pullNumber} has failing checks${failingCheck ? ` (${failingCheck})` : \"\"}`,\n );\n this.name = \"ChecksFailedError\";\n this.pullNumber = pullNumber;\n this.failingCheck = failingCheck;\n }\n}\n","/**\n * The one place that talks to api.github.com.\n *\n * ── Every call is bounded ────────────────────────────────────────────────────\n * Same pattern as 5degrees-website's HighLevel client: a module constant plus\n * AbortSignal.timeout on every fetch. That fix exists because a slow upstream\n * ran past the serverless function limit and the process was killed before its\n * own error handler could run. A publish is ~10 sequential GitHub calls, so the\n * same failure is available here. Observed probe latency was 200–1400 ms; 10 s\n * is generous, and if GitHub is slower than that we want the error, not the wait.\n *\n * ── No module state ──────────────────────────────────────────────────────────\n * The token arrives in RepoContext on every call. Nothing is read from\n * process.env here or anywhere in src/git (BUILD-ENV law).\n */\n\nimport type { RepoContext } from \"./types\";\nimport { GitHubError, TokenScopeError } from \"./errors\";\n\nconst API = \"https://api.github.com\";\nexport const GITHUB_TIMEOUT_MS = 10_000;\n\nexport interface GitHubResponse<T = unknown> {\n ok: boolean;\n status: number;\n json: T;\n}\n\n/**\n * Low-level request. Returns instead of throwing on non-2xx so callers can\n * inspect the status when a \"failure\" is a meaningful answer (405 on merge,\n * 422 on a duplicate ref). Use `ghFetchOrThrow` when it is not.\n */\nexport async function ghFetch<T = unknown>(\n ctx: RepoContext,\n method: \"GET\" | \"POST\" | \"PATCH\" | \"PUT\" | \"DELETE\",\n path: string,\n body?: unknown,\n): Promise<GitHubResponse<T>> {\n const res = await fetch(API + path, {\n method,\n signal: AbortSignal.timeout(GITHUB_TIMEOUT_MS),\n headers: {\n Authorization: `Bearer ${ctx.token}`,\n Accept: \"application/vnd.github+json\",\n \"X-GitHub-Api-Version\": \"2022-11-28\",\n \"User-Agent\": \"realiizlabs-admin\",\n ...(body !== undefined ? { \"Content-Type\": \"application/json\" } : {}),\n },\n body: body !== undefined ? JSON.stringify(body) : undefined,\n });\n\n const text = await res.text();\n let json: unknown = null;\n if (text) {\n try {\n json = JSON.parse(text);\n } catch {\n json = { raw: text };\n }\n }\n return { ok: res.ok, status: res.status, json: json as T };\n}\n\n/** Same as ghFetch but a non-2xx becomes a typed error. */\nexport async function ghFetchOrThrow<T = unknown>(\n ctx: RepoContext,\n method: \"GET\" | \"POST\" | \"PATCH\" | \"PUT\" | \"DELETE\",\n path: string,\n body?: unknown,\n): Promise<T> {\n const res = await ghFetch<T>(ctx, method, path, body);\n if (!res.ok) throw toError(res, path);\n return res.json;\n}\n\nexport function toError(res: GitHubResponse, path: string): GitHubError {\n const opts = { status: res.status, path, body: res.json };\n if (res.status === 403) return new TokenScopeError(opts);\n const message =\n (res.json as { message?: string } | null)?.message ?? `GitHub ${res.status} on ${path}`;\n return new GitHubError(message, opts);\n}\n\n/** `/repos/{owner}/{repo}` prefix, built per call. */\nexport function repoPath(ctx: RepoContext): string {\n return `/repos/${encodeURIComponent(ctx.owner)}/${encodeURIComponent(ctx.repo)}`;\n}\n\nexport function baseBranch(ctx: RepoContext): string {\n return ctx.baseBranch ?? \"main\";\n}\n","/**\n * createBranch — a new branch off the base branch, as it is RIGHT NOW.\n *\n * The base sha is fetched from the refs API on every call and never stored.\n * This module is one of four writers to the same repo (github.com, GitHub\n * Desktop, Cowork are the others), so a ref that was current a minute ago is\n * routinely stale. Branching from a cached sha would silently build on old\n * content.\n */\n\nimport type { CreateBranchResult, RepoContext } from \"./types\";\nimport { baseBranch, ghFetchOrThrow, repoPath } from \"./client\";\n\ninterface RefResponse {\n ref: string;\n object: { sha: string; type: string };\n}\n\n/** Read the current tip of a branch. Always a live call. */\nexport async function getBranchSha(ctx: RepoContext, branch: string): Promise<string> {\n const ref = await ghFetchOrThrow<RefResponse>(\n ctx,\n \"GET\",\n `${repoPath(ctx)}/git/ref/heads/${encodeURIComponent(branch)}`,\n );\n return ref.object.sha;\n}\n\nexport async function createBranch(\n ctx: RepoContext,\n branch: string,\n): Promise<CreateBranchResult> {\n const baseSha = await getBranchSha(ctx, baseBranch(ctx));\n\n await ghFetchOrThrow<RefResponse>(ctx, \"POST\", `${repoPath(ctx)}/git/refs`, {\n ref: `refs/heads/${branch}`,\n sha: baseSha,\n });\n\n return { branch, baseSha };\n}\n\n/**\n * Remove a branch. 204 on success. Used after a squash merge so the client's\n * repo does not accumulate one dead branch per publish.\n */\nexport async function deleteBranch(ctx: RepoContext, branch: string): Promise<void> {\n await ghFetchOrThrow(\n ctx,\n \"DELETE\",\n `${repoPath(ctx)}/git/refs/heads/${encodeURIComponent(branch)}`,\n );\n}\n","/**\n * writeFiles — one or more files in ONE commit on an existing branch.\n *\n * Why the trees API and not the Contents API: a post is an MDX file plus a\n * hero image. Two Contents calls make two commits, and the first triggers a\n * build of a half-finished state. One tree, one commit.\n *\n * Encoding rule, observed in the probe: a tree entry can inline utf-8 text as\n * `content`, but has no `encoding` field — a binary must be uploaded first via\n * POST /git/blobs with `encoding: \"base64\"` and referenced in the tree by sha.\n * So a two-file publish with one image is six calls, not five.\n *\n * `opts.remove` deletes paths in the same commit (tree entry with `sha: null`) —\n * how the dashboard takes a post down: one PR that removes the file.\n */\n\nimport type { FileInput, RepoContext, WriteFilesResult } from \"./types\";\nimport { getBranchSha } from \"./branch\";\nimport { ghFetchOrThrow, repoPath } from \"./client\";\n\ntype TreeEntry =\n | { path: string; mode: \"100644\"; type: \"blob\"; content: string }\n | { path: string; mode: \"100644\"; type: \"blob\"; sha: string }\n /* sha: null in a tree entry deletes the path — GitHub's documented way to remove a file via the trees API. */\n | { path: string; mode: \"100644\"; type: \"blob\"; sha: null };\n\nexport async function writeFiles(\n ctx: RepoContext,\n branch: string,\n files: FileInput[],\n message: string,\n opts: { remove?: string[] } = {},\n): Promise<WriteFilesResult> {\n const remove = opts.remove ?? [];\n if (files.length === 0 && remove.length === 0) throw new Error(\"writeFiles: no files given\");\n\n const base = repoPath(ctx);\n\n // The branch tip as of now — not the sha createBranch returned earlier.\n const parentSha = await getBranchSha(ctx, branch);\n\n // Binary files become blobs first; text is inlined.\n const tree: TreeEntry[] = [];\n for (const file of files) {\n if (file.encoding === \"base64\") {\n const blob = await ghFetchOrThrow<{ sha: string }>(ctx, \"POST\", `${base}/git/blobs`, {\n content: file.content,\n encoding: \"base64\",\n });\n tree.push({ path: file.path, mode: \"100644\", type: \"blob\", sha: blob.sha });\n } else {\n tree.push({ path: file.path, mode: \"100644\", type: \"blob\", content: file.content });\n }\n }\n\n for (const path of remove) tree.push({ path, mode: \"100644\", type: \"blob\", sha: null });\n\n const newTree = await ghFetchOrThrow<{ sha: string }>(ctx, \"POST\", `${base}/git/trees`, {\n base_tree: parentSha,\n tree,\n });\n\n const commit = await ghFetchOrThrow<{ sha: string }>(ctx, \"POST\", `${base}/git/commits`, {\n message,\n tree: newTree.sha,\n parents: [parentSha],\n });\n\n // Fast-forward the branch onto the new commit. `force: false` means a\n // concurrent write to this same branch surfaces as a 422 \"Update is not a\n // fast forward\" rather than being silently overwritten.\n await ghFetchOrThrow(\n ctx,\n \"PATCH\",\n `${base}/git/refs/heads/${encodeURIComponent(branch)}`,\n { sha: commit.sha, force: false },\n );\n\n return { branch, commitSha: commit.sha, treeSha: newTree.sha, fileCount: files.length + remove.length };\n}\n","/**\n * openPullRequest — the PR that CI runs on and the client eventually publishes.\n *\n * PR Law (site-starter AGENTS.md): nothing lands on main except through a PR.\n * This module never pushes to the base branch directly.\n */\n\nimport type { PullRequest, RepoContext } from \"./types\";\nimport { baseBranch, ghFetchOrThrow, repoPath } from \"./client\";\n\n/** The subset of GitHub's PR payload this module reads. Observed in the probe. */\nexport interface PullPayload {\n number: number;\n title: string;\n html_url: string;\n state: \"open\" | \"closed\";\n head: { sha: string; ref: string };\n base: { sha: string; ref: string };\n /** null while GitHub is still computing — usually the first ~2 s after creation. */\n mergeable: boolean | null;\n mergeable_state: \"unknown\" | \"clean\" | \"dirty\" | \"blocked\" | \"unstable\" | \"behind\" | \"draft\" | \"has_hooks\";\n merged: boolean;\n}\n\nexport async function openPullRequest(\n ctx: RepoContext,\n branch: string,\n opts: { title: string; body?: string },\n): Promise<PullRequest> {\n const pr = await ghFetchOrThrow<PullPayload>(ctx, \"POST\", `${repoPath(ctx)}/pulls`, {\n title: opts.title,\n head: branch,\n base: baseBranch(ctx),\n body: opts.body ?? \"\",\n });\n\n return { number: pr.number, html_url: pr.html_url, headSha: pr.head.sha, branch };\n}\n\n/** Live read of a PR — the source of truth for mergeability. */\nexport async function getPullRequest(ctx: RepoContext, number: number): Promise<PullPayload> {\n return ghFetchOrThrow<PullPayload>(ctx, \"GET\", `${repoPath(ctx)}/pulls/${number}`);\n}\n\nexport interface OpenPullRequest extends PullRequest {\n title: string;\n}\n\n/**\n * Open PRs whose head branch starts with `headPrefix` — the dashboard's own\n * in-flight changes (04b names them `admin/{type}/{file}-{stamp}`). One page of\n * 100 is far more than a content site ever has open at once.\n */\nexport async function listOpenPullRequests(ctx: RepoContext, opts: { headPrefix: string }): Promise<OpenPullRequest[]> {\n const prs = await ghFetchOrThrow<PullPayload[]>(ctx, \"GET\", `${repoPath(ctx)}/pulls?state=open&per_page=100`);\n return prs\n .filter((p) => p.head.ref.startsWith(opts.headPrefix))\n .map((p) => ({ number: p.number, title: p.title, html_url: p.html_url, headSha: p.head.sha, branch: p.head.ref }));\n}\n\n/**\n * Close a PR without merging — the client changed their mind. The branch is\n * the caller's to delete (deleteBranch) once the close succeeds; closing first\n * means a failed delete leaves a closed PR, never an open one pointing at nothing.\n */\nexport async function closePullRequest(ctx: RepoContext, number: number): Promise<void> {\n await ghFetchOrThrow<PullPayload>(ctx, \"PATCH\", `${repoPath(ctx)}/pulls/${number}`, { state: \"closed\" });\n}\n","/**\n * getPullRequestStatus — green, red or pending. Nothing else.\n *\n * ── Why not the check-runs endpoint ──────────────────────────────────────────\n * A fine-grained PAT cannot read it. Observed 403 on every probe run, and the\n * token permission picker has no \"Checks\" entry to grant. So status is derived\n * from two things a PAT can always read:\n *\n * 1. the PR's `mergeable_state`, which folds in required checks when branch\n * protection is on;\n * 2. the combined commit status for the head sha, which is where Vercel and\n * any status-based CI report — and the only place a failing check has a\n * name.\n *\n * The accepted limitation: a GitHub Actions failure surfaces only as\n * `blocked`/`unstable`, not by job name. That is red without a name.\n *\n * ── Two traps, both observed ─────────────────────────────────────────────────\n * `mergeable` is null and `mergeable_state` is \"unknown\" for ~2 s after a PR is\n * created. That is pending, never red.\n *\n * The combined status of ZERO statuses has `state: \"pending\"`, not \"success\".\n * A no-CI repo read naively would be pending forever. So `total_count` is\n * checked before `state`.\n */\n\nimport type { PullRequestStatus, RepoContext } from \"./types\";\nimport { ghFetchOrThrow, repoPath } from \"./client\";\nimport { getPullRequest } from \"./pulls\";\n\n/** Observed shape of GET /commits/{sha}/status. */\nexport interface CombinedStatus {\n state: \"success\" | \"failure\" | \"pending\" | \"error\";\n total_count: number;\n statuses: Array<{ context: string; state: string; description: string | null; target_url?: string | null }>;\n}\n\nexport async function getPullRequestStatus(\n ctx: RepoContext,\n number: number,\n log: Pick<Console, \"warn\"> = console,\n): Promise<PullRequestStatus> {\n const pr = await getPullRequest(ctx, number);\n\n if (pr.mergeable === null || pr.mergeable_state === \"unknown\") {\n return { state: \"pending\" };\n }\n\n const combined = await ghFetchOrThrow<CombinedStatus>(\n ctx,\n \"GET\",\n `${repoPath(ctx)}/commits/${pr.head.sha}/status`,\n );\n\n const failing = combined.statuses.find((s) => s.state === \"failure\" || s.state === \"error\");\n\n // dirty is a conflict, not a check failure — mergePullRequest turns it into\n // StaleBaseError. Report it as red here so a poller stops polling.\n if (pr.mergeable_state === \"dirty\") {\n return { state: \"red\", failingCheck: failing?.context ?? \"merge conflict\" };\n }\n\n if (failing || pr.mergeable_state === \"unstable\") {\n return { state: \"red\", failingCheck: failing?.context ?? null };\n }\n\n if (combined.total_count === 0) {\n if (pr.mergeable_state === \"clean\" || pr.mergeable_state === \"behind\") {\n const warning = `[admin/git] ${ctx.owner}/${ctx.repo} has no CI statuses on PR #${number} — treating as green. A client site with no CI is a setup bug.`;\n log.warn(warning);\n return { state: \"green\", warning };\n }\n // blocked with nothing reported yet: required checks have not started.\n return { state: \"pending\" };\n }\n\n if (combined.state === \"success\" && pr.mergeable_state !== \"blocked\") {\n // A successful deploy-preview status carries the preview's URL — the client can look before publishing.\n const preview = combined.statuses.find((s) => s.state === \"success\" && /vercel|netlify|deploy|preview/i.test(s.context) && s.target_url);\n return preview?.target_url ? { state: \"green\", previewUrl: preview.target_url } : { state: \"green\" };\n }\n\n return { state: \"pending\" };\n}\n","/**\n * mergePullRequest — publish. Squash, then delete the branch.\n *\n * `requireGreen` defaults to true. The whole point of the PR flow is that CI\n * catches bad frontmatter before it freezes the site; merging red automates the\n * exact failure the flow exists to prevent.\n *\n * Pending is the common case (checks are always pending right after a push),\n * so it is returned for the caller to poll — never thrown.\n */\n\nimport type { MergeResult, RepoContext } from \"./types\";\nimport { deleteBranch } from \"./branch\";\nimport { ghFetch, repoPath, toError } from \"./client\";\nimport { ChecksFailedError, StaleBaseError } from \"./errors\";\nimport { getPullRequest } from \"./pulls\";\nimport { getPullRequestStatus } from \"./status\";\n\ninterface MergeResponse {\n sha: string;\n merged: boolean;\n message: string;\n}\n\nexport interface MergeOptions {\n /** Refuse to merge unless checks are green. Default true. */\n requireGreen?: boolean;\n /** Delete the head branch after a successful merge. Default true. */\n deleteBranch?: boolean;\n /** Squash commit title. Defaults to GitHub's (the PR title + number). */\n commitTitle?: string;\n}\n\nexport async function mergePullRequest(\n ctx: RepoContext,\n number: number,\n opts: MergeOptions = {},\n): Promise<MergeResult> {\n const requireGreen = opts.requireGreen ?? true;\n const path = `${repoPath(ctx)}/pulls/${number}/merge`;\n\n if (requireGreen) {\n const status = await getPullRequestStatus(ctx, number);\n if (status.state === \"pending\") return { merged: false, state: \"pending\" };\n if (status.state === \"red\") {\n // A conflict is not a failed check — it is a stale base. Distinguish it.\n const pr = await getPullRequest(ctx, number);\n if (pr.mergeable_state === \"dirty\") {\n throw new StaleBaseError(number, { status: 0, path, body: pr });\n }\n throw new ChecksFailedError(number, status.failingCheck);\n }\n }\n\n const res = await ghFetch<MergeResponse>(ctx, \"PUT\", path, {\n merge_method: \"squash\",\n ...(opts.commitTitle ? { commit_title: opts.commitTitle } : {}),\n });\n\n if (!res.ok) {\n // Observed: 405 { message: \"Pull Request has merge conflicts\" } for a\n // stale base. Also 405 for \"not mergeable\" generally, and 409 if the head\n // sha moved between read and merge — both are the same recovery for the\n // caller (re-read, rebuild), so both map to StaleBaseError.\n if (res.status === 405 || res.status === 409) {\n throw new StaleBaseError(number, { status: res.status, path, body: res.json });\n }\n throw toError(res, path);\n }\n\n if (opts.deleteBranch ?? true) {\n const pr = await getPullRequest(ctx, number);\n await deleteBranch(ctx, pr.head.ref);\n }\n\n return { merged: true, sha: res.json.sha };\n}\n","/**\n * Read helpers — list a folder, open a file — straight from the repo at call time.\n *\n * The dashboard lists and opens content from `main` via the Contents API, never\n * from the deployed filesystem: Vercel's bundle only contains what the tracer\n * saw, and it is a snapshot of the last deploy rather than of the repo. This\n * is also how hand-written files (github.com, Desktop, Cowork) show up — the\n * dashboard never assumes it authored anything.\n */\n\nimport type { RepoContext } from \"./types\";\nimport { baseBranch, ghFetch, repoPath, toError } from \"./client\";\nimport { GitHubError } from \"./errors\";\n\nexport interface RepoEntry {\n name: string;\n path: string;\n sha: string;\n size: number;\n type: \"file\";\n}\n\nexport interface RepoFile {\n path: string;\n sha: string;\n content: string;\n}\n\nexport class NotFoundError extends GitHubError {\n constructor(path: string) {\n super(`Not found in repo: ${path}`, { status: 404, path, body: null });\n this.name = \"NotFoundError\";\n }\n}\n\ninterface ContentsEntry {\n name: string;\n path: string;\n sha: string;\n size: number;\n type: \"file\" | \"dir\" | \"symlink\" | \"submodule\";\n content?: string;\n encoding?: string;\n}\n\nfunction contentsPath(ctx: RepoContext, path: string, ref?: string): string {\n const clean = path.replace(/^\\/+|\\/+$/g, \"\");\n const encoded = clean.split(\"/\").map(encodeURIComponent).join(\"/\");\n return `${repoPath(ctx)}/contents/${encoded}?ref=${encodeURIComponent(ref ?? baseBranch(ctx))}`;\n}\n\n/** Files directly inside `path`, sorted by name. Missing folder → []. */\nexport async function listDirectory(ctx: RepoContext, path: string, opts: { ref?: string } = {}): Promise<RepoEntry[]> {\n const url = contentsPath(ctx, path, opts.ref);\n const res = await ghFetch<ContentsEntry[] | ContentsEntry>(ctx, \"GET\", url);\n if (res.status === 404) return [];\n if (!res.ok) throw toError(res, url);\n const list = Array.isArray(res.json) ? res.json : [];\n return list\n .filter((e) => e.type === \"file\")\n .map((e) => ({ name: e.name, path: e.path, sha: e.sha, size: e.size, type: \"file\" as const }))\n .sort((a, b) => a.name.localeCompare(b.name));\n}\n\n/** One file, decoded to utf-8. */\nexport async function readFile(ctx: RepoContext, path: string, opts: { ref?: string } = {}): Promise<RepoFile> {\n const url = contentsPath(ctx, path, opts.ref);\n const res = await ghFetch<ContentsEntry>(ctx, \"GET\", url);\n if (res.status === 404) throw new NotFoundError(path);\n if (!res.ok) throw toError(res, url);\n const entry = res.json;\n if (Array.isArray(entry) || entry.type !== \"file\") throw new GitHubError(`${path} is not a file`, { status: res.status, path: url, body: entry });\n if (typeof entry.content !== \"string\") {\n // GitHub omits content for files over 1 MB; MDX never gets near that.\n throw new GitHubError(`${path} is too large to read through the Contents API`, { status: res.status, path: url, body: null });\n }\n const content = decodeBase64Utf8(entry.content);\n return { path: entry.path, sha: entry.sha, content };\n}\n\n/** The Contents API wraps base64 at 60 columns; strip whitespace before decoding. */\nfunction decodeBase64Utf8(b64: string): string {\n const clean = b64.replace(/\\s+/g, \"\");\n const bin = atob(clean);\n const bytes = Uint8Array.from(bin, (c) => c.charCodeAt(0));\n return new TextDecoder(\"utf-8\").decode(bytes);\n}\n"]}