| 37 | * them via `env`, `extraProfileCreateArgs`, or `extraDaemonStartArgs`. |
| 38 | */ |
| 39 | export class HindsightServer { |
| 40 | private readonly profile: string; |
| 41 | private readonly port: number; |
| 42 | private readonly host: string; |
| 43 | private readonly baseUrl: string; |
| 44 | private readonly embedVersion: string | undefined; |
| 45 | private readonly embedPackagePath: string | undefined; |
| 46 | private readonly userEnv: Record<string, string | undefined>; |
| 47 | private readonly extraProfileCreateArgs: string[]; |
| 48 | private readonly extraDaemonStartArgs: string[]; |
| 49 | private readonly platformCpuWorkaround: boolean; |
| 50 | private readonly readyTimeoutMs: number; |
| 51 | private readonly readyPollIntervalMs: number; |
| 52 | private readonly logger: Logger; |
| 53 | |
| 54 | constructor(opts: HindsightServerOptions = {}) { |
| 55 | this.profile = opts.profile ?? DEFAULT_PROFILE; |
| 56 | this.port = opts.port ?? DEFAULT_PORT; |
| 57 | this.host = opts.host ?? DEFAULT_HOST; |
| 58 | this.baseUrl = `http://${this.host}:${this.port}`; |
| 59 | this.embedVersion = opts.embedVersion; |
| 60 | this.embedPackagePath = opts.embedPackagePath; |
| 61 | this.userEnv = opts.env ?? {}; |
| 62 | this.extraProfileCreateArgs = opts.extraProfileCreateArgs ?? []; |
| 63 | this.extraDaemonStartArgs = opts.extraDaemonStartArgs ?? []; |
| 64 | this.platformCpuWorkaround = opts.platformCpuWorkaround ?? process.platform === "darwin"; |
| 65 | this.readyTimeoutMs = opts.readyTimeoutMs ?? DEFAULT_READY_TIMEOUT_MS; |
| 66 | this.readyPollIntervalMs = opts.readyPollIntervalMs ?? DEFAULT_READY_POLL_INTERVAL_MS; |
| 67 | this.logger = opts.logger ?? silentLogger; |
| 68 | } |
| 69 | |
| 70 | /** The base URL the daemon listens on (`http://host:port`). */ |
| 71 | getBaseUrl(): string { |
| 72 | return this.baseUrl; |
| 73 | } |
| 74 | |
| 75 | /** The profile name this server operates on. */ |
| 76 | getProfile(): string { |
| 77 | return this.profile; |
| 78 | } |
| 79 | |
| 80 | /** |
| 81 | * Ensure the daemon is configured and running. Idempotent — the underlying |
| 82 | * `profile create --merge` and `daemon start` commands tolerate re-runs. |
| 83 | */ |
| 84 | async start(): Promise<void> { |
| 85 | this.logger.info(`[hindsight] starting daemon for profile "${this.profile}"`); |
| 86 | |
| 87 | const env = this.buildEnv(); |
| 88 | await this.configureProfile(env); |
| 89 | await this.startDaemon(env); |
| 90 | await this.waitForReady(); |
| 91 | |
| 92 | this.logger.info(`[hindsight] daemon ready at ${this.baseUrl}`); |
| 93 | } |
| 94 | |
| 95 | /** Stop the daemon. Never throws — logs and resolves even on failure. */ |
| 96 | async stop(): Promise<void> { |
no outgoing calls