import fs from "node:fs";
import path from "node:path";
import { createSeaworkDaemon } from "./bootstrap.js";
import { loadConfig } from "./config.js";
import { resolveSeaworkHome } from "./seawork-home.js";
import { createRootLogger } from "./logger.js";
import { loadPersistedConfig } from "./persisted-config.js";
import { acquirePidLock, PidLockError, releasePidLock, updatePidLock } from "./pid-lock.js";
async function main() {
    let seaworkHome;
    let logger;
    let config;
    let daemon = null;
    let shutdownPromise = null;
    let exitHookInstalled = false;
    const supervised = process.env.SEAWORK_SUPERVISED === "1" && typeof process.send === "function";
    let pidLockAcquired = false;
    try {
        seaworkHome = resolveSeaworkHome();
        const persistedConfig = loadPersistedConfig(seaworkHome);
        logger = createRootLogger(persistedConfig, { seaworkHome });
        config = loadConfig(seaworkHome);
    }
    catch (err) {
        const message = err instanceof Error ? err.message : String(err);
        process.stderr.write(`${message}\n`);
        process.exit(1);
    }
    if (process.argv.includes("--no-relay")) {
        config.relayEnabled = false;
    }
    if (process.argv.includes("--no-mcp")) {
        config.mcpEnabled = false;
    }
    if (process.argv.includes("--no-inject-mcp")) {
        config.mcpInjectIntoAgents = false;
    }
    const installExitHook = () => {
        if (exitHookInstalled || !shutdownPromise) {
            return;
        }
        exitHookInstalled = true;
        void shutdownPromise.then((exitCode) => {
            process.exit(exitCode);
        });
    };
    const beginShutdown = (signal, options) => {
        if (!shutdownPromise) {
            logger.info(`${signal} received, shutting down gracefully...`);
            shutdownPromise = (async () => {
                const forceExit = setTimeout(() => {
                    logger.warn("Forcing shutdown - HTTP server didn't close in time");
                    process.exit(1);
                }, 10000);
                try {
                    if (!daemon) {
                        logger.error("Shutdown requested before daemon initialization completed");
                        clearTimeout(forceExit);
                        return 1;
                    }
                    await daemon.stop();
                    if (pidLockAcquired) {
                        await releasePidLock(seaworkHome);
                        pidLockAcquired = false;
                    }
                    clearTimeout(forceExit);
                    logger.info("Server closed");
                    return options?.successExitCode ?? 0;
                }
                catch (err) {
                    clearTimeout(forceExit);
                    logger.error({ err }, "Shutdown failed");
                    return 1;
                }
            })();
        }
        else {
            logger.info(`${signal} received while shutdown is already in progress`);
        }
        installExitHook();
    };
    const sendSupervisorLifecycleMessage = (message) => {
        if (typeof process.send !== "function") {
            return false;
        }
        try {
            process.send(message);
            return true;
        }
        catch (err) {
            logger.error({ err, message }, "Failed to send lifecycle IPC message to supervisor");
            return false;
        }
    };
    const handleLifecycleIntent = (intent) => {
        if (intent.type === "shutdown") {
            logger.warn({ clientId: intent.clientId, requestId: intent.requestId }, "Shutdown requested via websocket");
            if (sendSupervisorLifecycleMessage({ type: "seawork:shutdown" })) {
                return;
            }
            beginShutdown("shutdown lifecycle intent");
            return;
        }
        logger.warn({ clientId: intent.clientId, requestId: intent.requestId, reason: intent.reason }, "Restart requested via websocket");
        if (sendSupervisorLifecycleMessage({
            type: "seawork:restart",
            ...(intent.reason ? { reason: intent.reason } : {}),
        })) {
            return;
        }
        beginShutdown("restart lifecycle intent", { successExitCode: 0 });
    };
    logger.info({
        hasAnthropicKey: !!process.env.ANTHROPIC_API_KEY,
        anthropicKeyPrefix: process.env.ANTHROPIC_API_KEY?.slice(0, 10) ?? null,
        anthropicBaseUrl: process.env.ANTHROPIC_BASE_URL ?? null,
        openaiBaseUrl: process.env.OPENAI_BASE_URL ?? null,
        seaworkApiBaseUrl: process.env.SEAWORK_API_BASE_URL ?? null,
    }, "daemon_env_check");
    // Write/remove a settings file for spawned claude CLI processes so they use
    // our gateway auth regardless of the user's ~/.claude/settings.json.
    const claudeSettingsPath = path.join(seaworkHome, "claude-settings.json");
    if (process.env.ANTHROPIC_API_KEY) {
        const settings = {
            // Override user-level auth helpers so the spawned claude CLI authenticates
            // purely via ANTHROPIC_API_KEY below. If a user has e.g. a Vtrix/Bedrock
            // helper in ~/.claude/settings.json, the SDK's f$() key resolution would
            // otherwise prefer apiKeyHelper over ANTHROPIC_API_KEY and send a wrong
            // token to the Seawork gateway, producing "API key not found" 401s.
            apiKeyHelper: "",
            awsAuthRefresh: "",
            awsCredentialExport: "",
            env: {
                ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY,
                ...(process.env.ANTHROPIC_BASE_URL
                    ? { ANTHROPIC_BASE_URL: process.env.ANTHROPIC_BASE_URL }
                    : {}),
                // Explicitly clear conflicting auth vars from user settings
                // (flag settings has highest priority and overrides user settings.env)
                ANTHROPIC_AUTH_TOKEN: "",
                ANTHROPIC_MODEL: "",
                ANTHROPIC_SMALL_FAST_MODEL: "",
                CLAUDE_CODE_USE_BEDROCK: "",
                CLAUDE_CODE_USE_VERTEX: "",
                AWS_BEARER_TOKEN_BEDROCK: "",
            },
        };
        try {
            fs.writeFileSync(claudeSettingsPath, JSON.stringify(settings, null, 2));
            logger.info({ settingsPath: claudeSettingsPath }, "claude_settings_written");
        }
        catch (err) {
            logger.warn({ err, settingsPath: claudeSettingsPath }, "claude_settings_write_failed");
        }
    }
    else {
        // No API key — remove stale settings file to prevent credential pollution
        try {
            fs.unlinkSync(claudeSettingsPath);
        }
        catch {
            // File doesn't exist, fine
        }
    }
    try {
        if (!supervised) {
            await acquirePidLock(seaworkHome, null);
            pidLockAcquired = true;
        }
        daemon = await createSeaworkDaemon({
            ...config,
            onLifecycleIntent: handleLifecycleIntent,
        }, logger);
    }
    catch (err) {
        if (pidLockAcquired) {
            await releasePidLock(seaworkHome);
            pidLockAcquired = false;
        }
        if (err instanceof PidLockError) {
            logger.error({ pid: err.existingLock?.pid }, err.message);
            process.exit(1);
        }
        logger.fatal({ err }, "Daemon bootstrap failed");
        throw err;
    }
    try {
        await daemon.start();
        if (!supervised) {
            const listenTarget = daemon.getListenTarget();
            const listen = listenTarget?.type === "tcp"
                ? `${listenTarget.host}:${listenTarget.port}`
                : listenTarget?.path;
            if (!listen) {
                throw new Error("Daemon did not expose a listen target after startup");
            }
            await updatePidLock(seaworkHome, { listen });
        }
    }
    catch (err) {
        if (pidLockAcquired) {
            await releasePidLock(seaworkHome);
            pidLockAcquired = false;
        }
        if (err instanceof PidLockError) {
            logger.error({ pid: err.existingLock?.pid }, err.message);
            process.exit(1);
        }
        logger.fatal({ err }, "Daemon failed to start listening");
        throw err;
    }
    process.on("SIGTERM", () => beginShutdown("SIGTERM"));
    process.on("SIGINT", () => beginShutdown("SIGINT"));
    process.on("uncaughtException", (err) => {
        logger.fatal({ err }, "Uncaught exception — daemon crashing");
        process.exit(1);
    });
    process.on("unhandledRejection", (reason) => {
        logger.fatal({ err: reason }, "Unhandled promise rejection — daemon crashing");
        process.exit(1);
    });
}
main().catch((err) => {
    process.stderr.write(`${err instanceof Error ? (err.stack ?? err.message) : String(err)}\n`);
    // Give pino streams a moment to flush the fatal log entry to daemon.log
    // before the process exits. Without this, async file streams may lose the
    // last few entries that explain why the daemon crashed.
    setTimeout(() => process.exit(1), 200);
});
//# sourceMappingURL=index.js.map