← Files ForgeMindARCHIVED FILE
src/paths.mjs
2.42 KB · Oct 2, 2026 · 00:31 UTC
import { access, stat } from 'node:fs/promises';
import path from 'node:path';
import { ForgeMindError } from './errors.mjs';
let artifactRedirection = null;
export function setArtifactRedirection(projectRoot, stateRoot) {
artifactRedirection = { projectRoot: path.resolve(projectRoot), stateRoot: path.resolve(stateRoot) };
}
export function clearArtifactRedirection() {
artifactRedirection = null;
}
export async function resolvePluginRoot(start = process.cwd()) {
let current = path.resolve(start);
if (!(await isDirectory(current))) {
current = path.dirname(current);
}
while (true) {
const manifest = path.join(current, '.codex-plugin', 'plugin.json');
try {
await access(manifest);
return current;
} catch {
const parent = path.dirname(current);
if (parent === current) {
throw new ForgeMindError(
'FM_PLUGIN_ROOT_NOT_FOUND',
`No .codex-plugin/plugin.json found above ${path.resolve(start)}`,
);
}
current = parent;
}
}
}
export async function resolveWorkspace(candidate = process.cwd()) {
const resolved = path.resolve(candidate);
if (!(await isDirectory(resolved))) {
throw new ForgeMindError('FM_WORKSPACE_INVALID', `Workspace is not a directory: ${resolved}`);
}
return resolved;
}
export function assertContained(parent, target) {
const resolvedParent = path.resolve(parent);
const resolvedTarget = path.resolve(target);
if (artifactRedirection?.projectRoot === resolvedParent) {
const projectRelative = path.relative(resolvedParent, resolvedTarget);
if (projectRelative === '.codex-orchestrator' || projectRelative.startsWith(`.codex-orchestrator${path.sep}`)) {
return assertContained(artifactRedirection.stateRoot, path.join(artifactRedirection.stateRoot, projectRelative.slice('.codex-orchestrator'.length).replace(/^[\\/]+/, '')));
}
}
const relative = path.relative(resolvedParent, resolvedTarget);
if (relative === '..' || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) {
throw new ForgeMindError('FM_PATH_ESCAPE', `Path escapes allowed root: ${resolvedTarget}`, {
details: { parent: resolvedParent, target: resolvedTarget },
});
}
return resolvedTarget;
}
async function isDirectory(candidate) {
try {
return (await stat(candidate)).isDirectory();
} catch {
return false;
}
}
SHA-256: bd7a2af49d85d880928f76a2585f5a3b2ad9e0db82547c583928d74f06f27404