mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-17 20:52:15 +03:00
Stage 7 of issue #10321 moves the optional ML and browser automation dependency closures out of the desktop bundle into checksummed, versioned packs installed on demand through the omniroute packs command. - scripts/build/optionalPackStaging.mjs stages pack members under .build/optional-packs, creates release tarballs, and emits optional-packs.index.json with per-member SHA-256 checksums. - scripts/packs provides manifest, install, remove, and verification helpers plus the packs CLI commands. - Runtime lookup includes installed pack node_modules directories, while LLMLingua and browser executors continue to degrade gracefully when packs are absent. The measured darwin-arm64 staging closure was about 534 MB of the 929 MB standalone node_modules tree (57%).
167 lines
5.5 KiB
JavaScript
167 lines
5.5 KiB
JavaScript
import path from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
import { t } from "../i18n.mjs";
|
|
import { resolveDataDir } from "../data-dir.mjs";
|
|
import {
|
|
EXIT_CODES,
|
|
emit,
|
|
exitWith,
|
|
printError,
|
|
printInfo,
|
|
printSuccess,
|
|
printWarning,
|
|
} from "../output.mjs";
|
|
import { findPack } from "../../../scripts/packs/optionalPackManifest.mjs";
|
|
import {
|
|
findPackIndexFile,
|
|
installPack,
|
|
listPackStates,
|
|
packState,
|
|
packsRoot,
|
|
readPackIndex,
|
|
removePack,
|
|
} from "../../../scripts/packs/optionalPackInstaller.mjs";
|
|
|
|
const CLI_DIR = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
|
|
|
|
/**
|
|
* Locate + parse the bundle-shipped `optional-packs.index.json`.
|
|
* Search order: explicit --source dir, then walking up from the CLI module
|
|
* (bundle installs keep the index at the bundle root), then cwd.
|
|
*/
|
|
function loadIndex(sourceDir) {
|
|
const indexFile = findPackIndexFile([sourceDir, CLI_DIR, process.cwd()]);
|
|
if (!indexFile) return { indexFile: null, index: null };
|
|
return { indexFile, index: readPackIndex(indexFile) };
|
|
}
|
|
|
|
function stateRow(state, dataDir) {
|
|
return {
|
|
pack: state.name,
|
|
packVersion: state.packVersion,
|
|
installed: state.installed ? "yes" : "no",
|
|
verified: state.verified === null ? "-" : state.verified ? "ok" : "FAILED",
|
|
members: state.members.length,
|
|
installDir: path.join(packsRoot(dataDir), state.name),
|
|
errors: state.errors ?? [],
|
|
};
|
|
}
|
|
|
|
const STATE_SCHEMA = [
|
|
{ key: "pack", header: "pack" },
|
|
{ key: "packVersion", header: "packVersion" },
|
|
{ key: "installed", header: "installed" },
|
|
{ key: "verified", header: "verified" },
|
|
{ key: "members", header: "members" },
|
|
];
|
|
|
|
async function run(action) {
|
|
try {
|
|
await action();
|
|
} catch (err) {
|
|
exitWith(EXIT_CODES.ERROR, err instanceof Error ? err.message : String(err));
|
|
}
|
|
}
|
|
|
|
export function registerPacks(program) {
|
|
const packs = program.command("packs").description(t("packs.description"));
|
|
|
|
packs
|
|
.command("list")
|
|
.description(t("packs.listDescription"))
|
|
.option("--source <dir>", t("packs.sourceOpt"))
|
|
.action(async (opts) => {
|
|
await run(async () => {
|
|
const dataDir = resolveDataDir();
|
|
const { index } = loadIndex(opts.source);
|
|
emit(
|
|
(await listPackStates({ dataDir, index })).map((s) => stateRow(s, dataDir)),
|
|
opts,
|
|
STATE_SCHEMA
|
|
);
|
|
if (!index) printWarning(t("packs.warnNoIndex"));
|
|
});
|
|
});
|
|
|
|
packs
|
|
.command("install <name>")
|
|
.description(t("packs.installDescription"))
|
|
.option("--source <dir>", t("packs.sourceOpt"))
|
|
.action(async (name, opts) => {
|
|
await run(async () => {
|
|
if (!findPack(name)) exitWith(EXIT_CODES.INVALID_ARG, t("packs.errUnknown", { name }));
|
|
const { indexFile, index } = loadIndex(opts.source);
|
|
if (!index) exitWith(EXIT_CODES.ERROR, t("packs.errNoIndex"));
|
|
const dataDir = resolveDataDir();
|
|
// The payload (tarball or extracted pack dir) lives next to the index
|
|
// unless the caller pointed elsewhere via --source.
|
|
await installPack(name, {
|
|
dataDir,
|
|
index,
|
|
sourceDir: opts.source || path.dirname(indexFile),
|
|
log: (msg) => printInfo(msg.replace(/^\[optional-packs\]\s*/, "")),
|
|
});
|
|
const installDir = path.join(packsRoot(dataDir), name);
|
|
printSuccess(t("packs.installed", { name, dir: installDir }));
|
|
printInfo(t("packs.restartHint"));
|
|
emit({ pack: name, installed: "yes", verified: "ok", installDir }, opts, STATE_SCHEMA);
|
|
});
|
|
});
|
|
|
|
packs
|
|
.command("verify [name]")
|
|
.description(t("packs.verifyDescription"))
|
|
.option("--source <dir>", t("packs.sourceOpt"))
|
|
.action(async (name, opts) => {
|
|
await run(async () => {
|
|
if (name && !findPack(name))
|
|
exitWith(EXIT_CODES.INVALID_ARG, t("packs.errUnknown", { name }));
|
|
const { index } = loadIndex(opts.source);
|
|
if (!index) exitWith(EXIT_CODES.ERROR, t("packs.errNoIndex"));
|
|
const dataDir = resolveDataDir();
|
|
const states = name
|
|
? [await packState(name, { dataDir, index })]
|
|
: await listPackStates({ dataDir, index });
|
|
emit(
|
|
states.map((s) => stateRow(s, dataDir)),
|
|
opts,
|
|
STATE_SCHEMA
|
|
);
|
|
const broken = states.filter((s) => s.installed && s.verified !== true);
|
|
if (broken.length > 0) {
|
|
for (const state of broken) {
|
|
for (const error of state.errors ?? []) printError(`${state.name}: ${error}`);
|
|
}
|
|
exitWith(EXIT_CODES.ERROR, t("packs.verifyFailed", { count: broken.length }));
|
|
}
|
|
if (!states.some((s) => s.installed)) {
|
|
printInfo(t("packs.noneInstalled"));
|
|
return;
|
|
}
|
|
printSuccess(t("packs.verifyOk"));
|
|
});
|
|
});
|
|
|
|
packs
|
|
.command("remove <name>")
|
|
.description(t("packs.removeDescription"))
|
|
.action(async (name, opts) => {
|
|
await run(async () => {
|
|
if (!findPack(name)) exitWith(EXIT_CODES.INVALID_ARG, t("packs.errUnknown", { name }));
|
|
const dataDir = resolveDataDir();
|
|
const removed = removePack(name, {
|
|
dataDir,
|
|
log: (msg) => printInfo(msg.replace(/^\[optional-packs\]\s*/, "")),
|
|
});
|
|
if (removed) {
|
|
printSuccess(t("packs.removed", { name }));
|
|
printInfo(t("packs.restartHint"));
|
|
} else {
|
|
printInfo(t("packs.notInstalled", { name }));
|
|
}
|
|
emit({ pack: name, installed: removed ? "no" : "no" }, opts, STATE_SCHEMA);
|
|
});
|
|
});
|
|
}
|