fix(sf): run sf-server from source in dev
This commit is contained in:
parent
974d8e4b6d
commit
3f213f3131
5 changed files with 175 additions and 97 deletions
|
|
@ -81,7 +81,8 @@
|
|||
"build:native:dev": "node rust-engine/scripts/build.js --dev",
|
||||
"dev": "node scripts/dev.js",
|
||||
"sf": "node scripts/dev-cli.js",
|
||||
"sf:server": "node packages/daemon/dist/cli.js",
|
||||
"sf:server": "node scripts/dev-server.js",
|
||||
"sf:server:dist": "node packages/daemon/dist/cli.js",
|
||||
"sf:web": "npm run build:pi && npm run copy-resources && node scripts/build-web-if-stale.cjs && node scripts/dev-cli.js --web",
|
||||
"sf:web:stop": "node scripts/dev-cli.js web stop",
|
||||
"sf:web:stop:all": "node scripts/dev-cli.js web stop all",
|
||||
|
|
|
|||
3
packages/daemon/src/cli-dev.ts
Normal file
3
packages/daemon/src/cli-dev.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
import { handleFatalError, main } from './cli-main.js';
|
||||
|
||||
main().catch(handleFatalError);
|
||||
97
packages/daemon/src/cli-main.ts
Normal file
97
packages/daemon/src/cli-main.ts
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
import { parseArgs } from 'node:util';
|
||||
import { resolve } from 'node:path';
|
||||
import { resolveConfigPath, loadConfig } from './config.js';
|
||||
import { Logger } from './logger.js';
|
||||
import { Daemon } from './daemon.js';
|
||||
import { install, uninstall, status } from './launchd.js';
|
||||
|
||||
export const COMMAND_NAME = 'sf-server';
|
||||
|
||||
export const USAGE = `Usage: sf-server [options]
|
||||
|
||||
Alias: sf-daemon
|
||||
|
||||
Options:
|
||||
--config <path> Path to YAML config file (default: ~/.sf/daemon.yaml)
|
||||
--verbose Print log entries to stderr in addition to the log file
|
||||
--install Install the launchd LaunchAgent (auto-starts on login)
|
||||
--uninstall Uninstall the launchd LaunchAgent
|
||||
--status Show launchd agent status (registered, PID, exit code)
|
||||
--help Show this help message and exit
|
||||
`;
|
||||
|
||||
export async function main(): Promise<void> {
|
||||
const { values } = parseArgs({
|
||||
options: {
|
||||
config: { type: 'string', short: 'c' },
|
||||
verbose: { type: 'boolean', short: 'v', default: false },
|
||||
install: { type: 'boolean', default: false },
|
||||
uninstall: { type: 'boolean', default: false },
|
||||
status: { type: 'boolean', default: false },
|
||||
help: { type: 'boolean', short: 'h', default: false },
|
||||
},
|
||||
strict: true,
|
||||
});
|
||||
|
||||
if (values.help) {
|
||||
process.stdout.write(USAGE);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// --- launchd commands (dispatch before Daemon creation) ---
|
||||
|
||||
if (values.install) {
|
||||
const configPath = resolveConfigPath(values.config);
|
||||
const scriptPath = resolve(import.meta.dirname, 'cli.js');
|
||||
|
||||
install({
|
||||
nodePath: process.execPath,
|
||||
scriptPath,
|
||||
configPath,
|
||||
});
|
||||
process.stdout.write(`${COMMAND_NAME}: launchd agent installed and loaded.\n`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
if (values.uninstall) {
|
||||
uninstall();
|
||||
process.stdout.write(`${COMMAND_NAME}: launchd agent uninstalled.\n`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
if (values.status) {
|
||||
const result = status();
|
||||
if (!result.registered) {
|
||||
process.stdout.write(`${COMMAND_NAME}: not registered with launchd.\n`);
|
||||
} else if (result.pid != null) {
|
||||
process.stdout.write(
|
||||
`${COMMAND_NAME}: running (PID ${result.pid}, last exit status: ${result.lastExitStatus ?? 'n/a'})\n`,
|
||||
);
|
||||
} else {
|
||||
process.stdout.write(
|
||||
`${COMMAND_NAME}: registered but not running (last exit status: ${result.lastExitStatus ?? 'n/a'})\n`,
|
||||
);
|
||||
}
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// --- normal daemon start ---
|
||||
|
||||
const configPath = resolveConfigPath(values.config);
|
||||
const config = loadConfig(configPath);
|
||||
|
||||
const logger = new Logger({
|
||||
filePath: config.log.file,
|
||||
level: config.log.level,
|
||||
verbose: values.verbose,
|
||||
});
|
||||
|
||||
const daemon = new Daemon(config, logger);
|
||||
await daemon.start();
|
||||
}
|
||||
|
||||
export function handleFatalError(err: unknown): never {
|
||||
const msg = err instanceof Error ? err.message : String(err);
|
||||
process.stderr.write(`${COMMAND_NAME}: fatal: ${msg}\n`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
|
@ -1,98 +1,4 @@
|
|||
#!/usr/bin/env node
|
||||
import { parseArgs } from 'node:util';
|
||||
import { resolve } from 'node:path';
|
||||
import { resolveConfigPath, loadConfig } from './config.js';
|
||||
import { Logger } from './logger.js';
|
||||
import { Daemon } from './daemon.js';
|
||||
import { install, uninstall, status } from './launchd.js';
|
||||
import { handleFatalError, main } from './cli-main.js';
|
||||
|
||||
const COMMAND_NAME = 'sf-server';
|
||||
|
||||
const USAGE = `Usage: sf-server [options]
|
||||
|
||||
Alias: sf-daemon
|
||||
|
||||
Options:
|
||||
--config <path> Path to YAML config file (default: ~/.sf/daemon.yaml)
|
||||
--verbose Print log entries to stderr in addition to the log file
|
||||
--install Install the launchd LaunchAgent (auto-starts on login)
|
||||
--uninstall Uninstall the launchd LaunchAgent
|
||||
--status Show launchd agent status (registered, PID, exit code)
|
||||
--help Show this help message and exit
|
||||
`;
|
||||
|
||||
async function main(): Promise<void> {
|
||||
const { values } = parseArgs({
|
||||
options: {
|
||||
config: { type: 'string', short: 'c' },
|
||||
verbose: { type: 'boolean', short: 'v', default: false },
|
||||
install: { type: 'boolean', default: false },
|
||||
uninstall: { type: 'boolean', default: false },
|
||||
status: { type: 'boolean', default: false },
|
||||
help: { type: 'boolean', short: 'h', default: false },
|
||||
},
|
||||
strict: true,
|
||||
});
|
||||
|
||||
if (values.help) {
|
||||
process.stdout.write(USAGE);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// --- launchd commands (dispatch before Daemon creation) ---
|
||||
|
||||
if (values.install) {
|
||||
const configPath = resolveConfigPath(values.config);
|
||||
const scriptPath = resolve(import.meta.dirname, 'cli.js');
|
||||
|
||||
install({
|
||||
nodePath: process.execPath,
|
||||
scriptPath,
|
||||
configPath,
|
||||
});
|
||||
process.stdout.write(`${COMMAND_NAME}: launchd agent installed and loaded.\n`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
if (values.uninstall) {
|
||||
uninstall();
|
||||
process.stdout.write(`${COMMAND_NAME}: launchd agent uninstalled.\n`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
if (values.status) {
|
||||
const result = status();
|
||||
if (!result.registered) {
|
||||
process.stdout.write(`${COMMAND_NAME}: not registered with launchd.\n`);
|
||||
} else if (result.pid != null) {
|
||||
process.stdout.write(
|
||||
`${COMMAND_NAME}: running (PID ${result.pid}, last exit status: ${result.lastExitStatus ?? 'n/a'})\n`,
|
||||
);
|
||||
} else {
|
||||
process.stdout.write(
|
||||
`${COMMAND_NAME}: registered but not running (last exit status: ${result.lastExitStatus ?? 'n/a'})\n`,
|
||||
);
|
||||
}
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// --- normal daemon start ---
|
||||
|
||||
const configPath = resolveConfigPath(values.config);
|
||||
const config = loadConfig(configPath);
|
||||
|
||||
const logger = new Logger({
|
||||
filePath: config.log.file,
|
||||
level: config.log.level,
|
||||
verbose: values.verbose,
|
||||
});
|
||||
|
||||
const daemon = new Daemon(config, logger);
|
||||
await daemon.start();
|
||||
}
|
||||
|
||||
main().catch((err: unknown) => {
|
||||
const msg = err instanceof Error ? err.message : String(err);
|
||||
process.stderr.write(`${COMMAND_NAME}: fatal: ${msg}\n`);
|
||||
process.exit(1);
|
||||
});
|
||||
main().catch(handleFatalError);
|
||||
|
|
|
|||
71
scripts/dev-server.js
Normal file
71
scripts/dev-server.js
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
import { spawn, spawnSync } from "node:child_process";
|
||||
import { resolve } from "node:path";
|
||||
|
||||
const __dirname = import.meta.dirname;
|
||||
const root = resolve(__dirname, "..");
|
||||
const sourceBinPath = resolve(root, "bin", "sf-from-source");
|
||||
const ensureResourcesPath = resolve(
|
||||
root,
|
||||
"scripts",
|
||||
"ensure-source-resources.cjs",
|
||||
);
|
||||
const daemonCliPath = resolve(root, "packages", "daemon", "src", "cli-dev.ts");
|
||||
const resolveTsPath = resolve(
|
||||
root,
|
||||
"src",
|
||||
"resources",
|
||||
"extensions",
|
||||
"sf",
|
||||
"tests",
|
||||
"resolve-ts.mjs",
|
||||
);
|
||||
|
||||
const resourceBuild = spawnSync(process.execPath, [ensureResourcesPath], {
|
||||
cwd: root,
|
||||
stdio: "inherit",
|
||||
env: process.env,
|
||||
});
|
||||
|
||||
if (resourceBuild.status !== 0) {
|
||||
process.exit(resourceBuild.status ?? 1);
|
||||
}
|
||||
|
||||
const child = spawn(
|
||||
process.execPath,
|
||||
[
|
||||
"--import",
|
||||
resolveTsPath,
|
||||
"--experimental-strip-types",
|
||||
"--no-warnings",
|
||||
daemonCliPath,
|
||||
...process.argv.slice(2),
|
||||
],
|
||||
{
|
||||
cwd: process.cwd(),
|
||||
stdio: "inherit",
|
||||
env: {
|
||||
...process.env,
|
||||
SF_SOURCE_ROOT: process.env.SF_SOURCE_ROOT || root,
|
||||
SF_RUNTIME_SOURCE_ROOT: process.env.SF_RUNTIME_SOURCE_ROOT || root,
|
||||
SF_BIN_PATH: process.env.SF_BIN_PATH || resolve(root, "dist", "loader.js"),
|
||||
SF_CLI_PATH: process.env.SF_CLI_PATH || sourceBinPath,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
child.on("error", (error) => {
|
||||
console.error(
|
||||
`[forge] Failed to launch local dev server: ${error instanceof Error ? error.message : String(error)}`,
|
||||
);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
child.on("exit", (code, signal) => {
|
||||
if (signal) {
|
||||
process.kill(process.pid, signal);
|
||||
return;
|
||||
}
|
||||
process.exit(code ?? 0);
|
||||
});
|
||||
Loading…
Add table
Reference in a new issue