import type { BrowserProfileConfig } from '../types.js';
import type { BrowserProfile, ProfileName } from './types.js';
export type { BrowserProfile } from './types.js';
export { declaringDevices, profileKind, profileRegistry, type ProfileDeclaration, } from './registry.js';
/**
 * Name of the profile the setup wizards pin as this machine's default browser
 * (`agents setup`, `agents setup browser`). Older builds also auto-created it
 * silently on the first `agents browser start`; PHNX-3296 removed that — see
 * {@link ensureDefaultBrowserProfile}.
 *
 * It is `auto-chrome`, NOT `default`, since RUSH-2709: `default` used to be
 * both this concrete profile AND the alias meaning "whatever profile the user
 * configured", so `--profile default` landed on a literal Chrome on one command
 * and on the user's configured Comet on another. The alias now lives alone in
 * {@link DEFAULT_PROFILE_ALIAS} and resolves in exactly one place
 * ({@link resolveProfileRef}).
 */
export declare const DEFAULT_BROWSER_PROFILE_NAME = "auto-chrome";
/**
 * What older builds named the auto-detected profile. Still honored everywhere:
 * a user whose machine already carries a `default` profile keeps using it (and
 * its running browser + runtime dirs) rather than having a second one created
 * beside it.
 */
export declare const LEGACY_DEFAULT_BROWSER_PROFILE_NAME = "default";
/**
 * The reserved ALIAS. `--profile default` (and a `browser.profile` config value
 * of `default`) means "the configured default profile", not a profile called
 * `default` — unless a profile literally named `default` exists, which still
 * wins, so no existing config breaks.
 */
export declare const DEFAULT_PROFILE_ALIAS = "default";
export type BrowserProfileWithDeclarations = BrowserProfile & {
    devices: string[];
};
/**
 * The device-local configured default profile name (set via
 * `agents browser use`), or undefined when unset. When set, it
 * is the profile `agents browser start` resolves to for BOTH the no-`--profile`
 * path and an explicit `--profile default`. Stored as this machine's
 * `browser.profile` device-config key (the per-device doc's `config:` block —
 * see lib/device-config.ts).
 */
export declare function getConfiguredDefaultProfileName(): string | undefined;
export declare function getBrowserRuntimeDir(): string;
export declare function getProfileRuntimeDir(name: string): string;
/**
 * True when a profile attaches to a browser the user already launched and MUST
 * NOT spawn its own (PHNX-3967). Arc is inherently attach-only — it is
 * single-instance and relaunching it just produces a stray window, never a
 * second debuggable process (issue #2779) — regardless of an explicit policy.
 * Any other browser is attach-only only when the profile says so.
 */
export declare function isAttachOnlyProfile(profile: Pick<BrowserProfile, 'browser' | 'launchPolicy'>): boolean;
/**
 * The DURABLE `--user-data-dir` for a profile (PHNX-3967). An explicit
 * `profile.userDataDir` wins; otherwise a per-profile dir under
 * `~/.agents/.history/browser-profiles/<name>/chrome-data`, which is outside
 * `~/.agents/.cache` so a one-time sign-in survives quit+relaunch and the
 * `profiles remove` cache sweep. This is the dir the canonical Comet is launched
 * with and the value the ownership guard compares a running instance against.
 *
 * Note this is distinct from {@link getProfileRuntimeDir}'s cache `chrome-data`,
 * which `launchBrowser` uses for a `launch`-policy profile it spawns itself.
 */
export declare function resolveProfileDataDir(profile: Pick<BrowserProfile, 'name' | 'userDataDir'>): string;
/**
 * Normalize a `--user-data-dir` path for the attach-only ownership comparison
 * (PHNX-3967): the real path when it resolves on this box, else the literal with
 * trailing slashes stripped. The single source both the runtime guard
 * (`drivers/local.ts` `verifyEndpointOwnership`) and `browser profiles doctor`
 * use, so they can never disagree about whether a running instance is foreign.
 */
export declare function normalizeDataDir(dir: string): string;
/**
 * Default destination for browser downloads for a profile. Set browser-global at
 * connect time (see BrowserService), so downloads land here even when the agent
 * never calls `browser download --path`. Keyed by the same composite name as the
 * runtime dir, so every profile is one self-contained tree.
 */
export declare function getProfileDownloadsDir(name: string): string;
/**
 * Per-profile directory for a task's captured artifacts (screenshots, PDFs,
 * recordings). Replaces the legacy global `browser/sessions/<task>/` root — the
 * one-shot migration in migrate.ts folds old captures into here.
 */
export declare function getProfileSessionsDir(name: string, task: string): string;
export declare function listProfiles(): Promise<BrowserProfileWithDeclarations[]>;
export declare function getProfile(name: string): Promise<BrowserProfileWithDeclarations | null>;
/**
 * True when `profile` can actually launch or attach on THIS machine.
 *
 * A remote (`ssh://`) profile runs its browser on the far host, so a local
 * binary check doesn't apply — assume launchable. A local profile must have its
 * configured browser/binary present on disk here; a `default` profile
 * auto-created on a different OS (e.g. a macOS Chrome path reused on Linux — the
 * "Custom binary not found" failure) fails this check so the caller re-detects
 * for the current machine.
 */
export declare function isProfileLaunchableHere(profile: BrowserProfile): boolean;
/**
 * The auto-detected profile this machine already carries, under either name —
 * the current `auto-chrome` or the `default` an older build wrote. Callers that
 * ask "is a default profile set up here?" MUST use this rather than probing one
 * name, or a machine that predates the RUSH-2709 rename grows a second one.
 */
export declare function getAutoDetectedProfile(): Promise<BrowserProfile | null>;
/**
 * Resolve what a caller typed after `--profile` to a real, BARE profile name.
 * Every command — stop, status, navigate, tab, use, and (through
 * {@link resolveProfileRefForStart}) start — routes through this one function,
 * which is the whole point: before RUSH-2709 only `start` honored the reserved
 * `default` alias, so `--profile default` meant three different profiles across
 * three commands.
 *
 * Order:
 *   1. A profile that literally bears the given name always wins — including
 *      one a user genuinely named `default`, which resolves to itself.
 *   2. `default` (the alias) or no argument at all → the configured
 *      `browser.profile` for this machine, if it names a profile that exists.
 *   3. → the auto-detected profile ({@link DEFAULT_BROWSER_PROFILE_NAME}), or
 *      its legacy `default`-named predecessor when that is what is on disk.
 *   4. Otherwise the input is returned unchanged (so the caller reports its own
 *      `Profile "x" not found`), or `undefined` when nothing was passed.
 *
 * It never creates a profile, never warns, and never checks whether the profile
 * can launch HERE. That is deliberate: for most callers `--profile` is a FILTER
 * (`status`, `tasks`, `navigate --task`), where an absent flag means "no filter"
 * and rewriting or warning about the user's config would be wrong. Note the
 * corollary at those call sites — pass a ref only when the user gave one; do not
 * resolve `undefined` into a concrete profile, or an unscoped listing silently
 * becomes a scoped one.
 */
export declare function resolveProfileRef(ref?: string): Promise<string | undefined>;
/**
 * `start`'s resolver. Same order as {@link resolveProfileRef} for an explicit
 * name, but the IMPLICIT path (no `--profile`, or the bare `default` alias with
 * no profile of that name) goes through {@link ensureDefaultBrowserProfile} —
 * which additionally verifies the resolved default can launch on THIS machine.
 * An undeclared configured default is an error. A declared default whose
 * browser isn't installed here warns and falls through to an existing profile,
 * else the actionable throw below.
 *
 * `start` is the only command that launches a browser, so it is the only one
 * that may do those things; routing a filter-only command through this would
 * warn about config the user never asked it to touch.
 *
 * Throws ({@link noDefaultBrowserError}) when the configured default is
 * undeclared, or when no profile exists that can launch here.
 */
export declare function resolveProfileRefForStart(ref?: string): Promise<string>;
/**
 * The error a bare `agents browser start` raises when this machine has no
 * launchable default browser. Its own function so the wording — the one thing
 * the user reads when a browser won't start — stays in one place and is
 * testable without spawning anything.
 *
 * Since PHNX-3296 this is a hard stop, NOT a silent auto-create. The old
 * behavior probed the installed Chromium-family browsers and minted a
 * logged-out `auto-chrome` profile on the spot; agents then drove a signed-out
 * Chrome that popped up on the user's Mac unbidden. Which browser agents drive
 * is a choice the user makes once, in `agents setup` — never one this code
 * makes for them.
 */
export declare function noDefaultBrowserError(): Error;
/**
 * Resolve the profile a bare `agents browser start` uses.
 *
 * Order: (1) the device-local configured default (`agents browser use <name>`)
 * when it names a profile that exists and can launch here; (2) an existing
 * auto-detected profile (`auto-chrome`, or a legacy `default`) that can launch
 * here. When neither resolves, THROW ({@link noDefaultBrowserError}) rather than
 * detect-and-create — see that function for why (PHNX-3296).
 *
 * Two failure modes at the configured-default step are not the same:
 *   - No device declares the name (including a leftover central `browser:`
 *     entry that was never claimed) → throw. Falling back to a minted profile
 *     would hand the agent a logged-out browser while `browser.profile` still
 *     names the credentialed one.
 *   - The name is declared, but its browser/binary is not installed HERE →
 *     warn and fall through to an existing profile, else the actionable throw.
 *     That is a missing binary on this box, not a missing identity.
 *
 * This RECOGNIZES a pre-existing `auto-chrome`/legacy `default` so installs that
 * already carry one keep resolving it (and its running browser + runtime dirs),
 * but it never CREATES one.
 */
export declare function ensureDefaultBrowserProfile(): Promise<BrowserProfile>;
/**
 * Compute the LOCAL port a profile will occupy at runtime:
 *   - `cdp://127.0.0.1:N` → N (we listen on N directly)
 *   - `ssh://host?port=N` → N (the SSH tunnel binds local N → remote N now)
 *   - `ws[s]://`, `http[s]://` → undefined (we don't claim a local port)
 *
 * This is what callers should compare to detect collisions; the (host,
 * port) tuple is no longer enough because SSH profiles do compete with
 * cdp:// profiles for local ports under the new tunnel scheme.
 */
export declare function effectiveLocalPort(profile: BrowserProfile): number | undefined;
/**
 * Find a port in 9222–9399 that is not already claimed by ANY existing
 * profile (cdp:// or ssh://) and is not in use by any OS process. The
 * SSH change to bind locally on `?port=N` means we no longer get to
 * skip remote profiles in this scan.
 */
export declare function findFreeProfilePort(): Promise<number>;
export declare function hasSshEndpoint(endpoints: BrowserProfileConfig['endpoints']): boolean;
/**
 * Whether the automatic central-tombstone drain (PHNX-3315) may claim `config`
 * into THIS device's doc during `agents sync`.
 *
 * Only REMOTE (`ssh://`) profiles qualify. An `ssh://` endpoint names a specific
 * host, so the profile is fungible by design — any box resolves it to the same
 * browser, and a concurrent double-claim across machines is harmless. A
 * local/`cdp://` profile has NO per-machine ownership signal: "that browser is
 * installed here" is not "I hold this profile's credentialed session", so two
 * boxes with the same common browser installed would each auto-claim the same
 * tombstone on their first post-merge sync and flip it identity->fungible
 * fleet-wide (the exact logged-out-browser failure this module exists to prevent
 * — PHNX-3315 review). Local/cdp tombstones stay central for the explicit
 * `agents browser profiles claim`.
 */
export declare function shouldAutoClaimCentralProfile(config: BrowserProfileConfig): boolean;
/**
 * Refuse a profile whose LOCAL port another profile already owns.
 *
 * Every CDP/SSH profile ends up listening on (or tunneling to) the same LOCAL
 * port number as the one configured in the endpoint URL — SSH profiles reuse
 * `?port=N` locally, so we no longer scope by host. Two profiles that would
 * need the same local port can't both run at the same time.
 *
 * `opts.ignore` is the profile's own name on an edit. Without it every edit
 * collides with itself, since the stored copy still owns the port being kept.
 */
export declare function assertLocalPortFree(profile: BrowserProfile, opts?: {
    ignore?: string;
}): void;
/** Declare a profile on this device. The same name may be declared by peers. */
export declare function createProfile(profile: BrowserProfile): Promise<void>;
export declare function updateProfile(profile: BrowserProfile): Promise<void>;
/** Fields an existing profile may be edited in place.
 *
 * Deliberately a SUBSET of {@link BrowserProfile}. `name` and `browser` are
 * identity, not settings: both key the on-disk runtime dir
 * ({@link getProfileRuntimeDir}) and any live `<name>@<endpoint>` connection, so
 * changing either orphans the cached browser data. Delete and recreate instead.
 */
export type EditableProfileFields = Partial<Pick<BrowserProfile, 'description' | 'binary' | 'electron' | 'targetFilter' | 'endpoints' | 'launchPolicy' | 'userDataDir' | 'chrome' | 'secrets' | 'viewport'>>;
export interface EditProfileResult {
    profile: BrowserProfile;
    devices: string[];
    /** Field names that actually changed. Empty means the edit was a no-op. */
    changed: string[];
}
/**
 * Merge `patch` onto the stored profile and persist it in the store it already
 * lives in (see {@link updateProfile} for the scope rules).
 *
 * Runs {@link createProfile}'s validations against the MERGED record, not just
 * the patched fields — a binary edit re-resolves the browser path, a
 * targetFilter edit re-checks the electron gate, and the local-port scan runs
 * with this profile excluded so an unchanged port is not a self-collision.
 */
export declare function editProfile(name: string, patch: EditableProfileFields): Promise<EditProfileResult>;
/**
 * Throw if `name` cannot be used for a NEW profile.
 *
 * Shared by `profiles create` and {@link renameProfile} so the two cannot drift
 * — the shape rule used to live inline in the command, which is how `rename`
 * would have accepted a name `create` rejects.
 *
 * `default` is refused because it is the reserved ALIAS meaning "whatever
 * profile this machine is configured to use" (RUSH-2709). A literal profile by
 * that name makes `--profile default` mean two different things.
 */
export declare function assertRegistrableProfileName(name: string): void;
/**
 * Rename a profile, taking its on-disk state with it.
 *
 * `editProfile` deliberately refuses a name change, and this is why: the name
 * keys the runtime dir ({@link getProfileRuntimeDir}), every fork/endpoint dir
 * derived from it, and the `browser.profile` pointer. Delete-and-recreate — the
 * only route before this — silently abandons the browser's `--user-data-dir`,
 * which is where a profile's logins live. On a real agent browser that is
 * gigabytes of session state and every account it has ever signed into.
 *
 * Refuses when the profile is in use: moving a `--user-data-dir` out from under
 * a running browser corrupts it.
 */
export declare function renameProfile(from: ProfileName, to: ProfileName): Promise<{
    devices: string[];
    movedDirs: string[];
    repointedDefault: boolean;
    repointedViewer: boolean;
    /** Peers still pinning the OLD name, and which key each used. */
    stalePins: Array<{
        device: string;
        key: 'browser.profile' | 'browser.viewer';
    }>;
}>;
export declare function deleteProfile(name: string): Promise<void>;
/**
 * Pad `text` to exactly `width` visible characters, truncating with an ellipsis
 * when it does not fit. `padEnd` alone does NOT bound a long value — it returns
 * the string unchanged when it already exceeds the width, which is what pushed
 * every later column out of alignment on a name longer than 20 characters
 * (RUSH-2710). Every column here goes through this, never a bare `padEnd`.
 */
export declare function padColumn(text: string, width: number): string;
/**
 * Render the `agents browser profiles list` table.
 *
 * Pure (no fs, no config reads) so the alignment and the default-marking rules
 * are unit-testable without a configured machine.
 *
 * Two things used to be called "default" in this listing with no way to tell
 * them apart (RUSH-2710): the profile literally NAMED `default` (the
 * auto-detected one), and whichever profile this machine resolves a bare
 * `agents browser start` to (`agents browser use <name>`). They
 * are frequently different profiles. Now only the second is a marker — a `*` in
 * a leading column, explained by a legend line — so the name column carries just
 * the name and `default` in it always means the profile of that name.
 *
 * WHERE lists the devices whose own files declare the name.
 */
export declare function formatProfilesTable(rows: BrowserProfileWithDeclarations[], configuredDefault?: string): string[];
/**
 * Resolve a profile's endpoint presets into a normalized map regardless of
 * whether the YAML uses the legacy `string[]` shape or the new map shape.
 * The legacy entries get auto-named `endpoint-0`, `endpoint-1`, ... .
 */
export declare function getEndpointPresets(profile: BrowserProfile): Record<string, import('./types.js').EndpointPreset>;
/**
 * Pick the endpoint preset to use. Order:
 *   1. Explicit name passed in (errors if unknown)
 *   2. `profile.defaultEndpoint` if set
 *   3. First entry (preserves legacy string[] behavior)
 *
 * Returns the resolved name + the preset (with per-endpoint overrides
 * already applied to binary / targetFilter), so callers don't have to
 * remember the precedence rules.
 */
export declare function resolveEndpoint(profile: BrowserProfile, endpointName?: string): {
    name: string;
    target: string;
    binary?: string;
    targetFilter?: string;
};
/**
 * Extract the (host, port) pair intended by the profile's default endpoint.
 * Returns undefined for endpoint shapes that don't carry a port (e.g. ws:// without one).
 *
 * Ports are scoped by host: a `cdp://127.0.0.1:9222` profile (local Chrome on
 * this machine) and an `ssh://remote-host:9222` profile (Comet on a remote
 * host) point at different physical ports — the host disambiguates them.
 *
 * Accepts both `scheme://host:port` and `scheme://host?port=N` shapes (the
 * latter is the documented form in `types.ts` for `ssh://`). Without this,
 * `ssh://remote-host?port=18805` would silently fall back to 9222 and every
 * `?port=`-style SSH profile would collide on creation.
 */
export declare function extractConfiguredEndpoint(profile: BrowserProfile): {
    host: string;
    port: number;
} | undefined;
/**
 * Shared endpoint parser used by both the collision-detection code path and
 * the connection drivers. Returning a single normalized `(host, port)` here
 * keeps `extractConfiguredEndpoint` and the SSH driver from drifting on URL
 * conventions (which is how `?port=N` ended up being silently ignored).
 */
export declare function parseEndpointUrl(endpoint: string): {
    host: string;
    port: number;
} | undefined;
/**
 * Extract the port intended by the profile's default endpoint.
 * Returns undefined for endpoint shapes that don't carry a port (e.g. ws:// without one).
 *
 * Note: this loses the host dimension — for collision detection use
 * `extractConfiguredEndpoint` instead, which returns the (host, port) pair.
 */
export declare function extractConfiguredPort(profile: BrowserProfile): number | undefined;
export declare function isLocalHost(host: string): boolean;
