/** * Guards 1 + 2 of `qfg push`, as a pure function. * * See `project/plans/cli-git-sync.md` — "Safety model (three independent guards)". * * The caller collects three identity signals about the local directory the user * is about to push: * * A. Requested target — slug or UUID the user asked for (--workspace or profile default) * B. Repo pin — `workspace` slug from `quonfig.json`, if present * C. Remote URLs — `git remote -v` URLs on the local repo, if any * * ...and the canonical `backend` identity (the workspace the gitea.token * response was minted for). This module cross-checks them and returns one of: * * - ok all defined signals agree * - abort any defined signal disagrees * - requires-typed-slug-confirmation the repo is unpinned and has no * remotes, so only the requested * target supports our identity — * we demand the user type the slug * * Multi-remote support (qfg-glrd.3): customers often use GitHub for PR review * (origin = github.com/their-org/configs) and a secondary remote for Quonfig. * As long as ANY configured remote matches the backend's repo URL, the remote * signal is `match`. Only when every configured remote points elsewhere do we * abort — and the abort details list every remote that was considered. * * The module is intentionally pure: no I/O, no prompting, no UI. The UI layer * (confirm prompts, diff summary) and the data-fetch layer (reading quonfig.json, * shelling out to git, calling gitea.token) live elsewhere. */ export interface IdentityCheckInput { /** The canonical workspace identity from the backend (gitea.token response). */ backend: { workspaceSlug: string; workspaceId: string; repoUrl: string; }; /** * "C. Remote URLs" — every configured `git remote` URL on the local repo. * Empty array means "no remotes configured" (either not a git repo, or a * fresh repo with no remotes). Length ≥ 1 means at least one remote must * match the backend's repo URL. */ remoteUrls: string[]; /** "B. Repo pin" — slug from `quonfig.json.workspace`, if present. */ repoPinSlug: string | undefined; /** "A. Requested target" — what the user asked for. Slug or UUID. */ requestedTarget: string; } export type IdentityCheckOutcome = { kind: 'ok'; canonicalSlug: string; } | { kind: 'abort'; reason: string; details: Record; } | { kind: 'requires-typed-slug-confirmation'; canonicalSlug: string; reason: string; }; export declare function checkIdentity(input: IdentityCheckInput): IdentityCheckOutcome; /** * Walk a list of configured git remote URLs and return the first one that * normalizes to the same repo as `backendRepoUrl`. Returns `undefined` if no * configured remote matches. * * Useful for callers that have already resolved the backend and want to know * *which* remote authenticated against it (e.g. `qfg pull` updating the URL * to embed a fresh token). */ export declare function findQuonfigRemote(remotes: string[], backendRepoUrl: string): string | undefined; export type OriginGuardResult = { kind: 'ok'; } | { kind: 'no-quonfig-remote'; } | { kind: 'not-origin'; matching: string; }; /** * Decide — WITHOUT mutating git — whether a working dir's remotes are safe to * point at `backendRepoUrl`. Pure, so it unit-tests cleanly. Mirrors the * inline guard `qfg pull` already runs (qfg-glrd.3); `qfg sync` uses it so it * stops blindly rewriting `origin` to the active workspace and corrupting a * dir pinned to a different one (qfg-08i). * * - No remotes configured -> 'ok' (fresh dir; caller may set origin). * - A remote matches & is origin -> 'ok'. * - A remote matches but isn't origin -> 'not-origin' (refuse; don't clobber). * - Remotes exist, none match -> 'no-quonfig-remote' (refuse). */ export declare function evaluateOriginGuard(allRemotes: string[], originUrl: string | null, backendRepoUrl: string): OriginGuardResult;