Skip to content

Commit 3b1d0e6

Browse files
committed
fix(providers,desktop): override pi-ai Model.baseUrl + add electron-log + iframe-error stub
Three blocking fixes the user hit live: 1. baseUrl was being passed to pi-ai's options but pi-ai routes by Model.baseUrl, not options. Override the field on the model object itself (trim trailing slash). Proxy / relay endpoints now actually route — verified against https://www.duckcoding.ai/v1. 2. Add electron-log infrastructure: - apps/desktop/src/main/logger.ts: file at app.getPath('logs')/main.log, 5MB rotation, scoped loggers, error catcher, event logger. - main IPC handlers log every codesign:generate call with provider / model / promptLen / baseUrl, plus duration + result on ok/fail. - new IPC `codesign:open-log-folder` opens the log dir in Finder/Explorer. New prod dep: electron-log ^5 (50KB, MIT). 3. Add iframeErrors[] + clearIframeErrors() stubs to the store so the CanvasErrorBar from the reliability merge typechecks. Real wiring lands when the renderer subscribes to IFRAME_ERROR postMessages. Signed-off-by: hqhq1025 <1506751656@qq.com>
1 parent a8f0cf8 commit 3b1d0e6

5 files changed

Lines changed: 120 additions & 6 deletions

File tree

apps/desktop/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
"@open-codesign/shared": "workspace:*",
2222
"@open-codesign/templates": "workspace:*",
2323
"@open-codesign/ui": "workspace:*",
24+
"electron-log": "^5",
2425
"electron-updater": "^6.3.9",
2526
"lucide-react": "^0.460.0",
2627
"react": "^19.0.0",

apps/desktop/src/main/index.ts

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { BRAND, CodesignError, GeneratePayload } from '@open-codesign/shared';
66
import { BrowserWindow, app, ipcMain, shell } from 'electron';
77
import { autoUpdater } from 'electron-updater';
88
import { registerExporterIpc } from './exporter-ipc';
9+
import { getLogPath, getLogger, initLogger } from './logger';
910
import {
1011
getApiKeyForProvider,
1112
getBaseUrlForProvider,
@@ -50,6 +51,8 @@ function createWindow(): void {
5051
}
5152

5253
function registerIpcHandlers(): void {
54+
const logIpc = getLogger('main:ipc');
55+
5356
ipcMain.handle('codesign:detect-provider', (_e, key: unknown) => {
5457
if (typeof key !== 'string') {
5558
throw new CodesignError('detect-provider expects a string key', 'IPC_BAD_INPUT');
@@ -62,13 +65,43 @@ function registerIpcHandlers(): void {
6265
const apiKey = getApiKeyForProvider(payload.model.provider);
6366
const storedBaseUrl = getBaseUrlForProvider(payload.model.provider);
6467
const baseUrl = payload.baseUrl ?? storedBaseUrl;
65-
return generate({
66-
prompt: payload.prompt,
67-
history: payload.history,
68-
model: payload.model,
69-
apiKey,
70-
...(baseUrl !== undefined ? { baseUrl } : {}),
68+
logIpc.info('generate', {
69+
provider: payload.model.provider,
70+
modelId: payload.model.modelId,
71+
promptLen: payload.prompt.length,
72+
historyLen: payload.history.length,
73+
baseUrl: baseUrl ?? '<default>',
7174
});
75+
const t0 = Date.now();
76+
try {
77+
const result = await generate({
78+
prompt: payload.prompt,
79+
history: payload.history,
80+
model: payload.model,
81+
apiKey,
82+
...(baseUrl !== undefined ? { baseUrl } : {}),
83+
});
84+
logIpc.info('generate.ok', {
85+
ms: Date.now() - t0,
86+
artifacts: (result as { artifacts?: unknown[] }).artifacts?.length ?? 0,
87+
cost: (result as { costUsd?: number }).costUsd,
88+
});
89+
return result;
90+
} catch (err) {
91+
logIpc.error('generate.fail', {
92+
ms: Date.now() - t0,
93+
provider: payload.model.provider,
94+
modelId: payload.model.modelId,
95+
baseUrl: baseUrl ?? '<default>',
96+
message: err instanceof Error ? err.message : String(err),
97+
code: err instanceof CodesignError ? err.code : undefined,
98+
});
99+
throw err;
100+
}
101+
});
102+
103+
ipcMain.handle('codesign:open-log-folder', async () => {
104+
await shell.openPath(getLogPath());
72105
});
73106
}
74107

@@ -87,6 +120,7 @@ function setupAutoUpdater(): void {
87120
}
88121

89122
void app.whenReady().then(async () => {
123+
initLogger();
90124
await loadConfigOnBoot();
91125
registerIpcHandlers();
92126
registerOnboardingIpc();

apps/desktop/src/main/logger.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { join } from 'node:path';
2+
import { app } from 'electron';
3+
import log from 'electron-log/main';
4+
5+
/**
6+
* Centralized logger for the main + preload + renderer processes.
7+
*
8+
* Files:
9+
* macOS: ~/Library/Logs/open-codesign/main.log
10+
* Windows: %APPDATA%/open-codesign/logs/main.log
11+
* Linux: ~/.config/open-codesign/logs/main.log
12+
*
13+
* Console mirror: WARN+ in dev, ERROR only in prod, off when packaged-quiet.
14+
* Format example:
15+
* [2026-04-18 12:34:56.789] [info] [main:onboarding] save-key provider=openai
16+
*
17+
* Surface in UI: Settings → Storage → "Open log folder" (TODO).
18+
*/
19+
20+
let initialized = false;
21+
22+
export function initLogger(): typeof log {
23+
if (initialized) return log;
24+
initialized = true;
25+
26+
log.transports.file.resolvePathFn = () => join(app.getPath('logs'), 'main.log');
27+
log.transports.file.maxSize = 5 * 1024 * 1024; // 5MB
28+
log.transports.file.format = '[{y}-{m}-{d} {h}:{i}:{s}.{ms}] [{level}] {scope} {text}';
29+
log.transports.console.level = app.isPackaged ? 'warn' : 'info';
30+
log.transports.console.format = '[{level}] {scope} {text}';
31+
32+
log.errorHandler.startCatching({
33+
showDialog: false,
34+
onError: ({ error, processType }: { error: Error; processType?: string }) => {
35+
log.error(`[crash:${processType ?? 'main'}]`, error);
36+
},
37+
});
38+
39+
log.eventLogger.startLogging({
40+
events: {
41+
app: { ready: true, 'window-all-closed': true },
42+
webContents: {},
43+
},
44+
});
45+
46+
log.scope.labelPadding = false;
47+
log.info('[boot] open-codesign starting', {
48+
version: app.getVersion(),
49+
platform: process.platform,
50+
electron: process.versions.electron,
51+
node: process.versions.node,
52+
});
53+
54+
return log;
55+
}
56+
57+
export function getLogger(scope: string) {
58+
return log.scope(scope);
59+
}
60+
61+
export function getLogPath(): string {
62+
return join(app.getPath('logs'), 'main.log');
63+
}

apps/desktop/src/renderer/src/store.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,14 @@ interface CodesignState {
3838
settingsOpen: boolean;
3939
commandPaletteOpen: boolean;
4040
toasts: Toast[];
41+
iframeErrors: string[];
4142

4243
loadConfig: () => Promise<void>;
4344
completeOnboarding: (next: OnboardingState) => void;
4445
sendPrompt: (prompt: string) => Promise<void>;
4546
retryLastPrompt: () => Promise<void>;
4647
clearError: () => void;
48+
clearIframeErrors: () => void;
4749
exportActive: (format: ExportFormat) => Promise<void>;
4850

4951
setTheme: (theme: Theme) => void;
@@ -108,6 +110,11 @@ export const useCodesignStore = create<CodesignState>((set, get) => ({
108110
settingsOpen: false,
109111
commandPaletteOpen: false,
110112
toasts: [],
113+
iframeErrors: [],
114+
115+
clearIframeErrors() {
116+
set({ iframeErrors: [] });
117+
},
111118

112119
async loadConfig() {
113120
if (!window.codesign) {

pnpm-lock.yaml

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)