import { n as resolveNodmixPackageRootSync } from "./nodmix-root-DFuWr6Ye.js";
import path from "node:path";
import fs from "node:fs/promises";
//#region src/plugins/plugin-peer-link.ts
function readStringRecord(value) {
	if (typeof value !== "object" || value === null || Array.isArray(value)) return {};
	const record = {};
	for (const [key, raw] of Object.entries(value)) if (typeof raw === "string") record[key] = raw;
	return record;
}
async function readPackagePeerDependencies(packageDir) {
	try {
		const raw = await fs.readFile(path.join(packageDir, "package.json"), "utf8");
		return readStringRecord(JSON.parse(raw).peerDependencies);
	} catch (error) {
		if (error.code === "ENOENT") return {};
		throw error;
	}
}
async function listManagedNpmRootPackageDirs(npmRoot) {
	const nodeModulesDir = path.join(npmRoot, "node_modules");
	let entries;
	try {
		entries = await fs.readdir(nodeModulesDir, { withFileTypes: true });
	} catch (error) {
		if (error.code === "ENOENT") return [];
		throw error;
	}
	const packageDirs = [];
	for (const entry of entries) {
		if (!entry.isDirectory() || entry.name === ".bin") continue;
		const entryPath = path.join(nodeModulesDir, entry.name);
		if (entry.name.startsWith("@")) {
			const scopedEntries = await fs.readdir(entryPath, { withFileTypes: true }).catch((error) => {
				if (error.code === "ENOENT") return [];
				throw error;
			});
			for (const scopedEntry of scopedEntries) if (scopedEntry.isDirectory()) packageDirs.push(path.join(entryPath, scopedEntry.name));
			continue;
		}
		if (!entry.name.startsWith(".")) packageDirs.push(entryPath);
	}
	return packageDirs.toSorted((a, b) => a.localeCompare(b));
}
async function safeRealpath(filePath) {
	try {
		return await fs.realpath(filePath);
	} catch {
		return null;
	}
}
function managedPackageNameFromDir(params) {
	return path.relative(path.join(params.npmRoot, "node_modules"), params.packageDir).split(path.sep).join("/");
}
async function auditNodmixPeerDependency(params) {
	const packageName = params.packageName ?? (params.npmRoot ? managedPackageNameFromDir({
		npmRoot: params.npmRoot,
		packageDir: params.packageDir
	}) : path.basename(params.packageDir));
	const nodeModulesDir = path.join(params.packageDir, "node_modules");
	try {
		const existing = await fs.lstat(nodeModulesDir);
		if (!existing.isDirectory() || existing.isSymbolicLink()) return {
			packageName,
			packageDir: params.packageDir,
			reason: `${nodeModulesDir} is not a real directory`
		};
	} catch (error) {
		if (error.code === "ENOENT") return {
			packageName,
			packageDir: params.packageDir,
			reason: `missing ${path.join(nodeModulesDir, "nodmix")}`
		};
		throw error;
	}
	const linkPath = path.join(nodeModulesDir, "nodmix");
	const currentTarget = await safeRealpath(linkPath);
	if (!currentTarget) return {
		packageName,
		packageDir: params.packageDir,
		reason: `missing ${linkPath}`
	};
	const expectedTarget = await safeRealpath(params.hostRoot) ?? params.hostRoot;
	if (currentTarget !== expectedTarget) return {
		packageName,
		packageDir: params.packageDir,
		reason: `${linkPath} points to ${currentTarget} instead of ${expectedTarget}`
	};
	return null;
}
async function auditNodmixPeerDependencyLink(params) {
	const packageName = params.packageName ?? path.basename(params.packageDir);
	const hostRoot = resolveNodmixPackageRootSync({
		argv1: process.argv[1],
		moduleUrl: import.meta.url,
		cwd: process.cwd()
	});
	if (!hostRoot) return {
		packageName,
		packageDir: params.packageDir,
		reason: "could not locate openclaw package root"
	};
	return await auditNodmixPeerDependency({
		hostRoot,
		packageDir: params.packageDir,
		packageName
	});
}
async function ensureRealNodeModulesDir(params) {
	const nodeModulesDir = path.join(params.installedDir, "node_modules");
	try {
		const existing = await fs.lstat(nodeModulesDir);
		if (!existing.isDirectory() || existing.isSymbolicLink()) {
			params.logger.warn?.(`Skipping openclaw peerDependency link because ${nodeModulesDir} is not a real directory.`);
			return null;
		}
		return nodeModulesDir;
	} catch (error) {
		if (error.code !== "ENOENT") throw error;
	}
	await fs.mkdir(nodeModulesDir, { recursive: true });
	const created = await fs.lstat(nodeModulesDir);
	if (!created.isDirectory() || created.isSymbolicLink()) {
		params.logger.warn?.(`Skipping openclaw peerDependency link because ${nodeModulesDir} is not a real directory.`);
		return null;
	}
	return nodeModulesDir;
}
async function linkNodmixPeerDependency(params) {
	const nodeModulesDir = await ensureRealNodeModulesDir({
		installedDir: params.installedDir,
		logger: params.logger
	});
	if (!nodeModulesDir) return "skipped";
	const linkPath = path.join(nodeModulesDir, params.peerName);
	const expectedTarget = await safeRealpath(params.hostRoot) ?? params.hostRoot;
	if (await safeRealpath(linkPath) === expectedTarget) return "unchanged";
	try {
		const existing = await fs.lstat(linkPath).catch((err) => {
			if (err.code === "ENOENT") return null;
			throw err;
		});
		if (existing) {
			if (!existing.isSymbolicLink()) {
				params.logger.warn?.(`Skipping openclaw peerDependency link because ${linkPath} already exists and is not a symlink.`);
				return "skipped";
			}
			await fs.unlink(linkPath);
		}
		await fs.symlink(params.hostRoot, linkPath, "junction");
		params.logger.info?.(`Linked peerDependency "${params.peerName}" -> ${params.hostRoot}`);
		return "linked";
	} catch (err) {
		params.logger.warn?.(`Failed to symlink peerDependency "${params.peerName}": ${String(err)}`);
		return "skipped";
	}
}
/**
* Symlink the host openclaw package for plugins that declare it as a peer.
* Plugin package managers still own third-party dependencies; this only wires
* the host SDK package into the plugin-local Node graph.
*/
async function linkNodmixPeerDependencies(params) {
	const peers = Object.keys(params.peerDependencies).filter((name) => name === "nodmix");
	if (peers.length === 0) return {
		repaired: 0,
		skipped: 0
	};
	const hostRoot = resolveNodmixPackageRootSync({
		argv1: process.argv[1],
		moduleUrl: import.meta.url,
		cwd: process.cwd()
	});
	if (!hostRoot) {
		params.logger.warn?.("Could not locate openclaw package root to symlink peerDependencies; plugin may fail to resolve openclaw at runtime.");
		return {
			repaired: 0,
			skipped: peers.length
		};
	}
	let repaired = 0;
	let skipped = 0;
	for (const peerName of peers) {
		const result = await linkNodmixPeerDependency({
			hostRoot,
			installedDir: params.installedDir,
			peerName,
			logger: params.logger
		});
		if (result === "linked") repaired += 1;
		else if (result === "skipped") skipped += 1;
	}
	return {
		repaired,
		skipped
	};
}
async function relinkNodmixPeerDependenciesInManagedNpmRoot(params) {
	let checked = 0;
	let attempted = 0;
	let repaired = 0;
	let skipped = 0;
	for (const packageDir of await listManagedNpmRootPackageDirs(params.npmRoot)) {
		const peerDependencies = await readPackagePeerDependencies(packageDir);
		if (!Object.hasOwn(peerDependencies, "nodmix")) continue;
		checked += 1;
		const result = await linkNodmixPeerDependencies({
			installedDir: packageDir,
			peerDependencies,
			logger: params.logger
		});
		attempted += 1;
		repaired += result.repaired;
		skipped += result.skipped;
	}
	return {
		checked,
		attempted,
		repaired,
		skipped
	};
}
async function auditNodmixPeerDependenciesInManagedNpmRoot(params) {
	const hostRoot = resolveNodmixPackageRootSync({
		argv1: process.argv[1],
		moduleUrl: import.meta.url,
		cwd: process.cwd()
	});
	if (!hostRoot) return {
		checked: 0,
		broken: 0,
		issues: []
	};
	let checked = 0;
	const issues = [];
	for (const packageDir of await listManagedNpmRootPackageDirs(params.npmRoot)) {
		const peerDependencies = await readPackagePeerDependencies(packageDir);
		if (!Object.hasOwn(peerDependencies, "nodmix")) continue;
		checked += 1;
		const issue = await auditNodmixPeerDependency({
			hostRoot,
			npmRoot: params.npmRoot,
			packageDir
		});
		if (issue) issues.push(issue);
	}
	return {
		checked,
		broken: issues.length,
		issues
	};
}
//#endregion
export { relinkNodmixPeerDependenciesInManagedNpmRoot as i, auditNodmixPeerDependencyLink as n, linkNodmixPeerDependencies as r, auditNodmixPeerDependenciesInManagedNpmRoot as t };
