import { randomBytes } from "node:crypto";
import { mkdtemp, rm } from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { isRecord } from "../utils/json.js";
import { startBridge } from "./opencodeRuntimeSmoke.js";
import { delay } from "./smokeTiming.js";
const DEFAULT_TIMEOUT_MS = 30_000;
const LOCAL_UI_ORIGIN = "http://127.0.0.1:41732";
const WORKSPACE_ID = "authority-smoke-workspace";
const WRONG_WORKSPACE_ID = "authority-smoke-other-workspace";
const PROVIDER_KEY_SENTINEL = "sk-bridge-authority-smoke-provider-sentinel-1234567890";
const RAW_COMMAND_SENTINEL = "raw-command-authority-smoke --token should-not-appear";
const STACK_SENTINEL = "Error: bridge authority smoke stack sentinel\n    at unsafe";
const EXPECTED_LOCAL_UI_SCOPES = [
    "bridge:read",
    "bridge:review",
    "runs:write",
    "run-intents:create",
    "run-intents:prompt",
    "permissions:reply",
    "permissions:manage",
    "permissions:evaluate",
    "provider-auth:read",
    "provider-auth:write",
    "debug:reveal",
];
const DIAGNOSTIC_TOP_LEVEL_FIELDS = [
    "envelopeVersion",
    "error",
    "status",
    "guard",
    "reason",
    "message",
    "request",
    "metadata",
];
const SAFE_DIAGNOSTIC_REQUEST_FIELDS = ["method", "routeId"];
const SAFE_DIAGNOSTIC_METADATA_FIELDS = [
    "authScheme",
    "requiredScope",
    "grantedScopes",
    "originBinding",
    "workspaceBinding",
    "sessionPresent",
    "bridgeTokenPresent",
    "healthPathAllowed",
];
const PACKAGE_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "..");
const SENSITIVE_TOKEN_PATTERNS = [
    {
        label: "local UI handoff fragment",
        pattern: /patchTrailLocalHandoff=/,
    },
    {
        label: "local UI capability token",
        pattern: /\bpt_lui_(?!sess_)[A-Za-z0-9_-]{16,}\b/,
    },
    {
        label: "local UI session token",
        pattern: /\bpt_lui_sess_[A-Za-z0-9_-]{16,}\b/,
    },
    {
        label: "provider-key-like sentinel",
        pattern: /\bsk-[A-Za-z0-9][A-Za-z0-9_-]{10,}\b/,
    },
];
export async function main(argv = process.argv.slice(2)) {
    try {
        const options = parseBridgeAuthoritySmokeArgs(argv);
        if (options.help) {
            process.stdout.write(helpText());
            return;
        }
        const summary = await executeBridgeAuthoritySmoke(options, process.env);
        if (options.json) {
            process.stdout.write(`${JSON.stringify(summary)}\n`);
            return;
        }
        process.stdout.write(`Bridge authority contract smoke passed: contract=${summary.authorityContractVersion} diagnostic=${summary.diagnosticEnvelopeVersion} scopes=${summary.supportedScopes.length} rejections=${summary.rejectionChecks}\n`);
    }
    catch (error) {
        process.stderr.write(`${error instanceof Error ? error.message : "Bridge authority contract smoke failed."}\n`);
        process.exitCode = 1;
    }
}
export async function runBridgeAuthorityContractSmoke(argv = [], env = process.env) {
    const options = parseBridgeAuthoritySmokeArgs(argv);
    if (options.help) {
        throw new Error("--help is only supported by the authority smoke CLI.");
    }
    return executeBridgeAuthoritySmoke(options, env);
}
export function parseBridgeAuthoritySmokeArgs(argv) {
    const options = {
        timeoutMs: DEFAULT_TIMEOUT_MS,
        json: false,
        help: false,
    };
    for (let index = 0; index < argv.length; index += 1) {
        const arg = argv[index];
        if (!arg || arg === "--")
            continue;
        if (arg === "--help" || arg === "-h") {
            options.help = true;
            continue;
        }
        if (arg === "--json") {
            options.json = true;
            continue;
        }
        if (arg === "--timeout-ms") {
            options.timeoutMs = parseTimeout(requireArgValue(argv, ++index, arg));
            continue;
        }
        throw new Error(`Unknown authority smoke argument: ${arg}`);
    }
    return options;
}
async function executeBridgeAuthoritySmoke(options, env) {
    let tempRoot;
    let bridge;
    let summary;
    let flowError;
    const forbidden = [
        { label: "provider-key-like sentinel", value: PROVIDER_KEY_SENTINEL },
        { label: "raw command sentinel", value: RAW_COMMAND_SENTINEL },
        { label: "stack sentinel", value: STACK_SENTINEL },
    ];
    try {
        tempRoot = await phase("temp-state", () => mkdtemp(path.join(os.tmpdir(), "patchtrail-authority-smoke-")));
        const appDataDir = path.join(tempRoot, "app-data");
        const localDbPath = path.join(tempRoot, "state", "patchtrail.sqlite");
        const bridgeToken = `pt_auth_smoke_${randomBytes(24).toString("base64url")}`;
        const opencodeServerPassword = randomBytes(32).toString("base64url");
        forbidden.push({ label: "durable bridge token", value: bridgeToken }, { label: "OpenCode server password", value: opencodeServerPassword }, { label: "temp root", value: tempRoot }, { label: "temp app data dir", value: appDataDir }, { label: "temp SQLite path", value: localDbPath });
        const smokeEnv = createBridgeAuthoritySmokeEnvironment({
            env,
            bridgeToken,
            opencodeServerPassword,
            appDataDir,
            localDbPath,
        });
        const startedBridge = await phase("readiness", () => startBridge(smokeEnv, options.timeoutMs, forbidden));
        bridge = startedBridge;
        summary = await phase("authority-flow", () => runAuthorityHttpFlow({
            bridge: startedBridge,
            bridgeToken,
            forbidden,
            timeoutMs: options.timeoutMs,
        }));
    }
    catch (error) {
        flowError = error;
    }
    const cleanupError = await cleanupAuthoritySmoke({
        bridge,
        tempRoot,
        forbidden,
    });
    if (flowError) {
        throw new Error(sanitizeAuthoritySmokeError(flowError, forbidden, [tempRoot]));
    }
    if (cleanupError) {
        throw new Error(sanitizeAuthoritySmokeError(cleanupError, forbidden, [tempRoot]));
    }
    if (!summary) {
        throw new Error("authority-flow: smoke summary was not produced.");
    }
    return summary;
}
function createBridgeAuthoritySmokeEnvironment(input) {
    const smokeEnv = { ...input.env };
    for (const key of [
        "OPENAI_API_KEY",
        "ANTHROPIC_API_KEY",
        "GITHUB_TOKEN",
        "DATABASE_URL",
        "PATCHTRAIL_SMOKE_OPENAI_API_KEY",
        "PATCHTRAIL_CREDENTIAL_FALLBACK_KEY",
        "PATCHTRAIL_OPENCODE_METADATA_DIR",
        "PATCHTRAIL_OPENCODE_ARTIFACT_ROOT",
        "PATCHTRAIL_OPENCODE_TRUSTED_ROOT_KEY_IDS",
        "PATCHTRAIL_ALLOW_NON_LOOPBACK",
    ]) {
        delete smokeEnv[key];
    }
    smokeEnv.NODE_ENV = "test";
    smokeEnv.PATCHTRAIL_CREDENTIAL_STORE = "in-memory";
    smokeEnv.PATCHTRAIL_BRIDGE_HOST = "127.0.0.1";
    smokeEnv.PATCHTRAIL_BRIDGE_PORT = "0";
    smokeEnv.PATCHTRAIL_BRIDGE_CAPABILITY_TOKEN = input.bridgeToken;
    smokeEnv.PATCHTRAIL_OPENCODE_SERVER_PASSWORD = input.opencodeServerPassword;
    smokeEnv.PATCHTRAIL_APP_DATA_DIR = input.appDataDir;
    smokeEnv.PATCHTRAIL_LOCAL_DB_PATH = input.localDbPath;
    smokeEnv.PATCHTRAIL_OPENCODE_SMOKE_RESULT_PATH = path.join(input.appDataDir, "runtime", "opencode-smoke-last.json");
    return smokeEnv;
}
async function runAuthorityHttpFlow(input) {
    const request = (method, requestPath, body, options = {}) => requestJson(input.bridge.baseUrl, method, requestPath, body, {
        timeoutMs: input.timeoutMs,
        ...options,
    });
    const health = await request("GET", "/health");
    assertStatus(health, 200, "GET /health");
    assertCoarseHealth(health.body);
    assertNoLeaks("GET /health", health.body, input.forbidden);
    const readSession = await mintScopedLocalUiSession({
        request,
        bridgeToken: input.bridgeToken,
        forbidden: input.forbidden,
        scopes: ["bridge:read"],
        deviceId: "authority-smoke-read-device",
        userSessionId: "authority-smoke-read-user-session",
    });
    const writeSession = await mintScopedLocalUiSession({
        request,
        bridgeToken: input.bridgeToken,
        forbidden: input.forbidden,
        scopes: ["runs:write"],
        deviceId: "authority-smoke-write-device",
        userSessionId: "authority-smoke-write-user-session",
    });
    const success = await request("GET", "/local-ui/authority", undefined, {
        bearerToken: readSession.token,
        origin: readSession.origin,
    });
    assertStatus(success, 200, "GET /local-ui/authority");
    assertNoLeaks("GET /local-ui/authority success", success.body, input.forbidden);
    const successSummary = assertAuthoritySuccess(success.body, readSession);
    const invalidSessionToken = "pt_lui_sess_invalidAuthoritySmokeSecret123";
    input.forbidden.push({
        label: "invalid local UI session token",
        value: invalidSessionToken,
    });
    let rejectionChecks = 0;
    await assertAuthorityRejection(await request("GET", "/local-ui/authority"), {
        status: 401,
        guard: "auth",
        reason: "missing_authentication",
        routeId: "local-ui.authority",
    }, input.forbidden);
    rejectionChecks += 1;
    await assertAuthorityRejection(await request("GET", "/local-ui/authority", undefined, {
        bearerToken: invalidSessionToken,
        origin: readSession.origin,
    }), {
        status: 401,
        guard: "auth",
        reason: "invalid_authentication",
        routeId: "local-ui.authority",
    }, input.forbidden);
    rejectionChecks += 1;
    await assertAuthorityRejection(await request("GET", "/local-ui/authority", undefined, {
        bridgeToken: input.bridgeToken,
    }), {
        status: 403,
        guard: "local-ui-session-required",
        reason: "local_ui_session_required",
        routeId: "local-ui.authority",
    }, input.forbidden);
    rejectionChecks += 1;
    await assertAuthorityRejection(await request("GET", "/local-ui/authority", undefined, {
        bearerToken: readSession.token,
        origin: "https://evil.example",
    }), {
        status: 403,
        guard: "origin",
        reason: "wrong_origin",
        routeId: "local-ui.authority",
    }, input.forbidden);
    rejectionChecks += 1;
    await assertAuthorityRejection(await request("GET", "/local-ui/authority", undefined, {
        bearerToken: writeSession.token,
        origin: writeSession.origin,
    }), {
        status: 403,
        guard: "scope",
        reason: "insufficient_scope",
        routeId: "local-ui.authority",
    }, input.forbidden);
    rejectionChecks += 1;
    await assertAuthorityRejection(await request("GET", `/workspaces/${encodeURIComponent(WRONG_WORKSPACE_ID)}/runtime/status`, undefined, { bearerToken: readSession.token, origin: readSession.origin }), {
        status: 403,
        guard: "workspace",
        reason: "wrong_workspace",
    }, input.forbidden);
    rejectionChecks += 1;
    const finalHealth = await request("GET", "/health");
    assertStatus(finalHealth, 200, "final GET /health");
    assertCoarseHealth(finalHealth.body);
    assertNoLeaks("final GET /health", finalHealth.body, input.forbidden);
    return {
        status: "passed",
        authorityContractVersion: successSummary.contractVersion,
        diagnosticEnvelopeVersion: successSummary.diagnosticEnvelopeVersion,
        supportedScopes: successSummary.supportedScopes,
        rejectionChecks,
    };
}
async function mintScopedLocalUiSession(input) {
    const mint = await input.request("POST", "/local-ui/capabilities", {
        origin: LOCAL_UI_ORIGIN,
        workspaceId: WORKSPACE_ID,
        deviceId: input.deviceId,
        userSessionId: input.userSessionId,
        scopes: input.scopes,
        ttlSeconds: 60,
        sessionTtlSeconds: 300,
    }, { bridgeToken: input.bridgeToken });
    assertStatus(mint, 201, "POST /local-ui/capabilities");
    // The credential issuance response intentionally contains the one-time
    // capability token. Scan it for every pre-existing forbidden value, then add
    // the issued credential to the downstream full-response leak set.
    assertNoLeaks("POST /local-ui/capabilities", mint.body, input.forbidden, {
        includeSensitiveTokenPatterns: false,
    });
    const capabilityToken = readNestedString(mint.body, ["token"]);
    assertStringPrefix(capabilityToken, "pt_lui_", "local UI capability token");
    const handoffFragment = `patchTrailLocalHandoff=${capabilityToken}`;
    input.forbidden.push({ label: "local UI capability token", value: capabilityToken }, { label: "local UI handoff fragment", value: handoffFragment });
    const exchange = await input.request("POST", "/local-ui/session", {
        capabilityToken,
        workspaceId: WORKSPACE_ID,
        deviceId: input.deviceId,
        userSessionId: input.userSessionId,
    }, { origin: LOCAL_UI_ORIGIN });
    assertStatus(exchange, 201, "POST /local-ui/session");
    // The session exchange response intentionally contains the session bearer.
    // It must not echo the durable bridge token, one-time capability, handoff
    // fragment, sentinels, or temp filesystem paths.
    assertNoLeaks("POST /local-ui/session", exchange.body, input.forbidden, {
        includeSensitiveTokenPatterns: false,
    });
    const sessionToken = readNestedString(exchange.body, ["sessionToken"]);
    assertStringPrefix(sessionToken, "pt_lui_sess_", "local UI session token");
    const session = readNestedRecord(exchange.body, ["session"]);
    assertEqual(session.workspaceId, WORKSPACE_ID, "session.workspaceId");
    assertEqual(session.origin, LOCAL_UI_ORIGIN, "session.origin");
    assertEqual(session.deviceId, input.deviceId, "session.deviceId");
    assertEqual(session.userSessionId, input.userSessionId, "session.userSessionId");
    assertStringArrayEqual(session.scopes, input.scopes, "session.scopes");
    input.forbidden.push({
        label: "local UI session bearer token",
        value: sessionToken,
    });
    return {
        token: sessionToken,
        origin: LOCAL_UI_ORIGIN,
        workspaceId: WORKSPACE_ID,
    };
}
function assertAuthoritySuccess(body, sessionCredential) {
    const payload = readRecord(body, "authority success payload");
    const authority = readRecord(payload.authority, "authority contract");
    assertEqual(authority.service, "patchtrail-agent-bridge", "authority.service");
    assertEqual(authority.contractVersion, 1, "authority.contractVersion");
    assertEqual(authority.diagnosticEnvelopeVersion, 1, "authority.diagnosticEnvelopeVersion");
    const binding = readRecord(authority.binding, "authority.binding");
    assertEqual(binding.invariant, "loopback_only", "binding.invariant");
    assertStringArrayEqual(binding.allowedHosts, ["127.0.0.1", "::1", "localhost"], "binding.allowedHosts");
    assertEqual(binding.configuredHostClass, "loopback", "binding.configuredHostClass");
    assertEqual(binding.nonLoopbackOverride, "development_diagnostics_only", "binding.nonLoopbackOverride");
    const health = readRecord(authority.health, "authority.health");
    assertEqual(health.path, "/health", "health.path");
    assertEqual(health.authentication, "none", "health.authentication");
    assertEqual(health.invariant, "coarse_status_only", "health.invariant");
    assertStringArrayContains(health.exposes, ["status", "service", "version", "mode", "runtime.state"], "health.exposes");
    assertStringArrayContains(health.excludes, [
        "credentials",
        "credentialScope",
        "providerPayloads",
        "rawCommands",
        "stackTraces",
        "tokens",
    ], "health.excludes");
    const localUi = readRecord(authority.localUi, "authority.localUi");
    const browserPathAuthority = readRecord(localUi.browserPathAuthority, "localUi.browserPathAuthority");
    assertEqual(browserPathAuthority.requiredCredential, "local_ui_session", "browserPathAuthority.requiredCredential");
    assertEqual(browserPathAuthority.bridgeCapabilityTokenAccepted, false, "browserPathAuthority.bridgeCapabilityTokenAccepted");
    const handoff = readRecord(localUi.handoff, "localUi.handoff");
    assertEqual(handoff.tokenPrefix, "pt_lui_", "handoff.tokenPrefix");
    assertEqual(handoff.delivery, "url_fragment_only", "handoff.delivery");
    assertEqual(handoff.fragmentParameter, "patchTrailLocalHandoff", "handoff.fragmentParameter");
    assertEqual(handoff.exchange, "one_time", "handoff.exchange");
    assertEqual(handoff.routeCredentialAccepted, false, "handoff.routeCredentialAccepted");
    const sessionContract = readRecord(localUi.session, "localUi.session");
    assertEqual(sessionContract.tokenPrefix, "pt_lui_sess_", "session.tokenPrefix");
    assertStringArrayContains(sessionContract.acceptedCredentials, [
        "Authorization: Bearer <local-ui-session>",
        "x-patchtrail-local-ui-session",
    ], "session.acceptedCredentials");
    assertStringArrayEqual(sessionContract.requiredBindings, ["origin", "workspaceId", "deviceId", "userSessionId", "scopes"], "session.requiredBindings");
    assertEqual(sessionContract.scopeEvaluation, "per_route_required_scope", "session.scopeEvaluation");
    const currentSession = readRecord(sessionContract.currentSession, "session.currentSession");
    assertEqual(currentSession.binding, "origin_workspace_device_user_session_scope", "currentSession.binding");
    assertStringArrayEqual(currentSession.grantedScopes, ["bridge:read"], "currentSession.grantedScopes");
    const supportedScopes = assertSupportedScopeTaxonomy(localUi.supportedScopes);
    const diagnostics = readRecord(authority.diagnostics, "authority.diagnostics");
    assertEqual(diagnostics.envelopeVersion, 1, "diagnostics.envelopeVersion");
    assertEqual(diagnostics.error, "bridge_authority_rejected", "diagnostics.error");
    assertStringArrayEqual(diagnostics.safeRequestFields, [...SAFE_DIAGNOSTIC_REQUEST_FIELDS], "diagnostics.safeRequestFields");
    assertStringArrayEqual(diagnostics.safeMetadataFields, [...SAFE_DIAGNOSTIC_METADATA_FIELDS], "diagnostics.safeMetadataFields");
    assertStringArrayContains(diagnostics.unsafeValues, [
        "tokens",
        "urlFragments",
        "rawCommands",
        "providerKeys",
        "providerPayloads",
        "paths",
        "stackTraces",
    ], "diagnostics.unsafeValues");
    const session = readRecord(payload.session, "authority response session");
    assertEqual(session.origin, sessionCredential.origin, "response.session.origin");
    assertEqual(session.workspaceId, sessionCredential.workspaceId, "response.session.workspaceId");
    assertStringArrayEqual(session.scopes, ["bridge:read"], "response.session.scopes");
    assertNoUnsafeSessionFields(session);
    return {
        contractVersion: readNumber(authority.contractVersion, "contractVersion"),
        diagnosticEnvelopeVersion: readNumber(authority.diagnosticEnvelopeVersion, "diagnosticEnvelopeVersion"),
        supportedScopes,
    };
}
async function assertAuthorityRejection(response, expected, forbidden) {
    assertStatus(response, expected.status, `${expected.guard} rejection`);
    assertNoLeaks(`${expected.guard} rejection`, response.body, forbidden);
    const payload = readRecord(response.body, `${expected.guard} diagnostic`);
    assertStringArrayEqual(Object.keys(payload).sort(), [...DIAGNOSTIC_TOP_LEVEL_FIELDS].sort(), `${expected.guard} diagnostic fields`);
    assertEqual(payload.envelopeVersion, 1, "diagnostic.envelopeVersion");
    assertEqual(payload.error, "bridge_authority_rejected", "diagnostic.error");
    assertEqual(payload.status, expected.status, "diagnostic.status");
    assertEqual(payload.guard, expected.guard, "diagnostic.guard");
    assertEqual(payload.reason, expected.reason, "diagnostic.reason");
    if (typeof payload.message !== "string" || payload.message.length === 0) {
        throw new Error("diagnostic.message was not a non-empty string.");
    }
    const request = readRecord(payload.request, "diagnostic.request");
    assertOnlyKeys(request, [...SAFE_DIAGNOSTIC_REQUEST_FIELDS], "diagnostic.request");
    if (expected.routeId) {
        assertEqual(request.routeId, expected.routeId, "diagnostic.request.routeId");
        assertEqual(request.method, "GET", "diagnostic.request.method");
    }
    const metadata = readRecord(payload.metadata, "diagnostic.metadata");
    assertOnlyKeys(metadata, [...SAFE_DIAGNOSTIC_METADATA_FIELDS], "diagnostic.metadata");
}
function assertSupportedScopeTaxonomy(value) {
    if (!Array.isArray(value)) {
        throw new Error("localUi.supportedScopes was not an array.");
    }
    const scopes = value.map((entry, index) => {
        const scope = readRecord(entry, `supportedScopes[${index}]`);
        if (typeof scope.scope !== "string") {
            throw new Error(`supportedScopes[${index}].scope was not a string.`);
        }
        if (typeof scope.title !== "string" || scope.title.length === 0) {
            throw new Error(`supportedScopes[${index}].title was not non-empty.`);
        }
        if (typeof scope.description !== "string" ||
            scope.description.length === 0) {
            throw new Error(`supportedScopes[${index}].description was not non-empty.`);
        }
        return scope.scope;
    });
    assertStringArrayEqual([...scopes].sort(), [...EXPECTED_LOCAL_UI_SCOPES].sort(), "localUi.supportedScopes taxonomy");
    return scopes;
}
function assertCoarseHealth(value) {
    const health = readRecord(value, "health payload");
    if (health.status !== "ok" && health.status !== "degraded") {
        throw new Error("GET /health did not return a coarse status.");
    }
    assertEqual(health.service, "patchtrail-agent-bridge", "health.service");
    assertEqual(health.version, "0.1.0", "health.version");
    assertEqual(health.mode, "test", "health.mode");
    const runtime = readRecord(health.runtime, "health.runtime");
    if (typeof runtime.state !== "string") {
        throw new Error("GET /health omitted runtime.state.");
    }
    const serialized = JSON.stringify(health) ?? "";
    for (const unsafe of [
        "browserPathAuthority",
        "diagnosticEnvelopeVersion",
        "credentialScope",
        "providerPayloads",
        "rawCommands",
        "stackTraces",
        "tokens",
        "patchTrailLocalHandoff",
    ]) {
        if (serialized.includes(unsafe)) {
            throw new Error(`GET /health exposed ${unsafe}.`);
        }
    }
}
async function requestJson(baseUrl, method, requestPath, body, options = {}) {
    const headers = { accept: "application/json" };
    if (options.bridgeToken) {
        headers["x-patchtrail-bridge-token"] = options.bridgeToken;
    }
    if (options.bearerToken) {
        headers.authorization = `Bearer ${options.bearerToken}`;
    }
    if (options.origin)
        headers.origin = options.origin;
    let requestBody;
    if (body !== undefined) {
        headers["content-type"] = "application/json";
        requestBody = JSON.stringify(body);
    }
    const response = await fetch(new URL(requestPath, baseUrl), {
        method,
        headers,
        ...(requestBody ? { body: requestBody } : {}),
        signal: AbortSignal.timeout(options.timeoutMs ?? 10_000),
    });
    const text = await response.text();
    let parsed = text;
    if (text) {
        try {
            parsed = JSON.parse(text);
        }
        catch {
            parsed = text;
        }
    }
    return { status: response.status, body: parsed, text };
}
async function cleanupAuthoritySmoke(input) {
    const errors = [];
    if (input.bridge) {
        try {
            await terminateBridge(input.bridge);
            assertNoLeaks("bridge stdout", input.bridge.stdout.text(), input.forbidden);
            assertNoLeaks("bridge stderr", input.bridge.stderr.text(), input.forbidden);
        }
        catch (error) {
            errors.push(safeErrorMessage(error));
        }
    }
    if (input.tempRoot) {
        try {
            await rm(input.tempRoot, { recursive: true, force: true });
        }
        catch (error) {
            errors.push(safeErrorMessage(error));
        }
    }
    if (errors.length > 0) {
        return new Error(`cleanup: ${errors.join("; ")}`);
    }
    return undefined;
}
async function terminateBridge(bridge) {
    if (bridge.child.exitCode !== null || bridge.child.signalCode !== null)
        return;
    bridge.child.kill("SIGTERM");
    const deadline = Date.now() + 5_000;
    while (Date.now() < deadline &&
        bridge.child.exitCode === null &&
        bridge.child.signalCode === null) {
        await delay(50);
    }
    if (bridge.child.exitCode === null && bridge.child.signalCode === null) {
        bridge.child.kill("SIGKILL");
        throw new Error("bridge process did not exit after SIGTERM.");
    }
}
async function phase(label, fn) {
    try {
        return await fn();
    }
    catch (error) {
        throw new Error(`${label}: ${safeErrorMessage(error)}`);
    }
}
function assertNoLeaks(surface, value, forbidden, options = {}) {
    const serialized = serializeForLeakCheck(value);
    if (options.includeSensitiveTokenPatterns !== false) {
        for (const { label, pattern } of SENSITIVE_TOKEN_PATTERNS) {
            if (pattern.test(serialized)) {
                throw new Error(`${label} leaked through ${surface}.`);
            }
        }
    }
    for (const item of forbidden) {
        if (!item.value)
            continue;
        if (serialized.includes(item.value)) {
            throw new Error(`${item.label} leaked through ${surface}.`);
        }
    }
}
function sanitizeAuthoritySmokeError(error, forbidden, pathHints) {
    let message = safeErrorMessage(error);
    message = message.replace(/patchTrailLocalHandoff=[^\s"']+/g, "patchTrailLocalHandoff=[redacted]");
    message = message.replace(/\bpt_lui_sess_[A-Za-z0-9_-]{8,}\b/g, "[redacted:local-ui-session]");
    message = message.replace(/\bpt_lui_(?!sess_)[A-Za-z0-9_-]{8,}\b/g, "[redacted:local-ui-capability]");
    message = message.replace(/\bsk-[A-Za-z0-9][A-Za-z0-9_-]{8,}\b/g, "[redacted:provider-key]");
    for (const item of forbidden) {
        if (!item.value)
            continue;
        message = message.split(item.value).join(`[redacted:${item.label}]`);
    }
    for (const hint of [...pathHints, PACKAGE_ROOT, process.env.HOME]) {
        if (!hint)
            continue;
        message = message.split(hint).join("[redacted:path]");
    }
    message = message.replace(/(?:[A-Za-z]:)?\/[^\s'"]+/g, "[redacted:path]");
    return message.slice(0, 2_000);
}
function safeErrorMessage(error) {
    if (error instanceof Error)
        return error.message;
    if (typeof error === "string")
        return error;
    return "Bridge authority contract smoke failed.";
}
function serializeForLeakCheck(value) {
    if (typeof value === "string")
        return value;
    return JSON.stringify(value) ?? "";
}
function readNestedString(value, pathSegments) {
    const nested = readNested(value, pathSegments);
    if (typeof nested !== "string" || nested.length === 0) {
        throw new Error(`Expected ${pathSegments.join(".")} to be a non-empty string.`);
    }
    return nested;
}
function readNestedRecord(value, pathSegments) {
    return readRecord(readNested(value, pathSegments), pathSegments.join("."));
}
function readNested(value, pathSegments) {
    let current = value;
    for (const segment of pathSegments) {
        if (!isRecord(current))
            return undefined;
        current = current[segment];
    }
    return current;
}
function readRecord(value, label) {
    if (!isRecord(value))
        throw new Error(`Expected ${label} to be an object.`);
    return value;
}
function readNumber(value, label) {
    if (typeof value !== "number")
        throw new Error(`Expected ${label} to be a number.`);
    return value;
}
function assertStatus(result, expected, label) {
    if (result.status !== expected) {
        throw new Error(`${label} returned HTTP ${result.status}; expected ${expected}.`);
    }
}
function assertEqual(actual, expected, label) {
    if (actual !== expected) {
        throw new Error(`Expected ${label} to equal ${String(expected)}.`);
    }
}
function assertStringPrefix(value, prefix, label) {
    if (!value.startsWith(prefix)) {
        throw new Error(`Expected ${label} to use ${prefix} prefix.`);
    }
}
function assertStringArrayEqual(actual, expected, label) {
    if (!Array.isArray(actual) ||
        actual.some((item) => typeof item !== "string")) {
        throw new Error(`Expected ${label} to be a string array.`);
    }
    const actualStrings = actual;
    if (actualStrings.length !== expected.length ||
        actualStrings.some((item, index) => item !== expected[index])) {
        throw new Error(`Expected ${label} to equal ${expected.join(",")}.`);
    }
}
function assertStringArrayContains(actual, expectedValues, label) {
    if (!Array.isArray(actual) ||
        actual.some((item) => typeof item !== "string")) {
        throw new Error(`Expected ${label} to be a string array.`);
    }
    const actualSet = new Set(actual);
    const missing = expectedValues.filter((item) => !actualSet.has(item));
    if (missing.length > 0) {
        throw new Error(`Expected ${label} to include ${missing.join(",")}.`);
    }
}
function assertOnlyKeys(value, allowedKeys, label) {
    const allowed = new Set(allowedKeys);
    const unsafe = Object.keys(value).filter((key) => !allowed.has(key));
    if (unsafe.length > 0) {
        throw new Error(`${label} included unsafe fields: ${unsafe.join(",")}.`);
    }
}
function assertNoUnsafeSessionFields(session) {
    for (const unsafe of [
        "token",
        "sessionToken",
        "capabilityToken",
        "capabilityId",
        "tokenHash",
    ]) {
        if (unsafe in session) {
            throw new Error(`authority response session exposed ${unsafe}.`);
        }
    }
    for (const required of [
        "id",
        "origin",
        "workspaceId",
        "deviceId",
        "userSessionId",
        "scopes",
        "expiresAt",
    ]) {
        if (!(required in session)) {
            throw new Error(`authority response session omitted ${required}.`);
        }
    }
}
function parseTimeout(value) {
    if (!/^\d+$/.test(value)) {
        throw new Error("--timeout-ms must be an integer >= 1000.");
    }
    const parsed = Number(value);
    if (!Number.isInteger(parsed) || parsed < 1_000) {
        throw new Error("--timeout-ms must be an integer >= 1000.");
    }
    return parsed;
}
function requireArgValue(argv, index, flag) {
    const value = argv[index];
    if (!value)
        throw new Error(`Expected value after ${flag}.`);
    return value;
}
function helpText() {
    return `Usage: pnpm smoke:authority -- [options]\n\nStarts a compiled loopback bridge with isolated temp app data/state, verifies the local UI bridge authority contract, checks sanitized rejection diagnostics, scans outputs for secret/path leaks, then shuts the bridge down.\n\nOptions:\n  --timeout-ms <ms>   Bridge readiness and HTTP timeout. Default: ${DEFAULT_TIMEOUT_MS}.\n  --json              Print a machine-readable non-secret summary.\n  --help              Show this help.\n`;
}
