/**
 * PlaywrightProfileRegistry — the durable per-agent map of which Playwright
 * browser PROFILE holds which logged-in ACCOUNT, plus the compact boot-awareness
 * surface and the shared playwright-MCP-config resolver.
 *
 * Spec: docs/specs/playwright-profile-registry.md (converged + approved 2026-06-15).
 *
 * Three honesty disciplines are load-bearing (see spec Frontloaded Decisions):
 *   - STALENESS (D11): a login claim renders its age and is advisory, never authority.
 *   - PROVENANCE (D12/D20): every account carries an owner (agent|operator) and every
 *     write is auditable; the operator's account is flagged loud in the block.
 *   - FAIL-TOWARD-TRUTH (D15/D17): refs are re-checked on read, a dead profile dir is
 *     surfaced, a corrupt file is never silently overwritten.
 *
 * SECURITY INVARIANTS:
 *   - D3 — NO secret VALUE is ever stored, returned, injected, or resolved. Only vault
 *     secret NAMES. This module NEVER calls SecretStore.read() to obtain a value — the
 *     injected `listVaultNames` returns NAMES only.
 *   - D9 — a supplied userDataDir is path-jailed: resolved, absolute, confined under the
 *     agent home, never flag-shaped, no NUL.
 *   - D16 — every field rendered into the boot block passes through sanitizeForBlock so
 *     an envelope breakout is structurally impossible.
 *
 * NOTE: request-driven; no background loop → NO GUARD_MANIFEST entry. The state file is
 * machine-local BY DESIGN (D6) — a browser session lives in cookies on one disk.
 */
/** How the account is authenticated in the browser profile. */
export type PlaywrightLoginMethod = 'session-cookie' | 'password' | 'password+totp' | 'password+phone-2fa' | 'oauth-token' | 'unknown';
/** Who owns the account — Know Your Principal (D12). Advisory self-assertion, audited. */
export type PlaywrightAccountOwner = 'agent' | 'operator';
/** One account a profile is responsible for. Credentials are referenced by vault NAME only. */
export interface PlaywrightAccount {
    /** Service the account belongs to (e.g. 'github', 'google'). Clamped 64, sanitized on render. */
    service: string;
    /** The account login/handle (e.g. 'EchoOfDawn'). Clamped 128, sanitized on render. */
    identity: string;
    /** 'agent' | 'operator' — REQUIRED, rendered loud in the block (D12). */
    owner: PlaywrightAccountOwner;
    /** Vault secret NAMES only — NEVER values (D3). */
    vaultRefs: string[];
    /** Login method enum. */
    loginMethod: PlaywrightLoginMethod;
    /** Last-KNOWN session state — advisory, NOT a guarantee (D11). */
    lastAsserted: boolean;
    /** ISO timestamp or null; the block renders its AGE, never a bare "logged-in" (D11). */
    lastVerifiedAt: string | null;
    /** Free note. Clamped 256, sanitized on render. */
    note: string;
}
/** One browser profile (a physical user-data-dir on this machine's disk). */
export interface PlaywrightProfile {
    /** ^[a-z0-9-]{1,64}$, unique. */
    id: string;
    /** null = Playwright MCP's built-in default location; else an ABSOLUTE path jailed to the agent home (D2/D9). */
    userDataDir: string | null;
    /** Clamped 256, sanitized on render. */
    description: string;
    isDefault: boolean;
    createdAt: string;
    accounts: PlaywrightAccount[];
}
interface RegistryFile {
    version: 1;
    profiles: PlaywrightProfile[];
}
/** Result of locating the canonical playwright MCP server entry. */
export interface ResolvedPlaywrightMcpConfig {
    /** Absolute path to the file the entry lives in. */
    file: string;
    /** The raw mcpServers.playwright entry object. */
    entry: Record<string, unknown>;
    /** The resolved --user-data-dir value, or null if the arg is absent (the common case — D10). */
    userDataDir: string | null;
}
/** A resolve() outcome. */
export interface PlaywrightResolveResult {
    profile: PlaywrightProfile | null;
    /** True when a service-only fallback matched more than one profile (D18). */
    ambiguous?: boolean;
    candidates?: Array<{
        id: string;
        identities: string[];
    }>;
    /** Is the resolved profile's userDataDir physically present on this machine? null userDataDir → built-in present. */
    dirExists?: boolean;
}
/** A listProfiles() row — the FULL detail surface (GET-only), with dangling-ref flags (D17). */
export interface PlaywrightProfileDetail extends PlaywrightProfile {
    dirExists: boolean;
    accounts: Array<PlaywrightAccount & {
        danglingRefs: string[];
    }>;
}
/** The computed (not-yet-applied) activation mutation for a profile (D10). */
export interface PlaywrightActivationPlan {
    profileId: string;
    /** The authoritative config file the MCP loads. */
    file: string;
    /** The args array AFTER the mutation (what writeActivation would persist). */
    nextArgs: string[];
    /** Target userDataDir (null = default profile → arg removed). */
    userDataDir: string | null;
    /** Is the target dir physically present? (null userDataDir → built-in present.) */
    dirExists: boolean;
    /** True when the canonical config already carries the target (D19 already-active fast path). */
    alreadyActive: boolean;
}
/** A single audit record the CALLER appends to logs/playwright-profiles.jsonl (D20). */
export interface PlaywrightAuditRecord {
    ts: string;
    action: string;
    profileId: string;
    detail: Record<string, unknown>;
}
export interface PlaywrightProfileRegistryOptions {
    /** The agent's .instar state dir (holds the registry file under state/). */
    stateDir: string;
    /** The agent home (project dir) — the userDataDir jail root (D9). */
    projectDir: string;
    /**
     * Returns the live vault secret NAMES (never values). Throws / returns null when the
     * vault is unreadable (absent / decrypt-failed) so ref-validation can fail CLOSED (D17).
     * Injected so this module NEVER touches a secret value (D3) and tests stay hermetic.
     */
    listVaultNames: () => string[] | null;
    /** Optional override for os.hostname() in the block header (tests). */
    hostname?: string;
}
/** Thrown when the registry file exists but is unparseable — writes fail CLOSED (D15). */
export declare class PlaywrightRegistryCorruptError extends Error {
    constructor(message: string);
}
/** Thrown for caller-input validation failures (the route maps these to 400/409/422). */
export declare class PlaywrightRegistryError extends Error {
    readonly status: number;
    constructor(message: string, status?: number);
}
export declare const MAX_PROFILES = 25;
export declare const MAX_ACCOUNTS_PER_PROFILE = 25;
export declare const MAX_DESCRIPTION_CHARS = 256;
export declare const MAX_SERVICE_CHARS = 64;
export declare const MAX_IDENTITY_CHARS = 128;
export declare const MAX_NOTE_CHARS = 256;
export declare const DEFAULT_BLOCK_MAX_BYTES = 800;
export declare class PlaywrightProfileRegistry {
    private readonly stateDir;
    private readonly projectDir;
    private readonly listVaultNames;
    private readonly hostname;
    constructor(opts: PlaywrightProfileRegistryOptions);
    /** Absolute path to the registry file. */
    filePath(): string;
    /**
     * Locate the canonical playwright MCP server entry, checking `.claude/settings.json`
     * `mcpServers.playwright` FIRST (authoritative — init/migrator seed it there), then
     * `.mcp.json` `mcpServers.playwright`. Returns the file + entry + resolved
     * --user-data-dir (null when absent — the common case, D10), or null when no playwright
     * server is configured anywhere. Used by BOTH seed and activate so they cannot drift.
     */
    resolvePlaywrightMcpConfig(): ResolvedPlaywrightMcpConfig | null;
    /**
     * Read + parse the registry file. Absent → seed exactly ONE default profile
     * (metadata-only; NEVER writes MCP config — D10 seeding). Corrupt → throw
     * PlaywrightRegistryCorruptError (the WRITE path fails CLOSED; the boot block
     * swallows it — D15).
     */
    private read;
    /** Compute the in-memory seed (ONE default profile). Does NOT persist or touch MCP config. */
    private seedSkeleton;
    /** Ensure the file exists on disk with the seeded default (idempotent). Returns the seeded store. */
    ensureSeeded(): RegistryFile;
    private write;
    /**
     * Read-version → apply → write-if-unchanged → retry. Mirrors CommitmentTracker.mutate's
     * optimistic CAS, but file-backed: the "version" is the on-disk file's (mtimeMs, size)
     * captured BEFORE the read; if it changed by write time, retry. A corrupt file throws
     * (PlaywrightRegistryCorruptError) — the write path NEVER auto-overwrites (D15).
     *
     * `fn` receives a deep clone of the store and returns the next store plus a result.
     */
    mutate<T>(fn: (store: RegistryFile) => {
        next: RegistryFile;
        result: T;
    }): T;
    /** The FULL detail surface (GET-only): every account, vault NAMES, dangling-ref flags (D17). */
    listProfiles(): PlaywrightProfileDetail[];
    /**
     * The selector (D18). Precedence: exact (service,identity) → else service-only.
     * Service-only matching >1 profile → { profile:null, ambiguous:true, candidates }.
     * No match → { profile:null }. dirExists reports whether the profile's userDataDir is
     * physically present (null userDataDir → built-in present).
     */
    resolve(service: string, identity?: string): PlaywrightResolveResult;
    createProfile(input: {
        id: string;
        description?: string;
        userDataDir?: string | null;
    }): PlaywrightProfile;
    assignAccount(profileId: string, input: {
        service: string;
        identity: string;
        owner: PlaywrightAccountOwner;
        vaultRefs?: string[];
        loginMethod?: PlaywrightLoginMethod;
        note?: string;
    }): PlaywrightAccount;
    patchAccount(profileId: string, service: string, identity: string, patch: {
        lastAsserted?: boolean;
        lastVerifiedAt?: string | null;
        note?: string;
    }): PlaywrightAccount;
    deleteProfile(profileId: string): void;
    deleteAccount(profileId: string, service: string, identity: string): void;
    /**
     * Compute the intended .mcp.json/.settings.json args mutation WITHOUT writing.
     * INSERT `--user-data-dir <dir>` as two array elements when absent; REPLACE the value
     * (handling the joined `--user-data-dir=<x>` form) when present; for the default profile
     * (null userDataDir) REMOVE the arg. alreadyActive=true when the target is already set.
     */
    computeActivation(profileId: string): PlaywrightActivationPlan;
    /**
     * Persist the activation plan into the authoritative MCP config file (the CALLER gates
     * this behind dryRun + loop-guard + refresh). Writes mcpServers.playwright.args = nextArgs.
     * Returns the file written.
     */
    writeActivation(plan: PlaywrightActivationPlan): {
        file: string;
    };
    /**
     * Compact <playwright-profiles> boot pointer ≤maxBytes. ONE line per profile carrying
     * only the safety-critical signals (service/identity, owner marker, login-staleness).
     * Stable order (default first, then createdAt). Account-line truncation with a counted
     * '…(+N)' marker (marker bytes count vs budget). Every rendered field passes through
     * sanitizeForBlock (D16). NO vault values, NO vaultRefs (those are GET-only). Fail-open:
     * a corrupt/unreadable file → empty (never blocks boot — D15/D22).
     */
    buildSessionContextBlock(maxBytes?: number, opts?: {
        full?: boolean;
    }): {
        present: boolean;
        block: string;
    };
    /** Render one profile's compact boot line. Sanitizes every field. */
    private renderProfileLine;
    /** Best-effort vault names for READ paths — swallows the unreadable case to null. */
    private safeVaultNames;
    /** fs.existsSync on a userDataDir; null userDataDir → built-in profile present (true). */
    private dirExists;
    /** Auto-allocate <projectDir>/.instar/state/playwright-profiles/<id>/ (recorded only). */
    private autoAllocateDir;
    /**
     * Path-jail a caller-supplied userDataDir (D9): path.resolve'd, absolute, confined under
     * projectDir (agent home), not flag-shaped (`-` prefix), no NUL. Else throw 400.
     */
    private jailUserDataDir;
}
/** Stored-field sanitizer: strip control/ANSI, clamp length (NOT for block render — that uses sanitizeForBlock on top). */
export declare function sanitizeStored(input: string, maxChars: number): string;
/** Render a login-staleness note from lastVerifiedAt: 'seen Nd ago' / 'seen today' / 'unverified'. */
export declare function renderStaleness(lastVerifiedAt: string | null): string;
/** Extract the --user-data-dir value from an args array (two-element form OR joined `=` form). null if absent. */
export declare function extractUserDataDir(args: unknown): string | null;
/**
 * Return a NEW args array with --user-data-dir set to `target` (two separate elements),
 * replacing any existing value/joined form. When target is null, REMOVE the arg entirely.
 */
export declare function applyUserDataDirArg(args: string[], target: string | null): string[];
export {};
//# sourceMappingURL=PlaywrightProfileRegistry.d.ts.map