global setup and configuration.

This commit is contained in:
jb
2026-08-24 16:33:11 -04:00
parent 10f64b593b
commit 31c9363dbb
95 changed files with 12641 additions and 41 deletions
+635
View File
@@ -0,0 +1,635 @@
#!/usr/bin/env node
import { spawnSync } from "node:child_process";
import { constants } from "node:fs";
import {
access,
lstat,
mkdir,
readFile,
readdir,
readlink,
realpath,
symlink,
unlink,
} from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { fileURLToPath, pathToFileURL } from "node:url";
const SCRIPT_ROOT = path.resolve(
path.dirname(fileURLToPath(import.meta.url)),
"..",
);
const MANIFEST_PATH = "config/pi-links.json";
const COMMANDS = new Set(["check", "sync", "prune"]);
function usage() {
return (
`Usage: node scripts/pi-links.mjs <check|sync|prune> [--dry-run]\n\n` +
` check Report missing, stale, or conflicting exposure without writing\n` +
` sync Create or repair manifest-declared links and local package entries\n` +
` prune Remove repository-owned links and local package entries not in the manifest\n` +
` --dry-run Print sync/prune changes without applying them`
);
}
function isInside(parent, child) {
const relative = path.relative(parent, child);
return (
relative === "" ||
(!relative.startsWith("..") && !path.isAbsolute(relative))
);
}
async function pathExists(target) {
try {
await access(target, constants.F_OK);
return true;
} catch {
return false;
}
}
async function statOrUndefined(target) {
try {
return await lstat(target);
} catch (error) {
if (error?.code === "ENOENT") return undefined;
throw error;
}
}
async function canonical(target) {
try {
return await realpath(target);
} catch {
return path.resolve(target);
}
}
function parseSkillName(contents, skillFile) {
const frontmatter = contents.match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/);
if (!frontmatter) throw new Error(`${skillFile}: missing YAML frontmatter`);
const match = frontmatter[1].match(
/^name:\s*(?:"([^"]+)"|'([^']+)'|([^#\r\n]+))\s*$/m,
);
const name = (match?.[1] ?? match?.[2] ?? match?.[3] ?? "").trim();
if (!name) throw new Error(`${skillFile}: missing frontmatter name`);
if (!/^[a-z0-9][a-z0-9._-]*$/i.test(name)) {
throw new Error(`${skillFile}: unsafe skill name ${JSON.stringify(name)}`);
}
return name;
}
async function discoverSkills(root) {
const found = [];
async function visit(directory) {
const skillFile = path.join(directory, "SKILL.md");
if (await pathExists(skillFile)) {
const name = parseSkillName(await readFile(skillFile, "utf8"), skillFile);
found.push({ name, source: directory });
return;
}
const entries = await readdir(directory, { withFileTypes: true });
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
if (
!entry.isDirectory() ||
entry.name.startsWith(".") ||
entry.name === "node_modules"
)
continue;
await visit(path.join(directory, entry.name));
}
}
await visit(root);
return found;
}
async function readJson(file) {
const contents = await readFile(file, "utf8");
try {
return JSON.parse(contents);
} catch (error) {
const reason = error instanceof Error ? error.message : String(error);
throw new Error(`${file}: invalid JSON: ${reason}`, { cause: error });
}
}
function assertRecord(value, label) {
if (!value || typeof value !== "object" || Array.isArray(value)) {
throw new Error(`${label} must be an object`);
}
return value;
}
function assertStringArray(value, label) {
if (
!Array.isArray(value) ||
value.some((entry) => typeof entry !== "string")
) {
throw new Error(`${label} must be an array of strings`);
}
return value;
}
async function loadManifest(repoRoot) {
const manifestFile = path.join(repoRoot, MANIFEST_PATH);
const manifest = await readJson(manifestFile);
if (manifest.version !== 1)
throw new Error(`${manifestFile}: unsupported version`);
const skills = assertRecord(manifest.skills, "skills");
const extensions = assertRecord(manifest.extensions, "extensions");
const instructions = assertRecord(manifest.instructions, "instructions");
assertRecord(skills.projectOnly, "skills.projectOnly");
assertStringArray(skills.excluded, "skills.excluded");
assertRecord(extensions.globalSymlinks, "extensions.globalSymlinks");
assertRecord(
extensions.globalLocalPackages,
"extensions.globalLocalPackages",
);
assertRecord(instructions.globalSymlinks, "instructions.globalSymlinks");
if (typeof skills.root !== "string")
throw new Error("skills.root must be a string");
return manifest;
}
async function validateSource(repoRoot, relativeSource, kind) {
const source = path.resolve(repoRoot, relativeSource);
if (!isInside(repoRoot, source))
throw new Error(`${relativeSource}: source escapes repository`);
const stat = await statOrUndefined(source);
if (!stat) throw new Error(`${relativeSource}: source does not exist`);
if (kind === "skill") {
if (
!stat.isDirectory() ||
!(await pathExists(path.join(source, "SKILL.md")))
) {
throw new Error(`${relativeSource}: skill source must contain SKILL.md`);
}
} else if (kind === "instruction") {
if (!stat.isFile() || path.extname(source).toLowerCase() !== ".md") {
throw new Error(
`${relativeSource}: instruction source must be a Markdown file`,
);
}
} else if (stat.isDirectory()) {
if (
!(await pathExists(path.join(source, "index.ts"))) &&
!(await pathExists(path.join(source, "index.js")))
) {
throw new Error(
`${relativeSource}: extension directory must contain index.ts or index.js`,
);
}
} else if (!/\.[cm]?[jt]s$/i.test(source)) {
throw new Error(
`${relativeSource}: extension file must be JavaScript or TypeScript`,
);
}
return source;
}
async function resolveDesiredState(options = {}) {
const repoRoot = path.resolve(options.repoRoot ?? SCRIPT_ROOT);
const homeDir = path.resolve(options.homeDir ?? os.homedir());
const agentDir = path.resolve(
options.agentDir ??
process.env.PI_CODING_AGENT_DIR ??
path.join(homeDir, ".pi", "agent"),
);
const globalSkillsDir = path.resolve(
options.globalSkillsDir ?? path.join(homeDir, ".agents", "skills"),
);
const globalExtensionsDir = path.resolve(
options.globalExtensionsDir ?? path.join(agentDir, "extensions"),
);
const projectSkillsDir = path.join(repoRoot, ".pi", "skills");
const settingsFile = path.resolve(
options.settingsFile ?? path.join(agentDir, "settings.json"),
);
const manifest = await loadManifest(repoRoot);
const instructionDestinations = {
pi: path.join(agentDir, "AGENTS.md"),
codex: path.join(homeDir, ".codex", "AGENTS.md"),
claude: path.join(homeDir, ".claude", "CLAUDE.md"),
};
const skillsRoot = await validateSource(
repoRoot,
manifest.skills.root,
"skill-root",
).catch(async (error) => {
const source = path.resolve(repoRoot, manifest.skills.root);
const stat = await statOrUndefined(source);
if (!stat?.isDirectory()) throw error;
return source;
});
const discovered = await discoverSkills(skillsRoot);
const byName = new Map();
for (const skill of discovered) {
const previous = byName.get(skill.name);
if (previous) {
throw new Error(
`duplicate skill name ${skill.name}: ${previous.source} and ${skill.source}`,
);
}
byName.set(skill.name, skill);
}
const projectOnly = new Map(Object.entries(manifest.skills.projectOnly));
const excluded = new Set(manifest.skills.excluded);
for (const name of [...projectOnly.keys(), ...excluded]) {
if (!byName.has(name))
throw new Error(`manifest references unknown skill ${name}`);
}
for (const name of projectOnly.keys()) {
if (excluded.has(name))
throw new Error(`skill ${name} cannot be both project-only and excluded`);
}
const links = [];
for (const skill of discovered) {
if (excluded.has(skill.name)) continue;
const declaredProjectSource = projectOnly.get(skill.name);
if (declaredProjectSource !== undefined) {
const source = await validateSource(
repoRoot,
declaredProjectSource,
"skill",
);
if ((await canonical(source)) !== (await canonical(skill.source))) {
throw new Error(
`project-only skill ${skill.name} source does not match discovered source`,
);
}
links.push({
kind: "skill",
scope: "project",
name: skill.name,
source,
destination: path.join(projectSkillsDir, skill.name),
relative: true,
});
continue;
}
links.push({
kind: "skill",
scope: "global",
name: skill.name,
source: skill.source,
destination: path.join(globalSkillsDir, skill.name),
relative: false,
});
}
const extensionSources = new Set();
for (const [name, relativeSource] of Object.entries(
manifest.extensions.globalSymlinks,
)) {
const source = await validateSource(repoRoot, relativeSource, "extension");
const sourceKey = await canonical(source);
if (extensionSources.has(sourceKey))
throw new Error(
`extension source listed more than once: ${relativeSource}`,
);
extensionSources.add(sourceKey);
links.push({
kind: "extension",
scope: "global",
name,
source,
destination: path.join(globalExtensionsDir, name),
relative: false,
});
}
for (const [name, relativeSource] of Object.entries(
manifest.instructions.globalSymlinks,
)) {
const destination = instructionDestinations[name];
if (!destination)
throw new Error(`unknown global instruction target ${name}`);
const source = await validateSource(
repoRoot,
relativeSource,
"instruction",
);
links.push({
kind: "instruction",
scope: "global",
name,
source,
destination,
relative: false,
type: "file",
});
}
const packages = [];
for (const [name, relativeSource] of Object.entries(
manifest.extensions.globalLocalPackages,
)) {
const source = await validateSource(repoRoot, relativeSource, "extension");
const sourceKey = await canonical(source);
if (extensionSources.has(sourceKey)) {
throw new Error(
`extension ${name} is exposed as both a symlink and local package`,
);
}
extensionSources.add(sourceKey);
packages.push({ kind: "extension", scope: "global", name, source });
}
links.sort((a, b) => a.destination.localeCompare(b.destination));
packages.sort((a, b) => a.source.localeCompare(b.source));
return {
repoRoot,
settingsFile,
managedDirectories: [
globalSkillsDir,
globalExtensionsDir,
projectSkillsDir,
],
managedFiles: Object.values(instructionDestinations),
links,
packages,
};
}
function localPackagePath(source, settingsFile) {
if (typeof source !== "string") return undefined;
if (/^(?:npm:|git:|https?:|ssh:|git\+|file:)/i.test(source)) return undefined;
if (!path.isAbsolute(source) && !source.startsWith(".")) return undefined;
return path.resolve(path.dirname(settingsFile), source);
}
async function readLocalPackages(settingsFile) {
if (!(await pathExists(settingsFile))) return [];
const settings = await readJson(settingsFile);
if (!Array.isArray(settings.packages)) return [];
return settings.packages
.map((entry) => (typeof entry === "string" ? entry : entry?.source))
.map((source) => ({
source,
resolved: localPackagePath(source, settingsFile),
}))
.filter((entry) => entry.resolved);
}
async function inspectDesiredLink(link, repoRoot) {
const stat = await statOrUndefined(link.destination);
if (!stat) return { type: "create-link", link };
if (!stat.isSymbolicLink()) {
return {
type: "conflict",
path: link.destination,
reason: "destination is not a symlink",
};
}
const rawTarget = await readlink(link.destination);
const resolvedTarget = path.resolve(
path.dirname(link.destination),
rawTarget,
);
if ((await canonical(resolvedTarget)) === (await canonical(link.source)))
return undefined;
if (isInside(repoRoot, resolvedTarget))
return { type: "replace-link", link, previousTarget: resolvedTarget };
return {
type: "conflict",
path: link.destination,
reason: `symlink points outside this repository: ${rawTarget}`,
};
}
async function discoverStaleLinks(state, desiredDestinations) {
const stale = [];
for (const directory of state.managedDirectories) {
const stat = await statOrUndefined(directory);
if (!stat?.isDirectory()) continue;
const entries = await readdir(directory, { withFileTypes: true });
for (const entry of entries) {
if (!entry.isSymbolicLink()) continue;
const destination = path.join(directory, entry.name);
if (desiredDestinations.has(destination)) continue;
const rawTarget = await readlink(destination);
const resolvedTarget = path.resolve(directory, rawTarget);
if (isInside(state.repoRoot, resolvedTarget)) {
stale.push({
type: "remove-link",
destination,
previousTarget: resolvedTarget,
});
}
}
}
for (const destination of state.managedFiles) {
if (desiredDestinations.has(destination)) continue;
const stat = await statOrUndefined(destination);
if (!stat?.isSymbolicLink()) continue;
const rawTarget = await readlink(destination);
const resolvedTarget = path.resolve(path.dirname(destination), rawTarget);
if (isInside(state.repoRoot, resolvedTarget)) {
stale.push({
type: "remove-link",
destination,
previousTarget: resolvedTarget,
});
}
}
return stale;
}
export async function buildPlan(options = {}) {
const state = await resolveDesiredState(options);
const desiredDestinations = new Set(
state.links.map((link) => link.destination),
);
const desiredPackageSources = new Set(
await Promise.all(state.packages.map((entry) => canonical(entry.source))),
);
const operations = [];
const conflicts = [];
for (const link of state.links) {
const result = await inspectDesiredLink(link, state.repoRoot);
if (result?.type === "conflict") conflicts.push(result);
else if (result) operations.push(result);
}
operations.push(...(await discoverStaleLinks(state, desiredDestinations)));
const installedPackages = await readLocalPackages(state.settingsFile);
const installedCounts = new Map();
for (const entry of installedPackages) {
const key = await canonical(entry.resolved);
installedCounts.set(key, (installedCounts.get(key) ?? 0) + 1);
}
for (const [source, count] of installedCounts) {
if (count > 1 && isInside(state.repoRoot, source)) {
conflicts.push({
type: "conflict",
path: state.settingsFile,
reason: `local package appears ${count} times: ${source}`,
});
}
}
for (const pkg of state.packages) {
const source = await canonical(pkg.source);
if (!installedCounts.has(source))
operations.push({ type: "install-package", package: pkg });
}
for (const entry of installedPackages) {
const source = await canonical(entry.resolved);
if (
isInside(state.repoRoot, source) &&
!desiredPackageSources.has(source)
) {
operations.push({ type: "remove-package", source });
}
}
operations.sort((a, b) =>
describeOperation(a).localeCompare(describeOperation(b)),
);
conflicts.sort((a, b) =>
`${a.path}: ${a.reason}`.localeCompare(`${b.path}: ${b.reason}`),
);
return { state, operations, conflicts };
}
function describeOperation(operation) {
switch (operation.type) {
case "create-link":
return `create ${operation.link.destination} -> ${operation.link.source}`;
case "replace-link":
return `repair ${operation.link.destination} -> ${operation.link.source}`;
case "remove-link":
return `remove stale link ${operation.destination} -> ${operation.previousTarget}`;
case "install-package":
return `install local package ${operation.package.source}`;
case "remove-package":
return `remove stale local package ${operation.source}`;
default:
return `${operation.type}`;
}
}
function selectOperations(command, operations) {
if (command === "sync") {
return operations.filter(
(operation) =>
operation.type === "create-link" ||
operation.type === "replace-link" ||
operation.type === "install-package",
);
}
if (command === "prune") {
return operations.filter(
(operation) =>
operation.type === "remove-link" || operation.type === "remove-package",
);
}
return [];
}
async function createOrReplaceLink(operation) {
const { link } = operation;
await mkdir(path.dirname(link.destination), { recursive: true });
if (operation.type === "replace-link") await unlink(link.destination);
const target = link.relative
? path.relative(path.dirname(link.destination), link.source)
: link.source;
await symlink(target, link.destination, link.type ?? "dir");
}
function runPiPackage(command, source) {
const result = spawnSync("pi", [command, source], { stdio: "inherit" });
if (result.error) throw result.error;
if (result.status !== 0)
throw new Error(
`pi ${command} failed for ${source} with exit code ${result.status}`,
);
}
async function applyOperations(operations) {
for (const operation of operations) {
if (operation.type === "create-link" || operation.type === "replace-link") {
await createOrReplaceLink(operation);
} else if (operation.type === "remove-link") {
await unlink(operation.destination);
} else if (operation.type === "install-package") {
runPiPackage("install", operation.package.source);
} else if (operation.type === "remove-package") {
runPiPackage("remove", operation.source);
}
}
}
export async function run(command, options = {}) {
const plan = await buildPlan(options);
for (const conflict of plan.conflicts)
console.error(`CONFLICT ${conflict.path}: ${conflict.reason}`);
if (plan.conflicts.length > 0) return 2;
if (command === "check") {
if (plan.operations.length === 0) {
console.log(
`OK ${plan.state.links.length} links and ${plan.state.packages.length} local package entries match ${MANIFEST_PATH}`,
);
return 0;
}
for (const operation of plan.operations)
console.log(`DRIFT ${describeOperation(operation)}`);
return 1;
}
const selected = selectOperations(command, plan.operations);
if (selected.length === 0) {
console.log(`OK nothing to ${command}`);
return 0;
}
for (const operation of selected)
console.log(
`${options.dryRun ? "WOULD" : "APPLY"} ${describeOperation(operation)}`,
);
if (!options.dryRun) await applyOperations(selected);
return 0;
}
async function main(argv = process.argv.slice(2)) {
const command = argv.find((arg) => !arg.startsWith("-"));
const unknown = argv.filter(
(arg) => arg.startsWith("-") && arg !== "--dry-run",
);
if (!COMMANDS.has(command) || unknown.length > 0) {
console.error(usage());
return 2;
}
return run(command, { dryRun: argv.includes("--dry-run") });
}
if (
process.argv[1] &&
import.meta.url === pathToFileURL(path.resolve(process.argv[1])).href
) {
main()
.then((code) => {
process.exitCode = code;
})
.catch((error) => {
console.error(
`ERROR ${error instanceof Error ? error.message : String(error)}`,
);
process.exitCode = 2;
});
}
+218
View File
@@ -0,0 +1,218 @@
import assert from "node:assert/strict";
import {
mkdir,
mkdtemp,
readFile,
readlink,
symlink,
writeFile,
} from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import test from "node:test";
import { buildPlan, run } from "./pi-links.mjs";
async function writeJson(file, value) {
await mkdir(path.dirname(file), { recursive: true });
await writeFile(file, `${JSON.stringify(value, null, 2)}\n`);
}
async function writeSkill(directory, name) {
await mkdir(directory, { recursive: true });
await writeFile(
path.join(directory, "SKILL.md"),
`---\nname: ${name}\ndescription: test\n---\n\n# ${name}\n`,
);
}
async function writeExtension(target) {
if (path.extname(target)) {
await mkdir(path.dirname(target), { recursive: true });
await writeFile(target, "export default function () {}\n");
return;
}
await mkdir(target, { recursive: true });
await writeFile(
path.join(target, "index.ts"),
"export default function () {}\n",
);
}
async function fixture() {
const root = await mkdtemp(path.join(os.tmpdir(), "pi-links-test-"));
const repoRoot = path.join(root, "repo");
const homeDir = path.join(root, "home");
const agentDir = path.join(homeDir, ".pi", "agent");
const settingsFile = path.join(agentDir, "settings.json");
const globalSkill = path.join(repoRoot, "skills", "group", "global-skill");
const projectSkill = path.join(repoRoot, "skills", "tools", "project-skill");
const globalExtension = path.join(repoRoot, "extensions", "global-ext");
const packageExtension = path.join(
repoRoot,
"extensions",
"package-ext",
"index.ts",
);
const globalAgents = path.join(repoRoot, "docs", "global.AGENTS.md");
const globalClaude = path.join(repoRoot, "docs", "global.CLAUDE.md");
await writeSkill(globalSkill, "global-skill");
await writeSkill(projectSkill, "project-skill");
await writeExtension(globalExtension);
await writeExtension(packageExtension);
await mkdir(path.dirname(globalAgents), { recursive: true });
await writeFile(globalAgents, "# Global instructions\n");
await writeFile(globalClaude, "@global.AGENTS.md\n");
await writeJson(path.join(repoRoot, "config", "pi-links.json"), {
version: 1,
skills: {
root: "skills",
projectOnly: { "project-skill": "skills/tools/project-skill" },
excluded: [],
},
instructions: {
globalSymlinks: {
pi: "docs/global.AGENTS.md",
codex: "docs/global.AGENTS.md",
claude: "docs/global.CLAUDE.md",
},
},
extensions: {
globalSymlinks: { "global-ext": "extensions/global-ext" },
globalLocalPackages: { "package-ext": "extensions/package-ext/index.ts" },
},
});
await writeJson(settingsFile, {
packages: [path.relative(agentDir, packageExtension)],
});
return {
root,
repoRoot,
homeDir,
agentDir,
settingsFile,
globalSkill,
projectSkill,
globalExtension,
globalAgents,
globalClaude,
};
}
test("sync creates absolute global links and relative project links", async () => {
const options = await fixture();
assert.equal(await run("sync", options), 0);
assert.equal(await run("check", options), 0);
const globalSkillLink = path.join(
options.homeDir,
".agents",
"skills",
"global-skill",
);
const projectSkillLink = path.join(
options.repoRoot,
".pi",
"skills",
"project-skill",
);
const extensionLink = path.join(options.agentDir, "extensions", "global-ext");
const piInstructions = path.join(options.agentDir, "AGENTS.md");
const codexInstructions = path.join(options.homeDir, ".codex", "AGENTS.md");
const claudeInstructions = path.join(options.homeDir, ".claude", "CLAUDE.md");
assert.equal(await readlink(globalSkillLink), options.globalSkill);
assert.equal(await readlink(extensionLink), options.globalExtension);
assert.equal(await readlink(piInstructions), options.globalAgents);
assert.equal(await readlink(codexInstructions), options.globalAgents);
assert.equal(await readlink(claudeInstructions), options.globalClaude);
assert.equal(
await readlink(projectSkillLink),
path.relative(path.dirname(projectSkillLink), options.projectSkill),
);
});
test("removed global instruction declarations are prunable without scanning config directories", async () => {
const options = await fixture();
await run("sync", options);
const manifestFile = path.join(options.repoRoot, "config", "pi-links.json");
const manifest = JSON.parse(await readFile(manifestFile, "utf8"));
delete manifest.instructions.globalSymlinks.claude;
await writeJson(manifestFile, manifest);
const plan = await buildPlan(options);
assert.ok(
plan.operations.some(
(operation) =>
operation.type === "remove-link" &&
operation.destination ===
path.join(options.homeDir, ".claude", "CLAUDE.md"),
),
);
});
test("dry-run reports drift without writing", async () => {
const options = await fixture();
assert.equal(await run("sync", { ...options, dryRun: true }), 0);
const plan = await buildPlan(options);
assert.ok(
plan.operations.some((operation) => operation.type === "create-link"),
);
});
test("unmanaged destinations are conflicts and remain untouched", async () => {
const options = await fixture();
const destination = path.join(
options.homeDir,
".agents",
"skills",
"global-skill",
);
await mkdir(path.dirname(destination), { recursive: true });
await writeFile(destination, "keep me\n");
const plan = await buildPlan(options);
assert.deepEqual(
plan.conflicts.map((entry) => entry.path),
[destination],
);
assert.equal(await run("sync", options), 2);
});
test("prune removes only stale links whose targets are inside the repository", async () => {
const options = await fixture();
await run("sync", options);
const skillsDir = path.join(options.homeDir, ".agents", "skills");
const stale = path.join(skillsDir, "stale");
const external = path.join(skillsDir, "external");
await symlink(options.globalSkill, stale);
await symlink(path.join(options.root, "outside"), external);
const plan = await buildPlan(options);
assert.ok(
plan.operations.some(
(operation) =>
operation.type === "remove-link" && operation.destination === stale,
),
);
assert.ok(
!plan.operations.some(
(operation) =>
operation.type === "remove-link" && operation.destination === external,
),
);
assert.equal(await run("prune", options), 0);
assert.equal(await readlink(external), path.join(options.root, "outside"));
});
test("duplicate declared skill names fail before planning changes", async () => {
const options = await fixture();
await writeSkill(
path.join(options.repoRoot, "skills", "other"),
"global-skill",
);
await assert.rejects(
() => buildPlan(options),
/duplicate skill name global-skill/,
);
});
+14
View File
@@ -0,0 +1,14 @@
function configure_pi_orca_runtime --argument-names profile
set -g pi_orca_node_executable /usr/bin/node
set -gx PI_CODING_AGENT_DIR /home/jb/.pi/agent
if test "$profile" = pi-work
set pi_orca_node_executable /home/jb/.local/share/fnm/node-versions/v22.23.2/installation/bin/node
set PI_CODING_AGENT_DIR /home/jb/.pi/profiles/cyone
end
# Keep Pi and its subprocesses on the same Node installation. Orca inherits
# fnm's PATH, which otherwise makes tool commands resolve a different Node.
set -l node_bin (path dirname "$pi_orca_node_executable")
set -gx PATH "$node_bin" $PATH
end
+26 -30
View File
@@ -5,53 +5,49 @@
set -l resolver /home/jb/.local/bin/orca-nono-profile
if not test -x "$resolver"
printf 'pi-orca: profile resolver is unavailable: %s\n' "$resolver" >&2
exit 1
printf 'pi-orca: profile resolver is unavailable: %s\n' "$resolver" >&2
exit 1
end
set -l profile ("$resolver" resolve "$PWD" 2>/dev/null)
if test $status -ne 0; or test -z "$profile"
printf 'pi-orca: could not resolve a trusted profile for %s\n' "$PWD" >&2
exit 1
printf 'pi-orca: could not resolve a trusted profile for %s\n' "$PWD" >&2
exit 1
end
if not /usr/bin/nono profile show "$profile" >/dev/null 2>&1
printf 'pi-orca: resolved profile is unavailable: %s\n' "$profile" >&2
exit 1
printf 'pi-orca: resolved profile is unavailable: %s\n' "$profile" >&2
exit 1
end
set -l node_executable /usr/bin/node
set -l pi_agent_dir /home/jb/.pi/agent
source /srv/data/code/harness-tools/scripts/pi-orca-runtime.fish
configure_pi_orca_runtime "$profile"
# Native Node addons are ABI-specific. Keep the work profile's Node 22 package
# tree separate from the default Node 26 package tree.
if test "$profile" = pi-work
set node_executable /home/jb/.local/share/fnm/node-versions/v22.23.2/installation/bin/node
set pi_agent_dir /home/jb/.pi/profiles/cyone
end
set -l node_executable "$pi_orca_node_executable"
set -l pi_agent_dir "$PI_CODING_AGENT_DIR"
if not test -x "$node_executable"
printf 'pi-orca: Node runtime is unavailable: %s\n' "$node_executable" >&2
exit 1
printf 'pi-orca: Node runtime is unavailable: %s\n' "$node_executable" >&2
exit 1
end
if not test -d "$pi_agent_dir"
printf 'pi-orca: Pi agent directory is unavailable: %s\n' "$pi_agent_dir" >&2
exit 1
printf 'pi-orca: Pi agent directory is unavailable: %s\n' "$pi_agent_dir" >&2
exit 1
end
set -lx ORCA_NONO_PROFILE "$profile"
set -lx PI_CODING_AGENT_DIR "$pi_agent_dir"
exec /usr/bin/systemd-run \
--user \
--scope \
--collect \
--quiet \
--slice=orca-agents.slice \
--property=CPUQuota=1600% \
--property=MemoryHigh=48G \
--property=MemoryMax=64G \
--property=MemorySwapMax=8G \
--property=TasksMax=8192 \
--property=OOMPolicy=stop \
-- /usr/bin/nono run --profile "$profile" -- "$node_executable" /home/jb/.bun/bin/pi $argv
--user \
--scope \
--collect \
--quiet \
--slice=orca-agents.slice \
--property=CPUQuota=1600% \
--property=MemoryHigh=48G \
--property=MemoryMax=64G \
--property=MemorySwapMax=8G \
--property=TasksMax=8192 \
--property=OOMPolicy=stop \
-- /usr/bin/nono run --profile "$profile" -- "$node_executable" /home/jb/.bun/bin/pi $argv
+60
View File
@@ -0,0 +1,60 @@
from __future__ import annotations
import os
import subprocess
import unittest
from pathlib import Path
RUNTIME_SCRIPT = Path(__file__).with_name("pi-orca-runtime.fish")
SYSTEM_NODE = Path("/usr/bin/node")
WORK_NODE = Path(
"/home/jb/.local/share/fnm/node-versions/v22.23.2/installation/bin/node"
)
class ConfigurePiOrcaRuntimeTests(unittest.TestCase):
def configure_runtime(self, profile: str) -> list[str]:
environment = os.environ.copy()
environment["PATH"] = ":".join(
[str(WORK_NODE.parent), "/usr/local/bin", "/usr/bin"]
)
command = """
source $argv[1]
configure_pi_orca_runtime $argv[2]
printf '%s\\n' $pi_orca_node_executable $PI_CODING_AGENT_DIR $PATH[1] (command -v node)
"""
result = subprocess.run(
["/usr/bin/fish", "-c", command, str(RUNTIME_SCRIPT), profile],
check=True,
capture_output=True,
env=environment,
text=True,
)
return result.stdout.splitlines()
def test_default_profile_prioritizes_system_node(self) -> None:
self.assertEqual(
self.configure_runtime("pi-harness-tools"),
[
str(SYSTEM_NODE),
"/home/jb/.pi/agent",
str(SYSTEM_NODE.parent),
str(SYSTEM_NODE),
],
)
def test_work_profile_prioritizes_node_22(self) -> None:
self.assertEqual(
self.configure_runtime("pi-work"),
[
str(WORK_NODE),
"/home/jb/.pi/profiles/cyone",
str(WORK_NODE.parent),
str(WORK_NODE),
],
)
if __name__ == "__main__":
unittest.main()