Files
gnome-jarvis/vendor/agent-harness/lib/paths.js
T
2026-09-11 13:41:22 -04:00

111 lines
2.7 KiB
JavaScript

/**
* Local storage for the standalone harness (sessions, models, vision stills).
* Override with AGENT_HARNESS_HOME.
*/
const path = require('path');
const fs = require('fs');
const os = require('os');
function getHome() {
return (
process.env.AGENT_HARNESS_HOME ||
path.join(os.homedir(), '.agent-harness')
);
}
function getStorageRoot() {
return getHome();
}
function getQvacRoot() {
return path.join(getStorageRoot(), 'qvac');
}
function getAgentRoot() {
return path.join(getStorageRoot(), 'agent');
}
function ensureDir(dir) {
try {
fs.mkdirSync(dir, { recursive: true });
} catch (_) {}
return dir;
}
function ensureQvacRoot() {
return ensureDir(getQvacRoot());
}
function ensureAgentRoot() {
return ensureDir(getAgentRoot());
}
function sanitizeId(id) {
return String(id)
.replace(/[^a-zA-Z0-9._-]/g, '_')
.slice(0, 128);
}
function originHash(origin) {
const s = String(origin || 'local');
let h = 5381;
for (let i = 0; i < s.length; i++) h = ((h << 5) + h + s.charCodeAt(i)) | 0;
return Math.abs(h).toString(16);
}
function getAgentOriginRoot(origin) {
return path.join(ensureAgentRoot(), 'workspaces', sanitizeId(originHash(origin) || 'default'));
}
function isPathInside(root, candidate) {
const absRoot = path.resolve(root);
const abs = path.resolve(candidate);
const rel = path.relative(absRoot, abs);
return !(rel.startsWith('..') || path.isAbsolute(rel));
}
function resolveUnderRoot(root, userPath) {
if (!userPath || typeof userPath !== 'string') throw new Error('path is required');
const absRoot = path.resolve(ensureDir(root));
const resolved = path.isAbsolute(userPath)
? path.resolve(userPath)
: path.resolve(absRoot, userPath.replace(/^\/+/, ''));
const rel = path.relative(absRoot, resolved);
if (rel.startsWith('..') || path.isAbsolute(rel)) {
throw new Error('path escapes allowlisted root');
}
return resolved;
}
function resolveUnderAnyRoot(roots, userPath, fallbackRoot) {
if (!userPath || typeof userPath !== 'string') throw new Error('path is required');
const list = (roots || []).filter(Boolean).map((r) => path.resolve(r));
if (fallbackRoot) list.unshift(path.resolve(fallbackRoot));
if (!list.length) throw new Error('no allowlisted roots');
if (path.isAbsolute(userPath)) {
const resolved = path.resolve(userPath);
for (const root of list) {
if (isPathInside(root, resolved)) return resolved;
}
throw new Error('path escapes allowlisted roots');
}
return resolveUnderRoot(list[0], userPath);
}
module.exports = {
getHome,
getStorageRoot,
getQvacRoot,
getAgentRoot,
getAgentOriginRoot,
originHash,
ensureDir,
ensureQvacRoot,
ensureAgentRoot,
sanitizeId,
isPathInside,
resolveUnderRoot,
resolveUnderAnyRoot,
};