mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-25 16:42:16 +03:00
Landed with the design call resolved per the owner's pick — **option 1**: the synced store is now endpoint-agnostic (persistDiscoveredModels and managedModelImport no longer drop non-chat models at write time), and chat selectability moved to read time (auto-pool expansion in autoStrategy applies filterChatSelectableModels; the models-route projection already had its chatOnly filter). Your discovery test now passes end-to-end (3/3): /api/show capabilities persist per connection and image/embedding requests route through the advertising host. Reconciliation notes: conflicted areas merged onto the current tip (adobe discovery import, requestedModel preflight signature, resolvedProvider fast-path coexists with the synced-route override — explicit resolution wins); carried base-red drains (#10055 memoization, #11071 test variants) dropped as already-landed; the managed-model-import exclusion test was propagated to the new contract (image/video models persist; the read filter still hides them from chat pickers — pinned by a new assertion). Full battery: 205/206 focused (the one red is a confirmed periodic-timer timing flake on the loaded devbox — 20/20 isolated), autoCombo vitest 30/30, combo suites 46/46, gates + typecheck clean. Thank you @yourspraveen — the capability probe + routing design was right; it just needed the store contract opened up. Fixes #11087.
138 lines
5.1 KiB
JavaScript
138 lines
5.1 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Platform hydration for the shared Next standalone web build (issue #10321,
|
|
* Stage 8).
|
|
*
|
|
* The standalone bundle is built ONCE on ubuntu and restored on every desktop
|
|
* matrix leg. Everything except install-machine-forked optional packages is
|
|
* platform-independent:
|
|
*
|
|
* - Bundled-for-all (verify only): koffi ships every triplet under
|
|
* `build/koffi/<os>_<arch>`, better-sqlite3 v13 ships Node-API prebuilds for
|
|
* 8 platforms, wreq-js ships `rust/wreq-js.<plat>-<arch>[-libc].node`, and
|
|
* onnxruntime-node ships `bin/napi-v6/<os>/<arch>`.
|
|
* - Install-machine-forked (hydrate): `@img/sharp-*`, `@img/sharp-libvips-*`,
|
|
* `@ngrok/ngrok-*` and macOS-only `fsevents` resolve to whichever platform
|
|
* ran `npm ci`. The ubuntu-built tree carries the linux forks; each leg
|
|
* replaces them with the forks from its OWN `npm ci`d node_modules.
|
|
*/
|
|
|
|
import fs from "node:fs";
|
|
import path from "node:path";
|
|
|
|
/** Scope prefixes whose members are install-machine-forked. */
|
|
export const HYDRATED_SCOPES = ["@img/sharp-", "@img/sharp-libvips-", "@ngrok/ngrok-"];
|
|
|
|
/** Standalone packages that are not forked but must never be platform-forked. */
|
|
export const HYDRATED_ROOT_PACKAGES = ["fsevents"];
|
|
|
|
/**
|
|
* onnxruntime-node does not publish a darwin-x64 binary for napi-v6 (only
|
|
* linux/win32 x64 + darwin arm64), so existence cannot be asserted there.
|
|
*/
|
|
export const BUNDLED_EXEMPTIONS = new Set(["onnxruntime-node:darwin-x64"]);
|
|
|
|
function platformTriple(platform, arch) {
|
|
// koffi uses underscore triplets; better-sqlite3/wreq-js/onnx use dashes.
|
|
return { koffi: `${platform}_${arch}`, dash: `${platform}-${arch}` };
|
|
}
|
|
|
|
function rmrf(target) {
|
|
fs.rmSync(target, { recursive: true, force: true });
|
|
}
|
|
|
|
function copyDir(from, to) {
|
|
fs.cpSync(from, to, { recursive: true, verbatimSymlinks: false, force: true });
|
|
}
|
|
|
|
function directMemberNames(nodeModulesDir, scope) {
|
|
const scopeDir = path.join(nodeModulesDir, ...scope.split("/").slice(0, -1));
|
|
const prefix = scope.split("/").pop();
|
|
try {
|
|
return fs
|
|
.readdirSync(scopeDir)
|
|
.filter((name) => name.startsWith(prefix))
|
|
.map((name) => `${scope.slice(0, scope.lastIndexOf("/"))}/${name}`);
|
|
} catch {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Replace install-machine-forked packages inside the restored standalone tree
|
|
* with the forks resolved by THIS machine's node_modules.
|
|
*
|
|
* @param {{standaloneNodeModules: string, sourceNodeModules: string}} opts
|
|
* @returns {{replaced: string[], removed: string[], copied: string[]}}
|
|
*/
|
|
export function hydratePlatformNatives({ standaloneNodeModules, sourceNodeModules }) {
|
|
const replaced = [];
|
|
const removed = [];
|
|
const copied = [];
|
|
|
|
const forkedNames = new Set();
|
|
for (const scope of HYDRATED_SCOPES) {
|
|
for (const name of directMemberNames(sourceNodeModules, scope)) forkedNames.add(name);
|
|
for (const name of directMemberNames(standaloneNodeModules, scope)) forkedNames.add(name);
|
|
}
|
|
for (const pkg of HYDRATED_ROOT_PACKAGES) {
|
|
if (fs.existsSync(path.join(sourceNodeModules, pkg))) forkedNames.add(pkg);
|
|
if (fs.existsSync(path.join(standaloneNodeModules, pkg))) forkedNames.add(pkg);
|
|
}
|
|
|
|
for (const name of forkedNames) {
|
|
const standalonePath = path.join(standaloneNodeModules, ...name.split("/"));
|
|
const sourcePath = path.join(sourceNodeModules, ...name.split("/"));
|
|
const hadIt = fs.existsSync(standalonePath);
|
|
const hasIt = fs.existsSync(sourcePath);
|
|
if (hadIt) rmrf(standalonePath);
|
|
if (!hasIt) {
|
|
if (hadIt) removed.push(name);
|
|
continue; // e.g. fsevents on non-darwin legs: simply absent everywhere.
|
|
}
|
|
copyDir(sourcePath, standalonePath);
|
|
copied.push(name);
|
|
if (hadIt) replaced.push(name);
|
|
}
|
|
return { replaced, removed, copied };
|
|
}
|
|
|
|
/**
|
|
* Assert that every bundled native dependency can service `platform`/`arch`.
|
|
*
|
|
* @returns {{ok: true} | {ok: false, errors: string[]}}
|
|
*/
|
|
export function verifyBundledNatives({ nodeModulesDir, platform, arch }) {
|
|
const errors = [];
|
|
const triple = platformTriple(platform, arch);
|
|
|
|
const koffiDir = path.join(nodeModulesDir, "koffi", "build", "koffi", triple.koffi);
|
|
if (!fs.existsSync(koffiDir)) errors.push(`koffi: missing bundled triplet ${triple.koffi}`);
|
|
|
|
const sqlitePrebuild = path.join(
|
|
nodeModulesDir,
|
|
"better-sqlite3",
|
|
"prebuilds",
|
|
`${triple.dash}.node`
|
|
);
|
|
if (!fs.existsSync(sqlitePrebuild))
|
|
errors.push(`better-sqlite3: missing prebuild ${triple.dash}.node`);
|
|
|
|
const wreqDir = path.join(nodeModulesDir, "wreq-js", "rust");
|
|
const wreqNames = fs.existsSync(wreqDir)
|
|
? fs
|
|
.readdirSync(wreqDir)
|
|
.filter((n) => n.startsWith(`wreq-js.${triple.dash}`) && n.endsWith(".node"))
|
|
: [];
|
|
if (wreqNames.length === 0) errors.push(`wreq-js: missing rust binary for ${triple.dash}`);
|
|
|
|
const exempt = BUNDLED_EXEMPTIONS.has(`onnxruntime-node:${triple.dash}`);
|
|
if (!exempt) {
|
|
const onnxDir = path.join(nodeModulesDir, "onnxruntime-node", "bin", "napi-v6", platform, arch);
|
|
if (!fs.existsSync(onnxDir))
|
|
errors.push(`onnxruntime-node: missing ${platform}/${arch} binary`);
|
|
}
|
|
|
|
return errors.length === 0 ? { ok: true } : { ok: false, errors };
|
|
}
|