consortium
Version:
Remote control and session sharing CLI for AI coding agents
87 lines (84 loc) • 3.04 kB
JavaScript
import * as fs from 'node:fs/promises';
import * as os from 'node:os';
import * as path from 'node:path';
const EXTENSION_DIRNAME = "consortium-permission";
const FILES_TO_COPY_DEV = ["package.json", "index.ts"];
const FILES_TO_COPY_DIST = ["package.json", "index.js", "index.cjs", "index.mjs"];
async function pathExists(p) {
try {
await fs.access(p);
return true;
} catch {
return false;
}
}
async function readJson(p) {
try {
const raw = await fs.readFile(p, "utf8");
return JSON.parse(raw);
} catch {
return null;
}
}
function compareVersions(a, b) {
const pa = a.split(".").map((s) => parseInt(s, 10));
const pb = b.split(".").map((s) => parseInt(s, 10));
const len = Math.max(pa.length, pb.length);
for (let i = 0; i < len; i++) {
const na = pa[i];
const nb = pb[i];
if (Number.isNaN(na) || Number.isNaN(nb)) return a < b ? -1 : a > b ? 1 : 0;
if ((na ?? 0) !== (nb ?? 0)) return (na ?? 0) - (nb ?? 0);
}
return 0;
}
async function resolveSourceDir() {
const candidates = [
path.resolve(__dirname, "../ext/consortium-permission"),
path.resolve(__dirname, "./pi/ext/consortium-permission"),
path.resolve(__dirname, "./ext/consortium-permission"),
path.resolve(__dirname, "../../pi-ext")
];
for (const dir of candidates) {
if (!await pathExists(path.join(dir, "package.json"))) continue;
const hasJs = await pathExists(path.join(dir, "index.js")) || await pathExists(path.join(dir, "index.cjs"));
return { dir, files: hasJs ? FILES_TO_COPY_DIST : FILES_TO_COPY_DEV };
}
return null;
}
function resolveTargetDir(piConfigDir) {
const base = piConfigDir ?? process.env.PI_CODING_AGENT_DIR ?? path.join(os.homedir(), ".pi", "agent");
return path.join(base, "extensions", EXTENSION_DIRNAME);
}
async function atomicCopy(src, dest) {
const tmp = `${dest}.tmp`;
await fs.unlink(tmp).catch(() => void 0);
await fs.copyFile(src, tmp);
await fs.rename(tmp, dest);
}
async function ensurePiExtensionInstalled(opts = {}) {
const targetDir = resolveTargetDir(opts.piConfigDir);
if (process.env.CONSORTIUM_PI_NO_EXTENSION === "1") {
return { installed: false, targetDir, skipped: "env-disabled" };
}
const source = await resolveSourceDir();
if (!source) {
return { installed: false, targetDir };
}
const shippedPkg = await readJson(path.join(source.dir, "package.json"));
const installedPkg = await readJson(path.join(targetDir, "package.json"));
if (!opts.force && shippedPkg?.version && installedPkg?.version) {
if (compareVersions(installedPkg.version, shippedPkg.version) >= 0) {
return { installed: false, targetDir, skipped: "up-to-date" };
}
}
await fs.mkdir(targetDir, { recursive: true });
for (const file of source.files) {
const src = path.join(source.dir, file);
if (!await pathExists(src)) continue;
const dest = path.join(targetDir, file);
await atomicCopy(src, dest);
}
return { installed: true, targetDir };
}
export { ensurePiExtensionInstalled };