import { createCipheriv, createDecipheriv, randomBytes, scryptSync } from "node:crypto";
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
import { join } from "node:path";
import { execFileSync, execSync } from "node:child_process";
//#region extensions/crypto/src/services/agent-keystore.ts
/**
* Agent Keystore — Secure storage for the agent's private key.
*
* The agent account is a HybridDeleGator smart account deployed by the user.
* The agent's private key (delegate key) is what enables autonomous execution.
* Losing this key means the agent can't execute; the user can still access
* funds via their owner key.
*
* Storage hierarchy (checked in order):
* 1. macOS Keychain — most secure, survives app reinstalls
* 2. Encrypted file — AES-256-GCM with user passphrase
* 3. In-memory only — lost on restart (for testing)
*
* The key is NEVER logged, written to unencrypted files, or sent over the network.
*/
const AGENT_DIR = join(process.env.HOME ?? "/home/openclawnch", ".openclawnch", "agent");
const KEYSTORE_PATH = join(AGENT_DIR, "keystore.enc");
const META_PATH = join(AGENT_DIR, "meta.json");
const KEYCHAIN_SERVICE = "openclawnch-agent";
let _cachedKey = null;
let _cachedMeta = null;
function ensureDir() {
	if (!existsSync(AGENT_DIR)) mkdirSync(AGENT_DIR, {
		recursive: true,
		mode: 448
	});
}
function isKeychainAvailable() {
	if (process.platform !== "darwin") return false;
	try {
		execSync("which security", { stdio: "ignore" });
		return true;
	} catch {
		return false;
	}
}
function saveToKeychain(agentAddress, privateKey) {
	try {
		try {
			execFileSync("security", [
				"delete-generic-password",
				"-a",
				agentAddress,
				"-s",
				KEYCHAIN_SERVICE
			], { stdio: "ignore" });
		} catch {}
		execFileSync("security", [
			"add-generic-password",
			"-a",
			agentAddress,
			"-s",
			KEYCHAIN_SERVICE,
			"-w",
			privateKey,
			"-T",
			""
		], { stdio: "ignore" });
		return true;
	} catch {
		return false;
	}
}
function loadFromKeychain(agentAddress) {
	try {
		return execFileSync("security", [
			"find-generic-password",
			"-a",
			agentAddress,
			"-s",
			KEYCHAIN_SERVICE,
			"-w"
		], {
			encoding: "utf8",
			stdio: [
				"pipe",
				"pipe",
				"ignore"
			]
		}).trim();
	} catch {
		return null;
	}
}
function deriveFileKey(passphrase) {
	return scryptSync(passphrase, "openclawnch-agent-salt-v1", 32, {
		N: 131072,
		r: 8,
		p: 1,
		maxmem: 256 * 1024 * 1024
	});
}
function saveToEncryptedFile(privateKey, passphrase) {
	ensureDir();
	const key = deriveFileKey(passphrase);
	const iv = randomBytes(12);
	const cipher = createCipheriv("aes-256-gcm", key, iv);
	const encrypted = Buffer.concat([cipher.update(privateKey, "utf8"), cipher.final()]);
	const tag = cipher.getAuthTag();
	const data = Buffer.concat([
		iv,
		tag,
		encrypted
	]);
	const tmpPath = KEYSTORE_PATH + ".tmp." + Date.now();
	writeFileSync(tmpPath, data, { mode: 384 });
	renameSync(tmpPath, KEYSTORE_PATH);
}
function loadFromEncryptedFile(passphrase) {
	if (!existsSync(KEYSTORE_PATH)) return null;
	try {
		const data = readFileSync(KEYSTORE_PATH);
		const key = deriveFileKey(passphrase);
		const iv = data.subarray(0, 12);
		const tag = data.subarray(12, 28);
		const ciphertext = data.subarray(28);
		const decipher = createDecipheriv("aes-256-gcm", key, iv);
		decipher.setAuthTag(tag);
		return decipher.update(ciphertext) + decipher.final("utf8");
	} catch {
		return null;
	}
}
function saveMeta(meta) {
	ensureDir();
	_cachedMeta = meta;
	const tmpPath = META_PATH + ".tmp." + Date.now();
	writeFileSync(tmpPath, JSON.stringify(meta, null, 2), { mode: 384 });
	renameSync(tmpPath, META_PATH);
}
function loadMeta() {
	if (_cachedMeta) return _cachedMeta;
	if (!existsSync(META_PATH)) return null;
	try {
		_cachedMeta = JSON.parse(readFileSync(META_PATH, "utf8"));
		return _cachedMeta;
	} catch {
		return null;
	}
}
/**
* Store the agent's private key securely.
* Tries: macOS Keychain → encrypted file → memory only.
*
* Returns the storage method used.
*/
function storeAgentKey(agentAddress, privateKey, passphrase) {
	_cachedKey = privateKey;
	if (isKeychainAvailable()) {
		if (saveToKeychain(agentAddress, privateKey)) return "keychain";
	}
	if (passphrase && passphrase.length >= 8) {
		saveToEncryptedFile(privateKey, passphrase);
		return "encrypted_file";
	}
	return "memory";
}
/**
* Load the agent's private key.
* Tries: memory cache → macOS Keychain → encrypted file (passphrase from arg or env).
*/
function loadAgentKey(passphrase) {
	if (_cachedKey) return _cachedKey;
	const meta = loadMeta();
	if (!meta) return null;
	if (meta.storageMethod === "keychain" || isKeychainAvailable()) {
		const key = loadFromKeychain(meta.agentAddress);
		if (key) {
			_cachedKey = key;
			return key;
		}
	}
	if (!passphrase) passphrase = process.env.DELEGATOR_PASSPHRASE ?? void 0;
	if (passphrase) {
		const key = loadFromEncryptedFile(passphrase);
		if (key) {
			_cachedKey = key;
			return key;
		}
	}
	return null;
}
/**
* Check if an agent account exists (meta file present).
*/
function hasAgentAccount() {
	return loadMeta() !== null;
}
/**
* Clear all agent data (for testing).
*/
function resetAgentKeystore() {
	_cachedKey = null;
	_cachedMeta = null;
}
//#endregion
export { hasAgentAccount, loadAgentKey, loadMeta, resetAgentKeystore, saveMeta, storeAgentKey };

//# sourceMappingURL=agent-keystore.mjs.map