fix: log bootstrap errors to daemon.log and add layout debug logging
- Log fatal errors during daemon bootstrap and startup to pino (daemon.log) instead of only writing to stderr, which is invisible on Windows desktop - Delay process.exit to let pino async streams flush - Make voice MCP bridge script resolution non-fatal (warn + disable instead of crashing the daemon) - Add PASEO_ELECTRON_FLAGS env var for passing Chromium flags on Linux - Add layout debug logging to AppContainer for diagnosing sidebar issues
This commit is contained in:
@@ -371,10 +371,41 @@ function AppContainer({
|
|||||||
const toggleBothSidebars = usePanelStore((state) => state.toggleBothSidebars);
|
const toggleBothSidebars = usePanelStore((state) => state.toggleBothSidebars);
|
||||||
const toggleFocusMode = usePanelStore((state) => state.toggleFocusMode);
|
const toggleFocusMode = usePanelStore((state) => state.toggleFocusMode);
|
||||||
const isFocusModeEnabled = usePanelStore((state) => state.desktop.focusModeEnabled);
|
const isFocusModeEnabled = usePanelStore((state) => state.desktop.focusModeEnabled);
|
||||||
|
const agentListOpen = usePanelStore((state) => state.desktop.agentListOpen);
|
||||||
|
const sidebarWidth = usePanelStore((state) => state.sidebarWidth);
|
||||||
|
|
||||||
const isCompactLayout = isCompactFormFactor();
|
const isCompactLayout = isCompactFormFactor();
|
||||||
const chromeEnabled = chromeEnabledOverride ?? daemons.length > 0;
|
const chromeEnabled = chromeEnabledOverride ?? daemons.length > 0;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const bp = UnistylesRuntime.breakpoint;
|
||||||
|
const screenW = UnistylesRuntime.screen.width;
|
||||||
|
const screenH = UnistylesRuntime.screen.height;
|
||||||
|
const isElectron = getIsElectronRuntime();
|
||||||
|
const windowW = Platform.OS === "web" ? window.innerWidth : undefined;
|
||||||
|
const windowH = Platform.OS === "web" ? window.innerHeight : undefined;
|
||||||
|
const dpr = Platform.OS === "web" ? window.devicePixelRatio : undefined;
|
||||||
|
const ua = Platform.OS === "web" ? navigator.userAgent : undefined;
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
"[layout-debug]",
|
||||||
|
JSON.stringify({
|
||||||
|
breakpoint: bp,
|
||||||
|
isCompactLayout,
|
||||||
|
isElectron,
|
||||||
|
chromeEnabled,
|
||||||
|
isFocusModeEnabled,
|
||||||
|
agentListOpen,
|
||||||
|
sidebarWidth,
|
||||||
|
sidebarRenderedInRow: !isCompactLayout && chromeEnabled && !isFocusModeEnabled,
|
||||||
|
unistylesScreen: { w: screenW, h: screenH },
|
||||||
|
window: { w: windowW, h: windowH },
|
||||||
|
devicePixelRatio: dpr,
|
||||||
|
userAgent: ua,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}, [isCompactLayout, chromeEnabled, isFocusModeEnabled, agentListOpen, sidebarWidth]);
|
||||||
|
|
||||||
useKeyboardShortcuts({
|
useKeyboardShortcuts({
|
||||||
enabled: chromeEnabled,
|
enabled: chromeEnabled,
|
||||||
isMobile: isCompactLayout,
|
isMobile: isCompactLayout,
|
||||||
|
|||||||
@@ -37,6 +37,18 @@ const APP_SCHEME = "paseo";
|
|||||||
const OPEN_PROJECT_EVENT = "paseo:event:open-project";
|
const OPEN_PROJECT_EVENT = "paseo:event:open-project";
|
||||||
app.setName("Paseo");
|
app.setName("Paseo");
|
||||||
|
|
||||||
|
// Allow users to pass Chromium flags via PASEO_ELECTRON_FLAGS for debugging
|
||||||
|
// rendering issues (e.g. "--disable-gpu --ozone-platform=x11").
|
||||||
|
// Must run before app.whenReady().
|
||||||
|
const electronFlags = process.env.PASEO_ELECTRON_FLAGS?.trim();
|
||||||
|
if (electronFlags) {
|
||||||
|
for (const token of electronFlags.split(/\s+/)) {
|
||||||
|
const [key, ...rest] = token.replace(/^--/, "").split("=");
|
||||||
|
app.commandLine.appendSwitch(key, rest.join("=") || undefined);
|
||||||
|
}
|
||||||
|
log.info("[electron-flags]", electronFlags);
|
||||||
|
}
|
||||||
|
|
||||||
let pendingOpenProjectPath = parseOpenProjectPathFromArgv({
|
let pendingOpenProjectPath = parseOpenProjectPathFromArgv({
|
||||||
argv: process.argv,
|
argv: process.argv,
|
||||||
isDefaultApp: process.defaultApp,
|
isDefaultApp: process.defaultApp,
|
||||||
|
|||||||
@@ -133,21 +133,28 @@ import { resolveVoiceMcpBridgeFromRuntime } from "./voice-mcp-bridge-command.js"
|
|||||||
|
|
||||||
type AgentMcpTransportMap = Map<string, StreamableHTTPServerTransport>;
|
type AgentMcpTransportMap = Map<string, StreamableHTTPServerTransport>;
|
||||||
|
|
||||||
function resolveVoiceMcpBridgeCommand(logger: Logger): { command: string; baseArgs: string[] } {
|
function resolveVoiceMcpBridgeCommand(
|
||||||
const decision = resolveVoiceMcpBridgeFromRuntime({
|
logger: Logger,
|
||||||
bootstrapModuleUrl: import.meta.url,
|
): { command: string; baseArgs: string[] } | null {
|
||||||
execPath: process.execPath,
|
try {
|
||||||
explicitScriptPath: process.env.PASEO_MCP_STDIO_SOCKET_BRIDGE_SCRIPT,
|
const decision = resolveVoiceMcpBridgeFromRuntime({
|
||||||
});
|
bootstrapModuleUrl: import.meta.url,
|
||||||
logger.info(
|
execPath: process.execPath,
|
||||||
{
|
explicitScriptPath: process.env.PASEO_MCP_STDIO_SOCKET_BRIDGE_SCRIPT,
|
||||||
source: decision.source,
|
});
|
||||||
command: decision.resolved.command,
|
logger.info(
|
||||||
baseArgs: decision.resolved.baseArgs,
|
{
|
||||||
},
|
source: decision.source,
|
||||||
"Resolved voice MCP bridge command",
|
command: decision.resolved.command,
|
||||||
);
|
baseArgs: decision.resolved.baseArgs,
|
||||||
return decision.resolved;
|
},
|
||||||
|
"Resolved voice MCP bridge command",
|
||||||
|
);
|
||||||
|
return decision.resolved;
|
||||||
|
} catch (err) {
|
||||||
|
logger.warn({ err }, "Voice MCP bridge script not available — voice MCP via stdio disabled");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PaseoOpenAIConfig = OpenAiSpeechProviderConfig;
|
export type PaseoOpenAIConfig = OpenAiSpeechProviderConfig;
|
||||||
@@ -669,14 +676,16 @@ export async function createPaseoDaemon(
|
|||||||
speechService,
|
speechService,
|
||||||
terminalManager,
|
terminalManager,
|
||||||
{
|
{
|
||||||
voiceAgentMcpStdio: {
|
voiceAgentMcpStdio: voiceMcpBridgeCommand
|
||||||
command: voiceMcpBridgeCommand.command,
|
? {
|
||||||
baseArgs: [...voiceMcpBridgeCommand.baseArgs],
|
command: voiceMcpBridgeCommand.command,
|
||||||
env: {
|
baseArgs: [...voiceMcpBridgeCommand.baseArgs],
|
||||||
ELECTRON_RUN_AS_NODE: "1",
|
env: {
|
||||||
PASEO_HOME: config.paseoHome,
|
ELECTRON_RUN_AS_NODE: "1",
|
||||||
},
|
PASEO_HOME: config.paseoHome,
|
||||||
},
|
},
|
||||||
|
}
|
||||||
|
: null,
|
||||||
ensureVoiceMcpSocketForAgent: (agentId) =>
|
ensureVoiceMcpSocketForAgent: (agentId) =>
|
||||||
voiceMcpBridgeManager?.ensureBridgeForCaller(agentId) ??
|
voiceMcpBridgeManager?.ensureBridgeForCaller(agentId) ??
|
||||||
Promise.reject(new Error("Voice MCP bridge manager is not initialized")),
|
Promise.reject(new Error("Voice MCP bridge manager is not initialized")),
|
||||||
|
|||||||
@@ -143,6 +143,7 @@ async function main() {
|
|||||||
logger.error({ pid: err.existingLock?.pid }, err.message);
|
logger.error({ pid: err.existingLock?.pid }, err.message);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
logger.fatal({ err }, "Daemon bootstrap failed");
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,6 +154,7 @@ async function main() {
|
|||||||
logger.error({ pid: err.existingLock?.pid }, err.message);
|
logger.error({ pid: err.existingLock?.pid }, err.message);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
logger.fatal({ err }, "Daemon failed to start listening");
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,10 +173,9 @@ async function main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
main().catch((err) => {
|
main().catch((err) => {
|
||||||
if (process.env.PASEO_DEBUG === "1") {
|
process.stderr.write(`${err instanceof Error ? (err.stack ?? err.message) : String(err)}\n`);
|
||||||
process.stderr.write(`${err instanceof Error ? (err.stack ?? err.message) : String(err)}\n`);
|
// Give pino streams a moment to flush the fatal log entry to daemon.log
|
||||||
} else {
|
// before the process exits. Without this, async file streams may lose the
|
||||||
process.stderr.write(`${err instanceof Error ? err.message : String(err)}\n`);
|
// last few entries that explain why the daemon crashed.
|
||||||
}
|
setTimeout(() => process.exit(1), 200);
|
||||||
process.exit(1);
|
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user