/** * Managed gitconfig writer — runner-managed credential helper config for * Tier-2 mount credentials. * * The runner exposes `/.skaile/managed-gitconfig` to the agent * process via `GIT_CONFIG_GLOBAL` (with a fallback `[include]` in * `~/.gitconfig`). Each Tier-2 mount writes a tagged block into this file * pointing `credential..helper` at the shared `git-credentials` * store, plus a single line into the credentials store carrying the actual * token. Refresh = atomically rewrite the credential line; the helper picks * up the new token on the next git network op. * * Helpers are keyed by the full sanitized repository URL, not its owner. * Collision checks canonicalize repository aliases so one repository still * has only one credential-exposing mount. * * Spec: `_devlog/specs/2026-05-05-git-credential-tiers.md`. */ /** * Thrown when two mounts claim credential scopes for the same canonical * repository identity. One mount must own the exposed credential. */ export declare class ManagedGitconfigCollisionError extends Error { readonly urlPrefix: string; readonly existingMountId: string; readonly newMountId: string; constructor(urlPrefix: string, existingMountId: string, newMountId: string); } /** Required arguments for {@link writeMountBlock}. */ export interface WriteMountBlockOptions { /** Mount declaration id; used as the block tag. */ mountId: string; /** * Sanitized repository URL the credential should authenticate, e.g. * `https://github.com/org/repository.git`. Trailing slashes are preserved * verbatim (git treats them as different scopes). */ urlPrefix: string; /** * The token to expose. Written to {@link credentialsPath} as * `https://x-access-token:@` for the helper to read. */ token: string; /** Absolute path to the managed-gitconfig file. */ gitconfigPath: string; /** Absolute path to the git-credentials store. */ credentialsPath: string; /** * Optional: when set, the credential block points `helper = !sh ` at * this script instead of the static `store --file=` form. The script is * the wake-mid-401 helper rendered by `renderCredentialHelperScript` — * stats the credentials file's mtime, touches a refresh-flag if the cached * token is stale, and translates the `store --file=` line format into the * helper-output format git expects. * * Spec: `_devlog/specs/2026-05-07-unified-credential-mediation.md` Step 9a. */ helperScriptPath?: string; } /** Required arguments for {@link writeUnavailableMountBlock}. */ export interface WriteUnavailableMountBlockOptions { /** Mount declaration id; used as the block tag. */ mountId: string; /** Repository URL scope the failed mount would have authenticated. */ urlPrefix: string; /** Absolute path to the managed-gitconfig file. */ gitconfigPath: string; /** Absolute path to the git-credentials store. */ credentialsPath: string; /** * Absolute path to the rendered "unavailable" helper script — one built with * `renderCredentialHelperScript({ unavailableReason })`. */ helperScriptPath: string; } /** Required arguments for {@link removeUnavailableMountBlock}. */ export interface RemoveUnavailableMountBlockOptions { /** Mount declaration id whose unavailable block should be removed. */ mountId: string; /** Absolute path to the managed-gitconfig file. */ gitconfigPath: string; /** * Also remove unavailable aliases for this repository. Git matches helpers * by URL, not mount id, so a stale alias must not answer another mount. */ urlPrefix?: string; } /** Required arguments for {@link removeMountBlock}. */ export interface RemoveMountBlockOptions { /** Mount declaration id whose block should be removed. */ mountId: string; /** Absolute path to the managed-gitconfig file. */ gitconfigPath: string; /** Absolute path to the git-credentials store. */ credentialsPath: string; } /** Required arguments for {@link atomicReplaceCredential}. */ export interface AtomicReplaceCredentialOptions { /** Mount id (used to find the matching credential line). */ mountId: string; /** Repository URL scope the credential is keyed by. Same value passed to writeMountBlock. */ urlPrefix: string; /** New token to install. */ token: string; /** Absolute path to the git-credentials store. */ credentialsPath: string; } /** * Write a credential-helper block for `mountId` into the managed-gitconfig * file, and (re)write the matching line in the git-credentials store. * Idempotent: re-running with the same arguments is a no-op aside from * refreshing the on-disk token. Detects collisions where another mount * already owns the same `urlPrefix` and throws * {@link ManagedGitconfigCollisionError}. * * Files are created with mode `0640` — owner (skaile-runner) read+write, * group (skaile) read-only. See {@link writeCredentialLines} for the * UID-separation rationale. */ export declare function writeMountBlock(opts: WriteMountBlockOptions): void; /** * Register a credential helper for a mount that **failed to connect**. * * Without a block, git finds no helper for the host at all and dies with * `fatal: could not read Username for ''` — an error that names neither * the mount nor the reason, and that arrives only on writes, since reads * against a public remote keep succeeding. The block written here points at an * "unavailable" helper script (rendered with `unavailableReason`) so git stops * with the actual failure text instead. * * Also drops any credential line still tagged for `mountId`: the mount failed, * so a line left over from an earlier session holds a dead token, and serving * it would turn an explainable failure back into an opaque 401. * * A block is written only when no *available* mount already owns `urlPrefix` — * a healthy mount is never shadowed by a dead one. */ export declare function writeUnavailableMountBlock(opts: WriteUnavailableMountBlockOptions): void; /** * Remove unavailable blocks — the one tagged for `mountId`, and (when * `urlPrefix` is given) any that declare that prefix under another id. * * Called on a successful connect so a stale block from a previous failed * session cannot make a now-healthy mount report itself as disconnected, and so * a sibling mount that failed earlier in the same `connectAll` cannot answer * this one's network operations. The marker check is what makes it safe to call * unconditionally, including for Tier-1 mounts that write no block of their * own: an *available* block is never touched. Missing file / missing block / * only normal blocks are all no-ops. */ export declare function removeUnavailableMountBlock(opts: RemoveUnavailableMountBlockOptions): void; /** * Remove the block tagged for `mountId` from the managed-gitconfig file and * the matching credential line. Best-effort: missing files / missing block * are not errors. */ export declare function removeMountBlock(opts: RemoveMountBlockOptions): void; /** * Write (or refresh) the credential-helper script at `scriptPath` with mode * `0755` so git can execute it via `helper = !sh `. Idempotent — * re-rendering with identical opts produces the same script body, so re-runs * are safe. Atomic rename so a concurrent git invocation never sees a * half-written file. * * Spec: `_devlog/specs/2026-05-07-unified-credential-mediation.md` Step 9a. */ export declare function writeHelperScript(scriptPath: string, body: string): void; /** * Pre-create the helper's refresh-attempt stamp so the agent uid can truncate * it later. * * The helper script stamps this file itself to rate-limit its refresh dance, * but it runs as the agent uid while `.skaile/` is owned by skaile-runner — so * the agent can only ever truncate a file that already exists and is * group-writable. Mode 0660 (owner+group rw) mirrors what the runner does for * the refresh-request flag for exactly the same reason. * * Never truncates an existing stamp: doing so would reset a live cooldown on * every reconnect. Best-effort — a caller on the connect-failure path must not * be derailed by this, and the helper survives a missing stamp anyway (it just * dances on every git operation instead of once per cooldown window). */ export declare function ensureRefreshAttemptStamp(stampPath: string): void; /** * Replace just the credential line for `mountId` atomically — refresh path. * The gitconfig block (which only points at the helper) does not need to * change; the helper re-reads the credentials file on every git network op. * * Upserts: inserts the line when `mountId` has none yet. An emptied or missing * credentials file must be able to self-heal from the refresh path — refusing * to insert would strand interactive git until an external reconnect. * Other mounts' lines are left untouched. */ export declare function atomicReplaceCredential(opts: AtomicReplaceCredentialOptions): void; //# sourceMappingURL=managed-gitconfig.d.ts.map