/**
 * Secret bundles — named sets of environment variables backed by a secret store.
 * Metadata lives under `agents-cli.bundles.<name>`; values under
 * `agents-cli.secrets.<bundle>.<key>`. Backends: `keychain` (default),
 * `file` (headless/passphrase), and `vault` (age-encrypted, user-synced).
 */
import { type BundleValue, type SecretRef } from './index.js';
/** Which store carries a bundle's items. */
export type SecretsBackend = 'keychain' | 'file' | 'vault';
/** Disable the broker-only guard for in-memory keychain tests. */
export declare function setKeychainAgentOnlyBypassForTest(bypass: boolean): void;
/**
 * Discover a bundle's backend by location. File store is checked first (a plain
 * existence test, no passphrase); absent/locked vault falls back to keychain.
 */
export declare function bundleBackend(name: string): SecretsBackend;
/** Allowed values for a secret's `type` metadata field. */
export declare const SECRET_TYPES: readonly ["api-key", "token", "password", "url", "database-url", "ssh-key", "certificate", "webhook", "note"];
export type SecretType = typeof SECRET_TYPES[number];
/** Per-secret metadata; absent fields are omitted at write time. */
export interface VarMeta {
    type?: SecretType;
    /** Future-dated ISO date ('YYYY-MM-DD'). */
    expires?: string;
    note?: string;
}
/**
 * Bundle prompt policy. `hold` (default): one Touch ID per hold window (~7d),
 * then silent via the secrets-agent. `always`: prompt every read. `never`: no
 * biometry ACL — least-safe, automation-only. Configurable via `secrets.policy`;
 * persisted under the legacy `tier` key for cross-version sync.
 */
export type SecretsPolicy = 'always' | 'hold' | 'never';
/** A named set of environment variable definitions backed by secret stores. */
export interface SecretsBundle {
    name: string;
    description?: string;
    allow_exec?: boolean;
    /** Absent ⇒ `keychain`. */
    backend?: SecretsBackend;
    /** Absent ⇒ configured default (`hold`). */
    policy?: SecretsPolicy;
    created_at?: string;
    updated_at?: string;
    last_used?: string;
    vars: Record<string, BundleValue>;
    meta?: Record<string, VarMeta>;
}
export interface LegacyBundleCandidate {
    name: string;
    file: string;
    keys: string[];
}
export declare const BUNDLE_NAME_PATTERN: RegExp;
export declare const ENV_KEY_PATTERN: RegExp;
export declare const BUNDLE_KEY_PATTERN: RegExp;
export declare const BUNDLE_META_PREFIX = "agents-cli.bundles.";
export declare const RESERVED_ENV_NAMES: Set<string>;
/**
 * Reserved FILE-BACKED bundle for long-lived setup tokens. Must be file-backed;
 * a keychain/vault `auth` bundle is a misconfiguration and is ignored.
 */
export declare const AUTH_BUNDLE_NAME = "auth";
export declare const AUTH_BUNDLE_BACKEND: SecretsBackend;
export declare const RESERVED_BUNDLE_NAMES: Set<string>;
export declare function isReservedBundleName(name: string): boolean;
/** Thrown when a reserved bundle is written or resolved on the wrong backend. */
export declare class ReservedBundleWrongBackendError extends Error {
    readonly bundle: string;
    readonly backend: SecretsBackend;
    constructor(bundle: string, backend: SecretsBackend);
}
/** Fail loud when a reserved bundle is on the wrong backend. */
export declare function assertReservedBundleBackend(name: string, backend: SecretsBackend): void;
/**
 * Check the reserved `auth` bundle. `ok` is true when absent or file-backed.
 *
 * Read-only status probe (`agents doctor`, `agents fleet apply`) — unlike the
 * destructive-write guards `bundleExists()`/`hasKeychainToken()` document
 * themselves as failing loud for, a diagnostic MUST NOT crash the whole
 * command because ONE optional finding could not reach the keychain (e.g. the
 * macOS Keychain helper source is unavailable — PHNX-3385). Treat an
 * unreachable keychain the same as "bundle absent": nothing to warn about.
 */
export declare function inspectReservedAuthBundle(): {
    exists: boolean;
    backend: SecretsBackend | null;
    ok: boolean;
};
/**
 * After a file-backed import, verify the keys actually decrypt. Import used to
 * report success for ciphertext sealed under a forwarded passphrase this process
 * does not hold.
 */
export declare function assertFileBundleDecryptable(name: string, keys: string[]): void;
export declare function bundleToEnvPrefix(name: string): string;
export declare function isReservedEnvName(key: string): boolean;
export declare function bundleKeyToEnvKey(key: string): string;
export declare function isLoaderOrInterpreterEnv(name: string): boolean;
export declare function sanitizeProcessEnv(env?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
/** Validate a bundle name against the allowed pattern. Throws on invalid input. */
export declare function validateBundleName(name: string): void;
export declare function validateEnvKey(key: string): void;
/** Assert that `t` is one of the known SECRET_TYPES. Throws with the allowed list otherwise. */
export declare function validateSecretType(t: string): asserts t is SecretType;
/**
 * Validate a future `expires` date. Accepts strict 'YYYY-MM-DD'; end-of-day UTC
 * means "today" is past.
 */
export declare function validateExpiresFutureDated(iso: string): void;
export declare function bundleExists(name: string): boolean;
/**
 * Thrown for file-store ciphertext that will not decrypt (lost/rotated key or
 * tampered store). Narrow: temporarily-locked bundles are recoverable and must
 * not be collapsed here.
 */
export declare class BundleUndecryptableError extends Error {
    constructor(message: string);
}
/**
 * Read a bundle, returning null only when metadata is present but permanently
 * unreadable (`BundleUndecryptableError`). Other failures — including temporary
 * lockout — rethrow so a healthy bundle is never deleted by mistake.
 */
export declare function readBundleIfDecryptable(name: string): SecretsBundle | null;
export declare function readBundle(name: string): SecretsBundle;
/** Default policy for bundles without an explicit one (`secrets.policy`). */
export declare function secretsDefaultPolicy(): SecretsPolicy;
/** Effective prompt policy of a bundle. */
export declare function bundlePolicy(bundle: SecretsBundle): SecretsPolicy;
/** Options for writeBundle. */
export interface WriteBundleOptions {
    /**
     * Skip evicting the broker-held copy. Only for no-op writers such as
     * stampLastUsed; mutating writes must evict so stale values aren't served.
     */
    skipBrokerEviction?: boolean;
}
/**
 * Whether a bundle write should evict the broker-held copy. Exported for tests.
 * Skips when the writer opted out, the broker is disabled, or a test backend is
 * active (so tests don't evict the user's real unlocks).
 */
export declare function shouldEvictAfterBundleWrite(skipRequested: boolean, noAgentEnv: string | undefined, backendOverridden: boolean): boolean;
export declare function writeBundle(bundle: SecretsBundle, opts?: WriteBundleOptions): void;
export declare function writeBundleWithItems(bundle: SecretsBundle, items: Map<string, string>, opts?: WriteBundleOptions): void;
export declare function deleteBundle(name: string): boolean;
declare function readBundleMetaIndex(): string[] | null;
declare function writeBundleMetaIndex(services: string[]): void;
declare function addBundleToMetaIndex(name: string): void;
declare function removeBundleFromMetaIndex(name: string): void;
/** Test-only accessors for the metadata-name index. Never called in production. */
export declare const __metaIndexForTest: {
    read: typeof readBundleMetaIndex;
    write: typeof writeBundleMetaIndex;
    add: typeof addBundleToMetaIndex;
    remove: typeof removeBundleFromMetaIndex;
};
/**
 * Re-write keychain metadata items without the biometry ACL. The caller supplies
 * the JSON already read, so contents are preserved and no extra read is issued.
 * Exported for tests.
 */
export declare function healKeychainBundleMetadata(metaJsonByName: Map<string, string>): number;
/**
 * One-time driver for healKeychainBundleMetadata. macOS + real keychain only;
 * gated by a sentinel. Best-effort: a heal failure never breaks listing.
 */
export declare function healKeychainBundleMetadataAclOnce(metaJsonByName: Map<string, string>): void;
export declare function listBundles(): SecretsBundle[];
export interface BundleEntryInfo {
    key: string;
    kind: 'literal' | 'keychain' | 'env' | 'file' | 'exec';
    detail: string;
}
export declare function describeBundle(bundle: SecretsBundle): BundleEntryInfo[];
export declare function stampLastUsed(bundle: SecretsBundle): void;
/** Options for resolveBundleEnv. */
export interface ResolveBundleOptions {
    caller?: string;
    /** Harness type whose unlock may be reused. */
    agent?: string;
    /** Duration shown in the Touch ID prompt. */
    duration?: string;
    /** Allow this call to raise an interactive biometric prompt. */
    interactiveUnlock?: boolean;
    /** Skip the broker fast-path and read from the keychain directly. */
    noAgent?: boolean;
    /** Resolve only from an already-unlocked broker snapshot; fail before prompting. */
    agentOnly?: boolean;
    /** Inject only these keys. Errors if any requested key is absent. */
    keys?: string[];
    /** Skip the per-key expiry gate. */
    allowExpired?: boolean;
    /** `process` projects dotted keys to shell-safe env names; `storage` preserves them. */
    keyMode?: 'process' | 'storage';
}
export declare function canCacheResolvedEnv(bundle: SecretsBundle, selectedKeys: Set<string>, keyMode: ResolveBundleOptions['keyMode']): boolean;
/**
 * Apply --keys and --allow-expired to a broker snapshot so the fast path
 * mirrors the slow path's gates. Exported for tests.
 */
export declare function filterAgentHitBySubsetAndExpiry(hit: {
    bundle: SecretsBundle;
    env: Record<string, string>;
}, opts: ResolveBundleOptions): {
    bundle: SecretsBundle;
    env: Record<string, string>;
};
/**
 * Fail loud when remote bundle resolution is asked for flags the SSH resolver
 * does not yet thread. Exported so callers share the same error text.
 */
export declare function assertRemoteBundleFlagsUnsupported(bundleName: string, host: string, opts: {
    keys?: string[];
    allowExpired?: boolean;
}, flagLabels: {
    keysFlag: string;
    allowExpiredFlag: string;
}): void;
export declare function resolveBundleEnv(bundle: SecretsBundle, _opts?: ResolveBundleOptions): Record<string, string>;
/**
 * True when the current process is a background / non-interactive context that
 * must NEVER raise a Keychain biometry prompt on the interactive user's screen.
 * Re-exported from ./headless.js — the detector lives there so the raw-read
 * path in index.ts can share it without a bundles↔index import cycle. See that
 * module for the full contract.
 */
export { isHeadlessSecretsContext, isAgentInvocationContext } from './headless.js';
/**
 * Read a bundle's metadata AND resolve its env in a single Touch ID prompt.
 * `readBundle` + `resolveBundleEnv` used to issue two LAContext calls (two
 * prompts). This collapses them into one batch that includes the metadata item.
 */
export declare function readAndResolveBundleEnv(name: string, opts?: ResolveBundleOptions): {
    bundle: SecretsBundle;
    env: Record<string, string>;
};
export declare function humanUnlockDuration(ms: number): string;
export declare function keychainRef(key: string): string;
/** Options for rotateBundleSecret. */
export interface RotateOptions {
    /** New plaintext value to write into keychain (replaces the old one). */
    newValue: string;
    /** When true, drop existing meta for this key. Mutually exclusive with `meta`. */
    clearMeta?: boolean;
    /** Patch to merge into existing meta. Undefined fields preserve current values. */
    meta?: Partial<VarMeta>;
}
/**
 * Rotate a keychain-backed secret. Errors if the key is absent; preserves meta
 * unless cleared or patched.
 */
export declare function rotateBundleSecret(bundle: SecretsBundle, key: string, opts: RotateOptions): void;
/**
 * Reconcile keychain value items to the bundle's current policy. macOS gates
 * reads on each item's ACL, not the bundle's declared policy, so a policy change
 * alone would leave stale ACLs. hold/always → never strips ACL; never → *
 * re-attaches it. Non-keychain backends no-op.
 */
export declare function reAclBundleItems(bundle: SecretsBundle): void;
/** Options for renameBundle. */
export interface RenameOptions {
    /** Overwrite an existing destination bundle. */
    force?: boolean;
}
/**
 * Rename a bundle: copy metadata + keychain values to the new name, then delete
 * the source. Steps are ordered so a copy-phase failure leaves the source intact.
 */
export declare function renameBundle(oldName: string, newName: string, opts?: RenameOptions): void;
/**
 * The item store (keychain or encrypted file) for a bundle's per-key secrets.
 * Pass the resolved backend (`bundle.backend ?? 'keychain'`).
 */
export declare function bundleItemStore(backend: SecretsBackend | undefined, opts?: {
    noAcl?: boolean;
}): {
    set(item: string, value: string): void;
    delete(item: string): boolean;
    get(item: string): string;
    has(item: string): boolean;
};
export declare function keychainItemsForBundle(bundle: SecretsBundle): Array<{
    key: string;
    item: string;
}>;
export declare function parseDotenv(content: string): Record<string, string>;
export declare function migrateLegacyBundles(confirmBundle: (candidate: LegacyBundleCandidate) => boolean | Promise<boolean>): Promise<number>;
export type { SecretRef };
