diff --git a/packages/electron-app/electron/main/user-store.ts b/packages/electron-app/electron/main/user-store.ts index 68ea6a9..eae6770 100644 --- a/packages/electron-app/electron/main/user-store.ts +++ b/packages/electron-app/electron/main/user-store.ts @@ -124,6 +124,26 @@ export function ensureDefaultUsers(): UserRecord { roman.updatedAt = nowIso() writeStore(store) } + + // NEW: Check if roman needs data migration (e.g. if he was created before migration logic was robust) + const userDir = getUserDir(roman.id) + const configPath = join(userDir, "config.json") + let needsMigration = !existsSync(configPath) + if (!needsMigration) { + try { + const config = JSON.parse(readFileSync(configPath, "utf-8")) + if (!config.recentFolders || config.recentFolders.length === 0) { + needsMigration = true + } + } catch (e) { + needsMigration = true + } + } + + if (needsMigration) { + console.log(`[UserStore] Roman exists but seems to have missing data. Triggering migration to ${userDir}...`) + migrateLegacyData(userDir) + } } if (store.users.length > 0) { diff --git a/packages/ui/src/App.tsx b/packages/ui/src/App.tsx index dcda062..d6adb17 100644 --- a/packages/ui/src/App.tsx +++ b/packages/ui/src/App.tsx @@ -11,8 +11,6 @@ import { RemoteAccessOverlay } from "./components/remote-access-overlay" import { InstanceMetadataProvider } from "./lib/contexts/instance-metadata-context" import { initMarkdown } from "./lib/markdown" import QwenOAuthCallback from "./pages/QwenOAuthCallback" -import LoginView from "./components/auth/LoginView" -import { isLoggedIn, initializeUserContext } from "./lib/user-context" import { useTheme } from "./lib/theme" import { useCommands } from "./lib/hooks/use-commands" @@ -102,11 +100,6 @@ const App: Component = () => { }) onMount(() => { - // Initialize user context from Electron IPC - import("./lib/user-context").then(({ initializeUserContext }) => { - initializeUserContext() - }) - updateInstanceTabBarHeight() const handleResize = () => updateInstanceTabBarHeight() window.addEventListener("resize", handleResize) @@ -393,111 +386,106 @@ const App: Component = () => { - initializeUserContext()} />} - > -
- - setRemoteAccessOpen(true)} - /> +
+ + setRemoteAccessOpen(true)} + /> - - {(instance) => { - const isActiveInstance = () => activeInstanceId() === instance.id - const isVisible = () => isActiveInstance() && !showFolderSelection() - return ( -
- - handleCloseSession(instance.id, sessionId)} - onNewSession={() => handleNewSession(instance.id)} - handleSidebarAgentChange={(sessionId, agent) => handleSidebarAgentChange(instance.id, sessionId, agent)} - handleSidebarModelChange={(sessionId, model) => handleSidebarModelChange(instance.id, sessionId, model)} - onExecuteCommand={executeCommand} - tabBarOffset={instanceTabBarHeight()} - /> - + + {(instance) => { + const isActiveInstance = () => activeInstanceId() === instance.id + const isVisible = () => isActiveInstance() && !showFolderSelection() + return ( +
+ + handleCloseSession(instance.id, sessionId)} + onNewSession={() => handleNewSession(instance.id)} + handleSidebarAgentChange={(sessionId, agent) => handleSidebarAgentChange(instance.id, sessionId, agent)} + handleSidebarModelChange={(sessionId, model) => handleSidebarModelChange(instance.id, sessionId, model)} + onExecuteCommand={executeCommand} + tabBarOffset={instanceTabBarHeight()} + /> + -
- ) +
+ ) - }} -
+ }} + - - } - > - setIsAdvancedSettingsOpen(true)} - onAdvancedSettingsClose={() => setIsAdvancedSettingsOpen(false)} - onOpenRemoteAccess={() => setRemoteAccessOpen(true)} - /> -
- - -
-
- - setIsAdvancedSettingsOpen(true)} - onAdvancedSettingsClose={() => setIsAdvancedSettingsOpen(false)} - /> -
-
-
- - setRemoteAccessOpen(false)} /> - - - - + } + > + setIsAdvancedSettingsOpen(true)} + onAdvancedSettingsClose={() => setIsAdvancedSettingsOpen(false)} + onOpenRemoteAccess={() => setRemoteAccessOpen(true)} /> -
-
+ + + +
+
+ + setIsAdvancedSettingsOpen(true)} + onAdvancedSettingsClose={() => setIsAdvancedSettingsOpen(false)} + /> +
+
+
+ + setRemoteAccessOpen(false)} /> + + + + +
) } diff --git a/packages/ui/src/lib/api-client.ts b/packages/ui/src/lib/api-client.ts index d73a9dd..a433812 100644 --- a/packages/ui/src/lib/api-client.ts +++ b/packages/ui/src/lib/api-client.ts @@ -28,6 +28,7 @@ import type { PortAvailabilityResponse, } from "../../../server/src/api-types" import { getLogger } from "./logger" +import { getUserHeaders } from "./user-context" const FALLBACK_API_BASE = "http://127.0.0.1:9898" const RUNTIME_BASE = typeof window !== "undefined" ? window.location?.origin : undefined @@ -87,8 +88,10 @@ function logHttp(message: string, context?: Record) { async function request(path: string, init?: RequestInit): Promise { const url = API_BASE_ORIGIN ? new URL(path, API_BASE_ORIGIN).toString() : path + const userHeaders = getUserHeaders() const headers: HeadersInit = { "Content-Type": "application/json", + ...userHeaders, ...(init?.headers ?? {}), } diff --git a/packages/ui/src/lib/user-context.ts b/packages/ui/src/lib/user-context.ts index bcc4d52..51af3a2 100644 --- a/packages/ui/src/lib/user-context.ts +++ b/packages/ui/src/lib/user-context.ts @@ -45,22 +45,55 @@ export function getUserHeaders(): Record { */ export function withUserHeaders(options: RequestInit = {}): RequestInit { const userHeaders = getUserHeaders() + if (Object.keys(userHeaders).length === 0) return options + + const headers = new Headers(options.headers || {}) + for (const [key, value] of Object.entries(userHeaders)) { + headers.set(key, value) + } + return { ...options, - headers: { - ...options.headers, - ...userHeaders, - }, + headers, } } /** * Fetch wrapper that automatically includes user headers */ -export async function userFetch(url: string, options: RequestInit = {}): Promise { +export async function userFetch(url: string | URL | Request, options: RequestInit = {}): Promise { return fetch(url, withUserHeaders(options)) } +/** + * Globally patch fetch to include user headers for all internal /api/* requests + * This ensures compatibility with legacy code and 3rd party libraries. + */ +export function patchFetch(): void { + if ((window as any)._codenomad_fetch_patched) return + (window as any)._codenomad_fetch_patched = true + + const originalFetch = window.fetch + window.fetch = async function (input: RequestInfo | URL, init?: RequestInit) { + let url = "" + if (typeof input === "string") { + url = input + } else if (input instanceof URL) { + url = input.toString() + } else if (input instanceof Request) { + url = input.url + } + + // Only inject headers for internal API calls + if (url.startsWith("/api/") || url.includes(window.location.origin + "/api/")) { + return originalFetch(input, withUserHeaders(init)) + } + + return originalFetch(input, init) + } + console.log("[UserContext] Global fetch patched for /api/* requests") +} + /** * Initialize user context from Electron IPC * Call this on app startup diff --git a/packages/ui/src/main.tsx b/packages/ui/src/main.tsx index 6f74c47..57ba663 100644 --- a/packages/ui/src/main.tsx +++ b/packages/ui/src/main.tsx @@ -1,9 +1,12 @@ import { render } from "solid-js/web" +import { Show, onMount } from "solid-js" import App from "./App" import { ThemeProvider } from "./lib/theme" import { ConfigProvider } from "./stores/preferences" import { InstanceConfigProvider } from "./stores/instance-config" import { runtimeEnv } from "./lib/runtime-env" +import LoginView from "./components/auth/LoginView" +import { isLoggedIn, initializeUserContext, patchFetch } from "./lib/user-context" import "./index.css" import "@git-diff-view/solid/styles/diff-view-pure.css" @@ -18,15 +21,26 @@ if (typeof document !== "undefined") { document.documentElement.dataset.runtimePlatform = runtimeEnv.platform } -render( - () => ( - - - - - - - - ), - root, -) +const Root = () => { + onMount(() => { + patchFetch() + initializeUserContext() + }) + + return ( + initializeUserContext()} />} + > + + + + + + + + + ) +} + +render(() => , root)