chore: checkpoint current worktree state
This commit is contained in:
@@ -209,6 +209,8 @@ async function resolveStatus(): Promise<DesktopDaemonStatus> {
|
||||
error: null,
|
||||
};
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
logDesktopDaemonLifecycle("resolveStatus CLI command failed", { error: errorMessage });
|
||||
return {
|
||||
serverId: "",
|
||||
status: "stopped",
|
||||
@@ -218,7 +220,7 @@ async function resolveStatus(): Promise<DesktopDaemonStatus> {
|
||||
home,
|
||||
version: null,
|
||||
desktopManaged: false,
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
error: errorMessage,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -231,6 +233,14 @@ function normalizeVersion(version: string | null): string | null {
|
||||
|
||||
async function startDaemon(): Promise<DesktopDaemonStatus> {
|
||||
const current = await resolveStatus();
|
||||
logDesktopDaemonLifecycle("initial status check before start", {
|
||||
status: current.status,
|
||||
pid: current.pid,
|
||||
listen: current.listen,
|
||||
serverId: current.serverId || null,
|
||||
error: current.error,
|
||||
desktopManaged: current.desktopManaged,
|
||||
});
|
||||
if (current.status === "running") {
|
||||
const appVersion = normalizeVersion(resolveDesktopAppVersion());
|
||||
const daemonVersion = normalizeVersion(current.version);
|
||||
@@ -269,7 +279,16 @@ async function startDaemon(): Promise<DesktopDaemonStatus> {
|
||||
const child: ChildProcess = spawnProcess(invocation.command, invocation.args, {
|
||||
detached: true,
|
||||
env: { ...invocation.env, PASEO_DESKTOP_MANAGED: "1" },
|
||||
stdio: ["ignore", "ignore", "ignore"],
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
});
|
||||
|
||||
let stdout = "";
|
||||
let stderr = "";
|
||||
child.stdout!.on("data", (data: Buffer) => {
|
||||
stdout += data.toString();
|
||||
});
|
||||
child.stderr!.on("data", (data: Buffer) => {
|
||||
stderr += data.toString();
|
||||
});
|
||||
|
||||
logDesktopDaemonLifecycle("detached spawn returned", {
|
||||
@@ -280,41 +299,54 @@ async function startDaemon(): Promise<DesktopDaemonStatus> {
|
||||
|
||||
child.unref();
|
||||
|
||||
// Wait for process to survive the grace period
|
||||
const exitedEarly = await new Promise<boolean>((resolve) => {
|
||||
type GraceResult =
|
||||
| { exitedEarly: false }
|
||||
| { exitedEarly: true; code: number | null; signal: string | null; error?: Error };
|
||||
|
||||
const result = await new Promise<GraceResult>((resolve) => {
|
||||
let settled = false;
|
||||
const finish = (value: boolean) => {
|
||||
const finish = (value: GraceResult) => {
|
||||
if (settled) return;
|
||||
settled = true;
|
||||
resolve(value);
|
||||
};
|
||||
|
||||
const timer = setTimeout(() => finish(false), DETACHED_STARTUP_GRACE_MS);
|
||||
const timer = setTimeout(() => finish({ exitedEarly: false }), DETACHED_STARTUP_GRACE_MS);
|
||||
|
||||
child.once("error", () => {
|
||||
logDesktopDaemonLifecycle("detached child emitted error during grace period", {
|
||||
childPid: child.pid ?? null,
|
||||
});
|
||||
child.once("error", (error) => {
|
||||
clearTimeout(timer);
|
||||
finish(true);
|
||||
finish({ exitedEarly: true, code: null, signal: null, error });
|
||||
});
|
||||
child.once("exit", () => {
|
||||
logDesktopDaemonLifecycle("detached child emitted exit during grace period", {
|
||||
childPid: child.pid ?? null,
|
||||
});
|
||||
child.once("exit", (code, signal) => {
|
||||
clearTimeout(timer);
|
||||
finish(true);
|
||||
finish({ exitedEarly: true, code, signal });
|
||||
});
|
||||
});
|
||||
|
||||
logDesktopDaemonLifecycle("detached startup grace period completed", {
|
||||
childPid: child.pid ?? null,
|
||||
exitedEarly,
|
||||
exitedEarly: result.exitedEarly,
|
||||
...(result.exitedEarly
|
||||
? {
|
||||
exitCode: result.code,
|
||||
signal: result.signal,
|
||||
error: result.error?.message ?? null,
|
||||
stdout: stdout.slice(0, 2000),
|
||||
stderr: stderr.slice(0, 2000),
|
||||
}
|
||||
: {}),
|
||||
});
|
||||
|
||||
if (exitedEarly) {
|
||||
if (result.exitedEarly) {
|
||||
const reason = result.error
|
||||
? result.error.message
|
||||
: `exit code ${result.code ?? "unknown"}${result.signal ? ` (${result.signal})` : ""}`;
|
||||
const parts = [`Daemon failed to start: ${reason}`];
|
||||
if (stderr.trim()) parts.push(`stderr:\n${stderr.trim()}`);
|
||||
if (stdout.trim()) parts.push(`stdout:\n${stdout.trim()}`);
|
||||
const logs = tailFile(logFilePath(), 15);
|
||||
throw new Error(`Daemon failed to start.${logs ? `\n\nRecent logs:\n${logs}` : ""}`);
|
||||
if (logs) parts.push(`Recent logs (${logFilePath()}):\n${logs}`);
|
||||
throw new Error(parts.join("\n\n"));
|
||||
}
|
||||
|
||||
// Poll for PID file with server ID
|
||||
|
||||
@@ -4,6 +4,7 @@ import { spawnProcess } from "@getpaseo/server";
|
||||
import { createRequire } from "node:module";
|
||||
import path from "node:path";
|
||||
import { app } from "electron";
|
||||
import log from "electron-log/main";
|
||||
import {
|
||||
DESKTOP_CLI_ENV,
|
||||
createNodeEntrypointInvocation as createSharedNodeEntrypointInvocation,
|
||||
@@ -284,7 +285,18 @@ export async function runCliTextCommand(args: string[]): Promise<string> {
|
||||
|
||||
if (result.exitCode !== 0) {
|
||||
const stderr = result.stderr.trim();
|
||||
throw new Error(stderr.length > 0 ? stderr : `CLI command failed with exit code ${result.exitCode}`);
|
||||
const stdout = result.stdout.trim();
|
||||
log.warn("[desktop cli]", `CLI text command failed`, {
|
||||
args,
|
||||
exitCode: result.exitCode,
|
||||
stdout: stdout.slice(0, 500),
|
||||
stderr: stderr.slice(0, 500),
|
||||
});
|
||||
throw new Error(
|
||||
stderr.length > 0
|
||||
? stderr
|
||||
: `CLI command failed with exit code ${result.exitCode}${stdout.length > 0 ? `\nstdout: ${stdout.slice(0, 200)}` : ""}`,
|
||||
);
|
||||
}
|
||||
|
||||
return result.stdout.trimEnd();
|
||||
@@ -298,11 +310,24 @@ export async function runCliJsonCommand(args: string[]): Promise<unknown> {
|
||||
|
||||
if (result.exitCode !== 0) {
|
||||
const stderr = result.stderr.trim();
|
||||
throw new Error(stderr.length > 0 ? stderr : `CLI command failed with exit code ${result.exitCode}`);
|
||||
const stdout = result.stdout.trim();
|
||||
log.warn("[desktop cli]", `CLI command failed`, {
|
||||
args,
|
||||
exitCode: result.exitCode,
|
||||
stdout: stdout.slice(0, 500),
|
||||
stderr: stderr.slice(0, 500),
|
||||
command: invocation.command,
|
||||
});
|
||||
throw new Error(
|
||||
stderr.length > 0
|
||||
? stderr
|
||||
: `CLI command failed with exit code ${result.exitCode}${stdout.length > 0 ? `\nstdout: ${stdout.slice(0, 200)}` : ""}`,
|
||||
);
|
||||
}
|
||||
|
||||
const stdout = result.stdout.trim();
|
||||
if (stdout.length === 0) {
|
||||
log.warn("[desktop cli]", `CLI command produced no output`, { args });
|
||||
throw new Error("CLI command did not produce JSON output.");
|
||||
}
|
||||
|
||||
@@ -310,7 +335,11 @@ export async function runCliJsonCommand(args: string[]): Promise<unknown> {
|
||||
// Extract the first valid JSON object or array from the output.
|
||||
const jsonStart = stdout.search(/[{[]/);
|
||||
if (jsonStart < 0) {
|
||||
throw new Error("CLI command output contained no JSON.");
|
||||
log.warn("[desktop cli]", `CLI command output contained no JSON`, {
|
||||
args,
|
||||
stdout: stdout.slice(0, 500),
|
||||
});
|
||||
throw new Error(`CLI command output contained no JSON. Output: ${stdout.slice(0, 200)}`);
|
||||
}
|
||||
const jsonText = stdout.slice(jsonStart);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user