mirror of
https://github.com/Orkas-AI/Orkas.git
synced 2026-11-07 06:16:03 -10:00
Preserve keyboard focus and single activation across task-board refreshes. Add task-scoped grants, partial-permission recovery, validated CLI attachments, device-local project directories, and the public model catalog update.
Prompt confirmation:
Confirmed-by: Requester in the current implementation task
Scope: Apply the existing approved shared agent, skill, task, and connector
capabilities within the documented open-source product boundaries.
Prompt audit:
- Keep: Commander and actor task identity, ordered execution, user input,
project context, and tool-use responsibilities in the shared prompt
templates and their existing assembly path. Type: resident.
Why: These clauses determine behavior across ordinary turns; the update
preserves the shared product's source-of-truth context and recovery
ownership without adding an independent open-source policy layer.
- Keep: Project-task, scheduling, memory, connector setup, UI artifact,
image/video composition, Office, and SEO workflows in their owning
skills and referenced guides. Type: on-demand.
Why: Load specialized procedures only for the corresponding work; retain
source metadata and dependency ownership rather than duplicate those
procedures in every actor prompt.
- Keep: Scoped tool schemas, queue/permission state, persisted recovery
evidence, output storage, resource manifests, and deterministic validators
at their existing host boundaries. Type: non-prompt enforcement.
Why: Code and structured events own the invariants and prevent the prompt
from becoming a second authorization, persistence, or execution engine.
- Drop: Hosted account, membership, cloud/relay, telemetry, internal debug,
managed voice, watermark, welcome, and official updater instructions or
affordances from the public execution paths. Type: source-only/on-demand.
Why: Those paths require services absent from the open-source product;
shared local operations and public API-key/BYO capabilities remain usable.
Validation: Recorded 11,721 deterministic tests, 538 resource tests, 1,248 native tests, five GUI checks and 198 reconciled desktop scenarios; final keyboard owner regressions also passed. Initial failed runs and classified warnings are retained in validation evidence.
273 lines
9.8 KiB
JavaScript
273 lines
9.8 KiB
JavaScript
'use strict';
|
|
|
|
const { spawnSync } = require('node:child_process');
|
|
const fs = require('node:fs');
|
|
const os = require('node:os');
|
|
const path = require('node:path');
|
|
const { sharpPreparationFiles } = require('../bin/native-package-gate.cjs');
|
|
const {
|
|
ensureFile,
|
|
isPeX64,
|
|
markerMatches,
|
|
packagePath,
|
|
packageVersion,
|
|
readElectronVersion,
|
|
readLockPackage,
|
|
scriptHashes,
|
|
writeMarker,
|
|
} = require('./native-prepare-cache.cjs');
|
|
|
|
const PC_DIR = path.resolve(__dirname, '..');
|
|
const LOCK_FILE = path.join(PC_DIR, 'package-lock.json');
|
|
const CACHE_HELPER = path.join(__dirname, 'native-prepare-cache.cjs');
|
|
const TARGET_PLATFORM = 'win32';
|
|
const TARGET_ARCH = 'x64';
|
|
const WINDOWS_RM_OPTIONS = Object.freeze({
|
|
recursive: true,
|
|
force: true,
|
|
maxRetries: 6,
|
|
retryDelay: 100,
|
|
});
|
|
|
|
function npmCmd(platform = process.platform) {
|
|
return platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
}
|
|
|
|
function removeTree(target, fsImpl = fs) {
|
|
fsImpl.rmSync(target, WINDOWS_RM_OPTIONS);
|
|
}
|
|
|
|
function run(cwd, command, args) {
|
|
const result = spawnSync(command, args, {
|
|
cwd,
|
|
stdio: 'inherit',
|
|
shell: false,
|
|
windowsHide: true,
|
|
});
|
|
if (result.error) throw result.error;
|
|
if (result.status !== 0) {
|
|
throw new Error(`${command} ${args.join(' ')} failed with exit code ${result.status}`);
|
|
}
|
|
}
|
|
|
|
function npmPack(cwd, spec) {
|
|
const result = spawnSync(npmCmd(), ['pack', spec, '--json'], {
|
|
cwd,
|
|
encoding: 'utf8',
|
|
stdio: ['ignore', 'pipe', 'inherit'],
|
|
shell: process.platform === 'win32',
|
|
windowsHide: true,
|
|
});
|
|
if (result.error) throw result.error;
|
|
if (result.status !== 0) {
|
|
throw new Error(`npm pack ${spec} failed with exit code ${result.status}`);
|
|
}
|
|
const packs = JSON.parse(result.stdout);
|
|
const filename = packs?.[0]?.filename;
|
|
if (!filename) {
|
|
throw new Error(`npm pack ${spec} did not report an output filename`);
|
|
}
|
|
return path.join(cwd, filename);
|
|
}
|
|
|
|
function allFilesExist(files) {
|
|
return files.every((file) => fs.existsSync(file) && fs.statSync(file).isFile());
|
|
}
|
|
|
|
function extractTarballInTarget(targetDir, tarball, deps = {}) {
|
|
const fsImpl = deps.fsImpl || fs;
|
|
const runImpl = deps.runImpl || run;
|
|
// Cross-builds use host-native paths even though the extracted package is a
|
|
// Windows target. Keep Windows-shaped paths testable from a non-Windows host.
|
|
const pathImpl = path.win32.isAbsolute(targetDir) && !path.posix.isAbsolute(targetDir)
|
|
? path.win32
|
|
: path.posix.isAbsolute(targetDir) ? path.posix : path;
|
|
const archiveName = pathImpl.basename(tarball);
|
|
const localTarball = pathImpl.join(targetDir, archiveName);
|
|
fsImpl.copyFileSync(tarball, localTarball);
|
|
try {
|
|
// GNU tar treats a Windows absolute archive path (`C:\...`) as a remote
|
|
// host specification when release packaging runs under Git Bash/MSYS.
|
|
runImpl(targetDir, 'tar', ['-xzf', archiveName, '--strip-components=1']);
|
|
} finally {
|
|
fsImpl.rmSync(localTarball, { force: true });
|
|
}
|
|
}
|
|
|
|
function ensurePackageFromRegistry(packageName, requiredFiles = []) {
|
|
const version = readLockPackage(LOCK_FILE, packageName);
|
|
const targetDir = packagePath(PC_DIR, packageName);
|
|
|
|
if (packageVersion(PC_DIR, packageName) === version && allFilesExist(requiredFiles)) {
|
|
console.log(`[prepare-win-native-deps] reusing ${packageName}@${version}`);
|
|
return;
|
|
}
|
|
|
|
console.log(`[prepare-win-native-deps] ensuring ${packageName}@${version}`);
|
|
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'orkas-win-native-deps-'));
|
|
try {
|
|
const tarball = npmPack(tmpDir, `${packageName}@${version}`);
|
|
removeTree(targetDir);
|
|
fs.mkdirSync(targetDir, { recursive: true });
|
|
extractTarballInTarget(targetDir, tarball);
|
|
} finally {
|
|
removeTree(tmpDir);
|
|
}
|
|
}
|
|
|
|
function expectedState(electronVersion) {
|
|
return {
|
|
schema: 1,
|
|
platform: TARGET_PLATFORM,
|
|
arch: TARGET_ARCH,
|
|
electronVersion,
|
|
scriptHashes: scriptHashes({
|
|
prepareWinNativeDeps: __filename,
|
|
nativePrepareCache: CACHE_HELPER,
|
|
nativePackageGate: require.resolve('../bin/native-package-gate.cjs'),
|
|
}),
|
|
packages: {
|
|
'@esbuild/win32-x64': readLockPackage(LOCK_FILE, '@esbuild/win32-x64'),
|
|
'sqlite-vec-windows-x64': readLockPackage(LOCK_FILE, 'sqlite-vec-windows-x64'),
|
|
'@napi-rs/canvas-win32-x64-msvc': readLockPackage(LOCK_FILE, '@napi-rs/canvas-win32-x64-msvc'),
|
|
'@anush008/tokenizers-win32-x64-msvc': readLockPackage(LOCK_FILE, '@anush008/tokenizers-win32-x64-msvc'),
|
|
'better-sqlite3': readLockPackage(LOCK_FILE, 'better-sqlite3'),
|
|
'@img/sharp-win32-x64': readLockPackage(LOCK_FILE, '@img/sharp-win32-x64'),
|
|
},
|
|
};
|
|
}
|
|
|
|
function removeLegacyMarker() {
|
|
removeTree(path.join(PC_DIR, 'node_modules', '.orkas-native-prepared'));
|
|
}
|
|
|
|
function removeDirectories(parentDir, shouldRemove, fsImpl = fs) {
|
|
if (!fsImpl.existsSync(parentDir) || !fsImpl.statSync(parentDir).isDirectory()) {
|
|
return;
|
|
}
|
|
for (const entry of fsImpl.readdirSync(parentDir, { withFileTypes: true })) {
|
|
if (entry.isDirectory() && shouldRemove(entry.name)) {
|
|
removeTree(path.join(parentDir, entry.name), fsImpl);
|
|
}
|
|
}
|
|
}
|
|
|
|
function main() {
|
|
const electronVersion = readElectronVersion(PC_DIR);
|
|
const state = expectedState(electronVersion);
|
|
const sharpVersion = state.packages['@img/sharp-win32-x64'];
|
|
const sharpFiles = sharpPreparationFiles('win32', 'x64', sharpVersion);
|
|
const sqliteVecDir = path.join(PC_DIR, 'node_modules', 'sqlite-vec');
|
|
const betterSqliteDir = path.join(PC_DIR, 'node_modules', 'better-sqlite3');
|
|
const betterSqliteBinary = path.join(betterSqliteDir, 'build', 'Release', 'better_sqlite3.node');
|
|
const required = {
|
|
esbuild: path.join(PC_DIR, 'node_modules', '@esbuild', 'win32-x64', 'esbuild.exe'),
|
|
sqliteVec: path.join(PC_DIR, 'node_modules', 'sqlite-vec-windows-x64', 'vec0.dll'),
|
|
betterSqlite: betterSqliteBinary,
|
|
canvas: path.join(PC_DIR, 'node_modules', '@napi-rs', 'canvas-win32-x64-msvc', 'skia.win32-x64-msvc.node'),
|
|
tokenizers: path.join(PC_DIR, 'node_modules', '@anush008', 'tokenizers-win32-x64-msvc', 'tokenizers.win32-x64-msvc.node'),
|
|
sharp: path.join(PC_DIR, 'node_modules', sharpFiles.binding),
|
|
sharpVipsCpp: path.join(PC_DIR, 'node_modules', sharpFiles.cpp),
|
|
sharpVips: path.join(PC_DIR, 'node_modules', sharpFiles.libvips),
|
|
};
|
|
|
|
if (!fs.existsSync(sqliteVecDir) || !fs.statSync(sqliteVecDir).isDirectory()) {
|
|
throw new Error(`sqlite-vec is not installed: ${sqliteVecDir}`);
|
|
}
|
|
if (!fs.existsSync(betterSqliteDir) || !fs.statSync(betterSqliteDir).isDirectory()) {
|
|
throw new Error(`better-sqlite3 is not installed: ${betterSqliteDir}`);
|
|
}
|
|
|
|
const prepared = markerMatches(PC_DIR, state, Object.values(required), () => [
|
|
betterSqliteBinary,
|
|
required.sharp,
|
|
required.sharpVipsCpp,
|
|
required.sharpVips,
|
|
].every(isPeX64));
|
|
removeLegacyMarker();
|
|
if (prepared) {
|
|
console.log(`[prepare-win-native-deps] using cached Windows native dependencies for Electron ${electronVersion}`);
|
|
return;
|
|
}
|
|
|
|
ensurePackageFromRegistry('@esbuild/win32-x64', [required.esbuild]);
|
|
|
|
ensurePackageFromRegistry('sqlite-vec-windows-x64', [required.sqliteVec]);
|
|
ensurePackageFromRegistry('@napi-rs/canvas-win32-x64-msvc', [required.canvas]);
|
|
ensurePackageFromRegistry('@anush008/tokenizers-win32-x64-msvc', [required.tokenizers]);
|
|
ensurePackageFromRegistry('@img/sharp-win32-x64', [required.sharp, required.sharpVipsCpp, required.sharpVips]);
|
|
|
|
if (packageVersion(PC_DIR, 'better-sqlite3') === state.packages['better-sqlite3'] && isPeX64(betterSqliteBinary)) {
|
|
console.log(`[prepare-win-native-deps] reusing better-sqlite3 Electron ${electronVersion} win32-x64 prebuild`);
|
|
} else {
|
|
console.log(`[prepare-win-native-deps] ensuring better-sqlite3 Electron ${electronVersion} win32-x64 prebuild`);
|
|
run(betterSqliteDir, process.execPath, [
|
|
require.resolve('prebuild-install/bin.js'),
|
|
'--runtime',
|
|
'electron',
|
|
'--target',
|
|
electronVersion,
|
|
'--platform',
|
|
TARGET_PLATFORM,
|
|
'--arch',
|
|
TARGET_ARCH,
|
|
'--force',
|
|
]);
|
|
}
|
|
|
|
console.log('[prepare-win-native-deps] pruning non-Windows native runtime packages');
|
|
removeDirectories(path.join(PC_DIR, 'node_modules', '@esbuild'), (name) => name !== 'win32-x64');
|
|
removeDirectories(path.join(PC_DIR, 'node_modules'), (name) => /^sqlite-vec-(darwin|linux)-/i.test(name));
|
|
removeDirectories(path.join(PC_DIR, 'node_modules', '@napi-rs'), (name) => /^canvas-/i.test(name) && name !== 'canvas-win32-x64-msvc');
|
|
removeDirectories(path.join(PC_DIR, 'node_modules', '@anush008'), (name) => /^tokenizers-/i.test(name) && name !== 'tokenizers-win32-x64-msvc');
|
|
removeDirectories(path.join(PC_DIR, 'node_modules', '@img'), (name) => (
|
|
(/^sharp-(darwin|linux|win32)-/i.test(name) && name !== 'sharp-win32-x64')
|
|
|| /^sharp-libvips-/i.test(name)
|
|
));
|
|
removeTree(path.join(PC_DIR, 'node_modules', 'fsevents'));
|
|
|
|
ensureFile(
|
|
'Windows esbuild runtime binary',
|
|
required.esbuild,
|
|
);
|
|
ensureFile(
|
|
'Windows sqlite-vec runtime binary',
|
|
required.sqliteVec,
|
|
);
|
|
ensureFile(
|
|
'Windows better-sqlite3 runtime binary',
|
|
required.betterSqlite,
|
|
);
|
|
if (!isPeX64(required.betterSqlite)) {
|
|
throw new Error(`Windows better-sqlite3 runtime binary is not PE x64: ${required.betterSqlite}`);
|
|
}
|
|
ensureFile(
|
|
'Windows canvas runtime binary',
|
|
required.canvas,
|
|
);
|
|
ensureFile(
|
|
'Windows tokenizers runtime binary',
|
|
required.tokenizers,
|
|
);
|
|
for (const [label, file] of [
|
|
['Windows sharp runtime binding', required.sharp],
|
|
['Windows sharp libvips C++ runtime', required.sharpVipsCpp],
|
|
['Windows sharp libvips runtime', required.sharpVips],
|
|
]) {
|
|
ensureFile(label, file);
|
|
if (!isPeX64(file)) throw new Error(`${label} is not PE x64: ${file}`);
|
|
}
|
|
writeMarker(PC_DIR, state);
|
|
}
|
|
|
|
module.exports = {
|
|
WINDOWS_RM_OPTIONS,
|
|
allFilesExist,
|
|
extractTarballInTarget,
|
|
npmCmd,
|
|
removeDirectories,
|
|
removeTree,
|
|
};
|
|
|
|
if (require.main === module) main();
|