akm-cli
Version:
akm (Agent Knowledge Manager) — a portable, local-first capability library for AI agents. Discover, load, share, and improve reusable skills, scripts, workflows, and knowledge across any shell-capable coding agent, including Claude Code, OpenCode, and Cur
79 lines (78 loc) • 3.34 kB
JavaScript
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
import fs from "node:fs";
import path from "node:path";
import { UsageError } from "../core/errors.js";
export function captureFrozenDirectoryIdentity(rootInput, relativeCwd) {
const requestedRoot = path.resolve(rootInput);
const requestedCwd = path.resolve(requestedRoot, relativeCwd ?? ".");
requireContained(requestedRoot, requestedCwd);
const realRoot = fs.realpathSync(requestedRoot);
const realCwd = fs.realpathSync(requestedCwd);
requireContained(realRoot, realCwd);
const rootStat = fs.statSync(realRoot, { bigint: true });
const cwdStat = fs.statSync(realCwd, { bigint: true });
if (!rootStat.isDirectory() || !cwdStat.isDirectory()) {
throw new UsageError("Execution root and cwd must both be directories.", "INVALID_FLAG_VALUE");
}
return Object.freeze({
requestedRoot,
realRoot,
rootDevice: rootStat.dev.toString(),
rootInode: rootStat.ino.toString(),
requestedCwd,
realCwd,
cwdDevice: cwdStat.dev.toString(),
cwdInode: cwdStat.ino.toString(),
});
}
/**
* Re-verify, immediately before spawn, that a frozen cwd still resolves
* inside its execution root — path containment and resolved-path identity,
* NOT device/inode identity. Unlike the removed assertFrozenDirectoryIdentity,
* this does not compare device/inode numbers against the frozen snapshot: a
* remount, an rsync, or a container rebuild that leaves the same real
* content at the same real PATH must not abort a dispatch. What still
* throws is anything that changes the resolved real PATH itself — a
* symlink swap, an ancestor swap, a file replacing the directory, or the
* root itself being replaced by a symlink elsewhere (root-vs-cwd
* containment alone can't catch that last one, since both resolve together
* under the new root; comparing the root's own resolved path to its frozen
* value catches it).
*/
export function assertFrozenDirectoryContained(identity) {
let realRoot;
let realCwd;
try {
realRoot = fs.realpathSync(identity.requestedRoot);
realCwd = fs.realpathSync(identity.requestedCwd);
}
catch {
throw changed(identity.requestedCwd);
}
if (realRoot !== identity.realRoot)
throw changed(identity.requestedCwd);
const relative = path.relative(realRoot, realCwd);
if (relative.startsWith("..") || path.isAbsolute(relative)) {
throw changed(identity.requestedCwd);
}
let cwdStat;
try {
cwdStat = fs.statSync(realCwd, { bigint: true });
}
catch {
throw changed(identity.requestedCwd);
}
if (!cwdStat.isDirectory())
throw changed(identity.requestedCwd);
}
function changed(cwd) {
return new UsageError(`Frozen execution cwd ${cwd} changed and no longer resolves inside its root.`, "PATH_ESCAPE_VIOLATION");
}
function requireContained(root, candidate) {
const relative = path.relative(root, candidate);
if (relative.startsWith("..") || path.isAbsolute(relative)) {
throw new UsageError(`${candidate} escapes its execution root.`, "PATH_ESCAPE_VIOLATION");
}
}