agent-ecosystem/src/renderer/api/index.ts
matt ce4116dd85 feat(docker): add standalone mode and Docker support
- Introduced a new Docker setup for running claude-devtools in standalone mode without Electron.
- Added Dockerfile and docker-compose.yml for easy deployment.
- Implemented .dockerignore to exclude unnecessary files from the Docker context.
- Updated package.json with new scripts for building and running the standalone server.
- Enhanced README with Docker usage instructions and environment variable configurations.
- Modified HttpServer to support serving static files and API in standalone mode.
- Updated various components to ensure compatibility with standalone operation.
2026-02-16 22:57:48 +09:00

68 lines
2.4 KiB
TypeScript

/**
* Unified API adapter.
*
* When running inside Electron, the preload script exposes `window.electronAPI`.
* When running in a browser (e.g. via the HTTP server), we fall back to an
* HTTP+SSE client that implements the same interface.
*
* All renderer code should import `api` from this module instead of
* accessing `window.electronAPI` directly.
*
* The instance is resolved lazily on first property access so that test code
* can install mocks on `window.electronAPI` before the adapter resolves.
*/
import { HttpAPIClient } from './httpClient';
import type { ElectronAPI } from '@shared/types/api';
/**
* Resolves the base URL for the HTTP API client.
*
* - Electron "server mode" (browser opened via ?port=XXXX): use explicit port on 127.0.0.1
* - Standalone/Docker (page served by the same server): use window.location.origin
* to avoid cross-origin issues (localhost vs 127.0.0.1)
*/
function getHttpBaseUrl(): string {
const params = new URLSearchParams(window.location.search);
const explicitPort = params.get('port');
if (explicitPort) {
return `http://127.0.0.1:${parseInt(explicitPort, 10)}`;
}
return window.location.origin;
}
let httpClient: HttpAPIClient | null = null;
function getImpl(): ElectronAPI {
if (window.electronAPI) return window.electronAPI;
// Lazily create the HTTP client only when actually needed (browser mode).
// Caching avoids creating multiple EventSource connections.
if (!httpClient) {
httpClient = new HttpAPIClient(getHttpBaseUrl());
}
return httpClient;
}
/**
* Proxy that lazily resolves the underlying ElectronAPI on first property access.
* In Electron: delegates to `window.electronAPI` (set by preload).
* In browser: delegates to `HttpAPIClient` (created on first use).
* In tests: delegates to whatever mock is installed on `window.electronAPI`.
*/
/**
* Whether the app is running inside Electron (true) or in a browser via HTTP server (false).
* Use this to hide Electron-only UI (settings, traffic lights, etc.) in browser mode.
*/
export const isElectronMode = (): boolean => !!window.electronAPI;
export const api: ElectronAPI = new Proxy({} as ElectronAPI, {
get(_target, prop, receiver) {
const impl = getImpl();
const value = Reflect.get(impl, prop, receiver) as unknown;
if (typeof value === 'function') {
return (value as (...args: unknown[]) => unknown).bind(impl);
}
return value;
},
});