omnitool_on_hf / setup /launcher.js
manu-sapiens's picture
copy of omnitool_latest - should be working
b39afbe
raw
history blame
10.1 kB
/**
* Copyright (c) 2023 MERCENARIES.AI PTE. LTD.
* All rights reserved.
*/
const { fork, spawn, exec, execSync } = require('node:child_process');
const os = require('node:os');
const path = require('node:path');
const { assert } = require('console');
const { ensure } = require('./pocketdbutils.js');
const { sleep, checkGitForUpdates, omniCwd } = require('./utils.js');
const { ensureDirSync } = require('fs-extra');
const { update_build } = require('./updater.js');
const readline = require('node:readline');
const fs = require('node:fs');
const { copyFileSync, existsSync } = require('node:fs');
let args = process.argv.slice(2);
let server_entry = null;
const packagejson = JSON.parse(fs.readFileSync(path.join(omniCwd(), 'package.json'), { encoding: 'utf-8' }));
const { argv0 } = require('node:process');
const depjson = packagejson.dependenciesBin;
const platform = os.platform();
const arch = os.arch();
const IPC_CMD = 'cmd';
const IPC_CMD_restart = 'restart';
// move out of /setup/launcher.js where the mercs.yaml file exists
const project_abs_root = omniCwd();
const pocketbaseInstallPath = path.join(project_abs_root, depjson.root_dir, 'pocketbase');
let server_process = null;
let pretty_process_pid = [];
let child_processes = new Map();
function start_server(implicit_args) {
assert(server_process === null);
assert(server_entry !== null);
// IMPORTANT: omnitool need to run from /mercs as root and launcher in setup/launcher.js
const server_wd = path.join(project_abs_root, 'packages/omni-server');
// derive the server entry file using absolute paths from launcher.js
// this ensures we can also resolve whether clicking directy on .exe or launching from terminal
const entrypath = path.join(server_wd, server_entry);
console.log(`Server entry point resolved to ${entrypath}`);
server_process = fork(entrypath, implicit_args.concat(args), { cwd: server_wd, execArgv: ['--inspect'] });
pretty_process_pid.push(`|--OMNITOOL - Server PID ${server_process.pid}`);
child_processes.set(server_process.pid, server_process);
server_process.on('exit', (code) => {
console.log(`Server exited with code ${code}`);
process.exit(1);
});
server_process.on('error', (err) => {
console.error(err);
process.abort();
});
server_process.on('message', (message) => {
switch (message[IPC_CMD]) {
case IPC_CMD_restart:
console.log('Server Restarting...');
server_process.removeAllListeners('exit');
server_process.kill();
child_processes.delete(server_process.pid);
server_process = null;
// no need to re-open browser on restarts
implicit_args = implicit_args.filter((e) => e !== '--openBrowser');
args = args.filter((e) => e !== '--openBrowser');
start_server(implicit_args);
break;
}
});
}
function verifyConfig() {
// check if dep version is defined
if (packagejson.engines['pocketbase'] === undefined) {
console.error(`Missing version definition for pocketbase DB in package.json engines`);
return false;
}
// check if dep is defined
if (depjson['pocketbase'] === undefined) {
console.error(`Unable to find installer definition for pocketbase DB`);
return false;
}
// check if there's an installer for the platform
try {
let zippath = depjson['pocketbase'].zipfile[platform][arch];
if (zippath === undefined) {
console.error(`Unable to find installer download URL for pocketbase DB`);
return false;
}
} catch (e) {
console.error(`Unable to find pocketbase installer definition for [${platform}][${arch}]`);
return false;
}
return true;
}
function start_pocketbase() {
const pocketDBProcess = spawn(path.join(pocketbaseInstallPath, 'pocketbase'), ['serve'], { stdio: 'inherit' });
setup_listeners(pocketDBProcess, 'pocketbase');
pretty_process_pid.push(`|--OMNITOOL - PocketBaseDB PID ${pocketDBProcess.pid}`);
child_processes.set(pocketDBProcess.pid, pocketDBProcess);
}
function start_vite() {
const viteProcess = exec('yarn frontend');
viteProcess.stderr.pipe(process.stderr);
viteProcess.stdout.pipe(process.stdout);
setup_listeners(viteProcess, 'vite debugger');
pretty_process_pid.push(`|--OMNITOOL - Vite PID ${viteProcess.pid}`);
child_processes.set(viteProcess.pid, viteProcess);
}
async function user_confirmation(prompt) {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
return new Promise((resolve) =>
rl.question(prompt, (ans) => {
rl.close();
resolve(ans);
})
);
}
function setup_listeners(proc, name) {
proc.on('uncaughtException', (err) => {
console.err(err);
user_confirmation('Oops something went wrong. Press any key to exit...').then(() => process.exit(1));
});
// terminate parent for whatever reason
proc.on('exit', (code) => {
console.log(`${name} ${proc.pid} exited with code ${code}`);
process.exit(1);
});
}
async function check_for_updates() {
console.log('Checking for updates...');
const result = await checkGitForUpdates();
if (result.hasUpdates) {
const input = await user_confirmation(
`You are currently on ${result.local}.\nThere's a new version ${result.remote} available. Would you like to pull and update with git? [y/n]:`
);
if (input === 'y') {
try {
console.log('\nCalling git pull...');
execSync('git pull', { cwd: omniCwd(), stdio: 'inherit' });
console.log('Calling yarn install...');
execSync('yarn', { cwd: omniCwd(), stdio: 'inherit' });
console.log('\nUpdate complete. Please re-run the application with: yarn start');
process.exit(0);
}
catch(e) {
console.error(e);
console.warn(`Update failed. Please try running [yarn start] again.`);
process.exit(1);
}
} else {
console.log(`Continuing with current build ${result.local}...`);
}
}
else{
console.log(`You are currently on the latest version available ${result.local}.`);
}
}
async function run_development(server_args) {
const pocketready = await ensure(pocketbaseInstallPath);
if (!pocketready) {
console.error('Fatal error setting up PocketBase DB');
process.exit(1);
}
start_vite();
await sleep(1000);
start_pocketbase();
await sleep(1000);
start_server(server_args);
log_processes();
}
async function run_production(server_args) {
const pocketready = await ensure(pocketbaseInstallPath);
if (!pocketready) {
console.error('Fatal error setting up PocketBase DB');
process.exit(1);
}
start_pocketbase();
await sleep(1000);
start_server(server_args);
log_processes();
}
async function run_runtime_executable() {
const didupdate = await update_build();
if (didupdate) {
console.log('Omnitool has been patched with an update and needed to close.');
console.log('Please re-run the application!');
console.log('Press any key to exit...');
process.stdin.setRawMode(true);
process.stdin.resume();
process.stdin.on('data', process.exit.bind(process, 0));
} else {
await run_production(['-l', '127.0.0.1', '--openBrowser']);
}
}
function log_processes() {
pretty_process_pid.forEach((e) => console.log(e));
}
async function ensure_wasm() {
const { NodeProcessEnv } = await import('omni-shared');
// --- Copy wasm models
let wasmDir = null;
switch (process.env.NODE_ENV) {
case NodeProcessEnv.development:
wasmDir = path.join(omniCwd(), 'packages/omni-server', 'config.local', 'wasm');
break;
case NodeProcessEnv.production:
wasmDir = path.join(omniCwd(), 'packages/omni-server', 'dist');
break;
}
assert(wasmDir !== null);
ensureDirSync(wasmDir);
if (!existsSync(path.join(wasmDir, 'tfjs-backend-wasm.wasm'))) {
console.log('Installing WASM modules... nsfwjs/threaded/wasm');
copyFileSync(
path.join(omniCwd(), 'node_modules', '@tensorflow', 'tfjs-backend-wasm', 'dist', 'tfjs-backend-wasm.wasm'),
path.join(wasmDir, 'tfjs-backend-wasm.wasm')
);
}
if (!existsSync(path.join(wasmDir, 'tfjs-backend-wasm-simd.wasm'))) {
console.log('Installing WASM modules... nsfwjs/threaded/simd ');
copyFileSync(
path.join(omniCwd(), 'node_modules', '@tensorflow', 'tfjs-backend-wasm', 'dist', 'tfjs-backend-wasm-simd.wasm'),
path.join(wasmDir, 'tfjs-backend-wasm-simd.wasm')
);
}
}
if (!verifyConfig()) {
process.exit(1);
}
process.on('exit', (code) => {
child_processes.forEach((p) => p.kill());
child_processes.clear();
});
process.on('uncaughtException', (err) => {
console.error(err);
user_confirmation(
`Oops something went terribly wrong: \n\t ${err}\n\nPlease let us know!\n\nPress any key to exit...`
).then(() => process.exit(1));
});
pretty_process_pid.push(`OMNITOOL - Launcher PID ${process.pid}`);
// launched from omnitool executable - always production + updates
async function run() {
const { NodeProcessEnv } = await import('omni-shared');
const omnitool_exec = 'omnitool';
if (argv0.includes(omnitool_exec)) {
console.log('Runtime mode detected..launching');
server_entry = 'dist/server.cjs';
process.env.NODE_ENV ??= 'production';
ensure_wasm();
run_runtime_executable();
} else {
// detect help and just show that then exit
if (args.includes('--help') || args.includes('-h')) {
execSync('node dist/run.js --help', { cwd: 'packages/omni-server', stdio: 'inherit' });
process.exit(0);
}
console.log(`OMNITOOL Environment ${process.env.NODE_ENV}`);
switch (process.env.NODE_ENV) {
case NodeProcessEnv.development:
server_entry = `dist/run.js`;
ensure_wasm();
run_development([]);
break;
case NodeProcessEnv.production:
server_entry = 'dist/run.js';
await check_for_updates();
ensure_wasm();
run_production([]);
break;
default:
throw new Error('Unhandled environment - ' + process.env.NODE_ENV);
}
}
}
run();