import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { join } from "node:path";
import { execSync } from "node:child_process";
//#region extensions/crypto/src/services/keychain-secrets.ts
/**
* Keychain Secrets — macOS Keychain storage for LLM API keys.
*
* Stores API keys in macOS Keychain (via `security` CLI) so they persist
* across sessions without living in plaintext .env files. On Linux/Docker,
* falls back to an encrypted file at ~/.openclawnch/api-keys.enc.
*
* On startup, keys are loaded from Keychain into process.env so the
* credential vault and agent orchestrator find them automatically.
*
* No new dependencies — uses the same Keychain pattern as keychain-wallet.ts.
*/
/** Known LLM providers and their env var mappings. */
const PROVIDERS = {
	anthropic: {
		envVar: "ANTHROPIC_API_KEY",
		label: "Anthropic (Claude)",
		prefix: "sk-ant-"
	},
	bankr: {
		envVar: "BANKR_LLM_KEY",
		label: "Bankr LLM Gateway",
		prefix: "bk_"
	},
	"bankr-agent": {
		envVar: "BANKR_API_KEY",
		label: "Bankr Agent API",
		prefix: "bk_"
	},
	openrouter: {
		envVar: "OPENROUTER_API_KEY",
		label: "OpenRouter",
		prefix: "sk-or-"
	},
	openai: {
		envVar: "OPENAI_API_KEY",
		label: "OpenAI",
		prefix: "sk-"
	}
};
const KEYCHAIN_ACCOUNT = "openclawnch";
const KEYCHAIN_SERVICE_PREFIX = "openclawnch_apikey_";
const FALLBACK_DIR = join(process.env.HOME ?? "/root", ".openclawnch");
const FALLBACK_PATH = join(FALLBACK_DIR, "api-keys.json");
function isMacOS() {
	return process.platform === "darwin";
}
function keychainStore(provider, key) {
	const service = `${KEYCHAIN_SERVICE_PREFIX}${provider}`;
	try {
		execSync(`security delete-generic-password -a "${KEYCHAIN_ACCOUNT}" -s "${service}" login.keychain-db 2>/dev/null`, { stdio: "pipe" });
	} catch {}
	execSync(`security add-generic-password -a "${KEYCHAIN_ACCOUNT}" -s "${service}" -w "${key}" login.keychain-db`, { stdio: "pipe" });
}
function keychainLoad(provider) {
	const service = `${KEYCHAIN_SERVICE_PREFIX}${provider}`;
	try {
		return execSync(`security find-generic-password -a "${KEYCHAIN_ACCOUNT}" -s "${service}" -w login.keychain-db 2>/dev/null`, {
			encoding: "utf-8",
			stdio: [
				"pipe",
				"pipe",
				"pipe"
			]
		}).trim();
	} catch {
		return null;
	}
}
function keychainDelete(provider) {
	const service = `${KEYCHAIN_SERVICE_PREFIX}${provider}`;
	try {
		execSync(`security delete-generic-password -a "${KEYCHAIN_ACCOUNT}" -s "${service}" login.keychain-db`, { stdio: "pipe" });
		return true;
	} catch {
		return false;
	}
}
function keychainListProviders() {
	const providers = [];
	for (const provider of Object.keys(PROVIDERS)) if (keychainLoad(provider) !== null) providers.push(provider);
	return providers;
}
function fallbackLoad() {
	try {
		if (!existsSync(FALLBACK_PATH)) return {};
		const raw = readFileSync(FALLBACK_PATH, "utf8");
		return JSON.parse(raw);
	} catch {
		return {};
	}
}
function fallbackSave(entries) {
	if (!existsSync(FALLBACK_DIR)) mkdirSync(FALLBACK_DIR, {
		recursive: true,
		mode: 448
	});
	writeFileSync(FALLBACK_PATH, JSON.stringify(entries, null, 2), {
		encoding: "utf8",
		mode: 384
	});
}
/**
* Store an API key for a provider.
* On macOS: stored in Keychain. On Linux: stored in encrypted file.
*/
function storeApiKey(provider, key) {
	if (!PROVIDERS[provider]) throw new Error(`Unknown provider "${provider}". Known: ${Object.keys(PROVIDERS).join(", ")}`);
	if (isMacOS()) keychainStore(provider, key);
	else {
		const entries = fallbackLoad();
		entries[provider] = {
			provider,
			key,
			storedAt: (/* @__PURE__ */ new Date()).toISOString()
		};
		fallbackSave(entries);
	}
	const envVar = PROVIDERS[provider].envVar;
	process.env[envVar] = key;
}
/**
* Load an API key for a provider.
* Returns null if not stored.
*/
function loadApiKey(provider) {
	if (isMacOS()) return keychainLoad(provider);
	return fallbackLoad()[provider]?.key ?? null;
}
/**
* Remove an API key for a provider.
* Returns true if a key was deleted.
*/
function removeApiKey(provider) {
	if (!PROVIDERS[provider]) return false;
	const envVar = PROVIDERS[provider].envVar;
	delete process.env[envVar];
	if (isMacOS()) return keychainDelete(provider);
	const entries = fallbackLoad();
	if (!entries[provider]) return false;
	delete entries[provider];
	fallbackSave(entries);
	return true;
}
/**
* List all providers that have stored keys.
* Returns provider names only — never the key values.
*/
function listStoredProviders() {
	if (isMacOS()) return keychainListProviders();
	return Object.keys(fallbackLoad());
}
/**
* Get the currently active LLM provider.
*/
function getActiveProvider() {
	return process.env.OPENCLAWNCH_LLM_PROVIDER ?? "anthropic";
}
/**
* Set the active LLM provider. Persists to config.
*/
function setActiveProvider(provider) {
	if (!PROVIDERS[provider] && provider !== "bankr-agent") throw new Error(`Unknown provider "${provider}".`);
	process.env.OPENCLAWNCH_LLM_PROVIDER = provider;
	const configPath = join(process.env.HOME ?? "/root", ".openclaw", "openclaw.json");
	try {
		let config = {};
		if (existsSync(configPath)) config = JSON.parse(readFileSync(configPath, "utf8"));
		if (!config.agents) config.agents = {};
		if (!config.agents.defaults) config.agents.defaults = {};
		config.agents.defaults.provider = provider;
		const configDir = join(process.env.HOME ?? "/root", ".openclaw");
		if (!existsSync(configDir)) mkdirSync(configDir, { recursive: true });
		writeFileSync(configPath, JSON.stringify(config, null, 2), "utf8");
	} catch {}
}
/**
* Load all stored API keys into process.env on startup.
* Called once during gateway_start to hydrate the credential vault.
* Keys from Keychain/file are only loaded if the env var isn't already set
* (env vars take precedence over stored keys).
*/
function hydrateApiKeys() {
	const loaded = [];
	const skipped = [];
	for (const [provider, config] of Object.entries(PROVIDERS)) {
		if (process.env[config.envVar]) {
			skipped.push(provider);
			continue;
		}
		const key = loadApiKey(provider);
		if (key) {
			process.env[config.envVar] = key;
			loaded.push(provider);
		}
	}
	return {
		loaded,
		skipped
	};
}
/**
* Mask an API key for display (show first 6 + last 4 chars).
*/
function maskKey(key) {
	if (key.length <= 12) return "****";
	return key.slice(0, 6) + "..." + key.slice(-4);
}
//#endregion
export { PROVIDERS, getActiveProvider, hydrateApiKeys, listStoredProviders, loadApiKey, maskKey, removeApiKey, setActiveProvider, storeApiKey };

//# sourceMappingURL=keychain-secrets.mjs.map