/** * Relay route table (CLI side). * * `matchRoute` is the pure path matcher translating `method + path` into a * `RouteMatch`; `dispatchRoute` is the inner switch that calls the pure REST * handlers (`handlers/projects.ts`, `handlers/sessions.ts`, ...). Extracted * from `relay/dispatcher.ts` — `handleRestRequest` (`relay/rest.ts`) owns the * single try/catch boundary around `dispatchRoute`, and the dispatcher * re-exports `matchRoute` + the request-deps types so existing imports keep * working. */ import type { MetaEvent } from "@aexol/relay-protocol"; import type { McpExtensionState } from "../mcp/state.js"; import type { SessionStreamManager } from "../server/session-stream.js"; import type { DevProcessRegistry } from "../server/dev-process-registry.js"; import type { SessionStore } from "../server/storage.js"; import { type WizardLoginResult } from "./wizard-login.js"; /** * Result of matching a request path against a route table entry. * `id` is populated when the route had an `:id` placeholder. */ interface RouteMatch { route: "list_projects" | "create_project" | "update_project" | "delete_project" | "bind_studio" | "unbind_studio" | "get_project_observations" | "list_project_sessions" | "cleanup_project_sessions" | "cleanup_all_sessions" | "create_session" | "get_session" | "get_session_active_agent" | "update_session_active_agent" | "get_session_messages" | "update_session" | "delete_session" | "get_session_memory" | "get_session_memory_details" | "compact_session" | "remember_and_delete_session" | "fork_session" | "list_path_autocomplete" | "list_directory" | "get_home_directory" | "create_directory" | "search_project_files" | "enqueue_prompt" | "get_prompt_queue" | "remove_prompt" | "clear_prompt_queue" | "list_mcp_status" | "reload_mcp_config" | "start_mcp_auth" | "refresh_native_extensions" | "refresh_cloud_agents" | "get_settings" | "put_settings" | "get_agent_settings" | "put_agent_settings" | "get_prompt_mutation_settings" | "put_prompt_mutation_settings" | "wizard_login" | "list_agents" | "list_dev_processes" | "start_dev_process" | "kill_dev_process" | "restart_dev_process" | "scan_dev_servers" | "kill_dev_server" | "list_secrets" | "create_secret" | "delete_secret"; id?: string; /** Parsed query params, if the path carried a `?...` suffix. */ query?: URLSearchParams; /** For `remove_prompt`: the queue item id extracted from the path. */ itemId?: string; /** For `kill_dev_server`: optional target port from the query string. */ port?: number; } /** * Inline path matcher. Returns `null` for any path/method combination we * don't recognise; the caller turns that into a `404 Unknown route`. * * Intentionally literal — a regex table would be marginally fancier but * also marginally slower and harder to read for ~11 routes. */ export declare function matchRoute(method: string, path: string): RouteMatch | null; /** * Best-effort publisher for project/session lifecycle meta events. Bound * by `serve.ts` to `relay.send({kind:"meta_event", machineId, event})`; * tests pass a recording stub. Always called inside a try/catch by the * dispatcher so a publish failure never breaks the REST response — the * mutation already succeeded. * * Omit (`undefined`) to disable publishing entirely (used in the handler * unit tests where there's no relay). */ export type PublishMetaEvent = (event: MetaEvent) => void; export interface RestRequestDeps { store: SessionStore; manager: SessionStreamManager; /** Working directory for this machine. Used by settings persistence. Defaults to homedir. */ cwd?: string; /** Agent config dir (`~/.spectral/agent` by default). Passed through to agent-settings persistence. */ agentDir?: string; /** * Optional logger for unexpected errors and per-request timing. * Defaults to `console`; optional methods are guarded so a minimal * `{ error }` logger (used by tests) remains valid. */ logger?: { error?: (...args: unknown[]) => void; info?: (...args: unknown[]) => void; }; /** * Optional MCP extension state. Passed through by `serve.ts` from the * `AgentBridge`. When set, `GET /api/mcp-status` returns the current * machine-local MCP snapshot. */ mcpState?: McpExtensionState; /** Machine-level dev process registry. Used by `/api/dev-processes`. */ devProcessRegistry?: DevProcessRegistry; /** * Optional meta-event publisher. Invoked AFTER each successful mutation * (create/update/delete project, create/update/delete session) so all * tabs of all browsers subscribed to this machine refetch. Read paths * (`list_*`, `get_*`) never publish. * * Failure handling: any throw is caught + logged; we never propagate * publish failures into the REST response (the SQLite mutation has * already committed and the caller deserves the success it asked for). */ publishMetaEvent?: PublishMetaEvent; refreshNativeExtensions?: () => Promise; /** * Refresh the cloud-agent cache. Returns a result describing whether the * backend rejected auth; callers that just need a cache warm may ignore it * (the bridge handles auth-rejection separately via onCloudAgentsAuthRejected). */ refreshCloudAgents?: (teamId: string) => Promise; startWizardLogin?: () => Promise; } /** * Inner switch: route → handler call. Separated so `handleRestRequest` * owns ONE try/catch boundary for all error mapping. * * Meta publish is fired AFTER the handler returns successfully (i.e. * after SQLite has committed) so subscribers never see a "create" event * for a row the next read won't find. Publish failures are swallowed * inside `safePublish` — the wire response goes back regardless. * * The body is `unknown` from the wire; each handler validates its own * shape via the `BadRequestError`-throwing checks they already do. */ export declare function dispatchRoute(match: RouteMatch, body: unknown, deps: RestRequestDeps): Promise; export {}; //# sourceMappingURL=routing.d.ts.map