Download src/api/backend-registry/storage.ts from SaylorTwift/openhands: direct link, hf CLI and curl.
- Browser
- Download file 6.59 kB
-
https://huggingface.co/SaylorTwift/openhands/resolve/main/src/api/backend-registry/storage.ts
- Command line
-
hf download hf://SaylorTwift/openhands/src/api/backend-registry/storage.ts
-
curl -L -o storage.ts https://huggingface.co/SaylorTwift/openhands/resolve/main/src/api/backend-registry/storage.ts
6.59 kB
| import { | |
| SEEDED_DEFAULT_BACKEND_ID, | |
| makeDefaultLocalBackend, | |
| makeLockedCloudBackend, | |
| } from "./default-backend"; | |
| import type { | |
| Backend, | |
| BackendAuthMode, | |
| BackendKind, | |
| BackendSelection, | |
| } from "./types"; | |
| export const BACKENDS_STORAGE_KEY = "openhands-backends"; | |
| export const ACTIVE_BACKEND_STORAGE_KEY = "openhands-active-backend"; | |
| function isValidKind(value: unknown): value is BackendKind { | |
| return value === "local" || value === "cloud"; | |
| } | |
| function isValidAuthMode(value: unknown): value is BackendAuthMode { | |
| return value === undefined || value === "api-key" || value === "cookie"; | |
| } | |
| function isValidBackend(value: unknown): value is Backend { | |
| if (typeof value !== "object" || value === null) return false; | |
| const v = value as Partial<Backend>; | |
| return ( | |
| typeof v.id === "string" && | |
| v.id.length > 0 && | |
| typeof v.name === "string" && | |
| typeof v.host === "string" && | |
| typeof v.apiKey === "string" && | |
| isValidKind(v.kind) && | |
| isValidAuthMode(v.authMode) && | |
| (v.connectionRevision === undefined || | |
| (typeof v.connectionRevision === "number" && | |
| Number.isSafeInteger(v.connectionRevision) && | |
| v.connectionRevision >= 0)) | |
| ); | |
| } | |
| function isLoopbackUrl(value: string): boolean { | |
| try { | |
| const { hostname } = new URL(value); | |
| return ( | |
| hostname === "localhost" || | |
| hostname === "127.0.0.1" || | |
| hostname === "::1" || | |
| hostname === "[::1]" | |
| ); | |
| } catch { | |
| return false; | |
| } | |
| } | |
| function shouldSyncLauncherDefaultLocalBackend( | |
| backend: Backend, | |
| defaultBackend: Backend, | |
| ): boolean { | |
| if (backend.id !== SEEDED_DEFAULT_BACKEND_ID || backend.kind !== "local") { | |
| return false; | |
| } | |
| return ( | |
| backend.host === defaultBackend.host || | |
| (isLoopbackUrl(backend.host) && isLoopbackUrl(defaultBackend.host)) | |
| ); | |
| } | |
| function syncLauncherDefaultLocalBackend(backends: Backend[]): Backend[] { | |
| const defaultBackend = makeDefaultLocalBackend(); | |
| if (!defaultBackend) return backends; | |
| let didSync = false; | |
| const syncedBackends = backends.map((backend) => { | |
| if (!shouldSyncLauncherDefaultLocalBackend(backend, defaultBackend)) { | |
| return backend; | |
| } | |
| if (backend.apiKey === defaultBackend.apiKey) return backend; | |
| didSync = true; | |
| return { | |
| ...backend, | |
| apiKey: defaultBackend.apiKey, | |
| }; | |
| }); | |
| if (!didSync) return backends; | |
| writeStoredBackends(syncedBackends); | |
| return syncedBackends; | |
| } | |
| export function writeStoredBackends(backends: Backend[]): void { | |
| if (typeof window === "undefined") return; | |
| try { | |
| window.localStorage.setItem(BACKENDS_STORAGE_KEY, JSON.stringify(backends)); | |
| } catch { | |
| /* ignore quota / serialization errors */ | |
| } | |
| } | |
| export function readStoredBackends(): Backend[] { | |
| if (typeof window === "undefined") return []; | |
| try { | |
| const lockedCloudBackend = makeLockedCloudBackend(); | |
| if (lockedCloudBackend) { | |
| writeStoredBackends([lockedCloudBackend]); | |
| const activeSelection = readStoredActiveBackend(); | |
| if (activeSelection?.backendId !== lockedCloudBackend.id) { | |
| writeStoredActiveBackend({ backendId: lockedCloudBackend.id }); | |
| } | |
| return [lockedCloudBackend]; | |
| } | |
| const raw = window.localStorage.getItem(BACKENDS_STORAGE_KEY); | |
| // First install: seed only when the launcher supplied enough information | |
| // for a usable local backend. | |
| if (raw === null) { | |
| const defaultBackend = makeDefaultLocalBackend(); | |
| if (!defaultBackend) return []; | |
| writeStoredBackends([defaultBackend]); | |
| return [defaultBackend]; | |
| } | |
| const parsed = JSON.parse(raw); | |
| if (!Array.isArray(parsed)) return []; | |
| const valid = parsed.filter(isValidBackend); | |
| // If the stored array is empty (or everything in it failed validation), | |
| // only re-seed when the launcher supplied both a host and API key. | |
| if (valid.length === 0) { | |
| const defaultBackend = makeDefaultLocalBackend(); | |
| if (!defaultBackend) return []; | |
| writeStoredBackends([defaultBackend]); | |
| return [defaultBackend]; | |
| } | |
| const synced = syncLauncherDefaultLocalBackend(valid); | |
| return synced; | |
| } catch { | |
| return []; | |
| } | |
| } | |
| function parseBackendSelection(raw: string | null): BackendSelection | null { | |
| if (!raw) return null; | |
| try { | |
| const parsed = JSON.parse(raw); | |
| if ( | |
| typeof parsed !== "object" || | |
| parsed === null || | |
| typeof (parsed as BackendSelection).backendId !== "string" | |
| ) { | |
| return null; | |
| } | |
| const orgIdRaw = (parsed as BackendSelection).orgId; | |
| return { | |
| backendId: (parsed as BackendSelection).backendId, | |
| orgId: | |
| typeof orgIdRaw === "string" && orgIdRaw.length > 0 ? orgIdRaw : null, | |
| }; | |
| } catch { | |
| return null; | |
| } | |
| } | |
| function readStorageItem( | |
| storage: Storage | undefined, | |
| key: string, | |
| ): string | null { | |
| try { | |
| return storage?.getItem(key) ?? null; | |
| } catch { | |
| return null; | |
| } | |
| } | |
| function writeStorageItem( | |
| storage: Storage | undefined, | |
| key: string, | |
| value: string, | |
| ): void { | |
| try { | |
| storage?.setItem(key, value); | |
| } catch { | |
| /* ignore */ | |
| } | |
| } | |
| function removeStorageItem(storage: Storage | undefined, key: string): void { | |
| try { | |
| storage?.removeItem(key); | |
| } catch { | |
| /* ignore */ | |
| } | |
| } | |
| export function readStoredActiveBackend(): BackendSelection | null { | |
| if (typeof window === "undefined") return null; | |
| // Active backend is tab-scoped so reloading tab A does not adopt tab B's | |
| // backend. localStorage remains a last-used fallback for fresh tabs and old | |
| // persisted state. | |
| const sessionSelection = parseBackendSelection( | |
| readStorageItem(window.sessionStorage, ACTIVE_BACKEND_STORAGE_KEY), | |
| ); | |
| if (sessionSelection) return sessionSelection; | |
| return parseBackendSelection( | |
| readStorageItem(window.localStorage, ACTIVE_BACKEND_STORAGE_KEY), | |
| ); | |
| } | |
| export function writeStoredActiveBackend( | |
| selection: BackendSelection | null, | |
| ): void { | |
| if (typeof window === "undefined") return; | |
| if (!selection) { | |
| removeStorageItem(window.sessionStorage, ACTIVE_BACKEND_STORAGE_KEY); | |
| removeStorageItem(window.localStorage, ACTIVE_BACKEND_STORAGE_KEY); | |
| return; | |
| } | |
| const serialized = JSON.stringify({ | |
| backendId: selection.backendId, | |
| orgId: selection.orgId ?? null, | |
| }); | |
| // Mirror to localStorage only as the default for new tabs/backward | |
| // compatibility; reads in existing tabs prefer sessionStorage. | |
| writeStorageItem( | |
| window.sessionStorage, | |
| ACTIVE_BACKEND_STORAGE_KEY, | |
| serialized, | |
| ); | |
| writeStorageItem(window.localStorage, ACTIVE_BACKEND_STORAGE_KEY, serialized); | |
| } | |