Files
inkling/src/main/index.ts
altair823 df56661f4f feat(diag): log resolved Ollama endpoint + surface health reason in banner
When the OllamaBanner appears, it was generic enough that the user
couldn't tell whether the env var was missing, the LAN host was
unreachable, the model was uninstalled, or DNS was failing. Log the
resolved endpoint at startup with a `fromEnv` flag so we can confirm
INKLING_OLLAMA_ENDPOINT was actually read, and render the underlying
health-check reason as a small subtitle under the banner copy.

The user-facing primary message still avoids the forbidden tone words
("실패"/"끊김"/"연속 실패"); the diagnostic line is technical and only
appears when status.reason is set.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 16:20:57 +09:00

94 lines
3.4 KiB
TypeScript

import electron from 'electron';
const { app, BrowserWindow, Notification } = electron;
import '@shared/types';
import { initLogger, logger } from './logger.js';
import { resolveProfilePaths } from './paths.js';
import { openDb } from './db/index.js';
import { NoteRepository } from './repository/NoteRepository.js';
import { MediaStore } from './services/MediaStore.js';
import { ContinuityService } from './services/ContinuityService.js';
import { CaptureService } from './services/CaptureService.js';
import { NotificationService } from './services/NotificationService.js';
import { HotkeyService } from './services/HotkeyService.js';
import { IntentService } from './services/IntentService.js';
import { HealthChecker } from './services/HealthChecker.js';
import { LocalOllamaProvider } from './ai/LocalOllamaProvider.js';
import { AiWorker } from './ai/AiWorker.js';
import { registerCaptureApi } from './ipc/captureApi.js';
import { registerInboxApi, pushNoteUpdated } from './ipc/inboxApi.js';
import { createInboxWindow, getInboxWindow } from './windows/inboxWindow.js';
import {
createQuickCaptureWindow, showQuickCapture, getQuickCaptureWindow
} from './windows/quickCaptureWindow.js';
import { createTray } from './tray.js';
import { MediaGc } from './services/MediaGc.js';
app.whenReady().then(async () => {
initLogger();
logger.info('app.start', { platform: process.platform, version: app.getVersion() });
const paths = resolveProfilePaths('default');
const db = openDb(paths.dbFile);
const repo = new NoteRepository(db);
const store = new MediaStore(paths.profileDir);
const continuity = new ContinuityService(db);
const intent = new IntentService(repo);
const resolvedEndpoint = process.env.INKLING_OLLAMA_ENDPOINT ?? 'http://localhost:11434';
logger.info('ai.endpoint', {
endpoint: resolvedEndpoint,
fromEnv: process.env.INKLING_OLLAMA_ENDPOINT !== undefined
});
const provider = new LocalOllamaProvider({ endpoint: resolvedEndpoint });
const health = new HealthChecker(provider);
void health.runOnce().then((h) => logger.info('ai.health', { ...h } as Record<string, unknown>));
const worker = new AiWorker(repo, provider, {
onUpdate: (note) => pushNoteUpdated(getInboxWindow, note),
logger
});
const notify = new NotificationService({
isSupported: () => Notification.isSupported(),
send: (body) => {
new Notification({ title: 'Inkling', body, silent: false }).show();
}
});
const capture = new CaptureService(repo, store, {
enqueue: (id) => worker.enqueue(id),
celebrate: (id) => notify.celebrate(id)
});
registerCaptureApi(capture, getQuickCaptureWindow);
registerInboxApi({
repo, continuity, capture, health, intent,
getInboxWindow
});
const hotkeys = new HotkeyService();
const reg = hotkeys.register({
accelerator: process.platform === 'darwin' ? 'Cmd+Shift+J' : 'Ctrl+Shift+J',
onTrigger: () => showQuickCapture()
});
if (!reg.ok) logger.warn('hotkey.register.failed', { reason: reg.reason });
createInboxWindow();
createQuickCaptureWindow();
createTray(
() => createInboxWindow(),
() => showQuickCapture()
);
await worker.loadFromDb();
const gc = new MediaGc(db, store);
void gc.run().then((r) => logger.info('media.gc', { ...r } as Record<string, unknown>));
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) createInboxWindow();
});
});
app.on('before-quit', () => { app.isQuitting = true; });