/**
 * @license
 * Copyright 2026 HopCode Team
 * SPDX-License-Identifier: Apache-2.0
 */
import type { Application } from 'express';
import { type HttpAcpBridge } from './httpAcpBridge.js';
import { type ServeOptions } from './types.js';
export interface ServeAppDeps {
    /** Bridge instance; tests inject a fake. Defaults to a fresh real one. */
    bridge?: HttpAcpBridge;
    /**
     * Pre-canonicalized workspace path. When supplied, `createServeApp`
     * skips its own `canonicalizeWorkspace` call (which would issue a
     * redundant `realpathSync.native` syscall — idempotent, but a hot
     * boot-time stat we can avoid). `runQwenServe` passes this after
     * its own boot-time canonicalize so the value used by
     * `/capabilities`, the `POST /session` cwd fallback, and the
     * bridge are all the SAME canonical form. Callers that haven't
     * canonicalized yet (tests, direct embeds) omit this and
     * `createServeApp` falls back to canonicalizing `opts.workspace ??
     * process.cwd()` itself.
     */
    boundWorkspace?: string;
}
/**
 * Build the Express app for `hopcode serve`. Pure function — no side effects on
 * the network or process; `runHopCodeServe` does the listen/signal handling.
 *
 * `getPort` is invoked lazily by the host-allowlist middleware so callers
 * binding to port 0 (ephemeral) can supply the actual port after `listen()`
 * resolves. Defaults to `opts.port` for callers (e.g. tests) that pin a port
 * up front.
 *
 * Stage 1 routes shipped (matches §04 of issue #3803):
 *   - `GET  /health`
 *   - `GET  /capabilities`
 *   - `POST /session`
 *   - `GET  /workspace/:id/sessions`
 *   - `POST /session/:id/prompt`
 *   - `POST /session/:id/cancel`
 *   - `POST /session/:id/model`
 *   - `GET  /session/:id/events` (SSE)
 *   - `POST /permission/:requestId`
 *
 * **Workspace validation contract.** `createServeApp` itself does NOT
 * verify that `opts.workspace` exists or is a directory — it
 * canonicalizes via `canonicalizeWorkspace`, which falls back to
 * `path.resolve` on ENOENT so the app boots even against a missing
 * path. `runQwenServe` is the production entry point and DOES
 * perform the `fs.statSync` + `isDirectory()` boot-loud check before
 * calling this function. Tests inject synthetic paths (`/work/bound`
 * etc.) on purpose: they want to exercise the route layer's
 * canonicalization and `workspace_mismatch` translation without
 * needing a real directory on disk. If a future entry point binds
 * `createServeApp` directly to user input, it MUST replicate the
 * `runQwenServe` validation (or call into a shared helper if one is
 * extracted) — otherwise a non-existent `--workspace` would boot
 * a "healthy"-looking daemon whose every spawn fails with cryptic
 * child-process ENOENT.
 */
export declare function createServeApp(opts: ServeOptions, getPort?: () => number, deps?: ServeAppDeps): Application;
