This commit is contained in:
Oleg Maslov
2026-09-02 10:14:22 +02:00
parent 0c3e2ead3b
commit b20b138fe4
771 changed files with 161561 additions and 9027 deletions

View File

@@ -1,5 +1,5 @@
#!/usr/bin/env node
/** Build the npm-free hook + collaboration CLI payload staged into Tauri. */
/** Build the npm-free hook, memory MCP, and collaboration CLI payload staged into Tauri. */
import fs from 'node:fs';
import path from 'node:path';
@@ -10,6 +10,10 @@ const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
const tsc = path.join(root, 'node_modules', 'typescript', 'bin', 'tsc');
const projects = [
'packages/shared/tsconfig.json',
'packages/hive-mind-core/tsconfig.json',
'packages/core/tsconfig.json',
'packages/wiki-compiler/tsconfig.json',
'packages/hive-mind-cli/tsconfig.json',
'packages/hive-mind-hooks-claude-code/tsconfig.json',
'packages/hive-mind-hooks-claude-desktop/tsconfig.json',
@@ -18,6 +22,7 @@ const projects = [
'packages/hive-mind-hooks-cursor/tsconfig.json',
'packages/hive-mind-hooks-hermes/tsconfig.json',
'packages/hive-mind-hooks-openclaw/tsconfig.json',
'packages/memory-mcp/tsconfig.json',
];
if (!fs.existsSync(tsc)) {
@@ -25,7 +30,7 @@ if (!fs.existsSync(tsc)) {
process.exit(1);
}
console.log('[build-hook-runtime] Building CLI and seven hook adapters...');
console.log('[build-hook-runtime] Building CLI, seven hook adapters, and memory MCP...');
execFileSync(process.execPath, [tsc, '--build', ...projects], {
cwd: root,
stdio: 'inherit',
@@ -47,6 +52,7 @@ const expected = [
'packages/hive-mind-hooks-hermes/dist/bin/hermes-hooks.js',
'packages/hive-mind-hooks-openclaw/dist/bin/openclaw-hooks.js',
'packages/hive-mind-hooks-openclaw/dist/handler.bundle.cjs',
'packages/memory-mcp/dist/index.js',
];
const missing = expected.filter((entry) => !fs.existsSync(path.join(root, entry)));
if (missing.length > 0) {

View File

@@ -10,15 +10,89 @@
*/
import fs from 'node:fs';
import { createHash } from 'node:crypto';
import { execFileSync } from 'node:child_process';
import os from 'node:os';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import ts from 'typescript';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const root = path.resolve(__dirname, '..');
const resourcesDir = path.join(root, 'app', 'src-tauri', 'resources');
const outFile = path.join(resourcesDir, 'service.js');
const sourceMapFile = `${outFile}.map`;
const entryPoint = path.join(root, 'packages', 'server', 'src', 'local', 'service.ts');
const marketplaceDbRelative = 'packages/marketplace/marketplace.db';
const marketplaceDb = path.join(root, 'packages', 'marketplace', 'marketplace.db');
const marketplaceResource = path.join(resourcesDir, 'marketplace.db');
const provenancePrefix = '// Waggle-Sidecar-Provenance: ';
const buildScriptRelative = 'scripts/build-sidecar.mjs';
const entryPointRelative = 'packages/server/src/local/service.ts';
function sha256(value) {
return createHash('sha256').update(value).digest('hex');
}
function readGitBlob(revision, relative) {
return execFileSync(
'git',
['-C', root, 'cat-file', 'blob', `${revision}:${relative}`],
{ maxBuffer: 64 * 1024 * 1024, windowsHide: true },
);
}
function repositoryRelative(absolutePath) {
const relative = path.relative(root, absolutePath);
if (!relative || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) {
throw new Error(`Sidecar input is outside the repository: ${absolutePath}`);
}
return relative.split(path.sep).join('/');
}
function addImplicitBuildInputs(localInputs, inputRelative) {
let current = path.dirname(path.join(root, ...inputRelative.split('/')));
while (true) {
for (const name of ['package.json', 'tsconfig.json']) {
const candidate = path.join(current, name);
if (fs.existsSync(candidate)) localInputs.add(repositoryRelative(candidate));
}
if (current === root) break;
const parent = path.dirname(current);
if (parent === current) break;
current = parent;
}
}
function addExtendedTsconfigs(localInputs) {
const visited = new Set();
const pending = [...localInputs].filter((relative) => path.basename(relative) === 'tsconfig.json');
while (pending.length > 0) {
const relative = pending.pop();
if (visited.has(relative)) continue;
visited.add(relative);
const absolute = path.join(root, ...relative.split('/'));
const parsed = ts.parseConfigFileTextToJson(absolute, fs.readFileSync(absolute, 'utf8'));
if (parsed.error) throw new Error(`Could not parse sidecar tsconfig: ${relative}`);
const rawExtends = parsed.config?.extends;
const extendedConfigs = Array.isArray(rawExtends)
? rawExtends
: typeof rawExtends === 'string'
? [rawExtends]
: [];
for (const extendedConfig of extendedConfigs) {
if (typeof extendedConfig !== 'string' || !extendedConfig.startsWith('.')) continue;
let extended = path.resolve(path.dirname(absolute), extendedConfig);
if (!fs.existsSync(extended) && fs.existsSync(`${extended}.json`)) extended += '.json';
const extendedRelative = repositoryRelative(extended);
if (!fs.existsSync(extended)) {
throw new Error(`Extended sidecar tsconfig is missing: ${extendedRelative}`);
}
localInputs.add(extendedRelative);
pending.push(extendedRelative);
}
}
}
// Metafile goes to a temp path (NOT resources/) so it's neither bundled into
// the app nor left as an untracked repo artifact. stage-sidecar-deps.mjs reads
// it back from the same well-known path. Keep the two in sync.
@@ -26,6 +100,10 @@ const metaFile = path.join(os.tmpdir(), 'waggle-sidecar-meta.json');
// Ensure resources directory exists
fs.mkdirSync(resourcesDir, { recursive: true });
// Production resources are shipped verbatim by Tauri. Remove maps left by an
// older build before bundling so full TypeScript sources cannot ride along in
// an installer even when the resources directory is reused.
fs.rmSync(sourceMapFile, { force: true });
console.log('[build-sidecar] Bundling server into', outFile);
@@ -57,30 +135,58 @@ const EXTERNAL = [
];
try {
if (
!fs.existsSync(marketplaceDb)
|| !fs.lstatSync(marketplaceDb).isFile()
|| fs.lstatSync(marketplaceDb).isSymbolicLink()
) {
throw new Error(`Required marketplace database is missing or unsafe: ${marketplaceDb}`);
}
const sourceRevision = execFileSync(
'git',
['-C', root, 'rev-parse', 'HEAD'],
{ encoding: 'utf8', windowsHide: true },
).trim().toLowerCase();
if (!/^[0-9a-f]{40}$/.test(sourceRevision)) {
throw new Error(`Could not resolve an exact source revision: ${sourceRevision}`);
}
const marketplaceGitBlob = readGitBlob(sourceRevision, marketplaceDbRelative);
if (!fs.readFileSync(marketplaceDb).equals(marketplaceGitBlob)) {
throw new Error('canonical marketplace database does not match exact source revision');
}
// Dynamic import esbuild (available via vite dependency)
const esbuild = await import('esbuild');
const result = await esbuild.build({
entryPoints: [entryPoint],
absWorkingDir: root,
bundle: true,
platform: 'node',
target: 'node20',
format: 'esm',
outfile: outFile,
external: EXTERNAL,
// P4/D12: @waggle/shared and @waggle/hive-mind-core export ONLY their
// gitignored dist/ (unlike core/agent/server, which export src/*.ts).
// P4/D12: workspace package metadata can resolve to gitignored dist/.
// Alias all bundled first-party packages to their tracked source entry.
// Without these aliases the bundle silently embeds whatever dist/ was
// last compiled — the same stale-server-in-the-binary class D12 exists
// to kill — and a clean checkout can't build at all without
// build:packages. Alias to source so the bundle ALWAYS compiles from
// src, like the vitest aliases do. (No subpath imports of either
// package exist — verified before aliasing the bare names.)
// src, like the vitest aliases do. Exact subpath aliases must precede
// their package root so esbuild never appends a subpath to index.ts.
alias: {
'@waggle/agent/external-process-env': path.join(root, 'packages', 'agent', 'src', 'external-process-env.ts'),
'@waggle/agent': path.join(root, 'packages', 'agent', 'src', 'index.ts'),
'@waggle/core': path.join(root, 'packages', 'core', 'src', 'index.ts'),
'@waggle/marketplace': path.join(root, 'packages', 'marketplace', 'src', 'index.ts'),
'@waggle/shared': path.join(root, 'packages', 'shared', 'src', 'index.ts'),
'@waggle/hive-mind-core': path.join(root, 'packages', 'hive-mind-core', 'src', 'index.ts'),
'@waggle/waggle-dance': path.join(root, 'packages', 'waggle-dance', 'src', 'index.ts'),
'@waggle/weaver': path.join(root, 'packages', 'weaver', 'src', 'index.ts'),
'@waggle/wiki-compiler': path.join(root, 'packages', 'wiki-compiler', 'src', 'index.ts'),
},
sourcemap: true,
sourcemap: false,
minify: true,
// metafile lets stage-sidecar-deps.mjs enumerate exactly which of the
// EXTERNAL packages the bundle actually `require()`s at runtime, so it
@@ -108,16 +214,82 @@ try {
fs.writeFileSync(metaFile, JSON.stringify(result.metafile));
console.log('[build-sidecar] Wrote esbuild metafile', metaFile);
const trackedFiles = new Set(
execFileSync(
'git',
['-C', root, 'ls-files', '-z'],
{ encoding: 'utf8', windowsHide: true },
).split('\0').filter(Boolean),
);
const localInputs = new Set([buildScriptRelative, 'package.json', 'package-lock.json']);
for (const input of Object.keys(result.metafile.inputs)) {
const relative = repositoryRelative(path.resolve(root, input));
if (relative === 'node_modules' || relative.startsWith('node_modules/')) continue;
localInputs.add(relative);
}
for (const relative of [...localInputs]) {
addImplicitBuildInputs(localInputs, relative);
}
addExtendedTsconfigs(localInputs);
const sourceInputs = [...localInputs]
.sort((left, right) => left < right ? -1 : left > right ? 1 : 0)
.map((relative) => {
if (!trackedFiles.has(relative)) {
throw new Error(`Sidecar repository input is not tracked: ${relative}`);
}
const absolute = path.join(root, ...relative.split('/'));
const stat = fs.lstatSync(absolute);
if (!stat.isFile() || stat.isSymbolicLink()) {
throw new Error(`Sidecar repository input is not a regular file: ${relative}`);
}
return {
path: relative,
sha256: sha256(fs.readFileSync(absolute)),
};
});
const bundlePayload = fs.readFileSync(outFile);
const provenance = {
schemaVersion: 1,
sourceRevision,
entryPoint: entryPointRelative,
sourceInputs,
bundlePayload: {
sizeBytes: bundlePayload.byteLength,
sha256: sha256(bundlePayload),
},
};
const provenanceLine = Buffer.from(
`${provenancePrefix}${Buffer.from(JSON.stringify(provenance)).toString('base64')}\n`,
'utf8',
);
const provenanceTemp = `${outFile}.provenance-${process.pid}.tmp`;
try {
fs.writeFileSync(provenanceTemp, Buffer.concat([provenanceLine, bundlePayload]));
fs.rmSync(outFile, { force: true });
fs.renameSync(provenanceTemp, outFile);
} finally {
fs.rmSync(provenanceTemp, { force: true });
}
console.log(
`[build-sidecar] Embedded ${sourceInputs.length} source inputs at ${sourceRevision.slice(0, 12)}`,
);
const stat = fs.statSync(outFile);
const sizeMB = (stat.size / 1024 / 1024).toFixed(1);
console.log(`[build-sidecar] Done. Output: ${sizeMB} MB`);
// Copy marketplace seed database if it exists
const marketplaceDb = path.join(root, 'packages', 'marketplace', 'seed', 'marketplace.db');
if (fs.existsSync(marketplaceDb)) {
fs.copyFileSync(marketplaceDb, path.join(resourcesDir, 'marketplace.db'));
console.log('[build-sidecar] Copied marketplace.db seed');
// Production startup seeds the user's writable DB from this immutable
// packaged resource. Publish the exact Git blob through a temporary file so
// a failed write can never leave a partial database in the bundle.
const marketplaceTemp = `${marketplaceResource}.stage-${process.pid}.tmp`;
try {
fs.writeFileSync(marketplaceTemp, marketplaceGitBlob, { flag: 'wx' });
fs.renameSync(marketplaceTemp, marketplaceResource);
} finally {
fs.rmSync(marketplaceTemp, { force: true });
}
console.log('[build-sidecar] Copied canonical marketplace.db');
} catch (err) {
console.error('[build-sidecar] Build failed:', err.message);
process.exit(1);

View File

@@ -9,6 +9,7 @@
import fs from 'node:fs';
import path from 'node:path';
import { execFileSync } from 'node:child_process';
import { fileURLToPath } from 'node:url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
@@ -32,25 +33,48 @@ if (arch === 'universal') {
);
process.exit(1);
}
if (platform === 'darwin' && arch !== 'arm64' && arch !== 'x64') {
throw new Error(`[bundle-native-deps] FATAL — unsupported macOS target architecture: ${arch}`);
}
console.log(`[bundle-native-deps] Platform: ${platform}-${arch}`);
// Ensure output directory
// A previous matrix leg must never satisfy this build with stale native files.
fs.rmSync(nativeDir, { recursive: true, force: true });
fs.mkdirSync(nativeDir, { recursive: true });
fs.mkdirSync(path.join(nativeDir, 'onnxruntime'), { recursive: true });
fs.writeFileSync(path.join(nativeDir, '.gitkeep'), '');
fs.writeFileSync(path.join(nativeDir, 'onnxruntime', '.gitkeep'), '');
let totalFiles = 0;
let totalBytes = 0;
function assertDarwinArchitecture(srcPath) {
if (platform !== 'darwin' || !/\.(?:dylib|node)$/i.test(srcPath)) return;
const expected = arch === 'x64' ? 'x86_64' : 'arm64';
const architectures = execFileSync('/usr/bin/lipo', ['-archs', srcPath], {
encoding: 'utf8',
stdio: ['ignore', 'pipe', 'pipe'],
}).trim().split(/\s+/);
if (!architectures.includes(expected)) {
throw new Error(
`[bundle-native-deps] FATAL — ${path.relative(root, srcPath)} has Mach-O architecture `
+ `${architectures.join(', ') || 'unknown'}, expected ${expected}`,
);
}
}
function copyFile(src, destName) {
const srcPath = path.join(root, src);
const destPath = path.join(nativeDir, destName);
if (!fs.existsSync(srcPath)) {
console.warn(`[bundle-native-deps] WARNING: ${src} not found — skipping`);
return false;
throw new Error(`[bundle-native-deps] FATAL — required native dependency ${src} is missing`);
}
const sourceSize = fs.statSync(srcPath).size;
if (sourceSize === 0) throw new Error(`[bundle-native-deps] FATAL — required native dependency ${src} is empty`);
assertDarwinArchitecture(srcPath);
fs.copyFileSync(srcPath, destPath);
const size = fs.statSync(destPath).size;
totalBytes += size;
@@ -62,24 +86,28 @@ function copyFile(src, destName) {
function copyDir(srcDir, destSubDir) {
const srcPath = path.join(root, srcDir);
if (!fs.existsSync(srcPath)) {
console.warn(`[bundle-native-deps] WARNING: ${srcDir} not found — skipping`);
return;
throw new Error(`[bundle-native-deps] FATAL — required native dependency directory ${srcDir} is missing`);
}
const destPath = path.join(nativeDir, destSubDir);
fs.mkdirSync(destPath, { recursive: true });
let copied = 0;
for (const file of fs.readdirSync(srcPath)) {
const fullSrc = path.join(srcPath, file);
const stat = fs.statSync(fullSrc);
if (stat.isFile()) {
if (stat.size === 0) throw new Error(`[bundle-native-deps] FATAL — required native dependency ${fullSrc} is empty`);
assertDarwinArchitecture(fullSrc);
const destFile = path.join(destPath, file);
fs.copyFileSync(fullSrc, destFile);
totalBytes += stat.size;
totalFiles++;
copied++;
console.log(` ${destSubDir}/${file} (${(stat.size / 1024 / 1024).toFixed(1)} MB)`);
}
}
if (copied === 0) throw new Error(`[bundle-native-deps] FATAL — required native dependency directory ${srcDir} is empty`);
}
// 1. better-sqlite3
@@ -89,10 +117,9 @@ copyFile('node_modules/better-sqlite3/build/Release/better_sqlite3.node', 'bette
// 2. sqlite-vec
console.log('[bundle-native-deps] sqlite-vec:');
const vecOs = platform === 'win32' ? 'windows' : platform === 'darwin' ? 'darwin' : 'linux';
const vecArch = arch === 'arm64' ? 'aarch64' : 'x64';
const vecExt = platform === 'win32' ? 'dll' : platform === 'darwin' ? 'dylib' : 'so';
copyFile(
`node_modules/sqlite-vec-${vecOs}-${vecArch}/vec0.${vecExt}`,
`node_modules/sqlite-vec-${vecOs}-${arch}/vec0.${vecExt}`,
`vec0.${vecExt}`,
);

View File

@@ -1,130 +1,387 @@
#!/usr/bin/env node
/**
* Downloads the correct Node.js binary for the current platform and places it
* in app/src-tauri/resources/ for Tauri bundling.
* Download and verify the official Node.js distribution used by the desktop
* sidecar. The full archive is required because it is the authoritative source
* for the matching Node binary, Node license, and bundled npm runtime.
*
* Defaults to the Node version running this script so copied native modules
* from node_modules match the bundled runtime ABI. Set
* WAGGLE_BUNDLED_NODE_VERSION to pin a different version intentionally.
*
* Uses Node.js official distribution (https://nodejs.org/dist/).
* Caches in scripts/.cache/ to avoid re-downloading.
* Desktop packaging pins one exact supported Node.js release. Release and PR
* workflows install dependencies with the same version, and the extracted
* runtime proves it can load the installed native SQLite binding before any
* packaged runtime resource is replaced.
*/
import fs from 'node:fs';
import path from 'node:path';
import { execFileSync } from 'node:child_process';
import { createHash } from 'node:crypto';
import { fileURLToPath } from 'node:url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const root = path.resolve(__dirname, '..');
const resourcesDir = path.join(root, 'app', 'src-tauri', 'resources');
const cacheDir = path.join(__dirname, '.cache');
const stagedRuntimeDir = path.join(
resourcesDir,
'node_modules',
'waggle-node-runtime',
);
const SAFE_NPM_BRACE_EXPANSION_VERSION = '2.1.4';
const safeNpmBraceExpansionSource = path.join(
root,
'node_modules',
'archiver-utils',
'node_modules',
'brace-expansion',
);
const SAFE_NPM_IP_ADDRESS_VERSION = '10.4.0';
const safeNpmIpAddressSource = path.join(root, 'node_modules', 'ip-address');
const NODE_VERSION = process.env.WAGGLE_BUNDLED_NODE_VERSION ?? process.versions.node;
const DESKTOP_NODE_VERSION = '22.23.2';
const NODE_VERSION = DESKTOP_NODE_VERSION;
if (!/^\d+\.\d+\.\d+$/.test(NODE_VERSION)) {
console.error(`[bundle-node] FATAL invalid Node.js version: ${NODE_VERSION}`);
console.error(`[bundle-node] FATAL - invalid Node.js version: ${NODE_VERSION}`);
process.exit(1);
}
const platform = process.platform;
const arch = process.env.TARGET_ARCH || process.arch;
// macOS "universal" is not a valid download target: nodejs.org ships per-arch
// binaries (node-vX-darwin-arm64 / -x64), and a universal run would fall
// through to the x64 tarball and ship an x64-only node in an arm64 bundle.
// Build each arch separately and lipo the app bundle instead.
if (arch === 'universal') {
console.error(
'[bundle-node] FATAL TARGET_ARCH=universal is not supported.\n'
+ ' Node.js ships per-arch binaries. Build each arch separately:\n'
+ ' TARGET_ARCH=arm64 (aarch64-apple-darwin) and TARGET_ARCH=x64\n'
+ ' (x86_64-apple-darwin) — see release.yml\'s macOS matrix and the app\n'
+ ' tauri:build:mac:arm64 / :x64 scripts.',
'[bundle-node] FATAL - TARGET_ARCH=universal is not supported.\n'
+ ' Node.js ships per-arch binaries. Build arm64 and x64 separately.',
);
process.exit(1);
}
if (!['x64', 'arm64'].includes(arch)) {
console.error(`[bundle-node] FATAL - unsupported target architecture: ${arch}`);
process.exit(1);
}
const archivePlatform = platform === 'win32'
? 'win'
: platform === 'darwin'
? 'darwin'
: platform === 'linux'
? 'linux'
: null;
if (!archivePlatform) {
console.error(`[bundle-node] FATAL - unsupported platform: ${platform}`);
process.exit(1);
}
const archiveExtension = platform === 'win32' ? 'zip' : 'tar.gz';
const distributionName = `node-v${NODE_VERSION}-${archivePlatform}-${arch}`;
const archiveName = `${distributionName}.${archiveExtension}`;
const distributionUrl = `https://nodejs.org/dist/v${NODE_VERSION}`;
const archivePath = path.join(cacheDir, archiveName);
const shasumsPath = path.join(cacheDir, `node-v${NODE_VERSION}-SHASUMS256.txt`);
const extractDir = path.join(cacheDir, `${distributionName}-verified`);
const extractedRoot = path.join(extractDir, distributionName);
const nodeSource = platform === 'win32'
? path.join(extractedRoot, 'node.exe')
: path.join(extractedRoot, 'bin', 'node');
const npmSource = path.join(
extractedRoot,
...(platform === 'win32'
? ['node_modules', 'npm']
: ['lib', 'node_modules', 'npm']),
);
const nodeLicenseSource = path.join(extractedRoot, 'LICENSE');
const destBinary = path.join(resourcesDir, platform === 'win32' ? 'node.exe' : 'node');
function fail(message) {
console.error(`[bundle-node] FATAL - ${message}`);
process.exit(1);
}
function assertNativeRuntimeCompatible() {
const betterSqlitePath = path.join(root, 'node_modules', 'better-sqlite3');
const probe = `
const path = require('node:path');
const Database = require(path.join(process.argv[1], 'node_modules', 'better-sqlite3'));
const database = new Database(':memory:');
const row = database.prepare('SELECT 1 AS ok').get();
database.close();
if (row?.ok !== 1) throw new Error('SQLite query probe returned an invalid result');
process.stdout.write(JSON.stringify({
version: process.versions.node,
abi: process.versions.modules,
arch: process.arch,
}));
`;
try {
const output = execFileSync(nodeSource, ['-e', probe, root], {
cwd: root,
encoding: 'utf8',
stdio: ['ignore', 'pipe', 'pipe'],
}).trim();
const runtime = JSON.parse(output);
if (runtime.version !== NODE_VERSION || runtime.arch !== arch) {
fail(
`native ABI compatibility probe used Node.js v${runtime.version ?? 'unknown'} `
+ `(${runtime.arch ?? 'unknown'}, ABI ${runtime.abi ?? 'unknown'}); expected `
+ `v${NODE_VERSION} (${arch})`,
);
}
console.log(
`[bundle-node] Native ABI probe passed with Node.js v${runtime.version} `
+ `(${runtime.arch}, ABI ${runtime.abi})`,
);
} catch (error) {
const detail = String(error?.stderr ?? error?.message ?? error).trim().slice(0, 4_000);
fail(
`native ABI compatibility probe failed for Node.js v${NODE_VERSION} (${arch}) `
+ `against ${betterSqlitePath}. Reinstall dependencies with Node.js `
+ `v${NODE_VERSION} (npm ci) before packaging. ${detail}`,
);
}
}
async function download(url, destination) {
console.log(`[bundle-node] Downloading ${url}`);
const response = await fetch(url);
if (!response.ok) fail(`download failed: HTTP ${response.status} (${url})`);
const temporary = `${destination}.${process.pid}.tmp`;
fs.writeFileSync(temporary, Buffer.from(await response.arrayBuffer()));
fs.renameSync(temporary, destination);
}
function sha256(file) {
return createHash('sha256').update(fs.readFileSync(file)).digest('hex');
}
function expectedArchiveHash(shasums) {
for (const line of shasums.split(/\r?\n/)) {
const match = /^([a-f0-9]{64})\s+\*?(.+)$/.exec(line.trim());
if (match?.[2] === archiveName) return match[1];
}
return null;
}
function extractedRuntimeComplete() {
return [
nodeSource,
nodeLicenseSource,
path.join(npmSource, 'LICENSE'),
path.join(npmSource, 'bin', 'npm-cli.js'),
path.join(npmSource, 'bin', 'npx-cli.js'),
].every((file) => fs.existsSync(file) && fs.lstatSync(file).isFile());
}
function directorySizeBytes(dir) {
let bytes = 0;
const stack = [dir];
while (stack.length > 0) {
const current = stack.pop();
for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
const full = path.join(current, entry.name);
if (entry.isDirectory()) stack.push(full);
else if (entry.isFile()) bytes += fs.statSync(full).size;
}
}
return bytes;
}
function writeWrappers() {
const binDir = path.join(stagedRuntimeDir, 'bin');
fs.mkdirSync(binDir, { recursive: true });
if (platform === 'win32') {
const wrapper = (cli) => [
'@ECHO OFF',
'SETLOCAL',
'SET "NODE_EXE=%~dp0\\..\\..\\..\\node.exe"',
`SET "NPM_CLI_JS=%~dp0\\..\\node_modules\\npm\\bin\\${cli}-cli.js"`,
'"%NODE_EXE%" "%NPM_CLI_JS%" %*',
'EXIT /B %ERRORLEVEL%',
'',
].join('\r\n');
fs.writeFileSync(path.join(binDir, 'npm.cmd'), wrapper('npm'), 'utf8');
fs.writeFileSync(path.join(binDir, 'npx.cmd'), wrapper('npx'), 'utf8');
return;
}
const wrapper = (cli) => [
'#!/bin/sh',
'SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)',
`exec "$SCRIPT_DIR/../../../node" "$SCRIPT_DIR/../node_modules/npm/bin/${cli}-cli.js" "$@"`,
'',
].join('\n');
for (const cli of ['npm', 'npx']) {
const wrapperPath = path.join(binDir, cli);
fs.writeFileSync(wrapperPath, wrapper(cli), 'utf8');
fs.chmodSync(wrapperPath, 0o755);
}
}
fs.mkdirSync(cacheDir, { recursive: true });
fs.mkdirSync(resourcesDir, { recursive: true });
const destBinary = platform === 'win32'
? path.join(resourcesDir, 'node.exe')
: path.join(resourcesDir, 'node');
// Check cache
const cacheKey = `node-v${NODE_VERSION}-${platform}-${arch}`;
const cachedBinary = path.join(cacheDir, platform === 'win32' ? `${cacheKey}.exe` : cacheKey);
if (fs.existsSync(cachedBinary)) {
console.log(`[bundle-node] Using cached Node.js v${NODE_VERSION} (${platform}-${arch})`);
fs.copyFileSync(cachedBinary, destBinary);
if (platform !== 'win32') {
fs.chmodSync(destBinary, 0o755);
async function loadExpectedHash(refresh = false) {
if (refresh) fs.rmSync(shasumsPath, { force: true });
if (!fs.existsSync(shasumsPath)) {
await download(`${distributionUrl}/SHASUMS256.txt`, shasumsPath);
}
const size = (fs.statSync(destBinary).size / 1024 / 1024).toFixed(1);
console.log(`[bundle-node] → ${destBinary} (${size} MB)`);
process.exit(0);
const expected = expectedArchiveHash(fs.readFileSync(shasumsPath, 'utf8'));
if (expected) return expected;
if (!refresh) return loadExpectedHash(true);
fail(`official SHASUMS256.txt has no entry for ${archiveName}`);
}
// Download
if (platform === 'win32') {
// Windows: direct .exe download
const url = `https://nodejs.org/dist/v${NODE_VERSION}/win-${arch}/node.exe`;
console.log(`[bundle-node] Downloading Node.js v${NODE_VERSION} (${platform}-${arch})...`);
console.log(` ${url}`);
let expectedHash = await loadExpectedHash();
let refreshedShasums = false;
const response = await fetch(url);
if (!response.ok) {
console.error(`[bundle-node] Download failed: HTTP ${response.status}`);
process.exit(1);
if (fs.existsSync(archivePath) && sha256(archivePath) !== expectedHash) {
expectedHash = await loadExpectedHash(true);
refreshedShasums = true;
if (sha256(archivePath) !== expectedHash) {
console.warn(`[bundle-node] Discarding checksum-mismatched cache: ${archiveName}`);
fs.rmSync(archivePath, { force: true });
fs.rmSync(extractDir, { recursive: true, force: true });
}
const buffer = Buffer.from(await response.arrayBuffer());
fs.writeFileSync(cachedBinary, buffer);
fs.copyFileSync(cachedBinary, destBinary);
} else {
// macOS/Linux: download .tar.gz and extract bin/node
const nodeArch = arch === 'arm64' ? 'arm64' : 'x64';
const osPart = platform === 'darwin' ? 'darwin' : 'linux';
const archiveName = `node-v${NODE_VERSION}-${osPart}-${nodeArch}.tar.gz`;
const url = `https://nodejs.org/dist/v${NODE_VERSION}/${archiveName}`;
console.log(`[bundle-node] Downloading Node.js v${NODE_VERSION} (${osPart}-${nodeArch})...`);
console.log(` ${url}`);
const archivePath = path.join(cacheDir, archiveName);
const response = await fetch(url);
if (!response.ok) {
console.error(`[bundle-node] Download failed: HTTP ${response.status}`);
process.exit(1);
}
const buffer = Buffer.from(await response.arrayBuffer());
fs.writeFileSync(archivePath, buffer);
// Extract bin/node from the tarball. The archive root is the versioned dir
// (node-vX-os-arch/), so the member must include that prefix; --strip-
// components=1 then drops it so the file lands at <extractDir>/bin/node.
// BSD tar (macOS) matches members against the FULL archived path, so a bare
// "bin/node" matches nothing → "tar: bin/node: Not found in archive".
// (safe — no user input in args)
const extractDir = path.join(cacheDir, 'extract');
fs.mkdirSync(extractDir, { recursive: true });
const member = `node-v${NODE_VERSION}-${osPart}-${nodeArch}/bin/node`;
execFileSync('tar', ['xzf', archivePath, '-C', extractDir, '--strip-components=1', member], {
stdio: 'inherit',
});
const extractedNode = path.join(extractDir, 'bin', 'node');
fs.copyFileSync(extractedNode, cachedBinary);
fs.copyFileSync(cachedBinary, destBinary);
fs.chmodSync(destBinary, 0o755);
// Cleanup extracted files
}
if (!fs.existsSync(archivePath)) {
await download(`${distributionUrl}/${archiveName}`, archivePath);
}
let actualHash = sha256(archivePath);
if (actualHash !== expectedHash && !refreshedShasums) {
expectedHash = await loadExpectedHash(true);
refreshedShasums = true;
actualHash = sha256(archivePath);
}
if (actualHash !== expectedHash) {
fs.rmSync(archivePath, { force: true });
fs.rmSync(extractDir, { recursive: true, force: true });
fail(`SHA-256 mismatch for ${archiveName}: expected ${expectedHash}, got ${actualHash}`);
}
console.log(`[bundle-node] Verified ${archiveName} against official SHASUMS256.txt`);
fs.rmSync(extractDir, { recursive: true, force: true });
fs.mkdirSync(extractDir, { recursive: true });
execFileSync('tar', ['-xf', archivePath, '-C', extractDir], { stdio: 'inherit' });
if (!extractedRuntimeComplete()) {
fail(`verified archive is missing Node, npm, or required license files: ${archiveName}`);
}
const size = (fs.statSync(destBinary).size / 1024 / 1024).toFixed(1);
console.log(`[bundle-node] Node.js v${NODE_VERSION} (${platform}-${arch}) → ${destBinary} (${size} MB)`);
assertNativeRuntimeCompatible();
fs.copyFileSync(nodeSource, destBinary);
if (platform !== 'win32') fs.chmodSync(destBinary, 0o755);
fs.rmSync(stagedRuntimeDir, { recursive: true, force: true });
fs.mkdirSync(path.join(stagedRuntimeDir, 'node_modules'), { recursive: true });
fs.copyFileSync(nodeLicenseSource, path.join(stagedRuntimeDir, 'NODE-LICENSE'));
fs.cpSync(npmSource, path.join(stagedRuntimeDir, 'node_modules', 'npm'), {
recursive: true,
dereference: true,
});
const safeNpmBraceExpansionManifest = path.join(
safeNpmBraceExpansionSource,
'package.json',
);
if (!fs.existsSync(safeNpmBraceExpansionManifest)) {
fail('lock-installed brace-expansion hardening source is missing');
}
const safeNpmBraceExpansion = JSON.parse(
fs.readFileSync(safeNpmBraceExpansionManifest, 'utf8'),
);
if (safeNpmBraceExpansion.version !== SAFE_NPM_BRACE_EXPANSION_VERSION) {
fail(
`lock-installed brace-expansion is ${safeNpmBraceExpansion.version}; `
+ `expected ${SAFE_NPM_BRACE_EXPANSION_VERSION}`,
);
}
const stagedNpmBraceExpansion = path.join(
stagedRuntimeDir,
'node_modules',
'npm',
'node_modules',
'brace-expansion',
);
fs.rmSync(stagedNpmBraceExpansion, { recursive: true, force: true });
fs.cpSync(safeNpmBraceExpansionSource, stagedNpmBraceExpansion, {
recursive: true,
dereference: true,
});
const stagedNpmBraceExpansionVersion = JSON.parse(
fs.readFileSync(path.join(stagedNpmBraceExpansion, 'package.json'), 'utf8'),
).version;
if (stagedNpmBraceExpansionVersion !== SAFE_NPM_BRACE_EXPANSION_VERSION) {
fail(
`staged npm brace-expansion is ${stagedNpmBraceExpansionVersion}; `
+ `expected ${SAFE_NPM_BRACE_EXPANSION_VERSION}`,
);
}
console.log(
`[bundle-node] Hardened bundled npm with brace-expansion `
+ `${SAFE_NPM_BRACE_EXPANSION_VERSION}`,
);
const safeNpmIpAddressManifest = path.join(safeNpmIpAddressSource, 'package.json');
if (!fs.existsSync(safeNpmIpAddressManifest)) {
fail('lock-installed ip-address hardening source is missing');
}
const safeNpmIpAddress = JSON.parse(
fs.readFileSync(safeNpmIpAddressManifest, 'utf8'),
);
if (safeNpmIpAddress.version !== SAFE_NPM_IP_ADDRESS_VERSION) {
fail(
`lock-installed ip-address is ${safeNpmIpAddress.version}; `
+ `expected ${SAFE_NPM_IP_ADDRESS_VERSION}`,
);
}
const stagedNpmIpAddress = path.join(
stagedRuntimeDir,
'node_modules',
'npm',
'node_modules',
'ip-address',
);
fs.rmSync(stagedNpmIpAddress, { recursive: true, force: true });
fs.cpSync(safeNpmIpAddressSource, stagedNpmIpAddress, {
recursive: true,
dereference: true,
});
const stagedNpmIpAddressVersion = JSON.parse(
fs.readFileSync(path.join(stagedNpmIpAddress, 'package.json'), 'utf8'),
).version;
if (stagedNpmIpAddressVersion !== SAFE_NPM_IP_ADDRESS_VERSION) {
fail(
`staged npm ip-address is ${stagedNpmIpAddressVersion}; `
+ `expected ${SAFE_NPM_IP_ADDRESS_VERSION}`,
);
}
console.log(
`[bundle-node] Hardened bundled npm with ip-address ${SAFE_NPM_IP_ADDRESS_VERSION}`,
);
fs.writeFileSync(
path.join(stagedRuntimeDir, 'package.json'),
`${JSON.stringify({
name: 'waggle-node-runtime',
private: true,
version: NODE_VERSION,
description: 'Verified Node.js npm runtime staged for the Waggle desktop sidecar',
}, null, 2)}\n`,
'utf8',
);
writeWrappers();
const npmManifest = JSON.parse(
fs.readFileSync(path.join(stagedRuntimeDir, 'node_modules', 'npm', 'package.json'), 'utf8'),
);
for (const cli of ['npm', 'npx']) {
const cliPath = path.join(stagedRuntimeDir, 'node_modules', 'npm', 'bin', `${cli}-cli.js`);
const version = execFileSync(destBinary, [cliPath, '--version'], {
encoding: 'utf8',
stdio: ['ignore', 'pipe', 'pipe'],
}).trim();
if (version !== npmManifest.version) {
fail(`${cli} preflight returned ${version}; expected npm ${npmManifest.version}`);
}
}
const nodeSize = (fs.statSync(destBinary).size / 1024 / 1024).toFixed(1);
const npmSize = (directorySizeBytes(stagedRuntimeDir) / 1024 / 1024).toFixed(1);
console.log(
`[bundle-node] Node.js v${NODE_VERSION} + npm v${npmManifest.version} `
+ `(${platform}-${arch}) -> resources (${nodeSize} MB node, ${npmSize} MB npm runtime)`,
);

File diff suppressed because it is too large Load Diff

View File

@@ -15,31 +15,639 @@
*/
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { execFileSync } from 'node:child_process';
import { createHash } from 'node:crypto';
import { fileURLToPath } from 'node:url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const root = path.resolve(__dirname, '..');
const resourcesDir = path.join(root, 'app', 'src-tauri', 'resources');
const stagedDepsDir = path.join(resourcesDir, 'node_modules');
const bundledNpmRuntimeDir = path.join(stagedDepsDir, 'waggle-node-runtime');
const bundledNpmBinDir = path.join(bundledNpmRuntimeDir, 'bin');
const bundledNpmPackageDir = path.join(bundledNpmRuntimeDir, 'node_modules', 'npm');
const marketplaceDbRelative = 'packages/marketplace/marketplace.db';
const targetArch = process.env.TARGET_ARCH || process.arch;
const SIDECAR_PROVENANCE_PREFIX = '// Waggle-Sidecar-Provenance: ';
const SOURCE_ARTIFACT_PATTERN = /(?:\.map|\.(?:[cm]?ts|tsx)|\.tsbuildinfo)$/i;
const FIRST_PARTY_RUNTIME_ENTRY_PATTERN = /^(?:dist|package\.json|licen[cs]e(?:\.(?:md|txt))?|notice(?:\.(?:md|txt))?)$/i;
const MANUAL_FIRST_PARTY_RUNTIME_TARGETS = new Map([
['@waggle/hive-mind-hooks-openclaw', ['dist/handler.bundle.cjs']],
]);
const REQUIRED_SHARP_VERSION = '0.35.3';
const REQUIRED_BETTER_SQLITE_RANGE = '>=12.6.2 <13';
const STAGED_DEPENDENCY_VERSION_ALLOWLISTS = new Map([
['brace-expansion', new Set(['1.1.18', '2.1.4', '5.0.9'])],
['fast-uri', new Set(['3.1.5'])],
['ip-address', new Set(['10.4.0'])],
]);
const STAGED_DEPENDENCY_DENYLIST = new Set(['js-yaml']);
const missing = [];
const unsafe = [];
function listFiles(dir) {
const files = [];
const stack = [dir];
while (stack.length > 0) {
const current = stack.pop();
for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
const full = path.join(current, entry.name);
if (entry.isDirectory()) stack.push(full);
else if (entry.isFile()) files.push(full);
}
}
return files;
}
function resourceRelative(file) {
return path.relative(resourcesDir, file).split(path.sep).join('/');
}
function sha256File(file) {
return createHash('sha256').update(fs.readFileSync(file)).digest('hex');
}
function readGitBlob(revision, relative) {
return execFileSync(
'git',
['-C', root, 'cat-file', 'blob', `${revision}:${relative}`],
{ maxBuffer: 64 * 1024 * 1024, windowsHide: true },
);
}
function listSqliteSidecars(dir) {
if (!fs.existsSync(dir)) return [];
const sidecars = [];
const stack = [dir];
while (stack.length > 0) {
const current = stack.pop();
for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
const full = path.join(current, entry.name);
if (/\.db-(?:wal|shm|journal)$/i.test(entry.name)) sidecars.push(full);
if (entry.isDirectory()) stack.push(full);
}
}
return sidecars.sort();
}
function parseSidecarProvenance(serviceBuffer) {
const newlineIndex = serviceBuffer.indexOf(0x0a);
if (newlineIndex < 0) {
throw new Error('resources/service.js is missing embedded provenance');
}
const firstLine = serviceBuffer.subarray(0, newlineIndex).toString('utf8');
if (!firstLine.startsWith(SIDECAR_PROVENANCE_PREFIX)) {
throw new Error('resources/service.js is missing embedded provenance');
}
const encoded = firstLine.slice(SIDECAR_PROVENANCE_PREFIX.length);
if (
encoded.length === 0
|| encoded.length % 4 !== 0
|| !/^[A-Za-z0-9+/]+={0,2}$/.test(encoded)
) {
throw new Error('resources/service.js embedded provenance is not canonical base64');
}
const decoded = Buffer.from(encoded, 'base64');
if (decoded.toString('base64') !== encoded) {
throw new Error('resources/service.js embedded provenance is not canonical base64');
}
let provenance;
try {
provenance = JSON.parse(decoded.toString('utf8'));
} catch {
throw new Error('resources/service.js embedded provenance is not valid JSON');
}
if (!provenance || typeof provenance !== 'object' || Array.isArray(provenance)) {
throw new Error('resources/service.js embedded provenance must be an object');
}
if (provenance.schemaVersion !== 1) {
throw new Error('resources/service.js embedded provenance schemaVersion must be 1');
}
if (!/^[0-9a-f]{40}$/.test(provenance.sourceRevision)) {
throw new Error('resources/service.js embedded provenance sourceRevision is invalid');
}
if (provenance.entryPoint !== 'packages/server/src/local/service.ts') {
throw new Error('resources/service.js embedded provenance entryPoint is invalid');
}
if (!Array.isArray(provenance.sourceInputs) || provenance.sourceInputs.length === 0) {
throw new Error('resources/service.js embedded provenance sourceInputs are missing');
}
const requiredInputs = new Set([
'package-lock.json',
'package.json',
'packages/server/src/local/service.ts',
'scripts/build-sidecar.mjs',
]);
let previousPath = null;
for (const input of provenance.sourceInputs) {
if (!input || typeof input !== 'object' || Array.isArray(input)) {
throw new Error('resources/service.js embedded provenance source input is invalid');
}
const relative = input.path;
if (
typeof relative !== 'string'
|| relative.length === 0
|| relative.includes('\\')
|| relative.includes('\0')
|| path.posix.isAbsolute(relative)
|| relative.split('/').some((part) => part === '' || part === '.' || part === '..')
|| relative.split('/').includes('node_modules')
) {
throw new Error('resources/service.js embedded provenance source input path is unsafe');
}
if (previousPath !== null && previousPath >= relative) {
throw new Error('resources/service.js embedded provenance source inputs are not unique and sorted');
}
if (!/^[0-9a-f]{64}$/.test(input.sha256)) {
throw new Error('resources/service.js embedded provenance source input hash is invalid');
}
previousPath = relative;
requiredInputs.delete(relative);
}
if (requiredInputs.size > 0) {
throw new Error('resources/service.js embedded provenance omits required source inputs');
}
const payload = serviceBuffer.subarray(newlineIndex + 1);
if (
!provenance.bundlePayload
|| typeof provenance.bundlePayload !== 'object'
|| !Number.isSafeInteger(provenance.bundlePayload.sizeBytes)
|| provenance.bundlePayload.sizeBytes < 1
|| !/^[0-9a-f]{64}$/.test(provenance.bundlePayload.sha256)
|| provenance.bundlePayload.sizeBytes !== payload.byteLength
|| provenance.bundlePayload.sha256 !== createHash('sha256').update(payload).digest('hex')
) {
throw new Error('resources/service.js payload does not match embedded provenance');
}
return provenance;
}
function expectedSourceRevision() {
const index = process.argv.indexOf('--expected-source-revision');
const supplied = index >= 0 ? process.argv[index + 1] : null;
if (index >= 0 && !supplied) {
throw new Error('--expected-source-revision requires a 40-character revision');
}
const revision = supplied ?? execFileSync(
'git',
['-C', root, 'rev-parse', 'HEAD'],
{ encoding: 'utf8', windowsHide: true },
).trim();
if (!/^[0-9a-f]{40}$/.test(revision)) {
throw new Error('expected source revision must be exactly 40 lowercase hexadecimal characters');
}
return revision;
}
function readManifest(packageDir) {
try {
return JSON.parse(fs.readFileSync(path.join(packageDir, 'package.json'), 'utf8'));
} catch {
return {};
}
}
function listPackageDirs(nodeModulesDir) {
if (!fs.existsSync(nodeModulesDir)) return [];
const packageDirs = [];
const stack = [nodeModulesDir];
while (stack.length > 0) {
const current = stack.pop();
for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
if (!entry.isDirectory()) continue;
const full = path.join(current, entry.name);
if (fs.existsSync(path.join(full, 'package.json'))) packageDirs.push(full);
stack.push(full);
}
}
return packageDirs;
}
function isSupportedBetterSqliteVersion(version) {
if (typeof version !== 'string') return false;
const match = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/.exec(version);
if (!match) return false;
const [major, minor, patch] = match.slice(1).map(Number);
if (![major, minor, patch].every(Number.isSafeInteger)) return false;
return major === 12 && (minor > 6 || (minor === 6 && patch >= 2));
}
function stagedDependencyVersionFailures(nodeModulesDir, packageManifests) {
const manifests = packageManifests ?? listPackageDirs(nodeModulesDir)
.map((packageDir) => [packageDir, readManifest(packageDir)]);
const failures = [];
for (const [packageDir, manifest] of manifests) {
const relative = path.relative(nodeModulesDir, packageDir).split(path.sep).join('/');
const normalizedRelative = relative.toLowerCase();
const isBetterSqlitePath = (
normalizedRelative === 'better-sqlite3'
|| normalizedRelative.endsWith('/node_modules/better-sqlite3')
);
const allowedVersions = STAGED_DEPENDENCY_VERSION_ALLOWLISTS.get(manifest.name);
if (allowedVersions && !allowedVersions.has(manifest.version)) {
failures.push(
`node_modules/${relative} contains ${manifest.name}@${manifest.version}; `
+ `allowed versions: ${[...allowedVersions].join(', ')}`,
);
}
if (isBetterSqlitePath && manifest.name !== 'better-sqlite3') {
failures.push(
`node_modules/${relative} must identify as better-sqlite3; `
+ `found name ${JSON.stringify(manifest.name)}`,
);
}
if (
(isBetterSqlitePath || manifest.name === 'better-sqlite3')
&& !isSupportedBetterSqliteVersion(manifest.version)
) {
failures.push(
`node_modules/${relative} contains better-sqlite3@${manifest.version}; `
+ `required version: ${REQUIRED_BETTER_SQLITE_RANGE}`,
);
}
if (
(
manifest.name === 'sharp'
|| (
typeof manifest.name === 'string'
&& /^@img\/sharp-(?!libvips-)/.test(manifest.name)
)
)
&& manifest.version !== REQUIRED_SHARP_VERSION
) {
failures.push(
`node_modules/${relative} contains ${manifest.name}@${manifest.version}; `
+ `required version: ${REQUIRED_SHARP_VERSION}`,
);
}
if (STAGED_DEPENDENCY_DENYLIST.has(manifest.name)) {
failures.push(`node_modules/${relative} contains development-only ${manifest.name}`);
}
}
const bundledBraceDir = path.join(
nodeModulesDir,
'waggle-node-runtime',
'node_modules',
'npm',
'node_modules',
'brace-expansion',
);
const bundledBraceVersion = readManifest(bundledBraceDir).version;
if (bundledBraceVersion !== '2.1.4') {
failures.push(
`node_modules/waggle-node-runtime/node_modules/npm/node_modules/brace-expansion `
+ `must be exactly 2.1.4; found ${bundledBraceVersion ?? 'missing'}`,
);
}
return failures;
}
function localWorkspacePackageNames() {
const names = new Set();
for (const workspaceRoot of ['packages', 'apps'].map((entry) => path.join(root, entry))) {
if (!fs.existsSync(workspaceRoot)) continue;
for (const entry of fs.readdirSync(workspaceRoot, { withFileTypes: true })) {
if (!entry.isDirectory()) continue;
const manifest = readManifest(path.join(workspaceRoot, entry.name));
if (typeof manifest.name === 'string') names.add(manifest.name);
}
}
return names;
}
function collectRuntimeExportTargets(value, targets, condition = '') {
if (condition === 'types') return;
if (typeof value === 'string') {
targets.add(value);
return;
}
if (Array.isArray(value)) {
for (const entry of value) collectRuntimeExportTargets(entry, targets, condition);
return;
}
if (!value || typeof value !== 'object') return;
for (const [key, entry] of Object.entries(value)) {
collectRuntimeExportTargets(entry, targets, key);
}
}
function firstPartyRuntimeTargets(manifest) {
const targets = new Set();
if (typeof manifest.main === 'string') targets.add(manifest.main);
if (typeof manifest.module === 'string') targets.add(manifest.module);
if (typeof manifest.bin === 'string') targets.add(manifest.bin);
else if (manifest.bin && typeof manifest.bin === 'object') {
for (const entry of Object.values(manifest.bin)) {
if (typeof entry === 'string') targets.add(entry);
}
}
collectRuntimeExportTargets(manifest.exports, targets);
for (const entry of MANUAL_FIRST_PARTY_RUNTIME_TARGETS.get(manifest.name) || []) {
targets.add(entry);
}
return targets;
}
function validateFirstPartyRuntimeTargets(packageDir, manifest) {
const failures = [];
const distDir = path.resolve(packageDir, 'dist');
for (const target of firstPartyRuntimeTargets(manifest)) {
const relative = target.replace(/^\.\//, '').split('/').join(path.sep);
const resolved = path.resolve(packageDir, relative);
const withinDist = resolved.startsWith(`${distDir}${path.sep}`);
let regularRuntimeFile = false;
let realDistWithinPackage = false;
let realWithinDist = false;
if (withinDist && fs.existsSync(resolved)) {
const stat = fs.lstatSync(resolved);
regularRuntimeFile = stat.isFile() && !stat.isSymbolicLink();
if (regularRuntimeFile) {
const realPackageDir = fs.realpathSync.native(packageDir);
const realDistDir = fs.realpathSync.native(distDir);
const realTarget = fs.realpathSync.native(resolved);
realDistWithinPackage = realDistDir.startsWith(`${realPackageDir}${path.sep}`);
realWithinDist = realTarget.startsWith(`${realDistDir}${path.sep}`);
}
}
if (
!withinDist
|| !realDistWithinPackage
|| !realWithinDist
|| !regularRuntimeFile
|| target.includes('*')
) {
failures.push(target);
}
}
return failures;
}
const dependencyOnlyIndex = process.argv.indexOf('--dependency-versions-only');
if (dependencyOnlyIndex >= 0) {
const target = process.argv[dependencyOnlyIndex + 1];
if (!target) {
console.error('[check-sidecar-resources] --dependency-versions-only requires a directory');
process.exit(1);
}
const failures = stagedDependencyVersionFailures(path.resolve(target));
if (failures.length > 0) {
for (const failure of failures) console.error(failure);
process.exit(1);
}
console.log('[check-sidecar-resources] staged dependency versions are release-safe');
process.exit(0);
}
let expectedRevision = null;
try {
expectedRevision = expectedSourceRevision();
} catch (err) {
unsafe.push(err instanceof Error ? err.message : String(err));
}
const servicePath = path.join(resourcesDir, 'service.js');
if (!fs.existsSync(servicePath)) {
missing.push('resources/service.js (run: node scripts/build-sidecar.mjs)');
} else {
const serviceBuffer = fs.readFileSync(servicePath);
const service = serviceBuffer.toString('utf8');
if (/(?:\/\/|\/\*)[#@]\s*sourceMappingURL\s*=/.test(service)) {
unsafe.push('resources/service.js contains a sourceMappingURL directive');
}
try {
const provenance = parseSidecarProvenance(serviceBuffer);
if (expectedRevision && provenance.sourceRevision !== expectedRevision) {
throw new Error('resources/service.js source revision does not match expected revision');
}
for (const input of provenance.sourceInputs) {
const absolute = path.join(root, ...input.path.split('/'));
let sourceSafe = false;
if (fs.existsSync(absolute)) {
const stat = fs.lstatSync(absolute);
const relative = path.relative(root, absolute);
sourceSafe = stat.isFile()
&& !stat.isSymbolicLink()
&& relative !== ''
&& !relative.startsWith(`..${path.sep}`)
&& !path.isAbsolute(relative)
&& sha256File(absolute) === input.sha256;
}
if (!sourceSafe) {
throw new Error(
`resources/service.js source input hash does not match current source: ${input.path}`,
);
}
}
} catch (err) {
unsafe.push(err instanceof Error ? err.message : String(err));
}
}
const canonicalMarketplaceDb = path.join(root, ...marketplaceDbRelative.split('/'));
const marketplaceResource = path.join(resourcesDir, 'marketplace.db');
for (const suffix of ['-wal', '-shm', '-journal']) {
if (fs.existsSync(`${canonicalMarketplaceDb}${suffix}`)) {
unsafe.push(`packages/marketplace/marketplace.db${suffix} must not be present while staging`);
}
}
for (const sidecar of listSqliteSidecars(resourcesDir)) {
unsafe.push(`resources/${resourceRelative(sidecar)} must not be packaged`);
}
const canonicalMarketplaceIsRegular = fs.existsSync(canonicalMarketplaceDb)
&& fs.lstatSync(canonicalMarketplaceDb).isFile()
&& !fs.lstatSync(canonicalMarketplaceDb).isSymbolicLink();
const marketplaceResourceIsRegular = fs.existsSync(marketplaceResource)
&& fs.lstatSync(marketplaceResource).isFile()
&& !fs.lstatSync(marketplaceResource).isSymbolicLink();
if (!canonicalMarketplaceIsRegular) {
missing.push('packages/marketplace/marketplace.db canonical build input');
}
let marketplaceGitBlob = null;
if (expectedRevision) {
try {
marketplaceGitBlob = readGitBlob(expectedRevision, marketplaceDbRelative);
} catch {
unsafe.push('canonical marketplace database is missing from exact source revision');
}
}
const canonicalMarketplaceBytes = canonicalMarketplaceIsRegular
? fs.readFileSync(canonicalMarketplaceDb)
: null;
if (
marketplaceGitBlob
&& canonicalMarketplaceBytes
&& !canonicalMarketplaceBytes.equals(marketplaceGitBlob)
) {
unsafe.push(
'packages/marketplace/marketplace.db canonical marketplace database does not match exact source revision',
);
}
if (!fs.existsSync(marketplaceResource)) {
missing.push('resources/marketplace.db (run: node scripts/build-sidecar.mjs)');
} else if (!marketplaceResourceIsRegular) {
unsafe.push('resources/marketplace.db must be a regular file');
} else {
const marketplaceResourceBytes = fs.readFileSync(marketplaceResource);
if (marketplaceGitBlob && !marketplaceResourceBytes.equals(marketplaceGitBlob)) {
unsafe.push(
'resources/marketplace.db does not match the canonical marketplace database at the exact source revision',
);
} else if (
canonicalMarketplaceBytes
&& !marketplaceResourceBytes.equals(canonicalMarketplaceBytes)
) {
unsafe.push('resources/marketplace.db does not match the canonical marketplace database');
}
}
const sourceArtifacts = fs.existsSync(resourcesDir)
? fs.readdirSync(resourcesDir, { withFileTypes: true })
.filter((entry) => /\.(?:map|tsx?)$/i.test(entry.name))
.map((entry) => entry.name)
: [];
for (const artifact of sourceArtifacts) {
unsafe.push(`resources/${artifact} must not be packaged`);
}
const stagedPackageDirs = listPackageDirs(stagedDepsDir);
const stagedPackageManifests = stagedPackageDirs
.map((packageDir) => [packageDir, readManifest(packageDir)]);
for (const failure of stagedDependencyVersionFailures(stagedDepsDir, stagedPackageManifests)) {
unsafe.push(`resources/${failure}`);
}
const firstPartyRoot = path.join(stagedDepsDir, '@waggle');
const firstPartyPackageDirs = new Map();
if (fs.existsSync(firstPartyRoot)) {
for (const packageEntry of fs.readdirSync(firstPartyRoot, { withFileTypes: true })) {
if (!packageEntry.isDirectory()) continue;
firstPartyPackageDirs.set(
path.join(firstPartyRoot, packageEntry.name),
`@waggle/${packageEntry.name}`,
);
}
}
const workspacePackageNames = localWorkspacePackageNames();
for (const name of workspacePackageNames) {
const directPackageDir = path.join(stagedDepsDir, ...name.split('/'));
if (fs.existsSync(directPackageDir)) {
firstPartyPackageDirs.set(directPackageDir, name);
}
}
for (const [packageDir, manifest] of stagedPackageManifests) {
const { name } = manifest;
if (
typeof name === 'string'
&& (name.startsWith('@waggle/') || workspacePackageNames.has(name))
) {
firstPartyPackageDirs.set(packageDir, name);
}
}
for (const [packageDir, expectedName] of firstPartyPackageDirs) {
let manifest = {};
const manifestPath = path.join(packageDir, 'package.json');
try {
const stat = fs.lstatSync(manifestPath);
if (!stat.isFile() || stat.isSymbolicLink()) {
throw new Error('manifest must be a regular file');
}
const parsed = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
throw new Error('manifest must contain a JSON object');
}
if (parsed.name !== expectedName) {
throw new Error(`manifest name ${JSON.stringify(parsed.name)} does not match ${expectedName}`);
}
manifest = parsed;
} catch (err) {
unsafe.push(
`resources/${resourceRelative(manifestPath)} is missing or invalid: `
+ (err instanceof Error ? err.message : String(err)),
);
}
for (const entry of fs.readdirSync(packageDir, { withFileTypes: true })) {
if (FIRST_PARTY_RUNTIME_ENTRY_PATTERN.test(entry.name)) continue;
const relative = resourceRelative(path.join(packageDir, entry.name));
unsafe.push(`resources/${relative} is not a runtime package entry`);
}
for (const file of listFiles(packageDir)) {
if (SOURCE_ARTIFACT_PATTERN.test(file)) {
unsafe.push(`resources/${resourceRelative(file)} must not be packaged`);
}
if (
/\.(?:[cm]?js)$/i.test(file)
&& /(?:\/\/|\/\*)[#@]\s*sourceMappingURL\s*=/.test(fs.readFileSync(file, 'utf8'))
) {
unsafe.push(`resources/${resourceRelative(file)} contains a sourceMappingURL directive`);
}
}
for (const target of validateFirstPartyRuntimeTargets(packageDir, manifest)) {
unsafe.push(
`resources/${resourceRelative(packageDir)} has an invalid or missing runtime target: ${target}`,
);
}
}
const nodeBinary = process.platform === 'win32' ? 'node.exe' : 'node';
const nodePath = path.join(resourcesDir, nodeBinary);
const npmCliPath = path.join(bundledNpmPackageDir, 'bin', 'npm-cli.js');
const npxCliPath = path.join(bundledNpmPackageDir, 'bin', 'npx-cli.js');
const npmWrapperPath = path.join(
bundledNpmBinDir,
process.platform === 'win32' ? 'npm.cmd' : 'npm',
);
const npxWrapperPath = path.join(
bundledNpmBinDir,
process.platform === 'win32' ? 'npx.cmd' : 'npx',
);
const bundledNpmFiles = [
path.join(bundledNpmRuntimeDir, 'package.json'),
path.join(bundledNpmRuntimeDir, 'NODE-LICENSE'),
path.join(bundledNpmPackageDir, 'LICENSE'),
npmCliPath,
npxCliPath,
npmWrapperPath,
npxWrapperPath,
];
for (const file of bundledNpmFiles) {
if (!fs.existsSync(file) || !fs.lstatSync(file).isFile()) {
missing.push(`resources/${resourceRelative(file)} (run: node scripts/bundle-node.mjs)`);
}
}
if (process.platform !== 'win32') {
for (const wrapper of [npmWrapperPath, npxWrapperPath]) {
if (fs.existsSync(wrapper) && (fs.statSync(wrapper).mode & 0o111) === 0) {
unsafe.push(`resources/${resourceRelative(wrapper)} is not executable`);
}
}
}
let bundledNodeVersion = null;
if (!fs.existsSync(nodePath)) {
missing.push(`resources/${nodeBinary} (run: node scripts/bundle-node.mjs)`);
} else {
try {
const bundledAbi = execFileSync(nodePath, ['-p', 'process.versions.modules'], {
const bundledRuntime = JSON.parse(execFileSync(nodePath, [
'-p',
'JSON.stringify({ arch: process.arch, version: process.versions.node })',
], {
encoding: 'utf-8',
stdio: ['ignore', 'pipe', 'pipe'],
}).trim();
const currentAbi = process.versions.modules;
if (bundledAbi !== currentAbi) {
}).trim());
bundledNodeVersion = bundledRuntime.version;
// The checker may run under a different Node major than the bundled runtime.
// The native-module probe below is the authoritative ABI compatibility check.
if (bundledRuntime.arch !== targetArch) {
missing.push(
`resources/${nodeBinary} ABI ${bundledAbi} does not match current Node ABI ${currentAbi} ` +
'(run: node scripts/bundle-node.mjs with the same Node used for npm install/stage-sidecar-deps)',
`resources/${nodeBinary} architecture ${bundledRuntime.arch} does not match target ${targetArch}`,
);
}
} catch (err) {
@@ -47,11 +655,97 @@ if (!fs.existsSync(nodePath)) {
}
}
if (
fs.existsSync(nodePath)
&& bundledNpmFiles.every((file) => fs.existsSync(file))
) {
try {
const runtimeManifest = readManifest(bundledNpmRuntimeDir);
const npmManifest = readManifest(bundledNpmPackageDir);
if (runtimeManifest.name !== 'waggle-node-runtime') {
throw new Error('runtime manifest has an unexpected name');
}
if (runtimeManifest.version !== bundledNodeVersion) {
throw new Error(
`runtime manifest Node ${runtimeManifest.version} does not match bundled Node ${bundledNodeVersion}`,
);
}
if (typeof npmManifest.version !== 'string' || npmManifest.version.length === 0) {
throw new Error('npm manifest has no version');
}
const runBundledCli = (cliPath) => execFileSync(nodePath, [cliPath, '--version'], {
encoding: 'utf8',
stdio: ['ignore', 'pipe', 'pipe'],
}).trim();
const runWrapper = (wrapperPath) => {
if (process.platform === 'win32') {
return execFileSync(process.env.ComSpec || 'cmd.exe', [
'/d',
'/s',
'/c',
`""${wrapperPath}" --version"`,
], {
encoding: 'utf8',
stdio: ['ignore', 'pipe', 'pipe'],
windowsVerbatimArguments: true,
}).trim();
}
return execFileSync(wrapperPath, ['--version'], {
encoding: 'utf8',
stdio: ['ignore', 'pipe', 'pipe'],
}).trim();
};
const versions = [
runBundledCli(npmCliPath),
runBundledCli(npxCliPath),
runWrapper(npmWrapperPath),
runWrapper(npxWrapperPath),
];
if (versions.some((version) => version !== npmManifest.version)) {
throw new Error(`npm/npx version mismatch: ${versions.join(', ')}`);
}
} catch (err) {
missing.push(
`resources bundled npm/npx runtime probe failed (${err instanceof Error ? err.message : String(err)})`,
);
}
}
const nativeDir = path.join(resourcesDir, 'native');
const nativeEntries = fs.existsSync(nativeDir)
? fs.readdirSync(nativeDir).filter((e) => e !== '.gitkeep' && e !== 'onnxruntime')
: [];
if (nativeEntries.length === 0) {
const requiredWindowsNativeFiles = [
'better_sqlite3.node',
'vec0.dll',
'onnxruntime/onnxruntime_binding.node',
];
if (process.platform === 'win32') {
for (const entry of requiredWindowsNativeFiles) {
if (!fs.existsSync(path.join(nativeDir, ...entry.split('/')))) {
missing.push(
`resources/native/${entry} (run: node scripts/bundle-native-deps.mjs)`,
);
}
}
} else if (process.platform === 'darwin') {
const requiredMacNativeFiles = [
'better_sqlite3.node',
'vec0.dylib',
'onnxruntime/onnxruntime_binding.node',
];
for (const entry of requiredMacNativeFiles) {
if (!fs.existsSync(path.join(nativeDir, ...entry.split('/')))) {
missing.push(`resources/native/${entry} (run: node scripts/bundle-native-deps.mjs)`);
}
}
const onnxDir = path.join(nativeDir, 'onnxruntime');
const hasOnnxLibrary = fs.existsSync(onnxDir)
&& fs.readdirSync(onnxDir).some((entry) => entry.endsWith('.dylib'));
if (!hasOnnxLibrary) {
missing.push('resources/native/onnxruntime/*.dylib (run: node scripts/bundle-native-deps.mjs)');
}
} else if (nativeEntries.length === 0) {
missing.push('resources/native/* (run: node scripts/bundle-native-deps.mjs)');
}
@@ -62,10 +756,187 @@ if (nativeEntries.length === 0) {
// npm scripts / CI (stage-sidecar-deps.mjs), NOT the arch-blind beforeBuildCommand
// hook — so a raw `npx tauri build` that skips those would package a sidecar that
// dies with MODULE_NOT_FOUND on first boot. Probe a canonical external.
const stagedDepsDir = path.join(resourcesDir, 'node_modules');
if (!fs.existsSync(path.join(stagedDepsDir, 'better-sqlite3', 'package.json'))) {
const stagedBetterSqlite = path.join(stagedDepsDir, 'better-sqlite3');
const stagedOnnxRuntime = path.join(stagedDepsDir, 'onnxruntime-node');
const stagedTransformers = path.join(stagedDepsDir, '@huggingface', 'transformers');
const stagedTransformersEntry = path.join(
stagedTransformers,
'dist',
'transformers.node.cjs',
);
const stagedSharp = path.join(stagedDepsDir, 'sharp');
const stagedSharpWindowsBinding = path.join(
stagedDepsDir,
'@img',
'sharp-win32-x64',
'lib',
`sharp-win32-x64-${REQUIRED_SHARP_VERSION}.node`,
);
const vecExtension = path.join(
nativeDir,
`vec0.${process.platform === 'win32' ? 'dll' : process.platform === 'darwin' ? 'dylib' : 'so'}`,
);
if (!fs.existsSync(path.join(stagedBetterSqlite, 'package.json'))) {
missing.push('resources/node_modules/* (run: node scripts/stage-sidecar-deps.mjs)');
}
if (!fs.existsSync(path.join(stagedOnnxRuntime, 'package.json'))) {
missing.push('resources/node_modules/onnxruntime-node (run: node scripts/stage-sidecar-deps.mjs)');
}
if (!fs.existsSync(path.join(stagedTransformers, 'package.json'))) {
missing.push(
'resources/node_modules/@huggingface/transformers '
+ '(run: node scripts/stage-sidecar-deps.mjs)',
);
}
if (!fs.existsSync(stagedTransformersEntry)) {
missing.push(
'resources/node_modules/@huggingface/transformers/dist/transformers.node.cjs '
+ '(run: node scripts/stage-sidecar-deps.mjs)',
);
}
if (!fs.existsSync(path.join(stagedSharp, 'package.json'))) {
missing.push('resources/node_modules/sharp (run: node scripts/stage-sidecar-deps.mjs)');
}
if (
process.platform === 'win32'
&& targetArch === 'x64'
&& !fs.existsSync(stagedSharpWindowsBinding)
) {
missing.push(
`resources/node_modules/@img/sharp-win32-x64/lib/`
+ `sharp-win32-x64-${REQUIRED_SHARP_VERSION}.node `
+ '(run: node scripts/stage-sidecar-deps.mjs)',
);
}
if (
fs.existsSync(nodePath)
&& fs.existsSync(path.join(stagedBetterSqlite, 'package.json'))
&& marketplaceResourceIsRegular
) {
let marketplaceProbeRoot;
try {
marketplaceProbeRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'waggle-marketplace-probe-'));
const marketplaceProbeDb = path.join(marketplaceProbeRoot, 'marketplace.db');
fs.copyFileSync(marketplaceResource, marketplaceProbeDb);
const marketplaceProbe = [
'const Database = require(process.argv[1]);',
'const database = new Database(process.argv[2], { readonly: true, fileMustExist: true });',
'const integrity = database.pragma("integrity_check", { simple: true });',
'if (integrity !== "ok") throw new Error(`integrity_check: ${integrity}`);',
'const foreignKeys = database.pragma("foreign_key_check");',
'if (foreignKeys.length !== 0) throw new Error("foreign_key_check failed");',
'const tables = database.prepare("SELECT name FROM sqlite_master WHERE type = \'table\' AND name IN (\'sources\', \'packages\')").all();',
'database.close();',
'if (new Set(tables.map((row) => row.name)).size !== 2) throw new Error("required tables missing");',
].join('');
execFileSync(nodePath, [
'-e',
marketplaceProbe,
stagedBetterSqlite,
marketplaceProbeDb,
], {
cwd: marketplaceProbeRoot,
env: { ...process.env, NODE_PATH: stagedDepsDir },
stdio: ['ignore', 'pipe', 'pipe'],
});
} catch {
unsafe.push('resources/marketplace.db failed its SQLite integrity/schema probe');
} finally {
if (marketplaceProbeRoot) {
fs.rmSync(marketplaceProbeRoot, { recursive: true, force: true });
}
}
}
if (
fs.existsSync(nodePath)
&& fs.existsSync(path.join(stagedTransformers, 'package.json'))
&& fs.existsSync(stagedTransformersEntry)
&& fs.existsSync(path.join(stagedSharp, 'package.json'))
&& (
process.platform !== 'win32'
|| targetArch !== 'x64'
|| fs.existsSync(stagedSharpWindowsBinding)
)
) {
try {
const imageProbe = [
'const { RawImage } = require(process.argv[1]);',
'const sharp = require(process.argv[2]);',
'if (process.argv[3]) require(process.argv[3]);',
'if (sharp.versions?.emscripten) throw new Error("Sharp fell back to WASM");',
'void (async () => {',
'const image = new RawImage(',
'Uint8Array.from([255,0,0,255,0,255,0,255,0,0,255,255,255,255,255,255]),',
'2,2,4);',
'const buffer = await image.toSharp().resize(1, 1).png().toBuffer();',
'const signature = Buffer.from([137,80,78,71,13,10,26,10]);',
'if (buffer.length < signature.length || !buffer.subarray(0, 8).equals(signature)) {',
'throw new Error("Sharp PNG probe failed");',
'}',
'})().catch((error) => { console.error(error); process.exit(1); });',
].join('');
execFileSync(nodePath, [
'-e',
imageProbe,
stagedTransformersEntry,
stagedSharp,
...(
process.platform === 'win32' && targetArch === 'x64'
? [stagedSharpWindowsBinding]
: []
),
], {
cwd: resourcesDir,
env: { ...process.env, NODE_PATH: stagedDepsDir },
stdio: ['ignore', 'pipe', 'pipe'],
});
} catch (err) {
missing.push(
`resources image runtime probe failed for @huggingface/transformers and sharp `
+ `using bundled ${nodeBinary} for ${targetArch} `
+ `(${err instanceof Error ? err.message : String(err)})`,
);
}
}
if (
fs.existsSync(nodePath)
&& fs.existsSync(path.join(stagedBetterSqlite, 'package.json'))
&& fs.existsSync(path.join(stagedOnnxRuntime, 'package.json'))
&& fs.existsSync(vecExtension)
) {
try {
const probe = [
'const Database = require(process.argv[1]);',
'if (process.arch !== process.argv[2]) throw new Error(`architecture ${process.arch}`);',
'const database = new Database(\':memory:\');',
'database.loadExtension(process.argv[3]);',
'const row = database.prepare(\'SELECT 1 AS ok\').get();',
'const vec = database.prepare(\'SELECT vec_version() AS version\').get();',
'database.close();',
'if (row.ok !== 1) throw new Error(\'SQLite query failed\');',
'if (typeof vec.version !== \'string\' || vec.version.length === 0) throw new Error(\'sqlite-vec query failed\');',
'const onnx = require(process.argv[4]);',
'if (typeof onnx.InferenceSession !== \'function\') throw new Error(\'ONNX binding failed\');',
].join('');
execFileSync(nodePath, [
'-e',
probe,
stagedBetterSqlite,
targetArch,
vecExtension,
stagedOnnxRuntime,
], {
cwd: resourcesDir,
env: { ...process.env, NODE_PATH: stagedDepsDir },
stdio: ['ignore', 'pipe', 'pipe'],
});
} catch {
missing.push(
`resources native runtime probe failed for better-sqlite3, sqlite-vec, or onnxruntime-node `
+ `using bundled ${nodeBinary} for ${targetArch}`,
);
}
}
// External agents and hook management run directly from this staged payload;
// none of these packages are available from npm in a packaged installation.
@@ -78,6 +949,8 @@ const hookRuntimeEntries = [
'@waggle/hive-mind-hooks-cursor/dist/bin/cursor-hooks.js',
'@waggle/hive-mind-hooks-hermes/dist/bin/hermes-hooks.js',
'@waggle/hive-mind-hooks-openclaw/dist/bin/openclaw-hooks.js',
'@waggle/hive-mind-hooks-openclaw/dist/handler.bundle.cjs',
'waggle-memory-mcp/dist/index.js',
];
for (const entry of hookRuntimeEntries) {
if (!fs.existsSync(path.join(stagedDepsDir, ...entry.split('/')))) {
@@ -85,9 +958,10 @@ for (const entry of hookRuntimeEntries) {
}
}
if (missing.length > 0) {
console.error('[check-sidecar-resources] FATAL — sidecar runtime artifacts missing:');
if (missing.length > 0 || unsafe.length > 0) {
console.error('[check-sidecar-resources] FATAL — sidecar resources are not release-safe:');
for (const m of missing) console.error(` - ${m}`);
for (const item of unsafe) console.error(` - ${item}`);
console.error(
'[check-sidecar-resources] Stage them with the bundle scripts (set TARGET_ARCH for\n' +
'cross-arch builds) or use the npm tauri:build* scripts / CI, which run them for you.',

View File

@@ -0,0 +1,309 @@
{
"schemaVersion": 1,
"mapping": {
"canonical": "packages/hive-mind-core/src",
"oss": "packages/core/src",
"ignoredDirectories": [
"dist",
"node_modules"
],
"ignoredFileSuffixes": [
".test.ts",
".tsbuildinfo"
]
},
"parityPaths": [
{
"path": "harvest/claude-code-adapter.ts",
"sha256": "13d4c7965b35c6cc66f53445ed752121cbba1b8b8ab80d196f3357977acc8d03"
},
{
"path": "harvest/decision-derivation.ts",
"sha256": "97e026befe980c4f754db5639fd96b715f09c351316f87a41e69899839d55f52"
},
{
"path": "harvest/stable-id.ts",
"sha256": "b1d8568bf3799f5054b6f2fc4119db4c8f67a0a49b22ff0b818a95c17a2eba56"
},
{
"path": "mind/erasure.ts",
"sha256": "6d5958c1b8e73a774324e1a4ca0348160b4abf3ff338d3cd794ef024d5ec5c5a"
},
{
"path": "mind/inprocess-reranker.ts",
"sha256": "0ed4a70147e0f7a17fdd14c3cc1127979bd653e3786a1a70189a5ab0996ec9f9"
}
],
"intentionalAdaptations": [
{
"path": "harvest/chatgpt-adapter.ts",
"kinds": ["branding", "import", "logger"],
"canonicalSha256": "24603b8ecb18f795c5cbbbe483113dbb24f08fab6b1bce2fb19ee7127f8957d0",
"ossSha256": "97b4aec1356a98f9ba72ed373954934a657db4579cc67906b7fa9ee52a458c1a"
},
{
"path": "harvest/chunk-utils.ts",
"kinds": ["import", "layout"],
"canonicalSha256": "868f8ede1f2201d214a012d9cc9c7157a9418205d8bb2eff8ed872d4658f6c6e",
"ossSha256": "88d2bf92cae38cc975baff5eb75c5cab89cb6bda2262ec7c381635589b10ea18"
},
{
"path": "harvest/claude-adapter.ts",
"kinds": ["branding", "import", "logger"],
"canonicalSha256": "3a22d18cf1ef2b12fa8689bd0909c2b34abfae3a9918c5ef8ca9a8cbfba8a522",
"ossSha256": "6dad506184e24ba98969fcb0a5885468167fd98da80fd29363ca131a7c7a46e1"
},
{
"path": "harvest/extract-memory-lanes.ts",
"kinds": ["import"],
"canonicalSha256": "800daac77c81e9daa3ecc0a8efa1f7203f6f41b64ad931e66cb97bc899d5d46d",
"ossSha256": "58e36562dfebd95cc1c2243746fcb2e755184fc7e2a5a05f334da2b6200855c1"
},
{
"path": "harvest/gemini-adapter.ts",
"kinds": ["branding", "import", "logger"],
"canonicalSha256": "e68a34a62653f5387ae204eaa92b8bc861fc11d5b449db1eaa3f08573b95680c",
"ossSha256": "c4346791f439c34514b93dd9eef905bda72b255665b97fc14d60469338c32dda"
},
{
"path": "harvest/index.ts",
"kinds": ["import", "layout"],
"canonicalSha256": "9f7bfdc50c031866071ec998b5eca65ec6ded832fb9ff07bbb654e2ea5b68820",
"ossSha256": "fda20a50ba0247808c245397d65e2858b50924cffc6c140668854c70496e580c"
},
{
"path": "harvest/markdown-adapter.ts",
"kinds": ["branding", "import", "logger"],
"canonicalSha256": "651c7cb76c9634896d69978f3537b74ca7f7fdd14aefe36e1c769aafbcab4e95",
"ossSha256": "77f495fcdbca87e90acf791d8c3c0e18021b2fec4dee5c5a827bd2b465b63b9d"
},
{
"path": "harvest/pdf-adapter.ts",
"kinds": ["branding", "import", "logger"],
"canonicalSha256": "b6be7a2017d96e92eb20f9bffa949a8c433633b9ed9d4e741db089bb9588d07f",
"ossSha256": "2896d0425781152eafbd5bc7a3076ca95f587e896ab0f9dea69afe99f6baf094"
},
{
"path": "harvest/perplexity-adapter.ts",
"kinds": ["branding", "import", "logger"],
"canonicalSha256": "95b54dac195949f2b53c21f908f1f0fc04564b55eac892ce757e81d53f0793cd",
"ossSha256": "20d7366d9848916994ac9f834ee3cd03700fde671c632ba5564a7122c8195454"
},
{
"path": "harvest/plaintext-adapter.ts",
"kinds": ["branding", "import", "logger"],
"canonicalSha256": "14fa80a29642a448391a529a5a8dd0b33460c09e0223b984bb293ec73ede10d0",
"ossSha256": "1e24105088181c46302718b886fe9a4400ce05a9e8a64345c7b6bc78b222bcd1"
},
{
"path": "harvest/prompts.ts",
"kinds": ["branding"],
"canonicalSha256": "c672d702e335af33c6674f4b7b2c3536541fe1f5482b49660d4412d99620baca",
"ossSha256": "2730b9cfdfc5fcf3940d7a43b86f3ebce0e53e4ed1127fd665c137434e9e83ac"
},
{
"path": "harvest/raw-types.ts",
"kinds": ["import", "layout"],
"canonicalSha256": "2a10f0374d91c4f6e3f87c0fd77fc9ade24a669f7d08f2b283c653ca114284c1",
"ossSha256": "1bf0a4c45090466589a8b49694fb182deb6ab7e8442b61eb65cb3387bd62f0aa"
},
{
"path": "harvest/run-store.ts",
"kinds": ["import", "logger"],
"canonicalSha256": "3ea55f2884ad8cfa40aaeeb7398f5a962dbe29436396696ba9776038fe5c8ab5",
"ossSha256": "956c976f0113c26f1b5c6e9826d96b72203b3b6468f926d6af768342d5cebadc"
},
{
"path": "harvest/source-store.ts",
"kinds": ["import", "logger"],
"canonicalSha256": "5f82788d632dfb85c243313e074a280bd7a49564ec525c9ad90496d12d1fb54d",
"ossSha256": "97d12f2c48564c748c394d09343a160c2e526a36830562139fb0cc579d020cc0"
},
{
"path": "harvest/types.ts",
"kinds": ["import", "layout"],
"canonicalSha256": "16f4757b7049617425e753ba4c14eb54c63ef113e4aaa23990ee6cf2188053ad",
"ossSha256": "2eda81e6277c61a8920f60ce1815785de5521f27915f8704577c771b1135a0b7"
},
{
"path": "harvest/universal-adapter.ts",
"kinds": ["branding", "import", "logger"],
"canonicalSha256": "a39368bc35d242ec6693b0c4964bac0dcdff80b9f659dccbbd5d18e99a894b32",
"ossSha256": "a9dfefd30d367f6d5618baeb1687a2b93074104d907719cae426f9fb1162de2b"
},
{
"path": "harvest/url-egress-guard.ts",
"kinds": ["branding", "import", "logger"],
"canonicalSha256": "4940b28d7ea06e460ad97a522214fac27a803e691b4fbfe110bf97e8dfd98ce5",
"ossSha256": "eacc5e69ee71b2000e7bf26a8ac2c9e64f15db884978789e24d708aeacc74668"
},
{
"path": "injection-scanner.ts",
"kinds": ["layout"],
"canonicalSha256": "a1b5db9afc71873262df1db9ae89d0821c4122e1f38d982ef048f3080db51adf",
"ossSha256": "beede3f82c66106367a2ae58f3bcfdec4884c5b9fb2b792ea08b88cd7284d9d1"
},
{
"path": "logger.ts",
"kinds": ["branding", "logger"],
"canonicalSha256": "cca0f01bd7bfaeea7166b4d51ad677320c241f68024885bc688f5d31307b12fe",
"ossSha256": "0ce6746ba197b80a067a10388764568bf97477766e924266ee4a48ca6d01a888"
},
{
"path": "memory-ingress-guard.ts",
"kinds": ["import"],
"canonicalSha256": "5c795775b580d20343471067d060192e5065787f35602713563512b2fde06007",
"ossSha256": "a00b66a3814aea50d6926b55996af883f06ee561760d463085847669183b98de"
},
{
"path": "mind/api-embedder.ts",
"kinds": ["branding", "import", "logger"],
"canonicalSha256": "16ad89d908dc3810e793d838c6ab8448394a6f7145dde374cd20980b72eb216d",
"ossSha256": "9ab9c766e138cf19a9a78a9e8a6f6b42e9f62048371b07a5123e4c0388f79c27"
},
{
"path": "mind/awareness.ts",
"kinds": ["import", "logger"],
"canonicalSha256": "bdc30923a6c753a7749850c9ab436dda111a816d18028c4274521ad08484cf36",
"ossSha256": "13fb221475e76fea2fe6ece872a3e34eb3c7bbe3c67d6d2215304878a9ac74b2"
},
{
"path": "mind/chunker.ts",
"kinds": ["import"],
"canonicalSha256": "f6121b2b1db5a1b5dcce39a909692dc96a997a95331cad1b6d2f3cc49d3d4bbd",
"ossSha256": "e5166ce85e1c93fa155e1e9bda636ecc50b56fa41b3f059186520f71fb8302f3"
},
{
"path": "mind/concept-tracker.ts",
"kinds": ["import", "logger"],
"canonicalSha256": "779ab17aad0d3751ea40d8eb558a63a7e36031b3fa97cd29fe211d2551965c51",
"ossSha256": "0aba6c53edd4e7357081e1722e08005f328ecbaa8e69add9efb8bf7c76d055a5"
},
{
"path": "mind/content-hash.ts",
"kinds": ["import"],
"canonicalSha256": "354a9648d480dea048dcd78ca784a56efcc417c7574e1fbea779d3df95da074f",
"ossSha256": "e1d34ca9b3cd1ce0351509ab04fdb4a6d96b78f96005b2ca23229b5bcee40bfc"
},
{
"path": "mind/embeddings.ts",
"kinds": ["import", "logger"],
"canonicalSha256": "74ce421fc82964ca8928d40be6cfb91370445692b49758601b3a5057d586a218",
"ossSha256": "827a9ec88ef33c7ff5838b9b9ac00fae0d12e0c16d0d2061415e6cbf9520fae1"
},
{
"path": "mind/entity-normalizer.ts",
"kinds": ["import"],
"canonicalSha256": "936ecb94d368fa1f010a047c3205f09331cfdd77111493af37909b0ae1cce1a2",
"ossSha256": "ea4ef0ecb8879eca6241a48a92c0e209fd00a64ef0325ee12ef480195021c695"
},
{
"path": "mind/fts-sanitize.ts",
"kinds": ["import"],
"canonicalSha256": "4affefd57348f58ef9a0de915e43548c9b13ebb3976b74b60b281798eedaae4e",
"ossSha256": "cb24324005ae69d35af69325c545555a2e79d0d71ed5695e7e71a12963ff4b48"
},
{
"path": "mind/litellm-embedder.ts",
"kinds": ["branding", "import", "logger"],
"canonicalSha256": "87e1da22f2e0047d2f9b754f5faf353c04654a12392fe1c2dca2ced12a84d712",
"ossSha256": "4f2d43f1a17961e8f94bff3a06b3008cd3b69a0807e03232fb96fe386308c1a4"
},
{
"path": "mind/ollama-embedder.ts",
"kinds": ["branding", "import", "logger"],
"canonicalSha256": "9fef2d801ab2c6918d0c352fa3b1fcc77f1a1c77070cd166f785d13ccfd4fe30",
"ossSha256": "1f2f543ed5d8f97a841bae27dc436f11f21d3dbd55473f303f5ef8f20bb4341a"
},
{
"path": "mind/ontology.ts",
"kinds": ["import"],
"canonicalSha256": "2c301a0ed4b5aecb470e33e8eba34f03ab985a9d4431e6a5ab5cbc36e3b222a0",
"ossSha256": "859ead772389e5854ca1c5681b36ef2f551e3d2bf8f8d9247823eaee68fe8666"
},
{
"path": "mind/parse-date-window.ts",
"kinds": ["import"],
"canonicalSha256": "4ba482d24958a4188f0e3ba0871f8d8199dc37b2999aa42010bac0631fe89365",
"ossSha256": "de0a8e7f59072031d4ff916f8b10c7451ee8607e0351ddf385e687b13633b252"
},
{
"path": "mind/recall-context.ts",
"kinds": ["import", "logger"],
"canonicalSha256": "56f291422e5f4d22c5d0eb5b5dc3a65bf814146963677d63e6378f070da5bcf3",
"ossSha256": "30529798ae7669d16a24765689d4f4b24287b8fdb0fa94a87adc19acc99fe5a8"
},
{
"path": "mind/reconcile.ts",
"kinds": ["import", "logger"],
"canonicalSha256": "3defdff6d3d5c831297c54143e525cf2f9539e9726a66f5288c1ea288c54e384",
"ossSha256": "6fd2b97b9a18d791dd89c71bd7c155be0719982cd7ee2110ebf1644cea7e5b87"
},
{
"path": "mind/resolve-relative-date.ts",
"kinds": ["import"],
"canonicalSha256": "bc2d8ec1baa95b12b4a374e6dce851e7290042ebcb27d22389098f0f4a624adc",
"ossSha256": "6afaee5857dc533b0a6730d6acd6fd4530291322db5e19c6bd9782cc5aad40f5"
},
{
"path": "mind/scoring.ts",
"kinds": ["import"],
"canonicalSha256": "8bcc3225c3aba5a9ef98d6db10206b0761c4e06ac94e3644f4881f8b80c4a017",
"ossSha256": "6660a2ba157b6e84561222b24ade3bcd68202e722b4a3ce6d9d716f9eba13f98"
},
{
"path": "mind/sessions.ts",
"kinds": ["import", "logger"],
"canonicalSha256": "4fc4ad9be81016310ce89ef142ed5cd7d7a99224750fb7a664ad2fd7ffe514ab",
"ossSha256": "94d56a29840bb317b5391bca8782c6901b88dbf7aafff6bac3f051242e0d8343"
},
{
"path": "mind/transformers-model-load.ts",
"kinds": ["branding", "import", "logger"],
"canonicalSha256": "43c13897de7ea8eb5ce27d89f9e5a50e58afb6aa47b266efbc2b0517d8920e92",
"ossSha256": "3df62eed18a28d0863ae667ea7d79d687e8809114250bd17dfd337972f3d113d"
}
],
"knownReviewedBlockers": [
{ "path": "harvest/dedup.ts", "state": "different", "disposition": "reconcile" },
{ "path": "harvest/extract-kg-entities.ts", "state": "only-canonical", "disposition": "reconcile" },
{ "path": "harvest/pipeline.ts", "state": "different", "disposition": "reconcile" },
{ "path": "harvest/url-adapter.ts", "state": "different", "disposition": "reconcile" },
{ "path": "hook-runtime.ts", "state": "only-canonical", "disposition": "product-curation" },
{ "path": "index.ts", "state": "different", "disposition": "reconcile" },
{ "path": "mind/db.ts", "state": "different", "disposition": "reconcile" },
{ "path": "mind/embedding-provider.ts", "state": "different", "disposition": "reconcile" },
{ "path": "mind/frames.ts", "state": "different", "disposition": "reconcile" },
{ "path": "mind/identity.ts", "state": "different", "disposition": "reconcile" },
{ "path": "mind/inprocess-embedder.ts", "state": "different", "disposition": "reconcile" },
{ "path": "mind/knowledge.ts", "state": "different", "disposition": "reconcile" },
{ "path": "mind/llm-extractor.ts", "state": "only-oss", "disposition": "reconcile" },
{ "path": "mind/raw-archive.ts", "state": "different", "disposition": "reconcile" },
{ "path": "mind/raw-detail-lane.ts", "state": "different", "disposition": "reconcile" },
{ "path": "mind/schema.ts", "state": "different", "disposition": "reconcile" },
{ "path": "mind/search.ts", "state": "different", "disposition": "reconcile" },
{ "path": "mind/supersede.ts", "state": "only-canonical", "disposition": "product-curation" },
{ "path": "mind/suppression.ts", "state": "different", "disposition": "reconcile" },
{ "path": "multi-mind-cache.ts", "state": "different", "disposition": "reconcile" },
{ "path": "multi-mind.ts", "state": "only-canonical", "disposition": "product-curation" },
{ "path": "workspace-manager.ts", "state": "different", "disposition": "reconcile" }
],
"unreviewedDifferences": [
{ "path": "harvest/raw-turns.ts", "state": "different" }
],
"forbiddenExports": {
"paths": [
"mind/evolution-runs.ts",
"mind/execution-traces.ts",
"mind/improvement-signals.ts"
],
"pathPrefixes": [
"vault.ts",
"compliance/"
],
"markers": [
{ "path": "mind/db.ts", "token": "install_audit" },
{ "path": "mind/schema.ts", "token": "install_audit" }
]
}
}

566
scripts/oss-drift-check.mjs Normal file
View File

@@ -0,0 +1,566 @@
#!/usr/bin/env node
import { execFileSync } from 'node:child_process';
import { createHash } from 'node:crypto';
import {
lstatSync,
readFileSync,
readdirSync,
realpathSync,
} from 'node:fs';
import { dirname, isAbsolute, join, posix, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
const SCRIPT_DIR = dirname(fileURLToPath(import.meta.url));
const BASELINE_PATH = join(SCRIPT_DIR, 'oss-drift-baseline.json');
const MAPPING = Object.freeze({
canonical: 'packages/hive-mind-core/src',
oss: 'packages/core/src',
ignoredDirectories: ['dist', 'node_modules'],
ignoredFileSuffixes: ['.test.ts', '.tsbuildinfo'],
});
const FORBIDDEN_EXPORTS = Object.freeze({
paths: [
'mind/evolution-runs.ts',
'mind/execution-traces.ts',
'mind/improvement-signals.ts',
],
pathPrefixes: ['vault.ts', 'compliance/'],
markers: [
{ path: 'mind/db.ts', token: 'install_audit' },
{ path: 'mind/schema.ts', token: 'install_audit' },
],
});
const VALID_ADAPTATION_KINDS = new Set(['branding', 'import', 'layout', 'logger']);
const VALID_STATES = new Set(['different', 'only-canonical', 'only-oss']);
const VALID_DISPOSITIONS = new Set([
'forward-port',
'product-curation',
'reconcile',
'reverse-port',
]);
const HASH_PATTERN = /^[a-f0-9]{64}$/;
const decoder = new TextDecoder('utf-8', { fatal: true });
class ConfigurationError extends Error {}
function compareOrdinal(left, right) {
return left < right ? -1 : left > right ? 1 : 0;
}
function assertRecord(value, label) {
if (value === null || typeof value !== 'object' || Array.isArray(value)) {
throw new ConfigurationError(`${label} must be an object`);
}
}
function assertExactKeys(value, expected, label) {
assertRecord(value, label);
const actual = Object.keys(value).sort(compareOrdinal);
const wanted = [...expected].sort(compareOrdinal);
if (JSON.stringify(actual) !== JSON.stringify(wanted)) {
throw new ConfigurationError(
`${label} has unexpected keys: expected ${wanted.join(', ')}, got ${actual.join(', ')}`,
);
}
}
function assertExactArray(actual, expected, label) {
if (!Array.isArray(actual) || JSON.stringify(actual) !== JSON.stringify(expected)) {
throw new ConfigurationError(`${label} does not match the hardcoded publication policy`);
}
}
function assertCanonicalPath(value, label) {
if (
typeof value !== 'string' ||
value.length === 0 ||
value.includes('\\') ||
isAbsolute(value) ||
value.startsWith('/') ||
value.split('/').some((segment) => segment === '' || segment === '.' || segment === '..') ||
posix.normalize(value) !== value
) {
throw new ConfigurationError(`${label} must be a canonical repository-relative path`);
}
}
function assertSortedUnique(values, label, pathSelector = (value) => value) {
if (!Array.isArray(values)) {
throw new ConfigurationError(`${label} must be an array`);
}
const paths = values.map(pathSelector);
const expected = [...new Set(paths)].sort(compareOrdinal);
if (JSON.stringify(paths) !== JSON.stringify(expected)) {
throw new ConfigurationError(`${label} must be sorted by path and contain no duplicates`);
}
}
function loadBaseline() {
let baseline;
try {
baseline = JSON.parse(readFileSync(BASELINE_PATH, 'utf-8'));
} catch (error) {
throw new ConfigurationError(`cannot read baseline ${BASELINE_PATH}: ${error.message}`);
}
assertExactKeys(
baseline,
[
'schemaVersion',
'mapping',
'parityPaths',
'intentionalAdaptations',
'knownReviewedBlockers',
'unreviewedDifferences',
'forbiddenExports',
],
'baseline',
);
if (baseline.schemaVersion !== 1) {
throw new ConfigurationError('baseline.schemaVersion must be 1');
}
assertExactKeys(
baseline.mapping,
['canonical', 'oss', 'ignoredDirectories', 'ignoredFileSuffixes'],
'baseline.mapping',
);
for (const key of Object.keys(MAPPING)) {
const expected = MAPPING[key];
if (Array.isArray(expected)) {
assertExactArray(baseline.mapping[key], expected, `baseline.mapping.${key}`);
} else if (baseline.mapping[key] !== expected) {
throw new ConfigurationError(`baseline.mapping.${key} does not match the hardcoded mapping`);
}
}
assertExactKeys(
baseline.forbiddenExports,
['paths', 'pathPrefixes', 'markers'],
'baseline.forbiddenExports',
);
assertExactArray(
baseline.forbiddenExports.paths,
FORBIDDEN_EXPORTS.paths,
'baseline.forbiddenExports.paths',
);
assertExactArray(
baseline.forbiddenExports.pathPrefixes,
FORBIDDEN_EXPORTS.pathPrefixes,
'baseline.forbiddenExports.pathPrefixes',
);
assertExactArray(
baseline.forbiddenExports.markers,
FORBIDDEN_EXPORTS.markers,
'baseline.forbiddenExports.markers',
);
assertSortedUnique(baseline.parityPaths, 'baseline.parityPaths', (entry) => entry?.path);
for (const [index, entry] of baseline.parityPaths.entries()) {
const label = `baseline.parityPaths[${index}]`;
assertExactKeys(entry, ['path', 'sha256'], label);
assertCanonicalPath(entry.path, `${label}.path`);
if (!HASH_PATTERN.test(entry.sha256)) {
throw new ConfigurationError(`${label}.sha256 must be a lowercase SHA-256 hash`);
}
}
assertSortedUnique(
baseline.intentionalAdaptations,
'baseline.intentionalAdaptations',
(entry) => entry?.path,
);
for (const [index, entry] of baseline.intentionalAdaptations.entries()) {
const label = `baseline.intentionalAdaptations[${index}]`;
assertExactKeys(entry, ['path', 'kinds', 'canonicalSha256', 'ossSha256'], label);
assertCanonicalPath(entry.path, `${label}.path`);
assertSortedUnique(entry.kinds, `${label}.kinds`);
if (entry.kinds.length === 0 || entry.kinds.some((kind) => !VALID_ADAPTATION_KINDS.has(kind))) {
throw new ConfigurationError(`${label}.kinds contains an unsupported adaptation kind`);
}
if (!HASH_PATTERN.test(entry.canonicalSha256) || !HASH_PATTERN.test(entry.ossSha256)) {
throw new ConfigurationError(`${label} must contain lowercase SHA-256 hashes`);
}
if (entry.canonicalSha256 === entry.ossSha256) {
throw new ConfigurationError(`${label} must describe an actual adaptation, not parity`);
}
}
assertSortedUnique(
baseline.knownReviewedBlockers,
'baseline.knownReviewedBlockers',
(entry) => entry?.path,
);
for (const [index, entry] of baseline.knownReviewedBlockers.entries()) {
const label = `baseline.knownReviewedBlockers[${index}]`;
assertExactKeys(entry, ['path', 'state', 'disposition'], label);
assertCanonicalPath(entry.path, `${label}.path`);
if (!VALID_STATES.has(entry.state)) {
throw new ConfigurationError(`${label}.state is unsupported`);
}
if (!VALID_DISPOSITIONS.has(entry.disposition)) {
throw new ConfigurationError(`${label}.disposition is unsupported`);
}
}
assertSortedUnique(
baseline.unreviewedDifferences,
'baseline.unreviewedDifferences',
(entry) => entry?.path,
);
for (const [index, entry] of baseline.unreviewedDifferences.entries()) {
const label = `baseline.unreviewedDifferences[${index}]`;
assertExactKeys(entry, ['path', 'state'], label);
assertCanonicalPath(entry.path, `${label}.path`);
if (!VALID_STATES.has(entry.state)) {
throw new ConfigurationError(`${label}.state is unsupported`);
}
}
const classified = [
...baseline.parityPaths.map((entry) => entry.path),
...baseline.intentionalAdaptations.map((entry) => entry.path),
...baseline.knownReviewedBlockers.map((entry) => entry.path),
...baseline.unreviewedDifferences.map((entry) => entry.path),
];
const duplicate = classified.find((path, index) => classified.indexOf(path) !== index);
if (duplicate) {
throw new ConfigurationError(`baseline classifies ${duplicate} more than once`);
}
const privateCategory = classified.find((path) => isPrivateExclusion(path));
if (privateCategory) {
throw new ConfigurationError(`baseline must not reclassify private exclusion ${privateCategory}`);
}
return baseline;
}
function resolveGitRoot(candidate, label) {
let resolved;
try {
resolved = realpathSync(candidate);
const root = execFileSync('git', ['-C', resolved, 'rev-parse', '--show-toplevel'], {
encoding: 'utf-8',
stdio: ['ignore', 'pipe', 'pipe'],
windowsHide: true,
}).trim();
return realpathSync(root);
} catch (error) {
throw new ConfigurationError(`${label} is not a readable Git worktree: ${candidate}`);
}
}
function readUtf8Normalized(filePath) {
let text;
try {
text = decoder.decode(readFileSync(filePath));
} catch (error) {
throw new ConfigurationError(`cannot read UTF-8 source file ${filePath}: ${error.message}`);
}
return text.replace(/^\uFEFF/, '').replace(/\r\n?/g, '\n');
}
function inventory(root, label) {
let rootStat;
try {
rootStat = lstatSync(root);
} catch {
throw new ConfigurationError(`${label} mapped source directory is missing: ${root}`);
}
if (rootStat.isSymbolicLink() || !rootStat.isDirectory()) {
throw new ConfigurationError(`${label} mapped source must be a real directory: ${root}`);
}
const files = new Map();
const casing = new Map();
const walk = (directory, prefix = '') => {
let entries;
try {
entries = readdirSync(directory, { withFileTypes: true }).sort((left, right) =>
compareOrdinal(left.name, right.name),
);
} catch (error) {
throw new ConfigurationError(`cannot inventory ${label} directory ${directory}: ${error.message}`);
}
for (const entry of entries) {
const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
const absolutePath = join(directory, entry.name);
const stat = lstatSync(absolutePath);
if (stat.isSymbolicLink()) {
throw new ConfigurationError(`${label} mapped inventory contains symlink: ${relativePath}`);
}
if (stat.isDirectory()) {
if (!MAPPING.ignoredDirectories.includes(entry.name)) {
walk(absolutePath, relativePath);
}
continue;
}
if (!stat.isFile()) {
throw new ConfigurationError(`${label} mapped inventory contains special node: ${relativePath}`);
}
if (MAPPING.ignoredFileSuffixes.some((suffix) => relativePath.endsWith(suffix))) {
continue;
}
const folded = relativePath.toLowerCase();
const previous = casing.get(folded);
if (previous && previous !== relativePath) {
throw new ConfigurationError(
`${label} mapped inventory has case collision: ${previous} / ${relativePath}`,
);
}
casing.set(folded, relativePath);
const text = readUtf8Normalized(absolutePath);
files.set(relativePath, {
hash: createHash('sha256').update(text, 'utf-8').digest('hex'),
text,
});
}
};
walk(root);
if (files.size === 0) {
throw new ConfigurationError(`${label} mapped inventory is empty: ${root}`);
}
return files;
}
function resolveMappedRoot(repositoryRoot, relativePath, label) {
let current = repositoryRoot;
for (const segment of relativePath.split('/')) {
current = join(current, segment);
let stat;
try {
stat = lstatSync(current);
} catch {
throw new ConfigurationError(`${label} mapped path component is missing: ${current}`);
}
if (stat.isSymbolicLink()) {
throw new ConfigurationError(`${label} mapped path contains ancestor symlink: ${current}`);
}
if (!stat.isDirectory()) {
throw new ConfigurationError(`${label} mapped path component is not a directory: ${current}`);
}
}
return current;
}
function isPrivateExclusion(path) {
if (FORBIDDEN_EXPORTS.paths.includes(path)) return true;
return FORBIDDEN_EXPORTS.pathPrefixes.some((prefix) =>
prefix.endsWith('/') ? path.startsWith(prefix) : path === prefix || path.startsWith(`${prefix}/`),
);
}
function observedState(canonical, oss) {
if (canonical && oss) return canonical.hash === oss.hash ? 'equal' : 'different';
if (canonical) return 'only-canonical';
if (oss) return 'only-oss';
return 'absent';
}
function hasForbiddenMarker(text, token) {
const escaped = token.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
return new RegExp(`\\b${escaped}\\b`, 'i').test(text);
}
function gitReceipt(root, scopedPaths) {
const head = execFileSync('git', ['-C', root, 'rev-parse', 'HEAD'], {
encoding: 'utf-8',
windowsHide: true,
}).trim();
const status = execFileSync(
'git',
['-C', root, 'status', '--porcelain=v1', '--untracked-files=all'],
{ encoding: 'utf-8', windowsHide: true },
).trim();
const scopedStatus = execFileSync(
'git',
['-C', root, 'status', '--porcelain=v1', '--untracked-files=all', '--', ...scopedPaths],
{ encoding: 'utf-8', windowsHide: true },
).trim();
return { head, dirty: status.length > 0, scopedStatus };
}
function showSection(title, entries) {
console.log(`${title}: ${entries.length}`);
for (const entry of [...entries].sort(compareOrdinal)) {
console.log(` ${entry}`);
}
}
function run() {
if (process.argv.length > 3) {
throw new ConfigurationError('usage: node scripts/oss-drift-check.mjs [path-to-oss-checkout]');
}
const baseline = loadBaseline();
const canonicalRoot = resolveGitRoot(join(SCRIPT_DIR, '..'), 'canonical repository');
const requestedOss =
process.argv[2] ??
(process.env.OSS_HIVE_MIND_DIR?.trim() || resolve(canonicalRoot, '..', 'hive-mind'));
const ossRoot = resolveGitRoot(resolve(process.cwd(), requestedOss), 'OSS repository');
if (canonicalRoot.toLowerCase() === ossRoot.toLowerCase()) {
throw new ConfigurationError('canonical and OSS repositories must be different Git worktrees');
}
const canonicalFiles = inventory(
resolveMappedRoot(canonicalRoot, MAPPING.canonical, 'canonical'),
'canonical',
);
const ossFiles = inventory(resolveMappedRoot(ossRoot, MAPPING.oss, 'OSS'), 'OSS');
const crossRepoCasing = new Map();
for (const path of [...canonicalFiles.keys(), ...ossFiles.keys()]) {
const folded = path.toLowerCase();
const previous = crossRepoCasing.get(folded);
if (previous && previous !== path) {
throw new ConfigurationError(`mapped inventories have case collision: ${previous} / ${path}`);
}
crossRepoCasing.set(folded, path);
}
const parity = new Map(baseline.parityPaths.map((entry) => [entry.path, entry]));
const adaptations = new Map(baseline.intentionalAdaptations.map((entry) => [entry.path, entry]));
const blockers = new Map(baseline.knownReviewedBlockers.map((entry) => [entry.path, entry]));
const knownUnreviewed = new Map(
baseline.unreviewedDifferences.map((entry) => [entry.path, entry]),
);
const reviewedAdaptations = [];
const intentionalExclusions = [];
const knownBlockers = [];
const unreviewed = [];
const forbidden = [];
const observedPaths = new Set();
const union = [...new Set([...canonicalFiles.keys(), ...ossFiles.keys()])].sort(compareOrdinal);
for (const path of union) {
observedPaths.add(path);
const canonical = canonicalFiles.get(path);
const oss = ossFiles.get(path);
const state = observedState(canonical, oss);
if (isPrivateExclusion(path)) {
if (oss) {
forbidden.push(`FORBIDDEN-OSS-CONTENT ${path}`);
} else if (canonical) {
intentionalExclusions.push(path);
}
continue;
}
const parityEntry = parity.get(path);
if (parityEntry) {
if (
state === 'equal' &&
canonical.hash === parityEntry.sha256 &&
oss.hash === parityEntry.sha256
) {
continue;
}
unreviewed.push(`BASELINE-DRIFT ${path} expected equal, observed ${state}`);
continue;
}
const adaptation = adaptations.get(path);
if (adaptation) {
if (
state === 'different' &&
canonical.hash === adaptation.canonicalSha256 &&
oss.hash === adaptation.ossSha256
) {
reviewedAdaptations.push(`${path} [${adaptation.kinds.join(',')}]`);
} else {
unreviewed.push(`BASELINE-DRIFT ${path} reviewed adaptation bytes changed (${state})`);
}
continue;
}
const blocker = blockers.get(path);
if (blocker) {
if (state === blocker.state) {
knownBlockers.push(`${path} (${state}; ${blocker.disposition})`);
} else {
unreviewed.push(
`BASELINE-DRIFT ${path} expected ${blocker.state}, observed ${state}`,
);
}
continue;
}
const unreviewedEntry = knownUnreviewed.get(path);
if (unreviewedEntry) {
if (state === unreviewedEntry.state) {
unreviewed.push(`${path} (${state})`);
} else {
unreviewed.push(
`BASELINE-DRIFT ${path} expected ${unreviewedEntry.state}, observed ${state}`,
);
}
continue;
}
unreviewed.push(`NEW ${path} (${state})`);
}
for (const path of [
...parity.keys(),
...adaptations.keys(),
...blockers.keys(),
...knownUnreviewed.keys(),
]) {
if (!observedPaths.has(path)) {
unreviewed.push(`STALE-BASELINE ${path} (absent)`);
}
}
for (const marker of FORBIDDEN_EXPORTS.markers) {
const file = ossFiles.get(marker.path);
if (!file) continue;
if (hasForbiddenMarker(file.text, marker.token)) {
forbidden.push(`FORBIDDEN-OSS-MARKER ${marker.path}:${marker.token}`);
}
}
const canonicalReceipt = gitReceipt(canonicalRoot, [
MAPPING.canonical,
'scripts/oss-drift-check.mjs',
'scripts/oss-drift-baseline.json',
'scripts/oss-drift-check.sh',
]);
const ossReceipt = gitReceipt(ossRoot, [MAPPING.oss]);
if (canonicalReceipt.scopedStatus) {
unreviewed.push('SCOPED-DIRTY canonical checker, baseline, wrapper, or mapped source');
}
if (ossReceipt.scopedStatus) {
unreviewed.push('SCOPED-DIRTY OSS mapped source');
}
console.log(
`CANONICAL ${canonicalReceipt.head} ${canonicalReceipt.dirty ? 'dirty' : 'clean'} ${canonicalRoot}`,
);
console.log(`OSS ${ossReceipt.head} ${ossReceipt.dirty ? 'dirty' : 'clean'} ${ossRoot}`);
console.log(`PARITY: ${parity.size}`);
showSection('REVIEWED ADAPTATIONS', reviewedAdaptations);
showSection('INTENTIONAL OSS EXCLUSIONS', intentionalExclusions);
showSection('KNOWN REVIEWED BLOCKERS', knownBlockers);
showSection('UNREVIEWED DIFFERENCES', unreviewed);
showSection('FORBIDDEN EXPORTS', forbidden);
const blocked = knownBlockers.length || unreviewed.length || forbidden.length;
if (blocked) {
console.log(
'[oss-drift-check] release blocked: reconcile in the canonical monorepo, then use a curated forward-port; never publish a raw split.',
);
}
return blocked ? 1 : 0;
}
try {
process.exitCode = run();
} catch (error) {
if (error instanceof ConfigurationError) {
console.error(`[oss-drift-check] CONFIGURATION ERROR: ${error.message}`);
} else {
console.error(`[oss-drift-check] UNEXPECTED ERROR: ${error?.stack ?? error}`);
}
process.exitCode = 2;
}

View File

@@ -1,123 +1,5 @@
#!/usr/bin/env bash
#
# oss-drift-check.sh — detect source drift between the canonical monorepo
# substrate and the public OSS mirror (github.com/marolinik/hive-mind).
#
# WHY THIS EXISTS (§7.5 policy, ratified 2026-06-11):
# The monorepo is the SOLE source of truth for the memory substrate; the
# OSS mirror is generated FROM it. That invariant broke once: the
# cross-encoder reranker (inprocess-reranker.ts + HybridSearch options)
# was authored directly on the OSS repo during the LoCoMo benchmark arc
# and existed ONLY there — discovered by the W4 recon (2026-06-11),
# reverse-ported in W4.2 (f47ee8f). This script makes that class of
# drift cheap to detect BEFORE it compounds.
#
# WHAT IT DOES:
# Recursively diffs the substrate source trees (src/ only — dist, deps,
# lockfiles, and docs churn excluded) between the monorepo and a local
# checkout of the OSS repo. Reports per-file status:
# ONLY-IN-OSS → candidate reverse-port (the W4.2 failure mode)
# ONLY-IN-MONO → not yet exported (fine if a split is pending)
# DIFFERS → divergent edits — inspect immediately
# Exit 0 = clean, exit 1 = drift found, exit 2 = setup error.
#
# USAGE:
# bash scripts/oss-drift-check.sh [path-to-oss-checkout]
# Default OSS path: ../hive-mind (sibling clone), override via arg or
# OSS_HIVE_MIND_DIR env var.
#
# WHEN TO RUN (maintainer ritual — manual, not CI):
# - before every OSS release push (alongside oss-subtree-split.sh)
# - after any benchmark/experiment arc that touched a hive-mind checkout
#
# Mapping (OSS repo keeps its own package layout):
# monorepo packages/hive-mind-core/src ↔ oss packages/core/src
# (extend MAPPINGS below as more packages get mirrored surfaces)
#!/usr/bin/env sh
set -eu
set -euo pipefail
REPO_ROOT="$(git rev-parse --show-toplevel)"
cd "$REPO_ROOT"
OSS_DIR="${1:-${OSS_HIVE_MIND_DIR:-$REPO_ROOT/../hive-mind}}"
if [[ ! -d "$OSS_DIR/.git" ]]; then
echo "[oss-drift-check] ERROR: OSS checkout not found at: $OSS_DIR" >&2
echo "[oss-drift-check] Clone it first: git clone https://github.com/marolinik/hive-mind \"$OSS_DIR\"" >&2
exit 2
fi
# mono-relative-dir : oss-relative-dir
MAPPINGS=(
"packages/hive-mind-core/src:packages/core/src"
)
# Excluded from comparison:
# - build artifacts (dist, node_modules, .tsbuildinfo)
# - *.test.ts — test LAYOUT is a permanent convention difference (OSS
# co-locates tests beside src; the monorepo keeps them in tests/), so
# co-located tests would be unfixable noise. Source drift is the target.
IGNORE_RE='(^|/)(dist|node_modules|\.tsbuildinfo)(/|$)|\.test\.ts$'
drift=0
for mapping in "${MAPPINGS[@]}"; do
mono_dir="${mapping%%:*}"
oss_dir="${mapping##*:}"
echo "[oss-drift-check] ${mono_dir}${OSS_DIR}/${oss_dir}"
if [[ ! -d "$mono_dir" ]]; then
echo "[oss-drift-check] ERROR: monorepo dir missing: $mono_dir" >&2
exit 2
fi
if [[ ! -d "$OSS_DIR/$oss_dir" ]]; then
echo "[oss-drift-check] ERROR: OSS dir missing: $OSS_DIR/$oss_dir" >&2
exit 2
fi
# File inventories (relative paths), excluding build artifacts.
mono_files=$(cd "$mono_dir" && find . -type f | sed 's|^\./||' | grep -Ev "$IGNORE_RE" | sort)
oss_files=$(cd "$OSS_DIR/$oss_dir" && find . -type f | sed 's|^\./||' | grep -Ev "$IGNORE_RE" | sort)
only_oss=$(comm -13 <(echo "$mono_files") <(echo "$oss_files"))
only_mono=$(comm -23 <(echo "$mono_files") <(echo "$oss_files"))
common=$(comm -12 <(echo "$mono_files") <(echo "$oss_files"))
if [[ -n "$only_oss" ]]; then
drift=1
echo " ONLY-IN-OSS (candidate reverse-port — the W4.2 failure mode):"
echo "$only_oss" | sed 's/^/ /'
fi
if [[ -n "$only_mono" ]]; then
drift=1
echo " ONLY-IN-MONO (pending export — fine if a split is queued):"
echo "$only_mono" | sed 's/^/ /'
fi
differing=""
while IFS= read -r f; do
[[ -z "$f" ]] && continue
if ! diff -q "$mono_dir/$f" "$OSS_DIR/$oss_dir/$f" >/dev/null 2>&1; then
differing+=" $f"$'\n'
fi
done <<< "$common"
if [[ -n "$differing" ]]; then
drift=1
echo " DIFFERS (divergent edits — inspect immediately):"
printf '%s' "$differing"
fi
if [[ -z "$only_oss" && -z "$only_mono" && -z "$differing" ]]; then
echo " ✓ clean"
fi
echo
done
if [[ $drift -eq 1 ]]; then
echo "[oss-drift-check] DRIFT DETECTED. Policy (§7.5): the monorepo is the"
echo "[oss-drift-check] sole source — reverse-port ONLY-IN-OSS work here first,"
echo "[oss-drift-check] then regenerate the mirror via scripts/oss-subtree-split.sh."
exit 1
fi
echo "[oss-drift-check] All mapped surfaces clean."
SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
exec node "$SCRIPT_DIR/oss-drift-check.mjs" "$@"

View File

@@ -14,7 +14,7 @@
# 1. PROPRIETARY FILES. `packages/hive-mind-core/src/mind/` contains
# evolution-runs.ts, execution-traces.ts, improvement-signals.ts — Waggle
# proprietary, EXCLUDED from the public mirror. A raw split carries them.
# (The hard abort guard below refuses to emit a branch that contains them,
# (The hard abort guard below refuses to publish a ref that contains them,
# so the leak can't happen silently — but the guard is a backstop, not the
# sync mechanism.)
# 2. INTERLEAVED PROPRIETARY CONTENT. The `install_audit` table DDL + its
@@ -43,13 +43,16 @@
# bash scripts/oss-subtree-split.sh # split all hive-mind-* packages
# bash scripts/oss-subtree-split.sh hive-mind-core # split only one package
#
# Idempotent: re-running drops + recreates the export branches with current state.
# Idempotent: re-running replaces export refs only after a candidate passes every guard.
set -euo pipefail
REPO_ROOT="$(git rev-parse --show-toplevel)"
cd "$REPO_ROOT"
VALIDATED_BRANCHES=()
VALIDATED_SHAS=()
# Default: split all hive-mind-* packages. Override via CLI args for targeted split.
if [[ $# -gt 0 ]]; then
PACKAGES=("$@")
@@ -74,22 +77,20 @@ for pkg in "${PACKAGES[@]}"; do
BRANCH="oss-${pkg}-export"
if [[ ! -d "$PREFIX" ]]; then
echo "[oss-subtree-split] SKIP $pkg directory $PREFIX not found." >&2
continue
echo "[oss-subtree-split] ERROR: requested package directory $PREFIX was not found." >&2
exit 2
fi
# Drop existing export branch if present (idempotent).
if git show-ref --verify --quiet "refs/heads/$BRANCH"; then
echo "[oss-subtree-split] Dropping existing branch $BRANCH"
git branch -D "$BRANCH" >/dev/null
echo "[oss-subtree-split] Splitting $PREFIX → detached candidate commit"
CANDIDATE_SHA=$(git subtree split --prefix="$PREFIX" | tail -n 1)
if ! git cat-file -e "${CANDIDATE_SHA}^{commit}" 2>/dev/null; then
echo "[oss-subtree-split] ERROR: subtree split did not return a valid commit." >&2
exit 2
fi
echo "[oss-subtree-split] Splitting $PREFIX$BRANCH"
git subtree split --prefix="$PREFIX" --branch="$BRANCH"
# Top-level summary for audit.
TOP_LEVEL=$(git ls-tree --name-only "$BRANCH" | sort | tr '\n' ' ')
echo "[oss-subtree-split] $BRANCH HEAD top-level: $TOP_LEVEL"
TOP_LEVEL=$(git ls-tree --name-only "$CANDIDATE_SHA" | sort | tr '\n' ' ')
echo "[oss-subtree-split] candidate HEAD top-level: $TOP_LEVEL"
# Negative assertion: monorepo-bleed sentinel. The subtree-split's prefix=
# arg already guarantees the export contains ONLY the subtree, but we
@@ -98,9 +99,9 @@ for pkg in "${PACKAGES[@]}"; do
# Forbidden = paths that ONLY exist as monorepo siblings, never as package contents.
for forbidden in apps packages sidecar .planning .scratch .mind benchmarks; do
if echo "$TOP_LEVEL" | grep -qE "(^| )$forbidden( |$)"; then
echo "[oss-subtree-split] ERROR: $BRANCH contains forbidden monorepo-level entry '$forbidden'." >&2
echo "[oss-subtree-split] ERROR: candidate contains forbidden monorepo-level entry '$forbidden'." >&2
echo "[oss-subtree-split] This indicates the subtree-split misbehaved or proprietary content leaked." >&2
echo "[oss-subtree-split] Inspect with: git checkout $BRANCH && ls" >&2
echo "[oss-subtree-split] No export refs were changed." >&2
exit 2
fi
done
@@ -120,10 +121,10 @@ for pkg in "${PACKAGES[@]}"; do
"src/vault.ts"
"src/compliance"
)
BRANCH_FILES=$(git ls-tree -r --name-only "$BRANCH")
BRANCH_FILES=$(git ls-tree -r --name-only "$CANDIDATE_SHA")
for pf in "${FORBIDDEN_FILES[@]}"; do
if echo "$BRANCH_FILES" | grep -qE "(^|/)${pf}(/|\$|\.ts\$)"; then
echo "[oss-subtree-split] ERROR: $BRANCH contains PROPRIETARY path '$pf'." >&2
echo "[oss-subtree-split] ERROR: candidate contains PROPRIETARY path '$pf'." >&2
echo "[oss-subtree-split] This export is NOT safe to push to the public OSS mirror." >&2
echo "[oss-subtree-split] These files are Waggle-proprietary (§7.5) and must be removed" >&2
echo "[oss-subtree-split] by the curated forward-port, not pushed raw. ABORTING." >&2
@@ -132,15 +133,52 @@ for pkg in "${PACKAGES[@]}"; do
fi
done
echo "[oss-subtree-split] ✓ $BRANCH split complete (no monorepo-level leak, no proprietary files)"
VALIDATED_BRANCHES+=("$BRANCH")
VALIDATED_SHAS+=("$CANDIDATE_SHA")
echo "[oss-subtree-split] ✓ candidate validated (no monorepo-level leak, no proprietary files)"
echo "[oss-subtree-split] NOTE: this is a RAW history branch — NOT OSS-publishable as-is"
echo "[oss-subtree-split] (wrong layout + interleaved install_audit). Curate before any push."
echo
done
# Update stable LOCAL inspection refs only after every requested package passes.
# Preflight checked-out refs, capture expected old OIDs, then use one compare-and-
# swap transaction so a lock/race/failure cannot leave a partially updated set.
ZERO_OID=$(printf '%040d' 0)
EXPECTED_OLD_SHAS=()
if ! WORKTREE_LIST=$(git worktree list --porcelain); then
echo "[oss-subtree-split] ERROR: could not inventory checked-out worktree refs." >&2
exit 4
fi
for i in "${!VALIDATED_BRANCHES[@]}"; do
ref="refs/heads/${VALIDATED_BRANCHES[$i]}"
if grep -Fxq "branch $ref" <<< "$WORKTREE_LIST"; then
echo "[oss-subtree-split] ERROR: refusing to update checked-out ref $ref." >&2
exit 4
fi
if git show-ref --verify --quiet "$ref"; then
EXPECTED_OLD_SHAS+=("$(git rev-parse "$ref")")
else
EXPECTED_OLD_SHAS+=("$ZERO_OID")
fi
done
if ! {
echo start
for i in "${!VALIDATED_BRANCHES[@]}"; do
printf 'update refs/heads/%s %s %s\n' \
"${VALIDATED_BRANCHES[$i]}" "${VALIDATED_SHAS[$i]}" "${EXPECTED_OLD_SHAS[$i]}"
done
echo prepare
echo commit
} | git update-ref --stdin; then
echo "[oss-subtree-split] ERROR: atomic export-ref transaction failed; no refs were changed." >&2
exit 4
fi
echo "[oss-subtree-split] All splits complete. Local branches ready:"
for pkg in "${PACKAGES[@]}"; do
echo " oss-${pkg}-export"
for branch in "${VALIDATED_BRANCHES[@]}"; do
echo " $branch"
done
echo
echo "[oss-subtree-split] These branches are for INSPECTION / as a curation starting"

View File

@@ -1,140 +1,16 @@
#!/usr/bin/env bash
#
# Local-dev parity check for memory substrate (waggle-os ↔ hive-mind).
# RETIRED 2026-04-30 — the memory substrate moved to
# packages/hive-mind-core/src and the public mirror adopted a curated layout.
# This pre-migration injector cannot prove current parity and must not run.
#
# Mirrors the logic of `.github/workflows/mind-parity-check.yml` so you can
# run the same check locally before pushing. Useful when you're modifying
# `packages/core/src/mind/` or `packages/core/src/harvest/` and want to
# catch parity failures without waiting for CI.
#
# USAGE:
# scripts/parity-check.sh [--keep-injected]
#
# Options:
# --keep-injected Don't clean up CI-injected -hive-mind suffix files
# after the run (useful for inspecting what CI sees).
#
# REQUIREMENTS:
# - hive-mind checked out at one of:
# D:/Projects/hive-mind (default Windows path)
# ~/Projects/hive-mind (default Unix path)
# $HIVE_MIND_PATH (override)
# - npm + Node + a working `npx vitest`
# - The waggle-os repo as the cwd
#
# See `.github/sync.md` for full design rationale and `.parity-allowlist`
# policy.
# Current maintainer workflow:
# 1. Read AGENTS.md §7.5 and packages/hive-mind-core/CONTRIBUTING.md.
# 2. Run scripts/oss-drift-check.sh against a clean OSS checkout.
# 3. Prepare and review a maintainer-curated forward-port.
set -euo pipefail
KEEP_INJECTED=0
for arg in "$@"; do
case "$arg" in
--keep-injected) KEEP_INJECTED=1 ;;
-h|--help)
sed -n '2,28p' "$0" | sed 's/^# \{0,1\}//'
exit 0
;;
*)
echo "Unknown arg: $arg" >&2
echo "Run with --help for usage" >&2
exit 2
;;
esac
done
# Locate hive-mind checkout
if [ -n "${HIVE_MIND_PATH:-}" ]; then
hive_root="$HIVE_MIND_PATH"
elif [ -d "D:/Projects/hive-mind/packages/core/src/mind" ]; then
hive_root="D:/Projects/hive-mind"
elif [ -d "$HOME/Projects/hive-mind/packages/core/src/mind" ]; then
hive_root="$HOME/Projects/hive-mind"
else
echo "::error:: hive-mind checkout not found. Set HIVE_MIND_PATH or clone marolinik/hive-mind to D:/Projects/hive-mind." >&2
exit 1
fi
echo "hive-mind root: $hive_root"
# Verify cwd is waggle-os
if [ ! -f "packages/core/src/mind/db.ts" ]; then
echo "::error:: this script must run from the waggle-os repo root." >&2
exit 1
fi
# Parse .parity-allowlist
allowlist_file=".parity-allowlist"
allowlist_basenames=()
if [ -f "$allowlist_file" ]; then
while IFS= read -r line; do
clean="${line%%#*}"
clean="$(echo "$clean" | tr -d '[:space:]')"
[ -z "$clean" ] && continue
allowlist_basenames+=("$clean")
done < "$allowlist_file"
echo "Allowlist: ${allowlist_basenames[*]:-<none>}"
fi
is_allowlisted() {
local name="$1"
for a in "${allowlist_basenames[@]:-}"; do
[ "$a" = "$name" ] && return 0
done
return 1
}
# Inject — track ONLY files we add new (not overwrite committed ones)
target_dir="packages/core/tests/mind"
source_dir="$hive_root/packages/core/src/mind"
mkdir -p tmp
> tmp/parity-injected.txt
injected=0
overwrote=0
skipped=0
for src in "$source_dir"/*.test.ts; do
[ -e "$src" ] || continue
base="$(basename "$src" .test.ts)"
target_name="${base}-hive-mind.test.ts"
target_path="$target_dir/$target_name"
if is_allowlisted "$target_name"; then
skipped=$((skipped + 1))
continue
fi
if [ -f "$target_path" ]; then
# File is committed (Step 2 port — possibly with bespoke header
# comments documenting port provenance + adaptation rationale).
# Don't overwrite: the committed version IS what CI/local should
# exercise. We track which files we observed already-present so the
# operator sees the count.
overwrote=$((overwrote + 1))
continue
fi
injected=$((injected + 1))
echo "$target_path" >> tmp/parity-injected.txt
cp "$src" "$target_path"
sed -i "s|from \"\\./|from \"../../src/mind/|g" "$target_path"
sed -i "s|from '\\./|from '../../src/mind/|g" "$target_path"
done
echo "Injected: $injected new + $overwrote already-committed-skip + $skipped allowlisted-skip"
# Run the suite
echo ""
echo "## Running combined waggle-os + hive-mind suite..."
exit_code=0
npx vitest run --reporter=default "$target_dir" || exit_code=$?
# Cleanup
if [ $KEEP_INJECTED -eq 0 ]; then
while IFS= read -r f; do
[ -n "$f" ] && rm -f "$f"
done < tmp/parity-injected.txt
echo ""
echo "Cleaned up $(wc -l < tmp/parity-injected.txt) injected files"
else
echo ""
echo "Kept injected files (--keep-injected). Track in tmp/parity-injected.txt"
fi
exit $exit_code
echo "[parity-check] RETIRED: this pre-migration parity injector is disabled." >&2
echo "[parity-check] Use scripts/oss-drift-check.sh and the curated workflow in AGENTS.md §7.5." >&2
exit 2

View File

@@ -0,0 +1,918 @@
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[ValidateSet('bootstrap', 'upgrade')]
[string]$Mode
)
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
function Assert-RemoteTagCommit {
param([string]$Tag, [string]$ExpectedCommit)
$remoteTagLines = @(
git ls-remote --tags origin "refs/tags/$Tag" "refs/tags/$Tag^{}"
)
if ($LASTEXITCODE -ne 0 -or $remoteTagLines.Count -eq 0) {
throw "Could not resolve remote release tag $Tag"
}
$resolvedTagLine = @(
$remoteTagLines |
Where-Object { $_ -match '\^\{\}$' } |
Select-Object -First 1
)
if ($resolvedTagLine.Count -eq 0) {
$resolvedTagLine = @($remoteTagLines | Select-Object -First 1)
}
$remoteTagSha = (
[regex]::Split(([string]$resolvedTagLine[0]).Trim(), '\s+')
)[0]
if (-not [string]::Equals(
$remoteTagSha,
$ExpectedCommit,
[System.StringComparison]::OrdinalIgnoreCase
)) {
throw "Remote release tag $Tag no longer resolves to the certified commit"
}
}
function Get-ProtectedSignerIdentity {
$approvedSubject = [string]$env:WINDOWS_CODESIGN_APPROVED_SUBJECT
$approvedThumbprint = [string]$env:WINDOWS_CODESIGN_APPROVED_THUMBPRINT
$hasApprovedSubject = -not [string]::IsNullOrWhiteSpace($approvedSubject)
$hasApprovedThumbprint = -not [string]::IsNullOrWhiteSpace($approvedThumbprint)
if ($hasApprovedSubject -eq $hasApprovedThumbprint) {
throw 'Exactly one protected publication signer identity must be configured'
}
if ($hasApprovedSubject) {
return [pscustomobject][ordered]@{
mode = 'subject'
subject = $approvedSubject
thumbprint = $null
}
}
$normalizedThumbprint = ($approvedThumbprint -replace '\s', '').ToUpperInvariant()
if ($normalizedThumbprint -notmatch '^[0-9A-F]{40}$') {
throw 'Protected publication signer thumbprint is invalid'
}
return [pscustomobject][ordered]@{
mode = 'thumbprint'
subject = $null
thumbprint = $normalizedThumbprint
}
}
function Assert-ExpectedAuthenticodeSignature {
param([System.IO.FileInfo]$Artifact, [object]$ExpectedIdentity)
$signature = Get-AuthenticodeSignature -FilePath $Artifact.FullName
if ($signature.Status -ne 'Valid' -or
[string]$signature.SignatureType -ne 'Authenticode' -or
$null -eq $signature.SignerCertificate -or
$null -eq $signature.TimeStamperCertificate) {
throw "Artifact signature is no longer valid for the approved signer: $($Artifact.FullName)"
}
$signerSubject = [string]$signature.SignerCertificate.Subject
$signerThumbprint = (
[string]$signature.SignerCertificate.Thumbprint -replace '\s', ''
).ToUpperInvariant()
$matchesProtectedIdentity = if ($ExpectedIdentity.mode -ceq 'subject') {
[string]::Equals(
$signerSubject,
[string]$ExpectedIdentity.subject,
[System.StringComparison]::Ordinal
)
} else {
[string]::Equals(
$signerThumbprint,
[string]$ExpectedIdentity.thumbprint,
[System.StringComparison]::Ordinal
)
}
if (-not $matchesProtectedIdentity) {
throw "Artifact signature is no longer valid for the approved signer: $($Artifact.FullName)"
}
return [pscustomobject][ordered]@{
subject = $signerSubject
thumbprint = $signerThumbprint
}
}
function Assert-ReceiptSignerIdentity {
param([object]$ReceiptArtifact, [object]$ActualIdentity, [string]$Label)
if ([string]::IsNullOrWhiteSpace([string]$ReceiptArtifact.signerSubject) -or
[string]::IsNullOrWhiteSpace([string]$ReceiptArtifact.signerThumbprint) -or
-not [string]::Equals(
[string]$ReceiptArtifact.signerSubject,
[string]$ActualIdentity.subject,
[System.StringComparison]::Ordinal
) -or
-not [string]::Equals(
([string]$ReceiptArtifact.signerThumbprint -replace '\s', '').ToUpperInvariant(),
[string]$ActualIdentity.thumbprint,
[System.StringComparison]::Ordinal
)) {
throw "$Label signer identity does not match the certified Authenticode artifact"
}
}
function Assert-LifecycleReceiptSignerIdentities {
param(
[object]$Receipt,
[object]$PreviousLiveIdentity,
[object]$CandidateLiveIdentity
)
Assert-ReceiptSignerIdentity `
$Receipt.previousInstaller `
$PreviousLiveIdentity `
'Upgrade previous-installer receipt'
Assert-ReceiptSignerIdentity `
$Receipt.previousInstalledApp `
$PreviousLiveIdentity `
'Upgrade previous-installed-app receipt'
Assert-ReceiptSignerIdentity `
$Receipt.installer `
$CandidateLiveIdentity `
'Upgrade candidate-installer receipt'
Assert-ReceiptSignerIdentity `
$Receipt.installedApp `
$CandidateLiveIdentity `
'Upgrade candidate-installed-app receipt'
}
function Assert-PassingWindowsCertificateReceipt {
param([object]$Receipt, [string]$ExpectedMode, [string]$Label)
$schemaVersionIsIntegral = (
$Receipt.schemaVersion -is [int] -or
$Receipt.schemaVersion -is [long]
)
if (-not $schemaVersionIsIntegral -or
[long]$Receipt.schemaVersion -ne 4 -or
-not ($Receipt.certificationMode -is [string]) -or
-not [string]::Equals(
$Receipt.certificationMode,
$ExpectedMode,
[System.StringComparison]::Ordinal
) -or
-not ($Receipt.status -is [string]) -or
-not [string]::Equals(
$Receipt.status,
'passed',
[System.StringComparison]::Ordinal
)) {
throw "$Label is not a passing schema-v4 certificate"
}
}
function Assert-PassingWindowsCertificateCheck {
param([object]$Receipt, [string]$CheckName, [string]$Label)
$check = $Receipt.checks.PSObject.Properties[$CheckName]
if ($null -eq $check -or -not [bool]::True.Equals($check.Value)) {
throw "$Label check did not pass: $CheckName"
}
}
function Assert-ExactReleaseAssets {
param([object]$Release, [object[]]$ExpectedAssets)
$expectedNames = [string[]]@(
$ExpectedAssets | ForEach-Object { [string]$_.Name }
)
$actualNames = [string[]]@(
$Release.assets | ForEach-Object { [string]$_.name }
)
[System.Array]::Sort($expectedNames, [System.StringComparer]::Ordinal)
[System.Array]::Sort($actualNames, [System.StringComparer]::Ordinal)
if ($expectedNames.Count -ne $actualNames.Count) {
throw 'Published release assets do not exactly match the certified artifact set'
}
for ($index = 0; $index -lt $expectedNames.Count; $index += 1) {
if (-not [string]::Equals(
$expectedNames[$index],
$actualNames[$index],
[System.StringComparison]::Ordinal
)) {
throw 'Published release assets do not exactly match the certified artifact set'
}
}
}
function Set-ReadOnlyCreatedReleaseId {
param([string]$Value)
if ([string]::IsNullOrWhiteSpace($Value) -or
$null -ne (
Get-Variable -Name createdReleaseId -Scope Script `
-ErrorAction SilentlyContinue
)) {
throw 'Created release identity must be set exactly once'
}
Set-Variable `
-Name createdReleaseId `
-Scope Script `
-Value $Value `
-Option ReadOnly
}
function Assert-ReleaseIdentity {
param(
[object]$Release,
[string]$ExpectedId,
[string]$ExpectedTag,
[bool]$ExpectedDraft,
[string]$Label
)
if ([string]::IsNullOrWhiteSpace($ExpectedId) -or
[string]$Release.id -cne $ExpectedId -or
[string]$Release.tagName -cne $ExpectedTag -or
[string]$Release.name -cne "Waggle $ExpectedTag" -or
-not ($Release.isDraft -is [bool]) -or
-not [bool]::Equals($Release.isDraft, $ExpectedDraft) -or
-not ($Release.isPrerelease -is [bool]) -or
[bool]::True.Equals($Release.isPrerelease)) {
throw "$Label identity does not match the newly-created release"
}
}
function New-ReleaseAssetManifest {
param([System.IO.FileInfo[]]$Assets)
$manifest = @(
foreach ($asset in $Assets) {
if ($null -eq $asset -or
-not $asset.Exists -or
[System.IO.Path]::GetFileName($asset.Name) -cne $asset.Name) {
throw 'Release asset manifest contains an invalid local file'
}
[pscustomobject]@{
Name = [string]$asset.Name
FullName = [string]$asset.FullName
SizeBytes = [int64]$asset.Length
Sha256 = (
Get-FileHash -LiteralPath $asset.FullName -Algorithm SHA256
).Hash.ToUpperInvariant()
}
}
)
for ($left = 0; $left -lt $manifest.Count; $left += 1) {
for ($right = $left + 1; $right -lt $manifest.Count; $right += 1) {
if ([string]::Equals(
[string]$manifest[$left].Name,
[string]$manifest[$right].Name,
[System.StringComparison]::Ordinal
)) {
throw 'Release asset manifest contains a duplicate exact filename'
}
}
}
return $manifest
}
function Assert-ReleaseAssetFileMatchesManifest {
param(
[string]$Path,
[object]$ExpectedAsset,
[string]$Label
)
$asset = Get-Item -LiteralPath $Path -ErrorAction Stop
$sha256 = (
Get-FileHash -LiteralPath $asset.FullName -Algorithm SHA256
).Hash
if (-not ($asset -is [System.IO.FileInfo]) -or
[string]$asset.Name -cne [string]$ExpectedAsset.Name -or
[int64]$asset.Length -ne [int64]$ExpectedAsset.SizeBytes -or
-not [string]::Equals(
$sha256,
[string]$ExpectedAsset.Sha256,
[System.StringComparison]::OrdinalIgnoreCase
)) {
throw "$Label does not match the immutable release-asset manifest"
}
}
function Assert-LocalReleaseAssetsUnchanged {
param([object[]]$Manifest)
foreach ($expectedAsset in $Manifest) {
Assert-ReleaseAssetFileMatchesManifest `
([string]$expectedAsset.FullName) `
$expectedAsset `
"Local release asset $($expectedAsset.Name)"
}
}
function Assert-RemoteReleaseAssetContents {
param(
[string]$Tag,
[object]$Release,
[object[]]$Manifest,
[string]$Stage
)
Assert-ExactReleaseAssets $Release $Manifest
Assert-LocalReleaseAssetsUnchanged $Manifest
if ([string]::IsNullOrWhiteSpace([string]$env:RUNNER_TEMP)) {
throw 'RUNNER_TEMP is required for remote release-asset verification'
}
$downloadRoot = Join-Path `
([string]$env:RUNNER_TEMP) `
"waggle-release-$Stage-$([guid]::NewGuid().ToString('N'))"
New-Item -ItemType Directory -Path $downloadRoot -ErrorAction Stop |
Out-Null
try {
gh release download $Tag --dir $downloadRoot
if ($LASTEXITCODE -ne 0) {
throw "Could not download $Stage release assets for byte verification"
}
$downloadedFiles = @(
Get-ChildItem -LiteralPath $downloadRoot -File
)
if ($downloadedFiles.Count -ne $Manifest.Count) {
throw "$Stage release download does not contain the exact asset count"
}
foreach ($expectedAsset in $Manifest) {
Assert-ReleaseAssetFileMatchesManifest `
(Join-Path $downloadRoot ([string]$expectedAsset.Name)) `
$expectedAsset `
"$Stage remote release asset $($expectedAsset.Name)"
}
} finally {
if (Test-Path -LiteralPath $downloadRoot) {
Remove-Item -LiteralPath $downloadRoot -Recurse -Force
}
}
Assert-LocalReleaseAssetsUnchanged $Manifest
}
function Assert-ManagedModelAndMemoryEvidence {
param([object]$Receipt, [string]$Label)
if (-not [bool]::True.Equals($Receipt.managedModelVerified)) {
throw "$Label does not prove a managed local-model chat completion"
}
if ([string]$Receipt.managedModelDigest -notmatch '^sha256:[0-9a-f]{64}$') {
throw "$Label does not bind the managed model to an immutable manifest digest"
}
if (-not [string]::Equals(
[string]$Receipt.certifiedTier,
'FREE',
[System.StringComparison]::Ordinal
)) {
throw "$Label does not certify the Solo/FREE tier"
}
if ([string]::IsNullOrWhiteSpace(
[string]$Receipt.lifecycleData.workspaceId
) -or
[long]$Receipt.lifecycleData.personalFrameId -lt 1 -or
[long]$Receipt.lifecycleData.workspaceFrameId -lt 1 -or
[int]$Receipt.lifecycleData.preUninstallManifestEntryCount -lt 1 -or
[string]$Receipt.lifecycleData.preUninstallManifestSha256 -notmatch '^[0-9A-F]{64}$' -or
[string]$Receipt.lifecycleData.postUninstallManifestSha256 -notmatch '^[0-9A-F]{64}$' -or
-not [string]::Equals(
[string]$Receipt.lifecycleData.preUninstallManifestSha256,
[string]$Receipt.lifecycleData.postUninstallManifestSha256,
[System.StringComparison]::Ordinal
)) {
throw "$Label does not bind real workspace and memory data to an unchanged uninstall manifest"
}
}
function Assert-ReceiptSourceHashes {
param(
[object[]]$Receipts,
[System.IO.FileInfo]$Installer,
[string]$SourceRevision
)
$currentCertifierHash = (
Get-FileHash -LiteralPath 'scripts/certify-windows-installer.ps1' `
-Algorithm SHA256
).Hash
$currentHookHash = (
Get-FileHash -LiteralPath 'app/src-tauri/nsis/installer.nsi' `
-Algorithm SHA256
).Hash
$releaseDirectory = Split-Path -Parent (
Split-Path -Parent $Installer.DirectoryName
)
$generatedInstallerScripts = @(
Get-ChildItem -LiteralPath (Join-Path $releaseDirectory 'nsis') `
-Recurse -Filter 'installer.nsi' -File
)
if ($generatedInstallerScripts.Count -ne 1) {
throw 'Could not uniquely resolve generated NSIS source during publication'
}
$currentGeneratedInstallerHash = (
Get-FileHash -LiteralPath $generatedInstallerScripts[0].FullName `
-Algorithm SHA256
).Hash
$currentSidecarPath = 'app/src-tauri/resources/service.js'
if (-not (Test-Path -LiteralPath $currentSidecarPath -PathType Leaf)) {
throw 'Current source-bound sidecar bundle is missing during publication'
}
$currentSidecarBytes = [System.IO.File]::ReadAllBytes(
(Get-Item -LiteralPath $currentSidecarPath).FullName
)
$sidecarNewlineIndex = [Array]::IndexOf($currentSidecarBytes, [byte]10)
if ($sidecarNewlineIndex -le 0) {
throw 'Current sidecar bundle is missing embedded provenance'
}
$sidecarFirstLine = [System.Text.Encoding]::UTF8.GetString(
$currentSidecarBytes,
0,
$sidecarNewlineIndex
)
$sidecarPrefix = '// Waggle-Sidecar-Provenance: '
if (-not $sidecarFirstLine.StartsWith($sidecarPrefix, [System.StringComparison]::Ordinal)) {
throw 'Current sidecar bundle is missing embedded provenance'
}
$sidecarEncodedProvenance = $sidecarFirstLine.Substring($sidecarPrefix.Length)
try {
$sidecarProvenanceBytes = [Convert]::FromBase64String($sidecarEncodedProvenance)
if (-not [string]::Equals(
[Convert]::ToBase64String($sidecarProvenanceBytes),
$sidecarEncodedProvenance,
[System.StringComparison]::Ordinal
)) {
throw 'non-canonical provenance'
}
$sidecarManifest = [System.Text.Encoding]::UTF8.GetString($sidecarProvenanceBytes) |
ConvertFrom-Json
} catch {
throw 'Current sidecar bundle has invalid embedded provenance'
}
if (-not [string]::Equals(
[string]$sidecarManifest.sourceRevision,
$SourceRevision,
[System.StringComparison]::Ordinal
) -or @($sidecarManifest.sourceInputs).Count -lt 4) {
throw 'Current sidecar provenance does not bind the publication source revision'
}
$sidecarProvenanceHasher = [System.Security.Cryptography.SHA256]::Create()
try {
$currentSidecarProvenanceHash = (
[System.BitConverter]::ToString(
$sidecarProvenanceHasher.ComputeHash($sidecarProvenanceBytes)
)
).Replace('-', '')
} finally {
$sidecarProvenanceHasher.Dispose()
}
$currentSidecarHash = (
Get-FileHash -LiteralPath $currentSidecarPath -Algorithm SHA256
).Hash
$currentSidecarSourceInputCount = @($sidecarManifest.sourceInputs).Count
foreach ($certificateData in $Receipts) {
if (-not [string]::Equals(
$currentCertifierHash,
[string]$certificateData.evidence.certifierSha256,
[System.StringComparison]::OrdinalIgnoreCase
) -or
-not [string]::Equals(
$currentHookHash,
[string]$certificateData.evidence.installerHookSha256,
[System.StringComparison]::OrdinalIgnoreCase
) -or
-not [string]::Equals(
$currentGeneratedInstallerHash,
[string]$certificateData.evidence.generatedInstallerScriptSha256,
[System.StringComparison]::OrdinalIgnoreCase
) -or
-not [string]::Equals(
$currentSidecarHash,
[string]$certificateData.evidence.sidecarBundleSha256,
[System.StringComparison]::OrdinalIgnoreCase
) -or
-not [string]::Equals(
$currentSidecarProvenanceHash,
[string]$certificateData.evidence.sidecarProvenanceSha256,
[System.StringComparison]::OrdinalIgnoreCase
) -or
-not [string]::Equals(
$SourceRevision,
[string]$certificateData.evidence.sidecarSourceRevision,
[System.StringComparison]::Ordinal
) -or
[int]$certificateData.evidence.sidecarSourceInputCount -ne
$currentSidecarSourceInputCount
) {
throw 'Lifecycle receipt source hashes do not match the release checkout'
}
}
}
function Assert-ReleaseDoesNotExist {
param([string]$Tag)
$probeOutput = @(gh release view $Tag --json id 2>&1)
$probeExitCode = $LASTEXITCODE
if ($probeExitCode -eq 0) {
throw "Refusing to use a pre-existing release: $Tag"
}
$probeText = [string]::Join("`n", [string[]]$probeOutput)
if ($probeText -notmatch '(?i)(release not found|HTTP\s+404)') {
throw "Could not prove that release $Tag does not already exist"
}
}
function Assert-PublicationTagBindings {
param([string]$ReleaseMode, [string]$Tag, [string]$Commit)
if ($ReleaseMode -ceq 'upgrade') {
Assert-RemoteTagCommit `
([string]$env:WINDOWS_UPGRADE_BASE_TAG) `
([string]$env:WAGGLE_UPGRADE_BASE_COMMIT)
}
Assert-RemoteTagCommit $Tag $Commit
}
if (-not [string]::Equals(
$Mode,
[string]$env:WAGGLE_RELEASE_MODE,
[System.StringComparison]::Ordinal
)) {
throw 'Publisher mode does not match the resolved Windows release mode'
}
$tag = [string]$env:GITHUB_REF_NAME
$sourceRevision = [string]$env:GITHUB_SHA
if ($tag -notmatch '^v\d+\.\d+\.\d+$' -or
$sourceRevision -cnotmatch '^[0-9a-f]{40}$') {
throw 'Publication requires an exact release tag and lowercase commit identity'
}
$baselineInputs = @(
[string]$env:WINDOWS_UPGRADE_BASE_TAG,
[string]$env:WINDOWS_UPGRADE_BASE_ASSET_NAME,
[string]$env:WINDOWS_UPGRADE_BASE_SHA256,
[string]$env:WINDOWS_UPGRADE_BASE_COMMIT
)
if (@($baselineInputs | Where-Object { $_ -ne $_.Trim() }).Count -gt 0) {
throw 'Protected Windows upgrade-baseline inputs must not contain surrounding whitespace'
}
if ($Mode -ceq 'bootstrap') {
$expectedBootstrapIdentity = "v0.2.0@$env:GITHUB_SHA"
if ($tag -cne 'v0.2.0' -or
-not [string]::Equals(
[string]$env:WINDOWS_BOOTSTRAP_RELEASE_IDENTITY,
$expectedBootstrapIdentity,
[System.StringComparison]::Ordinal
) -or
@($baselineInputs | Where-Object {
-not [string]::IsNullOrEmpty($_)
}).Count -ne 0) {
throw 'Bootstrap publication is not bound to the exact authorized v0.2.0 release'
}
} else {
if (-not [string]::IsNullOrEmpty(
[string]$env:WINDOWS_BOOTSTRAP_RELEASE_IDENTITY
) -or
@($baselineInputs | Where-Object {
[string]::IsNullOrEmpty($_)
}).Count -ne 0) {
throw 'Upgrade publication requires an empty bootstrap authorization and all baseline inputs'
}
}
$installers = @(
Get-ChildItem -LiteralPath 'app/src-tauri/target' -Recurse `
-Filter '*-setup.exe' -File |
Where-Object { $_.DirectoryName -match '[\\/]bundle[\\/]nsis$' }
)
if ($installers.Count -ne 1) {
throw "Expected exactly one certified NSIS installer, found $($installers.Count)"
}
$installer = $installers[0]
$cleanReceipt = Get-Item -LiteralPath (
Join-Path $installer.DirectoryName 'windows-installer-certificate.json'
)
$upgradeReceiptPath = Join-Path `
$installer.DirectoryName `
'windows-installer-upgrade-certificate.json'
if ($Mode -ceq 'bootstrap' -and
(Test-Path -LiteralPath $upgradeReceiptPath)) {
throw 'Bootstrap publication found an unexpected upgrade certificate'
}
$cleanReceiptData = Get-Content -Raw -LiteralPath $cleanReceipt.FullName |
ConvertFrom-Json
Assert-PassingWindowsCertificateReceipt `
$cleanReceiptData 'same-version-repair' 'Windows clean-install receipt'
if (-not [string]::Equals(
[string]$cleanReceiptData.evidence.sourceRevision,
$sourceRevision,
[System.StringComparison]::Ordinal
) -or
-not [string]::Equals(
[string]$cleanReceiptData.installer.name,
$installer.Name,
[System.StringComparison]::Ordinal
) -or
[int64]$cleanReceiptData.installer.sizeBytes -ne $installer.Length) {
throw 'Clean-install receipt identity does not match the release artifact and commit'
}
$candidateVersion = [string](
Get-Content -Raw -LiteralPath 'app/src-tauri/tauri.conf.json' |
ConvertFrom-Json
).version
if ($candidateVersion -notmatch '^\d+\.\d+\.\d+$' -or
$tag -cne "v$candidateVersion" -or
[string]$env:WAGGLE_CERTIFIED_CANDIDATE_VERSION -cne $candidateVersion -or
($Mode -ceq 'bootstrap' -and $candidateVersion -cne '0.2.0')) {
throw 'Published candidate version is not independently bound to the release tag'
}
$installerSha256 = (
Get-FileHash -LiteralPath $installer.FullName -Algorithm SHA256
).Hash
if (-not [string]::Equals(
$installerSha256,
[string]$env:WAGGLE_CERTIFIED_CANDIDATE_SHA256,
[System.StringComparison]::OrdinalIgnoreCase
) -or
-not [string]::Equals(
$installerSha256,
[string]$cleanReceiptData.installer.sha256,
[System.StringComparison]::OrdinalIgnoreCase
)) {
throw 'Certified installer hash no longer matches the clean receipt and pre-publication output'
}
$approvedSignerIdentity = Get-ProtectedSignerIdentity
$installerSignerIdentity = Assert-ExpectedAuthenticodeSignature $installer $approvedSignerIdentity
if ($cleanReceiptData.installer.authenticodeStatus -ne 'Valid' -or
$cleanReceiptData.installedApp.authenticodeStatus -ne 'Valid' -or
$cleanReceiptData.installer.signatureType -ne 'Authenticode' -or
$cleanReceiptData.installedApp.signatureType -ne 'Authenticode' -or
[string]::IsNullOrWhiteSpace(
[string]$cleanReceiptData.installer.timestampAuthorityThumbprint
) -or
[string]::IsNullOrWhiteSpace(
[string]$cleanReceiptData.installedApp.timestampAuthorityThumbprint
)) {
throw 'Clean-install receipt does not preserve valid signer and timestamp evidence'
}
Assert-ReceiptSignerIdentity `
$cleanReceiptData.installer $installerSignerIdentity 'Clean-install installer receipt'
Assert-ReceiptSignerIdentity `
$cleanReceiptData.installedApp $installerSignerIdentity 'Clean-install installed-app receipt'
Assert-ManagedModelAndMemoryEvidence `
$cleanReceiptData `
'Clean-install receipt'
$cleanRequiredChecks = @(
'sourceRevision', 'sourceFilesClean', 'sidecarSourceProvenance',
'generatedInstallerInclude',
'profileDataDeletionAbsent', 'baseAppDataDeletionNeutralized',
'authenticodeSignature', 'authenticodeSigner', 'authenticodeTimestamp',
'installedAppAuthenticodeSignature', 'installedAppAuthenticodeSigner',
'installedAppAuthenticodeTimestamp', 'silentInstall',
'vaultKeyAclRestricted', 'windowsInboxTools', 'noModelChatSetupRequired',
'soloTier', 'dockerIndependentRuntimePrerequisites',
'managedRuntimeBootstrap', 'managedModelPull', 'managedModelChat',
'managedModelProxyRestartChat', 'managedRuntimeCleanup',
'sameVersionRepair', 'repairSoloTier', 'repairManagedModelDigestPreserved',
'relaunchAfterRepair', 'silentUninstall', 'defaultProfileDataDir',
'realWorkspaceAndMemorySeeded', 'repairRealWorkspaceAndMemoryPreserved',
'uninstallRealWorkspaceAndMemoryPreserved', 'configuredDataDirPreserved',
'profileDataPathPreserved', 'certificateProfileCleanup',
'externalProfileRootsUnchanged', 'environmentRestored'
)
foreach ($checkName in $cleanRequiredChecks) {
Assert-PassingWindowsCertificateCheck `
$cleanReceiptData $checkName 'Clean-install receipt'
}
$receiptDataSet = @($cleanReceiptData)
if ($Mode -ceq 'bootstrap') {
$releaseAssets = @($installer, $cleanReceipt)
} else {
$upgradeReceipt = Get-Item -LiteralPath $upgradeReceiptPath
$receiptData = Get-Content -Raw -LiteralPath $upgradeReceipt.FullName |
ConvertFrom-Json
Assert-PassingWindowsCertificateReceipt `
$receiptData 'version-to-version-upgrade' 'Windows lifecycle receipt'
if (-not [string]::Equals(
[string]$receiptData.evidence.sourceRevision,
$sourceRevision,
[System.StringComparison]::Ordinal
) -or
-not [string]::Equals(
[string]$receiptData.installer.name,
$installer.Name,
[System.StringComparison]::Ordinal
) -or
[int64]$receiptData.installer.sizeBytes -ne $installer.Length -or
-not [string]::Equals(
$installerSha256,
[string]$receiptData.installer.sha256,
[System.StringComparison]::OrdinalIgnoreCase
)) {
throw 'Upgrade receipt does not match the release artifact and commit'
}
$baseInstaller = Get-Item -LiteralPath (
[string]$env:WAGGLE_UPGRADE_BASE_INSTALLER_PATH
)
$baseSha256 = (
Get-FileHash -LiteralPath $baseInstaller.FullName -Algorithm SHA256
).Hash
if (-not [string]::Equals(
[string]$env:WAGGLE_UPGRADE_BASE_COMMIT,
[string]$env:WINDOWS_UPGRADE_BASE_COMMIT,
[System.StringComparison]::OrdinalIgnoreCase
)) {
throw 'Certified Windows upgrade baseline commit no longer matches the protected commit'
}
Assert-RemoteTagCommit `
([string]$env:WINDOWS_UPGRADE_BASE_TAG) `
([string]$env:WAGGLE_UPGRADE_BASE_COMMIT)
if ([string]$env:WINDOWS_UPGRADE_BASE_TAG -notmatch '^v(?<version>\d+\.\d+\.\d+)$' -or
$Matches['version'] -cne [string]$receiptData.upgrade.previousVersion -or
$Matches['version'] -cne [string]$env:WAGGLE_UPGRADE_BASE_VERSION -or
[string]$receiptData.upgrade.previousSourceRevision -cne
[string]$env:WAGGLE_UPGRADE_BASE_COMMIT -or
[string]$receiptData.upgrade.candidateVersion -cne $candidateVersion -or
[string]$receiptData.upgrade.observedPreviousVersion -cne
[string]$receiptData.upgrade.previousVersion -or
[string]$receiptData.upgrade.observedCandidateVersion -cne
[string]$receiptData.upgrade.candidateVersion) {
throw 'Lifecycle receipt does not bind the observed upgrade versions to the protected release versions'
}
if ($baseInstaller.Name -cne [string]$env:WINDOWS_UPGRADE_BASE_ASSET_NAME -or
[string]$receiptData.previousInstaller.name -cne $baseInstaller.Name -or
[int64]$receiptData.previousInstaller.sizeBytes -ne $baseInstaller.Length -or
-not [string]::Equals(
$baseSha256,
[string]$env:WINDOWS_UPGRADE_BASE_SHA256,
[System.StringComparison]::OrdinalIgnoreCase
) -or
-not [string]::Equals(
$baseSha256,
[string]$receiptData.previousInstaller.sha256,
[System.StringComparison]::OrdinalIgnoreCase
)) {
throw 'Lifecycle receipt previous installer does not match the protected upgrade baseline'
}
if ($receiptData.installer.authenticodeStatus -ne 'Valid' -or
$receiptData.installedApp.authenticodeStatus -ne 'Valid' -or
$receiptData.previousInstaller.authenticodeStatus -ne 'Valid' -or
$receiptData.previousInstalledApp.authenticodeStatus -ne 'Valid' -or
$receiptData.installer.signatureType -ne 'Authenticode' -or
$receiptData.installedApp.signatureType -ne 'Authenticode' -or
$receiptData.previousInstaller.signatureType -ne 'Authenticode' -or
$receiptData.previousInstalledApp.signatureType -ne 'Authenticode') {
throw 'Lifecycle receipt does not contain valid previous and candidate signatures'
}
$baseInstallerSignerIdentity = Assert-ExpectedAuthenticodeSignature $baseInstaller $approvedSignerIdentity
Assert-LifecycleReceiptSignerIdentities `
$receiptData $baseInstallerSignerIdentity $installerSignerIdentity
foreach ($timestampThumbprint in @(
$receiptData.previousInstaller.timestampAuthorityThumbprint,
$receiptData.previousInstalledApp.timestampAuthorityThumbprint,
$receiptData.installer.timestampAuthorityThumbprint,
$receiptData.installedApp.timestampAuthorityThumbprint
)) {
if ([string]::IsNullOrWhiteSpace([string]$timestampThumbprint)) {
throw 'Lifecycle receipt is missing a validated Authenticode timestamp'
}
}
Assert-ManagedModelAndMemoryEvidence $receiptData 'Upgrade receipt'
if ([string]::IsNullOrWhiteSpace(
[string]$receiptData.upgrade.managedModelName
) -or
[string]$receiptData.upgrade.managedModelDigest -notmatch
'^sha256:[0-9a-f]{64}$' -or
-not [string]::Equals(
[string]$receiptData.upgrade.managedModelName,
[string]$receiptData.managedModel.name,
[System.StringComparison]::Ordinal
) -or
-not [string]::Equals(
[string]$receiptData.upgrade.managedModelDigest,
[string]$receiptData.managedModelDigest,
[System.StringComparison]::Ordinal
)) {
throw 'Upgrade receipt does not preserve the previous managed-model identity and digest'
}
$upgradeRequiredChecks = @(
'sourceRevision', 'sourceFilesClean', 'sidecarSourceProvenance',
'generatedInstallerInclude',
'profileDataDeletionAbsent', 'baseAppDataDeletionNeutralized',
'authenticodeSignature', 'authenticodeSigner', 'authenticodeTimestamp',
'installedAppAuthenticodeSignature', 'installedAppAuthenticodeSigner',
'installedAppAuthenticodeTimestamp', 'previousInstallerHash',
'previousInstallerAuthenticodeSignature',
'previousInstallerAuthenticodeSigner',
'previousInstallerAuthenticodeTimestamp',
'previousInstalledAppAuthenticodeSignature',
'previousInstalledAppAuthenticodeSigner',
'previousInstalledAppAuthenticodeTimestamp', 'candidateInstallerHash',
'versionOrder', 'previousVersion', 'candidateVersion', 'previousInstall',
'previousLaunch', 'sameApprovedSigner', 'versionToVersionUpgrade',
'upgradeSameInstallDirectory', 'upgradeConfiguredDataPreserved',
'upgradeProfileDataPreserved', 'upgradeVaultKeyPreserved',
'upgradeRegistrations', 'candidateLaunch', 'relaunchAfterUpgrade',
'silentInstall', 'vaultKeyAclRestricted', 'windowsInboxTools', 'soloTier',
'previousSoloTier', 'dockerIndependentRuntimePrerequisites',
'managedRuntimeBootstrap', 'managedModelPull', 'managedModelChat',
'managedModelProxyRestartChat', 'managedRuntimeCleanup',
'previousManagedModelSeeded', 'upgradeManagedModelPreserved',
'sameVersionRepair', 'repairSoloTier', 'repairManagedModelDigestPreserved',
'relaunchAfterRepair', 'silentUninstall', 'defaultProfileDataDir',
'realWorkspaceAndMemorySeeded', 'upgradeRealWorkspaceAndMemoryPreserved',
'repairRealWorkspaceAndMemoryPreserved',
'uninstallRealWorkspaceAndMemoryPreserved',
'configuredDataDirPreserved', 'profileDataPathPreserved',
'certificateProfileCleanup', 'externalProfileRootsUnchanged', 'environmentRestored'
)
foreach ($checkName in $upgradeRequiredChecks) {
Assert-PassingWindowsCertificateCheck `
$receiptData $checkName 'Upgrade receipt'
}
$receiptDataSet = @($cleanReceiptData, $receiptData)
$releaseAssets = @($installer, $cleanReceipt, $upgradeReceipt)
}
$nonPassingChecks = @(
foreach ($certificateData in $receiptDataSet) {
$certificateData.checks.PSObject.Properties |
Where-Object { -not [bool]::True.Equals($_.Value) }
}
)
if ($nonPassingChecks.Count -gt 0) {
throw "Lifecycle receipts contain non-passing checks: $($nonPassingChecks.Name -join ', ')"
}
$releaseAssetManifest = @(New-ReleaseAssetManifest $releaseAssets)
Assert-LocalReleaseAssetsUnchanged $releaseAssetManifest
Assert-ReceiptSourceHashes $receiptDataSet $installer $sourceRevision
Assert-PublicationTagBindings $Mode $tag $sourceRevision
Assert-ReleaseDoesNotExist $tag
gh release create $tag --verify-tag --draft --title "Waggle $tag" `
--notes 'See the release notes for details.'
if ($LASTEXITCODE -ne 0) {
throw "Could not create a dedicated $Mode draft release: $tag"
}
$releaseData = gh release view $tag `
--json id,isDraft,isPrerelease,tagName,name,assets |
ConvertFrom-Json
if ($LASTEXITCODE -ne 0) {
throw "Could not inspect newly-created $Mode draft release $tag"
}
Set-ReadOnlyCreatedReleaseId ([string]$releaseData.id)
Assert-ReleaseIdentity `
$releaseData $createdReleaseId $tag $true 'Newly-created draft release'
if (@($releaseData.assets).Count -ne 0) {
throw "Could not verify newly-created $Mode draft release $tag"
}
Assert-PublicationTagBindings $Mode $tag $sourceRevision
$releaseAssetPaths = [string[]]@(
$releaseAssetManifest | ForEach-Object { $_.FullName }
)
gh release upload $tag @releaseAssetPaths
if ($LASTEXITCODE -ne 0) {
throw "Could not upload certified Windows $Mode assets to $tag"
}
$uploadedRelease = gh release view $tag `
--json id,isDraft,isPrerelease,tagName,name,assets |
ConvertFrom-Json
if ($LASTEXITCODE -ne 0) {
throw "Could not verify uploaded $Mode draft $tag"
}
Assert-ReleaseIdentity `
$uploadedRelease $createdReleaseId $tag $true 'Uploaded draft release'
Assert-ExactReleaseAssets $uploadedRelease $releaseAssets
Assert-RemoteReleaseAssetContents $tag $uploadedRelease $releaseAssetManifest 'uploaded'
Assert-PublicationTagBindings $Mode $tag $sourceRevision
gh release edit $tag --draft=false --prerelease=false
if ($LASTEXITCODE -ne 0) {
throw "Could not publish certified Windows $Mode release $tag"
}
$publishedRelease = gh release view $tag `
--json id,isDraft,isPrerelease,tagName,name,assets |
ConvertFrom-Json
if ($LASTEXITCODE -ne 0) {
throw "Published Windows $Mode release $tag is not final"
}
Assert-ReleaseIdentity `
$publishedRelease $createdReleaseId $tag $false 'Published release'
Assert-ExactReleaseAssets $publishedRelease $releaseAssets
Assert-RemoteReleaseAssetContents $tag $publishedRelease $releaseAssetManifest 'published'

View File

@@ -0,0 +1,707 @@
import assert from 'node:assert/strict';
import { mkdtemp, rm } from 'node:fs/promises';
import { createServer } from 'node:http';
import os from 'node:os';
import path from 'node:path';
import { describe, it } from 'vitest';
import { CostTracker } from '../packages/agent/src/cost-tracker.js';
import { MindDB } from '../packages/hive-mind-core/src/mind/db.js';
import { ExecutionTraceStore } from '../packages/hive-mind-core/src/mind/execution-traces.js';
import {
assertQualifiedChatCase,
assertQualifiedToolContextCase,
assertCopiedModelIdentity,
assertObservedDispatch,
assertObservedProviderUsage,
assertRuntimeStartOwned,
assertSourceSnapshot,
aliasesForOwnedCleanup,
BUDGET_PROMPT,
buildOwnedProxyTarget,
buildRouterSettings,
buildSanitizedEnvironment,
canonicalizeManifestDigest,
extractDispatchedToolNames,
extractProviderUsage,
parseSse,
partitionWindowsProcesses,
postJsonForStatus,
requestSidecarSessionToken,
recordAliasBeforeCopy,
seedQualificationDailySpend,
startAuditProxy,
} from './qualify-smart-router.js';
import { routeMessage } from '../packages/agent/src/smart-router.js';
describe('qualify-smart-router helpers', () => {
it('seeds qualification spend through the durable ledger before server startup', async () => {
const dataDir = await mkdtemp(path.join(os.tmpdir(), 'waggle-router-spend-seed-'));
const timestamp = new Date().toISOString();
const day = timestamp.slice(0, 10);
try {
await seedQualificationDailySpend(dataDir, 0.8, timestamp);
const db = new MindDB(path.join(dataDir, 'personal.mind'));
try {
const persisted = new ExecutionTraceStore(db).getTotalCostSince(`${day}T00:00:00.000Z`);
assert.equal(persisted, 0.8);
const tracker = new CostTracker({});
tracker.initializeDailyCarryover(day, persisted);
assert.equal(tracker.getDailyTotal(), 0.8);
} finally {
db.close();
}
} finally {
await rm(dataDir, { recursive: true, force: true });
}
});
it('binds session bootstrap to the exact dynamic sidecar authority', async () => {
const baseUrl = 'http://127.0.0.1:49152';
const fetchImpl = async (input: string | URL, init?: RequestInit) => {
assert.equal(String(input), `${baseUrl}/api/auth/session-token`);
const headers = new Headers(init?.headers);
assert.equal(headers.get('origin'), baseUrl);
assert.equal(headers.get('sec-fetch-site'), 'same-origin');
return new Response(JSON.stringify({ token: 'session-token' }), {
status: 200,
headers: { 'content-type': 'application/json' },
});
};
const response = await requestSidecarSessionToken(baseUrl, fetchImpl);
assert.equal(response.token, 'session-token');
});
it('strictly parses JSON SSE events', () => {
const events = parseSse([
'event: token',
'data: {"content":"hello"}',
'',
'event: model_switch',
'data: {"model":"ollama/fallback","reason":"primary unavailable; configured fallback selected","primary":"ollama/primary"}',
'',
'event: done',
'data: {"content":"hello","model":"ollama/fallback","toolsUsed":[]}',
'',
].join('\r\n'));
assert.deepEqual(events, [
{ event: 'token', data: { content: 'hello' } },
{
event: 'model_switch',
data: {
model: 'ollama/fallback',
reason: 'primary unavailable; configured fallback selected',
primary: 'ollama/primary',
},
},
{ event: 'done', data: { content: 'hello', model: 'ollama/fallback', toolsUsed: [] } },
]);
});
it('rejects malformed or ambiguous SSE frames', () => {
assert.throws(() => parseSse('data: {"content":"orphan"}\n\n'), /event field/i);
assert.throws(() => parseSse('event: done\nevent: done\ndata: {}\n\n'), /duplicate event/i);
assert.throws(() => parseSse('event: done\ndata: not-json\n\n'), /invalid JSON/i);
assert.throws(() => parseSse('event: done\nunknown: value\n\n'), /unsupported SSE field/i);
});
it('requires HTTP SSE success, one done, content, expected model, and exact switch policy', () => {
const runtimeMetrics = {
estimatedSystemPromptTokens: 7_437,
providerInputTokens: 8_005,
providerOutputTokens: 244,
timeToFirstTokenMs: 5_497,
agentLatencyMs: 6_102,
totalServerLatencyMs: 6_198,
};
const rawSse = [
'event: tool',
'data: {"name":"auto_recall","input":{"query":"test"}}',
'',
'event: tool_result',
'data: {"name":"auto_recall","result":"No relevant memories found","isError":false}',
'',
'event: model_switch',
'data: {"model":"ollama/fallback","reason":"ollama/primary unavailable; configured fallback selected","primary":"ollama/primary"}',
'',
'event: token',
'data: {"content":"fallback answer"}',
'',
'event: done',
`data: ${JSON.stringify({
content: 'fallback answer',
model: 'ollama/fallback',
toolsUsed: [],
usage: { inputTokens: 8_005, outputTokens: 244 },
contextMetrics: runtimeMetrics,
})}`,
'',
].join('\n');
const result = assertQualifiedChatCase({
httpStatus: 200,
contentType: 'text/event-stream; charset=utf-8',
rawSse,
expectedModel: 'ollama/fallback',
expectedSwitch: {
model: 'ollama/fallback',
primary: 'ollama/primary',
reason: 'ollama/primary unavailable; configured fallback selected',
},
});
assert.equal(result.content, 'fallback answer');
assert.equal(result.events.filter(({ event }) => event === 'done').length, 1);
assert.equal(result.events.filter(({ event }) => event === 'model_switch').length, 1);
});
it('qualifies a bounded, relevant production chat tool context from at least 29 eligible tools', () => {
const selectedToolNames = [
'read_file', 'search_files', 'search_content', 'git_status',
...Array.from({ length: 10 }, (_, index) => `code_tool_${index}`),
];
const rawSse = (
contextMetrics: Record<string, unknown>,
usage: Record<string, unknown> = {
inputTokens: contextMetrics.providerInputTokens,
outputTokens: contextMetrics.providerOutputTokens,
},
) => [
'event: token',
'data: {"content":"tool context qualified"}',
'',
'event: done',
`data: ${JSON.stringify({
content: 'tool context qualified',
model: 'ollama/primary',
toolsUsed: [],
usage,
contextMetrics,
})}`,
'',
].join('\n');
const validMetrics = {
toolCatalogCount: 78,
toolEligibleCount: 52,
toolSelectedCount: 14,
toolOmittedCount: 38,
transmittedToolSchemaChars: 6_120,
estimatedToolSchemaTokens: 1_530,
selectorLatencyMs: 6,
estimatedSystemPromptTokens: 7_437,
providerInputTokens: 8_005,
providerOutputTokens: 244,
timeToFirstTokenMs: 5_497,
agentLatencyMs: 6_102,
totalServerLatencyMs: 6_198,
};
const qualify = (
overrides: Record<string, unknown> = {},
dispatchedToolNames: string[] = selectedToolNames,
usage?: Record<string, unknown>,
) => assertQualifiedToolContextCase({
httpStatus: 200,
contentType: 'text/event-stream; charset=utf-8',
rawSse: rawSse({ ...validMetrics, ...overrides }, usage),
expectedModel: 'ollama/primary',
selectedToolNames: dispatchedToolNames,
});
const qualified = qualify();
assert.deepEqual(qualified.toolContext.selectedToolNames, selectedToolNames);
assert.equal(qualified.toolContext.toolEligibleCount, 52);
assert.equal(qualified.toolContext.toolSelectedCount, 14);
const compacted = qualify({
estimatedSystemPromptTokens: 7_304,
providerInputTokens: 6_983,
});
assert.equal(compacted.runtimeMetrics.providerInputTokens, 6_983);
assert.throws(() => qualify({ toolEligibleCount: 28, toolOmittedCount: 14 }), /at least 29 eligible/i);
assert.throws(() => qualify({ toolSelectedCount: 15, toolOmittedCount: 37 }), /at most 14 tools/i);
assert.throws(() => qualify({ transmittedToolSchemaChars: 8_001, estimatedToolSchemaTokens: 2_001 }), /8,000 schema characters/i);
assert.throws(() => qualify({ toolOmittedCount: 37 }), /eligible minus selected/i);
assert.throws(() => qualify({}, selectedToolNames.slice(0, -1)), /selected names/i);
assert.throws(() => qualify({}, Array(14).fill('calendar_tool')), /unique selected names/i);
assert.throws(() => qualify({}, Array.from({ length: 14 }, (_, index) => `calendar_tool_${index}`)), /code-inspection relevance/i);
assert.throws(() => qualify({ selectorLatencyMs: 251 }), /250ms/i);
assert.throws(() => qualify({ providerInputTokens: 2_050 }), /provider input.*90%/i);
assert.throws(() => qualify({ providerInputTokens: 6_000 }), /provider input.*90%/i);
assert.throws(
() => qualify({ providerInputTokens: 6_693 }),
/provider input.*90%.*6,694.*received 6693/i,
);
assert.doesNotThrow(() => qualify({ providerInputTokens: 6_694 }));
assert.throws(
() => qualify({ estimatedSystemPromptTokens: 6_000, providerInputTokens: 6_499 }),
/provider input.*90%.*6,500.*received 6499/i,
);
assert.doesNotThrow(() => qualify({
estimatedSystemPromptTokens: 6_000,
providerInputTokens: 6_500,
}));
assert.throws(() => qualify({ providerInputTokens: undefined }), /positive integer providerInputTokens/i);
assert.throws(() => qualify({ providerOutputTokens: 0 }), /positive integer providerOutputTokens/i);
assert.throws(
() => qualify(
{ providerOutputTokens: 245 },
selectedToolNames,
{ inputTokens: 8_005, outputTokens: 244 },
),
/token counts.*done\.usage/i,
);
assert.throws(() => qualify({ timeToFirstTokenMs: undefined }), /finite timeToFirstTokenMs/i);
assert.throws(() => qualify({ agentLatencyMs: -1 }), /finite agentLatencyMs/i);
assert.throws(() => qualify({ totalServerLatencyMs: Number.POSITIVE_INFINITY }), /finite totalServerLatencyMs/i);
assert.throws(() => qualify({ timeToFirstTokenMs: 6_199 }), /timeToFirstTokenMs.*totalServerLatencyMs/i);
assert.throws(() => qualify({ agentLatencyMs: 6_199 }), /agentLatencyMs.*totalServerLatencyMs/i);
assert.throws(
() => qualify({ timeToFirstTokenMs: 15_001, totalServerLatencyMs: 15_002 }),
/timeToFirstTokenMs.*15,000.*received 15,001/i,
);
assert.throws(
() => qualify({ agentLatencyMs: 60_001, totalServerLatencyMs: 60_002 }),
/agentLatencyMs.*60,000/i,
);
assert.throws(
() => qualify({ totalServerLatencyMs: 60_001 }),
/totalServerLatencyMs.*60,000/i,
);
});
it('fails closed on errors, duplicate done events, wrong models, or unexpected switches', () => {
const done = 'event: token\ndata: {"content":"ok"}\n\nevent: done\ndata: {"content":"ok","model":"ollama/primary","toolsUsed":[]}\n\n';
assert.throws(() => assertQualifiedChatCase({
httpStatus: 500,
contentType: 'text/event-stream',
rawSse: done,
expectedModel: 'ollama/primary',
}), /HTTP 200/i);
assert.throws(() => assertQualifiedChatCase({
httpStatus: 200,
contentType: 'application/json',
rawSse: done,
expectedModel: 'ollama/primary',
}), /text\/event-stream/i);
assert.throws(() => assertQualifiedChatCase({
httpStatus: 200,
contentType: 'text/event-stream',
rawSse: `event: error\ndata: {"message":"boom"}\n\n${done}`,
expectedModel: 'ollama/primary',
}), /zero error/i);
assert.throws(() => assertQualifiedChatCase({
httpStatus: 200,
contentType: 'text/event-stream',
rawSse: done + done,
expectedModel: 'ollama/primary',
}), /exactly one done/i);
assert.throws(() => assertQualifiedChatCase({
httpStatus: 200,
contentType: 'text/event-stream',
rawSse: done,
expectedModel: 'ollama/budget',
}), /done\.model/i);
assert.throws(() => assertQualifiedChatCase({
httpStatus: 200,
contentType: 'text/event-stream',
rawSse: `event: model_switch\ndata: {"model":"ollama/fallback","reason":"unexpected","primary":"ollama/primary"}\n\n${done}`,
expectedModel: 'ollama/primary',
}), /zero model_switch/i);
assert.throws(() => assertQualifiedChatCase({
httpStatus: 200,
contentType: 'text/event-stream',
rawSse: `${done}event: step\ndata: {"content":"late"}\n\n`,
expectedModel: 'ollama/primary',
}), /final event/i);
assert.throws(() => assertQualifiedChatCase({
httpStatus: 200,
contentType: 'text/event-stream',
rawSse: 'event: token\ndata: {"content":"different"}\n\nevent: done\ndata: {"content":"ok","model":"ollama/primary","toolsUsed":[]}\n\n',
expectedModel: 'ollama/primary',
}), /token content/i);
assert.throws(() => assertQualifiedChatCase({
httpStatus: 200,
contentType: 'text/event-stream',
rawSse: 'event: token\ndata: {"content":"ok"}\n\nevent: done\ndata: {"content":"ok","model":"ollama/primary","toolsUsed":["bash"]}\n\n',
expectedModel: 'ollama/primary',
}), /zero tools/i);
assert.throws(() => assertQualifiedChatCase({
httpStatus: 200,
contentType: 'text/event-stream',
rawSse: 'event: tool\ndata: {"name":"bash","input":{}}\n\nevent: token\ndata: {"content":"ok"}\n\nevent: done\ndata: {"content":"ok","model":"ollama/primary","toolsUsed":[]}\n\n',
expectedModel: 'ollama/primary',
}), /zero tools/i);
});
it('canonicalizes immutable Ollama manifest digests and rejects weak identities', () => {
const digest = 'A'.repeat(64);
assert.equal(canonicalizeManifestDigest(digest), `sha256:${'a'.repeat(64)}`);
assert.equal(canonicalizeManifestDigest(`sha256:${digest}`), `sha256:${'a'.repeat(64)}`);
assert.throws(() => canonicalizeManifestDigest('latest'), /manifest digest/i);
});
it('requires a newly started owned watchdog and Ollama process', () => {
assert.doesNotThrow(() => assertRuntimeStartOwned({
installedNow: false,
startedNow: true,
status: { source: 'waggle-managed', running: true },
}, [
{ processId: 101, name: 'node.exe', executablePath: 'C:\\node.exe', creationDate: 'one' },
{ processId: 102, name: 'ollama.exe', executablePath: 'C:\\proof\\ollama.exe', creationDate: 'two' },
]));
assert.throws(() => assertRuntimeStartOwned({
installedNow: false,
startedNow: false,
status: { source: 'waggle-managed', running: true },
}, []), /newly started/i);
});
it('does not classify the qualifier or launcher as an owned runtime process', () => {
const runtimeRoot = 'C:\\proof\\managed-runtime';
const snapshot = partitionWindowsProcesses([
{
processId: 100,
name: 'node.exe',
executablePath: 'C:\\Program Files\\nodejs\\node.exe',
creationDate: 'one',
commandLine: `node qualify-smart-router.ts --runtime-data-dir ${runtimeRoot}`,
},
{
processId: 101,
name: 'node.exe',
executablePath: 'C:\\Program Files\\nodejs\\node.exe',
creationDate: 'two',
commandLine: `node -e "const stopTree = () => {}; process.once('disconnect', stopTree)" ${runtimeRoot}\\runtimes\\ollama\\0.32.0\\ollama.exe ["serve"]`,
},
{
processId: 102,
name: 'ollama.exe',
executablePath: `${runtimeRoot}\\runtimes\\ollama\\0.32.0\\ollama.exe`,
creationDate: 'three',
commandLine: 'ollama.exe serve',
},
{
processId: 103,
name: 'ollama.exe',
executablePath: 'C:\\Program Files\\Ollama\\ollama.exe',
creationDate: 'four',
commandLine: 'ollama.exe serve',
},
], runtimeRoot, new Set([100]));
assert.deepEqual(snapshot.owned.map(({ processId }) => processId), [101, 102]);
assert.deepEqual(snapshot.externalOllama.map(({ processId }) => processId), [103]);
});
it('never mutates aliases when runtime ownership was not confirmed', () => {
assert.deepEqual(aliasesForOwnedCleanup(false, ['primary:latest', 'fallback:latest']), []);
assert.deepEqual(aliasesForOwnedCleanup(true, ['primary:latest', 'fallback:latest']), [
'primary:latest',
'fallback:latest',
]);
});
it('records an owned alias before a copy response can fail', async () => {
const attemptedAliases = new Set<string>();
await assert.rejects(() => recordAliasBeforeCopy(
attemptedAliases,
'primary:latest',
async () => { throw new Error('response lost'); },
), /response lost/i);
assert.deepEqual([...attemptedAliases], ['primary:latest']);
});
it('accepts an empty successful Ollama copy response and rejects failed status', async () => {
await assert.doesNotReject(() => postJsonForStatus(
'http://127.0.0.1:11434/api/copy',
{ source: 'base', destination: 'alias' },
async () => new Response(null, { status: 200 }),
));
await assert.rejects(() => postJsonForStatus(
'http://127.0.0.1:11434/api/copy',
{ source: 'base', destination: 'alias' },
async () => new Response('copy failed', { status: 500 }),
), /HTTP 500.*copy failed/i);
});
it('pins audit-proxy requests to the owned runtime origin', () => {
assert.equal(
buildOwnedProxyTarget('/v1/chat/completions?proof=1', 'http://127.0.0.1:51111').href,
'http://127.0.0.1:51111/v1/chat/completions?proof=1',
);
assert.throws(() => buildOwnedProxyTarget(
'http://foreign.example/v1/chat/completions',
'http://127.0.0.1:51111',
), /absolute-form/i);
assert.throws(() => buildOwnedProxyTarget(
'//foreign.example/v1/chat/completions',
'http://127.0.0.1:51111',
), /absolute-form/i);
});
it('binds every copied alias to the immutable base-model digest', () => {
const digest = 'a'.repeat(64);
const aliases = {
primary: 'router-primary:latest',
budget: 'router-budget:latest',
fallback: 'router-fallback:latest',
};
assert.deepEqual(assertCopiedModelIdentity({
baseModel: 'qwen3:1.7b',
aliases,
models: [
{ name: 'qwen3:1.7b', digest },
...Object.values(aliases).map((name) => ({ name, digest: `sha256:${digest}` })),
],
}), {
baseModelDigest: `sha256:${digest}`,
aliasDigests: Object.fromEntries(Object.values(aliases).map((name) => [name, `sha256:${digest}`])),
});
assert.throws(() => assertCopiedModelIdentity({
baseModel: 'qwen3:1.7b',
aliases,
models: [
{ name: 'qwen3:1.7b', digest },
{ name: aliases.primary, digest: 'b'.repeat(64) },
{ name: aliases.budget, digest },
{ name: aliases.fallback, digest },
],
}), /does not match the base model digest/i);
});
it('requires independently observed Ollama dispatch to the expected alias', () => {
const dispatches = [
{
at: 'now',
path: '/v1/chat/completions',
model: 'router-primary:latest',
bodySha256: 'a'.repeat(64),
toolNames: [],
providerUsage: { inputTokens: 8_005, outputTokens: 244 },
},
];
assert.deepEqual(assertObservedDispatch(dispatches, 0, 'router-primary:latest'), dispatches);
assert.throws(() => assertObservedDispatch(dispatches, 0, 'router-budget:latest'), /observed Ollama dispatch/i);
assert.deepEqual(
assertObservedProviderUsage(dispatches, { inputTokens: 8_005, outputTokens: 244 }),
{ inputTokens: 8_005, outputTokens: 244 },
);
assert.throws(
() => assertObservedProviderUsage(
[{ ...dispatches[0], providerUsage: null }],
{ inputTokens: 8_005, outputTokens: 244 },
),
/provider-observed usage/i,
);
assert.throws(
() => assertObservedProviderUsage(dispatches, { inputTokens: 8_004, outputTokens: 244 }),
/does not match.*done usage/i,
);
assert.throws(
() => assertObservedProviderUsage([
{ ...dispatches[0], providerUsage: { inputTokens: 7_000, outputTokens: 122 } },
{ ...dispatches[0], providerUsage: { inputTokens: 7_000, outputTokens: 122 } },
], { inputTokens: 14_000, outputTokens: 244 }),
/exactly one provider dispatch/i,
);
});
it('extracts provider-observed token usage from the upstream Ollama stream', () => {
const raw = [
'data: {"choices":[{"delta":{"content":"ok"}}]}',
'',
'data: {"choices":[],"usage":{"prompt_tokens":14011,"completion_tokens":244}}',
'',
'data: [DONE]',
'',
].join('\n');
assert.deepEqual(extractProviderUsage(raw), { inputTokens: 14_011, outputTokens: 244 });
assert.equal(extractProviderUsage('data: {"choices":[]}\n\ndata: [DONE]\n\n'), null);
assert.equal(extractProviderUsage('data: {"usage":{"prompt_tokens":0,"completion_tokens":2}}\n\n'), null);
});
it('streams audited provider bytes and records usage before delayed HTTP EOF', async () => {
let releaseEof = (): void => {};
const eofGate = new Promise<void>((resolve) => { releaseEof = resolve; });
const upstream = createServer(async (request, response) => {
for await (const _chunk of request) {
// Drain the request before starting the controlled response.
}
response.writeHead(200, { 'content-type': 'text/event-stream' });
response.write('data: {"choices":[{"delta":{"content":"ok"}}]}\n\n');
response.write('data: {"choices":[],"usage":{"prompt_tokens":14011,"completion_tokens":244}}\n\n');
response.write('data: [DONE]\n\n');
await eofGate;
response.end();
});
await new Promise<void>((resolve, reject) => {
upstream.once('error', reject);
upstream.listen(0, '127.0.0.1', () => resolve());
});
const address = upstream.address();
assert.ok(address && typeof address !== 'string');
const dispatches: Parameters<typeof startAuditProxy>[0]['dispatches'] = [];
const proxy = await startAuditProxy({
port: 0,
targetEndpoint: `http://127.0.0.1:${address.port}`,
dispatches,
});
const proxyAddress = proxy.address();
assert.ok(proxyAddress && typeof proxyAddress !== 'string');
let reader: ReadableStreamDefaultReader<Uint8Array> | undefined;
try {
const response = await fetch(`http://127.0.0.1:${proxyAddress.port}/v1/chat/completions`, {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ model: 'router-primary:latest', stream: true }),
signal: AbortSignal.timeout(2_000),
});
assert.equal(response.status, 200);
assert.ok(response.body);
reader = response.body.getReader();
const readWithin = async () => {
let timer: ReturnType<typeof setTimeout> | undefined;
try {
return await Promise.race([
reader!.read(),
new Promise<never>((_resolve, reject) => {
timer = setTimeout(() => reject(new Error('Timed out before streamed [DONE]')), 1_000);
}),
]);
} finally {
if (timer) clearTimeout(timer);
}
};
let wire = '';
while (!wire.includes('data: [DONE]')) {
const next = await readWithin();
assert.equal(next.done, false);
wire += Buffer.from(next.value).toString('utf8');
}
assert.match(wire, /"content":"ok"/);
assert.deepEqual(dispatches[0]?.providerUsage, { inputTokens: 14_011, outputTokens: 244 });
releaseEof();
while (!(await reader.read()).done) {
// Drain the clean EOF.
}
} finally {
releaseEof();
await reader?.cancel().catch(() => undefined);
proxy.closeAllConnections();
upstream.closeAllConnections();
await Promise.all([
new Promise<void>((resolve) => proxy.close(() => resolve())),
new Promise<void>((resolve) => upstream.close(() => resolve())),
]);
}
});
it('extracts transmitted OpenAI tool names from the audited provider payload', () => {
assert.deepEqual(extractDispatchedToolNames({
tools: [
{ type: 'function', function: { name: 'read_file', parameters: { type: 'object' } } },
{ type: 'function', function: { name: 'search_files', parameters: { type: 'object' } } },
],
}), ['read_file', 'search_files']);
assert.deepEqual(extractDispatchedToolNames({ model: 'router-primary:latest' }), []);
assert.throws(() => extractDispatchedToolNames({ tools: [{}] }), /function metadata/i);
});
it('builds the real model-pilot settings payload', () => {
assert.deepEqual(buildRouterSettings({
primary: 'router-primary:latest',
budget: 'router-budget:latest',
fallback: 'router-fallback:latest',
}), {
defaultModel: 'ollama/router-primary:latest',
budgetModel: 'ollama/router-budget:latest',
fallbackModel: 'ollama/router-fallback:latest',
dailyBudget: 1,
budgetHardCap: false,
budgetThreshold: 0.8,
providers: {},
});
});
it('keeps the exact live arithmetic prompt inside the closed budget allowlist', () => {
assert.deepEqual(routeMessage(BUDGET_PROMPT, 'ollama/primary', 'ollama/budget'), {
model: 'ollama/budget',
reason: 'simple_turn',
});
});
it('fails sealing when the source snapshot changes during qualification', () => {
assert.doesNotThrow(() => assertSourceSnapshot({
expectedRevision: 'a'.repeat(40),
expectedScriptSha256: 'b'.repeat(64),
actualRevision: 'a'.repeat(40),
trackedStatus: '',
actualScriptSha256: 'b'.repeat(64),
}));
assert.throws(() => assertSourceSnapshot({
expectedRevision: 'a'.repeat(40),
expectedScriptSha256: 'b'.repeat(64),
actualRevision: 'c'.repeat(40),
trackedStatus: '',
actualScriptSha256: 'b'.repeat(64),
}), /source revision changed/i);
assert.throws(() => assertSourceSnapshot({
expectedRevision: 'a'.repeat(40),
expectedScriptSha256: 'b'.repeat(64),
actualRevision: 'a'.repeat(40),
trackedStatus: ' M packages/server/src/local/routes/chat.ts',
actualScriptSha256: 'b'.repeat(64),
}), /tracked source changed/i);
});
it('removes every provider, proxy, Docker, and inherited Waggle runtime credential', () => {
const environment = buildSanitizedEnvironment({
PATH: 'safe-path',
ANTHROPIC_API_KEY: 'secret',
OPENAI_API_KEY: 'secret',
GEMINI_API_KEY: 'secret',
GOOGLE_API_KEY: 'secret',
XAI_API_KEY: 'secret',
DEEPSEEK_API_KEY: 'secret',
MISTRAL_API_KEY: 'secret',
DASHSCOPE_API_KEY: 'secret',
MINIMAX_API_KEY: 'secret',
ZHIPU_API_KEY: 'secret',
MOONSHOT_API_KEY: 'secret',
PERPLEXITY_API_KEY: 'secret',
OPENROUTER_API_KEY: 'secret',
VOYAGE_API_KEY: 'secret',
WAGGLE_VOYAGE_API_KEY: 'secret',
LITELLM_API_KEY: 'secret',
LITELLM_MASTER_KEY: 'secret',
WAGGLE_LITELLM_URL: 'https://proxy.example',
DOCKER_HOST: 'tcp://docker.example',
WAGGLE_DATA_DIR: 'foreign-data',
OLLAMA_HOST: 'http://127.0.0.1:11434',
http_proxy: 'http://lowercase-proxy.example',
https_proxy: 'http://lowercase-proxy.example',
docker_host: 'tcp://lowercase-docker.example',
ollama_host: 'http://127.0.0.1:59999',
waggle_data_dir: 'lowercase-foreign-data',
}, {
WAGGLE_DATA_DIR: 'isolated-data',
OLLAMA_HOST: 'http://127.0.0.1:51111',
});
assert.equal(environment.PATH, 'safe-path');
assert.equal(environment.WAGGLE_DATA_DIR, 'isolated-data');
assert.equal(environment.OLLAMA_HOST, 'http://127.0.0.1:51111');
for (const name of [
'ANTHROPIC_API_KEY', 'OPENAI_API_KEY', 'GEMINI_API_KEY', 'GOOGLE_API_KEY',
'XAI_API_KEY', 'DEEPSEEK_API_KEY', 'MISTRAL_API_KEY', 'DASHSCOPE_API_KEY',
'MINIMAX_API_KEY', 'ZHIPU_API_KEY', 'MOONSHOT_API_KEY', 'PERPLEXITY_API_KEY',
'OPENROUTER_API_KEY', 'VOYAGE_API_KEY', 'WAGGLE_VOYAGE_API_KEY',
'LITELLM_API_KEY', 'LITELLM_MASTER_KEY', 'WAGGLE_LITELLM_URL', 'DOCKER_HOST',
'http_proxy', 'https_proxy', 'docker_host', 'ollama_host', 'waggle_data_dir',
]) {
assert.equal(environment[name], undefined, `${name} was not sanitized`);
}
});
});

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,262 @@
#!/usr/bin/env node
import process from 'node:process';
import WebSocket from 'ws';
const DEFAULT_TIMEOUT_MS = 60_000;
const MIN_TOKEN_LENGTH = 32;
const MAX_TOKEN_LENGTH = 200;
function fail(message) {
console.error(`tauri-bootstrap-token: ${message}`);
process.exitCode = 1;
}
function parseArgs(argv) {
const options = {
port: null,
timeoutMs: DEFAULT_TIMEOUT_MS,
selfTest: false,
allowLegacyUi: false,
};
for (let index = 0; index < argv.length; index += 1) {
const argument = argv[index];
if (argument === '--self-test') {
options.selfTest = true;
continue;
}
if (argument === '--allow-legacy-ui') {
options.allowLegacyUi = true;
continue;
}
if (argument === '--port' || argument === '--timeout-ms') {
const value = argv[index + 1];
index += 1;
if (value === undefined) throw new Error(`${argument} requires a value`);
if (argument === '--port') options.port = Number(value);
else options.timeoutMs = Number(value);
continue;
}
if (argument.startsWith('--port=')) options.port = Number(argument.slice(7));
else if (argument.startsWith('--timeout-ms=')) options.timeoutMs = Number(argument.slice(13));
else throw new Error(`unknown argument: ${argument}`);
}
if (!Number.isInteger(options.port) || options.port < 1024 || options.port > 65535) {
throw new Error('port must be an integer between 1024 and 65535');
}
if (!Number.isInteger(options.timeoutMs) || options.timeoutMs < 1000 || options.timeoutMs > 300_000) {
throw new Error('timeout-ms must be an integer between 1000 and 300000');
}
return options;
}
function isValidEndpoint(value) {
return value !== null
&& typeof value === 'object'
&& Number.isInteger(value.port)
&& value.port > 0
&& value.port <= 65535
&& typeof value.instanceId === 'string'
&& value.instanceId.trim().length > 0
&& typeof value.bootstrapToken === 'string'
&& value.bootstrapToken.length >= MIN_TOKEN_LENGTH
&& value.bootstrapToken.length <= MAX_TOKEN_LENGTH;
}
function isValidDesktopRuntime(value, allowLegacyUi = false) {
return isValidEndpoint(value)
&& value.uiReady === true
&& value.uiStartupState === (allowLegacyUi ? 'legacy-ready' : 'ready')
&& typeof value.uiPath === 'string'
&& value.uiPath.startsWith('/')
&& Number.isInteger(value.uiTextLength)
&& value.uiTextLength > 0;
}
async function fetchTargets(port) {
const response = await fetch(`http://127.0.0.1:${port}/json/list`);
if (!response.ok) throw new Error(`WebView debug endpoint returned HTTP ${response.status}`);
const targets = await response.json();
if (!Array.isArray(targets)) throw new Error('WebView debug endpoint returned an invalid target list');
return targets.filter((target) => target?.type === 'page' && typeof target.webSocketDebuggerUrl === 'string');
}
async function evaluateEnsureService(webSocketUrl, timeoutMs, allowLegacyUi) {
const socket = new WebSocket(webSocketUrl);
let nextId = 1;
const pending = new Map();
const timer = setTimeout(() => {
for (const pendingRequest of pending.values()) pendingRequest.reject(new Error('CDP request timed out'));
pending.clear();
socket.close();
}, timeoutMs);
try {
await new Promise((resolve, reject) => {
const openTimer = setTimeout(() => reject(new Error('CDP WebSocket open timed out')), timeoutMs);
socket.addEventListener('open', () => {
clearTimeout(openTimer);
resolve();
}, { once: true });
socket.addEventListener('error', () => {
clearTimeout(openTimer);
reject(new Error('CDP WebSocket connection failed'));
}, { once: true });
});
socket.addEventListener('message', (event) => {
let message;
try {
message = JSON.parse(String(event.data));
} catch {
return;
}
const request = pending.get(message.id);
if (!request) return;
pending.delete(message.id);
if (message.error) request.reject(new Error('Tauri IPC evaluation failed'));
else request.resolve(message.result);
});
const result = await new Promise((resolve, reject) => {
const id = nextId;
nextId += 1;
pending.set(id, { resolve, reject });
socket.send(JSON.stringify({
id,
method: 'Runtime.evaluate',
params: {
awaitPromise: true,
returnByValue: true,
expression: `
(async () => {
const invoke = globalThis.__TAURI_INTERNALS__?.invoke;
if (typeof invoke !== 'function') throw new Error('Tauri IPC is unavailable');
const endpoint = await invoke('ensure_service');
const deadline = Date.now() + 10_000;
for (;;) {
const root = document.getElementById('root');
const startup = root?.querySelector('[data-waggle-startup]');
const startupState = startup?.getAttribute('data-waggle-startup') ?? null;
const readyState = root?.getAttribute('data-waggle-ui-ready') ?? null;
const uiTextLength = root?.innerText?.trim().length ?? 0;
const strictUiReady = Boolean(
root
&& root.childElementCount > 0
&& !startup
&& readyState === 'ready'
&& uiTextLength > 0
);
const legacyUiReady = Boolean(
root
&& root.childElementCount > 0
&& !startup
&& uiTextLength > 0
);
const uiReady = ${allowLegacyUi ? 'legacyUiReady' : 'strictUiReady'};
if (uiReady || startupState === 'failed' || Date.now() >= deadline) {
return {
...endpoint,
uiPath: globalThis.location?.pathname ?? '',
uiReady,
uiStartupState: ${allowLegacyUi ? "uiReady ? 'legacy-ready' : (readyState ?? startupState)" : 'readyState ?? startupState'},
uiTextLength,
};
}
await new Promise((resolve) => setTimeout(resolve, 100));
}
})()
`,
},
}));
});
const remote = result?.result?.value;
if (!isValidEndpoint(remote)) throw new Error('Tauri returned an invalid service endpoint');
if (!isValidDesktopRuntime(remote, allowLegacyUi)) {
throw new Error('Waggle UI did not render its application shell');
}
return {
bootstrapToken: remote.bootstrapToken,
uiPath: remote.uiPath,
uiReady: remote.uiReady,
uiStartupState: remote.uiStartupState,
uiTextLength: remote.uiTextLength,
};
} finally {
clearTimeout(timer);
socket.close();
}
}
async function resolveToken(port, timeoutMs, allowLegacyUi) {
const deadline = Date.now() + timeoutMs;
let lastError = 'WebView target not ready';
while (Date.now() < deadline) {
try {
const targets = await fetchTargets(port);
for (const target of targets) {
try {
return await evaluateEnsureService(
target.webSocketDebuggerUrl,
Math.min(15_000, deadline - Date.now()),
allowLegacyUi,
);
} catch (error) {
lastError = error instanceof Error ? error.message : 'CDP evaluation failed';
}
}
} catch (error) {
lastError = error instanceof Error ? error.message : 'WebView debug endpoint unavailable';
}
await new Promise((resolve) => setTimeout(resolve, 250));
}
throw new Error(lastError);
}
async function main() {
if (process.argv.includes('--self-test')) {
if (typeof WebSocket !== 'function') throw new Error('WebSocket client is unavailable');
const valid = 'a'.repeat(MIN_TOKEN_LENGTH);
if (!isValidEndpoint({ port: 3333, instanceId: 'test-instance', bootstrapToken: valid })) {
throw new Error('valid endpoint fixture rejected');
}
if (isValidEndpoint({ port: 3333, instanceId: 'test-instance', bootstrapToken: 'short' })) {
throw new Error('short token fixture accepted');
}
const validRuntime = {
port: 3333,
instanceId: 'test-instance',
bootstrapToken: valid,
uiPath: '/home',
uiReady: true,
uiStartupState: 'ready',
uiTextLength: 10,
};
if (!isValidDesktopRuntime(validRuntime)) throw new Error('valid desktop runtime rejected');
if (isValidDesktopRuntime({ ...validRuntime, uiReady: false })) {
throw new Error('blank desktop runtime accepted');
}
if (isValidDesktopRuntime({ ...validRuntime, uiTextLength: 0 })) {
throw new Error('empty desktop runtime accepted');
}
if (isValidDesktopRuntime({ ...validRuntime, uiStartupState: 'loading' })) {
throw new Error('loading desktop runtime accepted');
}
if (isValidDesktopRuntime({ ...validRuntime, uiStartupState: 'failed' })) {
throw new Error('failed desktop runtime accepted');
}
if (!isValidDesktopRuntime({ ...validRuntime, uiStartupState: 'legacy-ready' }, true)) {
throw new Error('valid legacy desktop runtime rejected');
}
if (isValidDesktopRuntime({ ...validRuntime, uiReady: false, uiStartupState: 'legacy-ready' }, true)) {
throw new Error('blank legacy desktop runtime accepted');
}
console.log(JSON.stringify({ pass: true, cases: 9 }));
return;
}
const options = parseArgs(process.argv.slice(2));
const runtime = await resolveToken(options.port, options.timeoutMs, options.allowLegacyUi);
console.log(JSON.stringify(runtime));
}
main().catch((error) => fail(error instanceof Error ? error.message : 'unknown failure'));

View File

@@ -0,0 +1,71 @@
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
import { dirname, resolve } from 'node:path';
import {
buildPersonaAcceptanceSeal,
type PersonaAcceptanceSeal,
type PersonaAcceptanceSealManifest,
} from '../tests/vision/persona-acceptance-seal';
function arg(name: string): string | null {
const index = process.argv.indexOf(name);
return index >= 0 ? process.argv[index + 1] ?? null : null;
}
function renderMarkdown(seal: PersonaAcceptanceSeal): string {
const rescored = seal.receipts.filter(receipt => receipt.scoreMode === 'derived-rescore').length;
const rows = seal.receipts.map(receipt => (
`| ${receipt.personaId} | ${receipt.repeat} | ${receipt.score} | ${receipt.scoreMode} | ${receipt.model ?? 'unknown'} | ${receipt.estimatedCostUsd} | ${receipt.sourceRevision.slice(0, 12)} | \`${receipt.artifactSha256.slice(0, 16)}\` |`
));
const diagnosticRows = seal.diagnosticCostLedger.map(entry => (
`| ${entry.id} | ${entry.amountUsd} | ${entry.evidence.replace(/\|/g, '\\|')} |`
));
return [
'# Waggle 10-Persona Paid Acceptance Seal',
'',
`- Status: **${seal.status.toUpperCase()}**`,
`- Receipts: **${seal.completedReceiptCount}/${seal.expectedReceiptCount}**`,
`- Threshold: **${seal.threshold}/100 for every receipt**`,
`- Waggle-estimated accepted-run cost: **$${seal.acceptedEstimatedCostUsd}**`,
`- Operator-recorded diagnostic cost (with evidence note): **$${seal.diagnosticRecordedCostUsd}**`,
`- Total recorded spend: **$${seal.totalRecordedSpendUsd}**`,
`- Manifest SHA-256: \`${seal.manifestSha256}\``,
`- Derived rescoring disclosures: **${rescored}**`,
'',
'| Persona | Repeat | Score | Provenance | Model | Estimated cost USD | Source revision | Artifact SHA-256 |',
'|---|---:|---:|---|---|---:|---|---|',
...rows,
'',
'## Diagnostic Cost Evidence',
'',
'| ID | Cost USD | Evidence |',
'|---|---:|---|',
...(diagnosticRows.length > 0 ? diagnosticRows : ['| None | 0.000000 | No diagnostic spend recorded |']),
'',
'This report is generated only after exact 10 x 3 slot coverage passes live-provider health, transport, browser, persistence, isolation, current-scorer, and provenance checks.',
'',
].join('\n');
}
const manifestArg = arg('--manifest');
const jsonArg = arg('--json');
const markdownArg = arg('--markdown');
if (!manifestArg || !jsonArg || !markdownArg) {
console.error('Usage: npm run persona:seal -- --manifest <manifest.json> --json <seal.json> --markdown <report.md>');
process.exitCode = 2;
} else {
const manifestPath = resolve(manifestArg);
const manifest = JSON.parse(readFileSync(manifestPath, 'utf8')) as PersonaAcceptanceSealManifest;
const seal = buildPersonaAcceptanceSeal(manifest);
if (seal.status !== 'ready') {
console.error(JSON.stringify(seal, null, 2));
process.exitCode = 1;
} else {
const jsonPath = resolve(jsonArg);
const markdownPath = resolve(markdownArg);
mkdirSync(dirname(jsonPath), { recursive: true });
mkdirSync(dirname(markdownPath), { recursive: true });
writeFileSync(jsonPath, `${JSON.stringify(seal, null, 2)}\n`, 'utf8');
writeFileSync(markdownPath, renderMarkdown(seal), 'utf8');
console.log(JSON.stringify({ status: seal.status, jsonPath, markdownPath }, null, 2));
}
}

View File

@@ -0,0 +1,37 @@
import { copyFile, mkdir, readFile, rm, stat, writeFile } from 'node:fs/promises';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
const sourceRoot = path.join(root, 'vendor', 'pptxgenjs');
const agentDist = path.join(root, 'packages', 'agent', 'dist');
const targetRoot = path.join(agentDist, 'vendor', 'pptxgenjs');
const manifest = JSON.parse(await readFile(path.join(sourceRoot, 'package.json'), 'utf8'));
if (manifest.name !== 'pptxgenjs' || manifest.version !== '4.0.1-waggle.0') {
throw new Error(
`Refusing to stage unexpected PPTX runtime ${String(manifest.name)}@${String(manifest.version)}`,
);
}
const builtEntry = path.join(agentDist, 'presentation-tools.js');
if (!(await stat(builtEntry)).isFile()) {
throw new Error(`Agent build output is missing: ${builtEntry}`);
}
const sourceImport = "import PptxGenJS from 'pptxgenjs';";
const stagedImport = "import PptxGenJS from './vendor/pptxgenjs/dist/pptxgen.cjs.js';";
const builtSource = await readFile(builtEntry, 'utf8');
if (!builtSource.includes(sourceImport)) {
throw new Error(`Agent PPTX import is not recognized: ${builtEntry}`);
}
await rm(targetRoot, { recursive: true, force: true });
await mkdir(path.join(targetRoot, 'dist'), { recursive: true });
await copyFile(path.join(sourceRoot, 'LICENSE'), path.join(targetRoot, 'LICENSE'));
await copyFile(path.join(sourceRoot, 'package.json'), path.join(targetRoot, 'package.json'));
await copyFile(
path.join(sourceRoot, 'dist', 'pptxgen.cjs.js'),
path.join(targetRoot, 'dist', 'pptxgen.cjs.js'),
);
await writeFile(builtEntry, builtSource.replace(sourceImport, stagedImport), 'utf8');

View File

@@ -13,10 +13,10 @@
* EXTERNAL list — some of those, e.g. mammoth/sharp, aren't actually
* reached).
* 2. Walk the transitive production-dependency closure of that set from the
* repo's own node_modules and copy each package dir verbatim — preserving
* prebuilt native .node binaries in place (better-sqlite3/build/Release,
* onnxruntime-node/bin) so require('better-sqlite3') both RESOLVES and
* FINDS its binary via the package's own relative loader.
* repo's own node_modules. Third-party packages retain their published
* runtime layout; first-party workspaces are reduced to dist, manifest,
* and license notices so source/build artifacts never enter the installer.
* Prebuilt native binaries stay in their package-relative locations.
*
* Run after build-sidecar.mjs, before `tauri build`. Arch-parameterized: honors
* TARGET_ARCH (like bundle-native-deps.mjs) to prune onnxruntime-node's
@@ -39,6 +39,7 @@ const resourcesDir = path.join(root, 'app', 'src-tauri', 'resources');
// Must match the metafile path written by build-sidecar.mjs (temp, not repo).
const metaFile = path.join(os.tmpdir(), 'waggle-sidecar-meta.json');
const stageDir = path.join(resourcesDir, 'node_modules');
const bundledNpmRuntimeDir = path.join(stageDir, 'waggle-node-runtime');
const hookRuntimeBuild = path.join(root, 'scripts', 'build-hook-runtime.mjs');
const HOOK_RUNTIME_ROOTS = new Set([
'@waggle/hive-mind-cli',
@@ -49,7 +50,11 @@ const HOOK_RUNTIME_ROOTS = new Set([
'@waggle/hive-mind-hooks-cursor',
'@waggle/hive-mind-hooks-hermes',
'@waggle/hive-mind-hooks-openclaw',
'waggle-memory-mcp',
]);
// These are loaded through computed require() calls, so esbuild's metafile
// cannot discover them even though installed desktop features require them.
const DYNAMIC_RUNTIME_ROOTS = new Set(['adm-zip']);
const platform = process.platform;
const arch = process.env.TARGET_ARCH || process.arch;
@@ -102,6 +107,11 @@ const RUNTIME_PRUNED_DIR_NAMES = new Set([
'test',
'tests',
]);
const SOURCE_ARTIFACT_PATTERN = /(?:\.map|\.(?:[cm]?ts|tsx)|\.tsbuildinfo)$/i;
const WORKSPACE_RUNTIME_ENTRY_PATTERN = /^(?:dist|package\.json|licen[cs]e(?:\.(?:md|txt))?|notice(?:\.(?:md|txt))?)$/i;
const MANUAL_WORKSPACE_RUNTIME_TARGETS = new Map([
['@waggle/hive-mind-hooks-openclaw', ['dist/handler.bundle.cjs']],
]);
const WINDOWS_1252_EXTRA_CODEPOINTS = new Set([
0x20ac, 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021, 0x02c6, 0x2030,
0x0160, 0x2039, 0x0152, 0x017d, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022,
@@ -160,12 +170,19 @@ function resolvePkgDir(name, fromDir) {
let copiedPackages = 0;
let prunedRuntimeDirs = 0;
let prunedRuntimeFiles = 0;
let strippedSourceMapDirectives = 0;
const stagedWorkspaceNames = new Set();
function isWorkspacePackageDir(pkgDir) {
const realDir = fs.realpathSync.native(pkgDir);
return [path.join(root, 'packages'), path.join(root, 'apps')].some((workspaceRoot) => {
const relative = path.relative(workspaceRoot, realDir);
return relative !== '' && !relative.startsWith(`..${path.sep}`) && relative !== '..' && !path.isAbsolute(relative);
return relative !== ''
&& !relative.startsWith(`..${path.sep}`)
&& relative !== '..'
&& !path.isAbsolute(relative)
&& !relative.includes(path.sep);
});
}
@@ -174,14 +191,39 @@ function copyPackage(srcDir, name) {
const destDir = path.join(stageDir, name);
if (fs.existsSync(destDir)) return; // already staged (dedup by flat name)
fs.mkdirSync(path.dirname(destDir), { recursive: true });
const copyOptions = { recursive: true, dereference: true };
if (isWorkspacePackageDir(srcDir)) {
// npm does not publish a workspace package's local node_modules. Copying
// it from a dereferenced workspace symlink would leak dev-only packages;
// production dependencies are staged separately from the manifest below.
copyOptions.filter = (source) => path.basename(source) !== 'node_modules';
stagedWorkspaceNames.add(name);
const entries = fs.readdirSync(srcDir)
.filter((entry) => WORKSPACE_RUNTIME_ENTRY_PATTERN.test(entry));
for (const required of ['package.json', 'dist']) {
if (!entries.includes(required)) {
throw new Error(`Workspace package ${name} has no ${required} runtime payload`);
}
}
fs.mkdirSync(destDir, { recursive: true });
for (const entry of entries) {
if (entry === 'package.json') {
const runtimeManifest = readManifest(srcDir);
delete runtimeManifest.devDependencies;
delete runtimeManifest.files;
delete runtimeManifest.scripts;
delete runtimeManifest.types;
delete runtimeManifest.typings;
fs.writeFileSync(
path.join(destDir, entry),
`${JSON.stringify(runtimeManifest, null, 2)}\n`,
'utf8',
);
continue;
}
fs.cpSync(path.join(srcDir, entry), path.join(destDir, entry), {
recursive: true,
dereference: true,
});
}
} else {
fs.cpSync(srcDir, destDir, { recursive: true, dereference: true });
}
fs.cpSync(srcDir, destDir, copyOptions);
copiedPackages++;
}
@@ -320,6 +362,33 @@ function pruneRuntimeOnlyDirs(dir) {
}
}
/** Remove first-party source/build artifacts and dangling source-map directives. */
function pruneFirstPartyBuildArtifacts(dir) {
if (!fs.existsSync(dir)) return;
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
const full = path.join(dir, entry.name);
if (entry.isDirectory()) {
pruneFirstPartyBuildArtifacts(full);
continue;
}
if (entry.isFile() && SOURCE_ARTIFACT_PATTERN.test(entry.name)) {
fs.rmSync(full, { force: true });
prunedRuntimeFiles++;
continue;
}
if (entry.isFile() && /\.(?:[cm]?js)$/i.test(entry.name)) {
const source = fs.readFileSync(full, 'utf8');
const runtimeOnly = source
.replace(/^[ \t]*\/\/[#@]\s*sourceMappingURL\s*=.*(?:\r?\n|$)/gm, '')
.replace(/\/\*[#@]\s*sourceMappingURL\s*=.*?\*\//gs, '');
if (runtimeOnly !== source) {
fs.writeFileSync(full, runtimeOnly, 'utf8');
strippedSourceMapDirectives++;
}
}
}
}
function isWindows1252PathSafe(value) {
for (const char of value) {
const code = char.codePointAt(0) || 0;
@@ -344,6 +413,104 @@ function listFiles(dir) {
return files;
}
function collectRuntimeExportTargets(value, targets, condition = '') {
if (condition === 'types') return;
if (typeof value === 'string') {
targets.add(value);
return;
}
if (Array.isArray(value)) {
for (const entry of value) collectRuntimeExportTargets(entry, targets, condition);
return;
}
if (!value || typeof value !== 'object') return;
for (const [key, entry] of Object.entries(value)) {
collectRuntimeExportTargets(entry, targets, key);
}
}
function workspaceRuntimeTargets(manifest, packageName) {
const targets = new Set();
if (typeof manifest.main === 'string') targets.add(manifest.main);
if (typeof manifest.module === 'string') targets.add(manifest.module);
if (typeof manifest.bin === 'string') targets.add(manifest.bin);
else if (manifest.bin && typeof manifest.bin === 'object') {
for (const entry of Object.values(manifest.bin)) {
if (typeof entry === 'string') targets.add(entry);
}
}
collectRuntimeExportTargets(manifest.exports, targets);
for (const entry of MANUAL_WORKSPACE_RUNTIME_TARGETS.get(packageName) || []) {
targets.add(entry);
}
return targets;
}
function validateWorkspaceRuntimeTargets(name, packageDir) {
const failures = [];
const manifest = readManifest(packageDir);
const distDir = path.resolve(packageDir, 'dist');
const realPackageDir = fs.realpathSync.native(packageDir);
const realDistDir = fs.realpathSync.native(distDir);
const realDistWithinPackage = realDistDir.startsWith(`${realPackageDir}${path.sep}`);
for (const target of workspaceRuntimeTargets(manifest, name)) {
const relative = target.replace(/^\.\//, '').split('/').join(path.sep);
const resolved = path.resolve(packageDir, relative);
const withinDist = resolved.startsWith(`${distDir}${path.sep}`);
let regularRuntimeFile = false;
let realWithinDist = false;
if (withinDist && fs.existsSync(resolved)) {
const stat = fs.lstatSync(resolved);
regularRuntimeFile = stat.isFile() && !stat.isSymbolicLink();
if (regularRuntimeFile) {
const realTarget = fs.realpathSync.native(resolved);
realWithinDist = realTarget.startsWith(`${realDistDir}${path.sep}`);
}
}
if (
!withinDist
|| !realDistWithinPackage
|| !realWithinDist
|| !regularRuntimeFile
|| target.includes('*')
) {
failures.push(`${name} -> ${target}`);
}
}
return failures;
}
function assertWorkspaceRuntimeOnly() {
const unexpected = [];
for (const name of stagedWorkspaceNames) {
const packageDir = path.join(stageDir, name);
for (const entry of fs.readdirSync(packageDir, { withFileTypes: true })) {
if (!WORKSPACE_RUNTIME_ENTRY_PATTERN.test(entry.name)) {
unexpected.push(`${name}/${entry.name}`);
}
}
for (const file of listFiles(packageDir)) {
if (SOURCE_ARTIFACT_PATTERN.test(file)) {
unexpected.push(path.relative(stageDir, file).split(path.sep).join('/'));
}
if (
/\.(?:[cm]?js)$/i.test(file)
&& /(?:\/\/|\/\*)[#@]\s*sourceMappingURL\s*=/.test(fs.readFileSync(file, 'utf8'))
) {
unexpected.push(`${path.relative(stageDir, file).split(path.sep).join('/')} -> sourceMappingURL`);
}
}
unexpected.push(...validateWorkspaceRuntimeTargets(name, packageDir));
}
if (unexpected.length === 0) return;
console.error(
'[stage-sidecar-deps] FATAL - first-party runtime payload contains source/build artifacts:\n'
+ unexpected.map((entry) => ` - ${entry}`).join('\n'),
);
process.exit(1);
}
function listPackageDirs(nodeModulesDir) {
if (!fs.existsSync(nodeModulesDir)) return [];
const packageDirs = [];
@@ -426,6 +593,32 @@ function dirSizeMB(dir) {
return (bytes / 1024 / 1024).toFixed(1);
}
function preserveBundledNpmRuntime() {
const required = [
path.join(bundledNpmRuntimeDir, 'package.json'),
path.join(bundledNpmRuntimeDir, 'NODE-LICENSE'),
path.join(bundledNpmRuntimeDir, 'node_modules', 'npm', 'LICENSE'),
path.join(bundledNpmRuntimeDir, 'node_modules', 'npm', 'bin', 'npm-cli.js'),
path.join(bundledNpmRuntimeDir, 'node_modules', 'npm', 'bin', 'npx-cli.js'),
path.join(bundledNpmRuntimeDir, 'bin', platform === 'win32' ? 'npm.cmd' : 'npm'),
path.join(bundledNpmRuntimeDir, 'bin', platform === 'win32' ? 'npx.cmd' : 'npx'),
];
const missing = required.filter((file) => !fs.existsSync(file));
if (missing.length > 0) {
console.error(
'[stage-sidecar-deps] FATAL - bundled npm runtime is incomplete:\n'
+ missing.map((file) => ` - ${path.relative(resourcesDir, file)}`).join('\n')
+ '\n Run `node scripts/bundle-node.mjs` before staging sidecar dependencies.',
);
process.exit(1);
}
const temporaryRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'waggle-node-runtime-'));
const snapshot = path.join(temporaryRoot, 'waggle-node-runtime');
fs.cpSync(bundledNpmRuntimeDir, snapshot, { recursive: true, dereference: true });
return { snapshot, temporaryRoot };
}
// ── main ───────────────────────────────────────────────────────────
console.log(`[stage-sidecar-deps] Platform: ${platform}-${arch}`);
@@ -435,24 +628,57 @@ console.log(`[stage-sidecar-deps] Platform: ${platform}-${arch}`);
execFileSync(process.execPath, [hookRuntimeBuild], { cwd: root, stdio: 'inherit' });
// Fresh stage dir each run so a removed dep never lingers in a stale bundle.
fs.rmSync(stageDir, { recursive: true, force: true });
fs.mkdirSync(stageDir, { recursive: true });
const preservedNpmRuntime = preserveBundledNpmRuntime();
try {
fs.rmSync(stageDir, { recursive: true, force: true });
fs.mkdirSync(stageDir, { recursive: true });
fs.cpSync(preservedNpmRuntime.snapshot, bundledNpmRuntimeDir, {
recursive: true,
dereference: true,
});
} finally {
fs.rmSync(preservedNpmRuntime.temporaryRoot, { recursive: true, force: true });
}
const externals = readExternalPackages();
const runtimeRoots = new Set([...externals, ...HOOK_RUNTIME_ROOTS]);
const runtimeRoots = new Set([
...externals,
...HOOK_RUNTIME_ROOTS,
...DYNAMIC_RUNTIME_ROOTS,
]);
const staged = [...runtimeRoots].filter((n) => !SKIP.has(n)).sort();
const skipped = [...externals].filter((n) => SKIP.has(n)).sort();
console.log(`[stage-sidecar-deps] Bundle/runtime roots: ${runtimeRoots.size} (${staged.length} to stage, ${skipped.length} skipped)`);
if (skipped.length) console.log(`[stage-sidecar-deps] skipped: ${skipped.join(', ')}`);
const closure = stageClosure(runtimeRoots);
const archiveParser = readManifest(path.join(stageDir, 'adm-zip'));
const [archiveParserMajor, archiveParserMinor] = String(archiveParser.version || '')
.split('.')
.map(Number);
if (!(archiveParserMajor > 0 || archiveParserMinor >= 6)) {
console.error(
'[stage-sidecar-deps] FATAL - adm-zip runtime root must be version 0.6.0 or newer',
);
process.exit(1);
}
pruneOnnxRuntime();
pruneSkipListed(stageDir);
pruneRuntimeOnlyDirs(stageDir);
for (const name of stagedWorkspaceNames) {
pruneFirstPartyBuildArtifacts(path.join(stageDir, name));
}
if (prunedRuntimeDirs > 0) {
console.log(`[stage-sidecar-deps] Pruned ${prunedRuntimeDirs} runtime-unused package artifact dir(s)`);
}
if (prunedRuntimeFiles > 0) {
console.log(`[stage-sidecar-deps] Pruned ${prunedRuntimeFiles} first-party source/build artifact file(s)`);
}
if (strippedSourceMapDirectives > 0) {
console.log(`[stage-sidecar-deps] Stripped ${strippedSourceMapDirectives} first-party source-map directive(s)`);
}
assertWorkspaceRuntimeOnly();
assertWindowsMsiSafeResourcePaths();
assertStagedNodeModulesSelfContained();

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff