"use strict";
/**
 * Centralised Anthropic API key management.
 *
 * Provides read/write/validate operations for the Anthropic API key
 * used by the public agent. All consumers import from this module
 * instead of inlining the env-var-then-file lookup pattern.
 *
 * Follows the same shape as cloudflare/mcp/src/lib/cloudflared.ts
 * (readToken, writeToken, hasToken, validateToken).
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.readKey = readKey;
exports.writeKey = writeKey;
exports.hasKey = hasKey;
exports.validateKey = validateKey;
exports.keyFilePath = keyFilePath;
const node_fs_1 = require("node:fs");
const node_path_1 = require("node:path");
const node_os_1 = require("node:os");
// ---------------------------------------------------------------------------
// Path resolution — lazy, cached
//
// The key file lives at ~/{configDir}/.anthropic-api-key where configDir
// is read from brand.json. Resolution strategy:
//   1. PLATFORM_ROOT env var → brand.json → configDir  (MCP servers)
//   2. MAXY_PLATFORM_ROOT env var → brand.json → configDir  (UI/Next.js)
//   3. Fall back to .maxy  (dev environment, neither env var set)
// ---------------------------------------------------------------------------
let cachedKeyFilePath = null;
function resolveKeyFilePath() {
    if (cachedKeyFilePath)
        return cachedKeyFilePath;
    let configDirName = ".maxy"; // default for dev
    const platformRoot = process.env.PLATFORM_ROOT ?? process.env.MAXY_PLATFORM_ROOT;
    if (platformRoot) {
        const brandPath = (0, node_path_1.join)(platformRoot, "config", "brand.json");
        if (!(0, node_fs_1.existsSync)(brandPath)) {
            throw new Error(`brand.json not found at ${brandPath} — platform not properly installed`);
        }
        try {
            const brand = JSON.parse((0, node_fs_1.readFileSync)(brandPath, "utf-8"));
            if (!brand.configDir) {
                throw new Error(`brand.json at ${brandPath} is missing the configDir field`);
            }
            configDirName = brand.configDir;
        }
        catch (err) {
            if (err instanceof SyntaxError) {
                throw new Error(`brand.json at ${brandPath} is not valid JSON: ${err.message}`);
            }
            throw err;
        }
    }
    cachedKeyFilePath = (0, node_path_1.resolve)((0, node_os_1.homedir)(), configDirName, ".anthropic-api-key");
    return cachedKeyFilePath;
}
// ---------------------------------------------------------------------------
// readKey — two-tier lookup: env var → file
//
// Returns the key string or null if unavailable. Never throws on
// missing/unreadable key — callers handle null per their context.
// ---------------------------------------------------------------------------
function readKey() {
    const envKey = process.env.ANTHROPIC_API_KEY;
    if (envKey && envKey.trim()) {
        return envKey.trim();
    }
    let keyFilePath;
    try {
        keyFilePath = resolveKeyFilePath();
    }
    catch (err) {
        const msg = err instanceof Error ? err.message : String(err);
        console.error(`[anthropic-key] path resolution failed: ${msg}`);
        return null;
    }
    if (envKey !== undefined) {
        // env var was set but empty — log and fall through to file
        console.error(`[anthropic-key] ANTHROPIC_API_KEY env var is set but empty, falling back to file`);
    }
    try {
        const raw = (0, node_fs_1.readFileSync)(keyFilePath, "utf-8").trim();
        if (!raw) {
            console.error(`[anthropic-key] key file exists but is empty: ${keyFilePath}`);
            return null;
        }
        return raw;
    }
    catch (err) {
        const code = err instanceof Error && "code" in err
            ? err.code
            : undefined;
        if (code === "ENOENT") {
            // File does not exist — expected on fresh installs
            console.error(`[anthropic-key] no key available (env var unset, file not found: ${keyFilePath})`);
        }
        else {
            const msg = err instanceof Error ? err.message : String(err);
            console.error(`[anthropic-key] failed to read key file ${keyFilePath}: ${msg}`);
        }
        return null;
    }
}
// ---------------------------------------------------------------------------
// writeKey — validate format and write with restricted permissions
// ---------------------------------------------------------------------------
function writeKey(key) {
    const trimmed = key.trim();
    if (!trimmed.startsWith("sk-ant-")) {
        throw new Error("Invalid key format. Must start with sk-ant-.");
    }
    if (trimmed.length < 90) {
        throw new Error("Invalid key format. Must be at least 90 characters.");
    }
    const keyFilePath = resolveKeyFilePath();
    (0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(keyFilePath), { recursive: true });
    (0, node_fs_1.writeFileSync)(keyFilePath, trimmed, { mode: 0o600 });
    console.error(`[anthropic-key] key written to ${keyFilePath}`);
}
// ---------------------------------------------------------------------------
// hasKey — thin check, no validation
// ---------------------------------------------------------------------------
function hasKey() {
    return readKey() !== null;
}
async function validateKey(key) {
    const effectiveKey = key ?? readKey();
    if (!effectiveKey) {
        return {
            status: "missing",
            message: "No API key available (env var unset, file missing or empty).",
        };
    }
    const start = Date.now();
    try {
        const res = await fetch("https://api.anthropic.com/v1/messages", {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
                "x-api-key": effectiveKey,
                "anthropic-version": "2023-06-01",
            },
            body: JSON.stringify({
                model: "claude-haiku-4-5-20251001",
                max_tokens: 1,
                messages: [{ role: "user", content: "hi" }],
            }),
            signal: AbortSignal.timeout(10000),
        });
        const latency = Date.now() - start;
        if (res.ok || res.status === 429) {
            // Consume the response body to release the connection back to the pool.
            await res.text();
            console.error(`[anthropic-key] validate: valid (${res.status}, ${latency}ms)`);
            return {
                status: "valid",
                message: "API key is working. Credits available.",
            };
        }
        const body = await res.text();
        if (/credit balance|billing|purchase credits/i.test(body)) {
            console.error(`[anthropic-key] validate: billing (${res.status}, ${latency}ms)`);
            return {
                status: "billing",
                message: "API key is valid but credit balance is too low. The user must add credits at console.anthropic.com under Plans & Billing, or enable auto-top-up.",
            };
        }
        if (res.status === 401 ||
            res.status === 403 ||
            /authentication_error|permission_error/i.test(body)) {
            console.error(`[anthropic-key] validate: auth_error (${res.status}, ${latency}ms)`);
            return {
                status: "auth_error",
                message: "API key was rejected. It may be invalid, revoked, or expired. The user should create a new key at console.anthropic.com/settings/keys.",
            };
        }
        console.error(`[anthropic-key] validate: error (${res.status}, ${latency}ms)`);
        return {
            status: "error",
            message: `Could not verify API key. Anthropic returned ${res.status}. This may be a temporary outage — try again later.`,
        };
    }
    catch (err) {
        const latency = Date.now() - start;
        const msg = err instanceof Error ? err.message : String(err);
        console.error(`[anthropic-key] validate: error (network failure, ${latency}ms): ${msg}`);
        return {
            status: "error",
            message: `Could not reach the Anthropic API: ${msg}. Check network connectivity.`,
        };
    }
}
// ---------------------------------------------------------------------------
// keyFilePath — expose the resolved path for callers that need it
// (e.g., existsSync checks, user-facing messages about the key location)
// ---------------------------------------------------------------------------
function keyFilePath() {
    return resolveKeyFilePath();
}
//# sourceMappingURL=index.js.map