/**
 * olam keys — Manage operator-state LLM API keys at ~/.olam/keys.yaml.
 *
 * Subcommands/options:
 *   olam keys                          — list current keys (values redacted)
 *   olam keys --set OPENAI=sk-proj-...  — write/update a single key
 *   olam keys --unset OPENAI            — remove a key
 *   olam keys path                      — print ~/.olam/keys.yaml absolute path
 *
 * Keys propagate to worlds at boot as OLAM_LLM_<UPPER_KEY> env vars.
 * File is chmod 600; values are never printed in full by the CLI.
 */
import * as fs from 'node:fs';
import * as os from 'node:os';
import * as path from 'node:path';
import YAML from 'yaml';
import { printError, printSuccess, printHeader, printInfo } from '../output.js';
// ── File helpers ──────────────────────────────────────────────────
/** Honors OLAM_HOME for tests — mirrors olamHome() in host-cp.ts. */
function olamHome() {
    return process.env.OLAM_HOME ?? path.join(os.homedir(), '.olam');
}
/** Absolute path to ~/.olam/keys.yaml. */
export function keysFilePath() {
    return path.join(olamHome(), 'keys.yaml');
}
/**
 * Read ~/.olam/keys.yaml and return a plain key→value map.
 *
 * Returns null when the file is missing or empty — not an error, just
 * means the operator hasn't set any keys yet.
 * Returns null for malformed YAML so callers can surface a clear message
 * instead of a raw parse stack trace.
 */
export function readKeysFile() {
    const filePath = keysFilePath();
    if (!fs.existsSync(filePath))
        return null;
    const raw = fs.readFileSync(filePath, 'utf-8').trim();
    if (raw.length === 0)
        return null;
    try {
        const parsed = YAML.parse(raw);
        if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed))
            return null;
        const result = {};
        for (const [k, v] of Object.entries(parsed)) {
            if (typeof v === 'string')
                result[k] = v;
        }
        return Object.keys(result).length > 0 ? result : null;
    }
    catch {
        return null;
    }
}
/**
 * Ensure ~/.olam/ exists and write the given map to ~/.olam/keys.yaml
 * with chmod 600 so only the operator can read it.
 */
function writeKeysFile(keys) {
    const dir = olamHome();
    if (!fs.existsSync(dir)) {
        fs.mkdirSync(dir, { recursive: true });
    }
    const filePath = keysFilePath();
    const content = YAML.stringify(keys);
    fs.writeFileSync(filePath, content, { encoding: 'utf-8', mode: 0o600 });
    // Enforce 600 even if file already existed with looser perms.
    fs.chmodSync(filePath, 0o600);
}
/** Redact a secret value: keep first 8 chars, replace the rest with "...". */
function redact(value) {
    if (value.length <= 8)
        return value + '...';
    return value.slice(0, 8) + '...';
}
// ── Command registration ──────────────────────────────────────────
export function registerKeys(program) {
    const keysCmd = program
        .command('keys')
        .description('Manage LLM API keys stored at ~/.olam/keys.yaml')
        .option('--set <KEY=VALUE>', 'Write or update a key (e.g. --set OPENAI=sk-proj-...)')
        .option('--unset <KEY>', 'Remove a key')
        .action((opts) => {
        // olam keys path — print the file path
        // (handled as subcommand below; this action handles the flag paths)
        if (opts.set) {
            const eqIdx = opts.set.indexOf('=');
            if (eqIdx <= 0) {
                printError('--set requires KEY=VALUE format (e.g. --set OPENAI=sk-proj-...)');
                process.exitCode = 1;
                return;
            }
            const key = opts.set.slice(0, eqIdx).trim().toUpperCase();
            const value = opts.set.slice(eqIdx + 1).trim();
            if (key.length === 0) {
                printError('Key name cannot be empty');
                process.exitCode = 1;
                return;
            }
            if (value.length === 0) {
                printError('Value cannot be empty — to remove a key use --unset');
                process.exitCode = 1;
                return;
            }
            const existing = readKeysFile() ?? {};
            existing[key] = value;
            writeKeysFile(existing);
            printSuccess(`${key} saved to ${keysFilePath()}`);
            return;
        }
        if (opts.unset) {
            const key = opts.unset.trim().toUpperCase();
            const existing = readKeysFile();
            if (!existing || !(key in existing)) {
                printError(`Key ${key} not found in ${keysFilePath()}`);
                process.exitCode = 1;
                return;
            }
            const { [key]: _removed, ...rest } = existing;
            if (Object.keys(rest).length === 0) {
                // Remove the file entirely when the last key is unset.
                fs.unlinkSync(keysFilePath());
            }
            else {
                writeKeysFile(rest);
            }
            printSuccess(`${key} removed`);
            return;
        }
        // Default: list keys with redacted values
        const keys = readKeysFile();
        printHeader('LLM API keys (~/.olam/keys.yaml)');
        if (!keys) {
            console.log('  No keys configured. Use olam keys --set KEY=VALUE to add one.');
            return;
        }
        for (const [k, v] of Object.entries(keys)) {
            printInfo(k, redact(v));
        }
    });
    // olam keys path — print the file path (no redaction needed)
    keysCmd
        .command('path')
        .description('Print the absolute path to ~/.olam/keys.yaml')
        .action(() => {
        console.log(keysFilePath());
    });
}
//# sourceMappingURL=keys.js.map