/** The remote URL if configured (explicit option wins over CANON_REPO env), else null. */ export declare function resolveCanonRepo(explicit?: string): string | null; /** * Resolve the remote for an auto-clone, or throw a clear error. Call ONLY when a * clone is actually needed (no store at `storePath`) — an existing store never * needs this; its `origin` is the truth. */ export declare function requireCanonRepo(explicit: string | undefined, storePath: string, label?: string): string; /** * Strip any embedded credential from a git URL before it is logged. Turns * `https://x-access-token:@github.com/owner/repo` into * `https://***@github.com/owner/repo`. Defense-in-depth: the token should not be * in the URL at all (it rides in GH_TOKEN + http.extraheader), but any URL that * still carries one must never reach a log line, PTY, or captured transcript. */ export declare function redactRepoUrl(url: string): string; /** * Per-invocation git auth: the token rides in env (GH_TOKEN/GITHUB_TOKEN) and is * applied via http.extraheader — never embedded in the clone URL, .git/config, * argv, or a log line. Empty when no token is set (public repo / ambient creds). */ export declare function gitAuthArgs(env?: NodeJS.ProcessEnv): string[]; /** * Per-invocation commit identity, so a container with no global git identity * (hosted sandbox: `root@cloudchamber.(none)`) can still commit — fixes the * 'Author identity unknown' failure that hit every commit site EXCEPT canon-sync * (2026-08-13: translate + graph both leaked it to the hosted PTY). */ export declare function gitIdArgs(env?: NodeJS.ProcessEnv): string[]; /** * THE git exec for canon verbs. Every invocation carries auth + identity, and — * critically — pipes stdio: an unpiped child inherits the process's fd 2, so its * stderr bypasses the CANON_LOG_FILE console swap (canonSyncScheduler.runQuietly) * and corrupts the interactive TUI. On failure the redacted last stderr line is * surfaced via console.error (routed to the log file in hosted mode) and the * error rethrown for the caller's own handling. * * `cwd` null = repo-less commands (clone). Use this — never a bare execFileSync. */ export declare function canonGit(cwd: string | null, label: string): (args: string[]) => string; /** * Guarded stage-everything: `git add -A` + refuse to proceed when the staged * set contains a MASS DELETION. Canon verbs append and update; none has a * legitimate reason to delete more than a handful of paths in one pass — a * large staged-deletion set means the working tree is a partial clone/checkout * (the 2026-08-18 + 2026-08-20 incidents: a /tmp-reaped store was re-cloned / * operated on mid-checkout, `git add -A` staged every missing file as deleted, * and the commit message still read "N file(s) updated"). Every canon commit * path (sync/translate/graph/artifacts) MUST stage through this helper. * Returns true when it is safe to commit; false = staged set was reset, * nothing may be committed this pass. */ export declare function guardedAddAll(git: (args: string[]) => string, label: string, maxDeletes?: number): boolean; /** * Multi-writer-safe push: push main; on rejection (a concurrent writer — * another machine, a bench-worker fleet, watcher+cron overlap — landed * first) `pull --rebase` and retry. Canon writers are append-only over * mostly-disjoint files, so rebase is the correct recovery; a REAL * conflict aborts the rebase and returns false — never force, never leave * the clone mid-rebase (the next cycle must find a clean, usable tree; * the local commit is preserved and re-tried then). Every canon push path * (sync/translate/graph/artifacts) routes through this. */ export declare function guardedPush(git: (args: string[]) => string, label: string, maxAttempts?: number): boolean; /** * ARCHIVE EXCLUSION (2026-09-26) — the local-footprint half of the hot/archive split (canonArchive.ts). * The remote keeps everything; a LOCAL store must never materialize `archive/` (1.6 GB+ and growing on the * ~5 GB /tmp per-user quota). Implemented as a NON-cone sparse checkout (`/*` + `!archive/`), which is how * it is told apart from a SCOPED sync-only clone (cone mode) — see isScopedStore. * Found 09-26: every re-clone (atomicClone full checkout) re-downloaded archive/ because nothing re-applied * the exclusion, and full-surface verbs would have refused the store once it was applied. */ export declare const ARCHIVE_EXCLUSION_PATTERNS: string[]; /** True when HEAD's tree has a top-level `archive/` directory. */ export declare function headHasArchive(storePath: string): boolean; /** True when the archive exclusion is already in effect (non-cone sparse with `!archive/`). */ export declare function hasArchiveExclusion(storePath: string): boolean; /** * Apply the archive exclusion to a FULL-SURFACE store when HEAD has `archive/` (idempotent; no-op on scoped * cone stores, which already materialize only their legs). `sparse-checkout set` removes the now-excluded * tracked files from the worktree itself (they are up to date from a real checkout). Returns true when it * changed the store. */ export declare function ensureArchiveExclusion(storePath: string, label: string): boolean; /** * Atomic clone: clone into a sibling temp dir, then rename into place. The * store path NEVER contains a `.git` over a partially-checked-out tree, so a * concurrent canon verb either sees no store (and clones/waits itself) or a * COMPLETE one — the mid-checkout race that fed both mass-deletion incidents * is structurally closed. Cleans up the temp dir on failure. */ export declare function atomicClone(repoUrl: string, storePath: string, label: string, scope?: string[]): void; /** True when the store is a CONE-scoped (sparse) clone — a SYNC-ONLY store. The non-cone archive exclusion is not scoped. */ export declare function isScopedStore(storePath: string): boolean; /** Widen a scoped store's cone (idempotent no-op for already-included dirs). */ export declare function sparseAdd(storePath: string, dirs: string[], label: string): void; /** Full-surface verbs (translate/graph/artifacts) must never run on a scoped * store — they would read a PARTIAL native surface and derive a wrong * canonical line/graph from it. Fail fast with the remedy. */ export declare function requireFullSurfaceStore(storePath: string, label: string): void; /** * ONE WRITER PER STORE (2026-09-26). The cron's legs (sync/translate/graph/archive, one process each, flock'd only against * other crons) and the canon watcher's pipeline both commit to the same working clone. Interleaved, they fight over the * index (`index.lock` failures in the watcher log) and one's staging leaks into the other's commit — the browser fold-in * re-added 19 archived sessions past the sync guard that way. Every writer takes this lock first. The lock is a sibling * file (`.writer.lock`) so it works before the first clone; a dead pid or a 2-hour-old lock is stale and taken over; * a writer that cannot get it within `waitMs` (default 10 min, env CANON_LOCK_WAIT_MS) returns `onBusy()` — the next cycle * retries. Sequential calls in one process (the pipeline) each take and release it. */ export declare function withStoreLock(store: string, label: string, fn: () => Promise, onBusy: () => T, waitMs?: number): Promise; //# sourceMappingURL=canonRepo.d.ts.map