Files
crossdesk/apps/cli/npm/cli.cjs
T

88 lines
3.4 KiB
JavaScript

#!/usr/bin/env node
'use strict';
const fs = require('node:fs');
const path = require('node:path');
const { spawn } = require('node:child_process');
const platforms = require('./platforms.json');
const { name: launcherPackage, version } = require('../package.json');
function main() {
const target = `${process.platform}-${process.arch}`;
const platform = platforms[target];
if (!platform) {
throw new Error(`No crossdesk-cli build for ${target}. Supported targets: ${Object.keys(platforms).join(', ')}.`);
}
if (process.platform === 'linux' && !process.report.getReport().header.glibcVersionRuntime) {
throw new Error('This release requires glibc Linux. Alpine/musl needs a separate native build.');
}
const packageName = platform.packageName;
let manifestPath;
try {
manifestPath = require.resolve(`${packageName}/package.json`);
} catch {
throw new Error(`Missing ${packageName}@${version}. Install with optional dependencies enabled; this platform's package must be published before ${launcherPackage}.`);
}
if (require(manifestPath).version !== version) {
throw new Error(`Expected ${packageName}@${version}; reinstall matching package versions.`);
}
const binary = path.join(path.dirname(manifestPath), 'bin', process.platform === 'win32' ? 'crossdesk-cli.exe' : 'crossdesk-cli');
fs.accessSync(binary, process.platform === 'win32' ? fs.constants.F_OK : fs.constants.X_OK);
const args = process.argv.slice(2);
// Hosts such as Pi should spawn the native path directly, retaining descriptor
// inheritance and control of the process lifetime without an npm intermediary.
if (args.length === 1 && args[0] === '--print-binary-path') {
process.stdout.write(`${binary}\n`);
return;
}
const stdio = ['inherit', 'inherit', 'inherit'];
if (args[0] === 'bridge') {
const index = args.lastIndexOf('--password-fd');
const fd = index === -1 ? 3 : Number(args[index + 1]);
if (Number.isInteger(fd) && fd >= 3 && fd <= 1024) {
try {
const stat = fs.fstatSync(fd);
if (stat.isFIFO() || stat.isSocket() || stat.isFile()) {
while (stdio.length <= fd) stdio.push('ignore');
stdio[fd] = fd;
}
} catch { /* The native bridge reports a missing credential descriptor. */ }
}
}
const child = spawn(binary, args, { stdio, shell: false });
const handlers = new Map();
if (process.platform !== 'win32') {
for (const signal of ['SIGINT', 'SIGTERM', 'SIGHUP']) {
const handler = () => { if (child.exitCode === null) child.kill(signal); };
handlers.set(signal, handler);
process.on(signal, handler);
}
} else {
// Console Ctrl+C is delivered to both processes. Do not terminate the native
// child before it has restored console modes.
const handler = () => {};
handlers.set('SIGINT', handler);
process.on('SIGINT', handler);
}
const cleanup = () => {
for (const [signal, handler] of handlers) process.removeListener(signal, handler);
};
child.once('error', (error) => {
cleanup();
process.stderr.write(`crossdesk-cli: cannot start native program (${error.code || 'unknown error'}).\n`);
process.exitCode = 1;
});
child.once('exit', (code, signal) => {
cleanup();
if (signal && process.platform !== 'win32') process.kill(process.pid, signal);
else process.exitCode = code ?? 1;
});
}
try {
main();
} catch (error) {
process.stderr.write(`crossdesk-cli: ${error.message}\n`);
process.exitCode = 1;
}