feat(cli): add managed service lifecycle

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-07-27 15:32:12 +00:00
1 parent 189da8bb95
commit b4b596503d
7 files changed
+587

No files matched your search

+5
View File
@@ -72,6 +72,7 @@ import { maybePersistWorktreeRuntimePorts } from "./worktree-config.js";
import { initTelemetry, getTelemetryClient } from "./telemetry.js";
import { conflict } from "./errors.js";
import { coordinateHeartbeatSchedulerShutdown } from "./shutdown.js";
import { systemdNotify } from "./services/systemd-notify.js";
import type {
InstanceDatabaseBackupRunResult,
InstanceDatabaseBackupTrigger,
@@ -1215,6 +1216,9 @@ export async function startServer(): Promise<StartedServer> {
server.listen(listenPort, config.host, () => {
server.off("error", onError);
logger.info(`Server listening on ${config.host}:${listenPort}`);
void systemdNotify(["--ready", `--status=Listening on ${config.host}:${listenPort}`]).then((notified) => {
if (notified) logger.info("Notified systemd that Paperclip is ready");
});
if (process.env.PAPERCLIP_OPEN_ON_LISTEN === "true") {
const openHost = config.host === "0.0.0.0" || config.host === "::" ? "127.0.0.1" : config.host;
const url = `http://${openHost}:${listenPort}`;
@@ -1268,6 +1272,7 @@ export async function startServer(): Promise<StartedServer> {
{
const shutdown = async (signal: "SIGINT" | "SIGTERM") => {
await systemdNotify(["--stopping", `--status=Stopping after ${signal}`]);
heartbeatSchedulerStopped = true;
if (heartbeatSchedulerInterval) {
clearInterval(heartbeatSchedulerInterval);
+8
View File
@@ -0,0 +1,8 @@
import { execFile } from "node:child_process";
export async function systemdNotify(args: string[]): Promise<boolean> {
if (!process.env.NOTIFY_SOCKET?.trim()) return false;
return await new Promise<boolean>((resolve) => {
execFile("systemd-notify", args, { windowsHide: true }, (error) => resolve(!error));
});
}