@cleocode/runtime 2026.5.130 → 2026.5.132
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-5ZXRYEQ5.js +190 -0
- package/dist/chunk-5ZXRYEQ5.js.map +1 -0
- package/dist/chunk-R4XBXUQN.js +253 -0
- package/dist/chunk-R4XBXUQN.js.map +1 -0
- package/dist/chunk-WSQQ5DQL.js +215 -0
- package/dist/chunk-WSQQ5DQL.js.map +1 -0
- package/dist/chunk-YZBIPGMH.js +305 -0
- package/dist/chunk-YZBIPGMH.js.map +1 -0
- package/dist/daemon/index.d.ts +306 -0
- package/dist/daemon/index.js +15 -0
- package/dist/daemon/index.js.map +1 -0
- package/dist/gateway/http/index.d.ts +10 -0
- package/dist/gateway/http/index.js +21 -0
- package/dist/gateway/http/index.js.map +1 -0
- package/dist/gateway/index.d.ts +10 -0
- package/dist/gateway/index.js +1220 -0
- package/dist/gateway/index.js.map +1 -0
- package/dist/gateway/mcp/index.d.ts +10 -0
- package/dist/gateway/mcp/index.js +21 -0
- package/dist/gateway/mcp/index.js.map +1 -0
- package/dist/gateway/rpc/index.d.ts +1545 -0
- package/dist/gateway/rpc/index.js +17 -0
- package/dist/gateway/rpc/index.js.map +1 -0
- package/dist/index.d.ts +78 -2
- package/dist/index.js +253 -1
- package/dist/index.js.map +1 -1
- package/package.json +24 -3
|
@@ -0,0 +1,1545 @@
|
|
|
1
|
+
import { DomainHandler, Middleware, DispatchRequest, DispatchResponse, Gateway, GatewayStreamEvent, Source, DispatchResponseMeta, CanonicalDomain, Tier } from '@cleocode/contracts/gateway';
|
|
2
|
+
import { BackgroundJobManager } from '@cleocode/core/store/background-jobs.js';
|
|
3
|
+
import { Subsystem, OperationDef, SuggestedNextOp, Resolution } from '@cleocode/contracts';
|
|
4
|
+
import { Server } from 'node:http';
|
|
5
|
+
import '@cleocode/contracts/operations/orchestrate';
|
|
6
|
+
import * as _cleocode_core_internal from '@cleocode/core/internal';
|
|
7
|
+
import { EngineResult, ProblemDetails } from '@cleocode/core';
|
|
8
|
+
import { GatewayRpcRequestFrame, GatewayRpcErrorFrame, GatewayRpcErrorCode, GatewayRpcResponseFrame } from '@cleocode/contracts/gateway/rpc';
|
|
9
|
+
import { Server as Server$1 } from 'node:net';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Central Dispatcher -- Routes requests through middleware to domain handlers.
|
|
13
|
+
*
|
|
14
|
+
* The dispatcher is the single entry point for the CLI adapter.
|
|
15
|
+
* It resolves operations, validates parameters, runs the middleware pipeline,
|
|
16
|
+
* and delegates to the appropriate domain handler.
|
|
17
|
+
*
|
|
18
|
+
* Flow: DispatchRequest → resolve → param validation → middleware → handler
|
|
19
|
+
*
|
|
20
|
+
* ## T1088 — Dialectic Evaluator Hook (PSYCHE Wave 3)
|
|
21
|
+
*
|
|
22
|
+
* After each successful mutate operation, the dispatcher fires a background
|
|
23
|
+
* `evaluateDialectic` call via `setImmediate` if all of the following apply:
|
|
24
|
+
*
|
|
25
|
+
* 1. The response indicates `success: true`.
|
|
26
|
+
* 2. The request gateway is `"mutate"` (state-modifying user-facing commands).
|
|
27
|
+
* 3. A `sessionId` is present on the request (identifies conversation context).
|
|
28
|
+
* 4. The in-memory rate limiter allows the evaluation (1 per 10s per session).
|
|
29
|
+
*
|
|
30
|
+
* Errors from the background evaluation are caught and logged at `warn` level
|
|
31
|
+
* so they never affect the caller.
|
|
32
|
+
*
|
|
33
|
+
* @epic T4820
|
|
34
|
+
* @task T1088
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
interface DispatcherConfig {
|
|
38
|
+
handlers: Map<string, DomainHandler>;
|
|
39
|
+
middlewares?: Middleware[];
|
|
40
|
+
}
|
|
41
|
+
declare class Dispatcher {
|
|
42
|
+
private handlers;
|
|
43
|
+
private pipeline;
|
|
44
|
+
constructor(config: DispatcherConfig);
|
|
45
|
+
dispatch(request: DispatchRequest): Promise<DispatchResponse>;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* `node:http` server binding for the framework-agnostic HTTP gateway adapter.
|
|
50
|
+
*
|
|
51
|
+
* The HTTP adapter ({@link routeUnary} + the SSE primitives) is deliberately
|
|
52
|
+
* framework-agnostic — it owns NO port binding and NO `Request`/`Response`
|
|
53
|
+
* lifecycle, so an embedder (a SvelteKit route, a test harness) can drive it
|
|
54
|
+
* directly. The **daemon**, however, is itself the embedder: it must expose the
|
|
55
|
+
* gateway to external HTTP clients over a real listening socket. This module is
|
|
56
|
+
* that thin embedder — a minimal `node:http` server that parses the request URL
|
|
57
|
+
* into a `(gateway, domain, operation)` triple, reads the JSON body, and routes
|
|
58
|
+
* it through {@link routeUnary}. It deliberately stays in the runtime (no
|
|
59
|
+
* SvelteKit / no `@cleocode/cleo` / no drizzle dependency) so the daemon can
|
|
60
|
+
* host the gateway without reaching across a package boundary.
|
|
61
|
+
*
|
|
62
|
+
* Routing contract (intentionally minimal — the wire vocabulary is the same as
|
|
63
|
+
* every other transport, only the framing differs):
|
|
64
|
+
*
|
|
65
|
+
* `POST /<gateway>/<domain>/<operation>` with a JSON body → operation params
|
|
66
|
+
*
|
|
67
|
+
* where `<gateway>` is `query` | `mutate`. A non-POST method, an unparseable
|
|
68
|
+
* path, or a body that is not a JSON object is rejected at the wire edge with a
|
|
69
|
+
* LAFS-shaped error envelope — the injected {@link GatewayHandler} is never
|
|
70
|
+
* invoked with a malformed request.
|
|
71
|
+
*
|
|
72
|
+
* @packageDocumentation
|
|
73
|
+
* @module @cleocode/runtime/gateway/http
|
|
74
|
+
*
|
|
75
|
+
* @task T11451
|
|
76
|
+
* @epic T11254
|
|
77
|
+
* @saga T11243 SG-RUNTIME-UNIFICATION
|
|
78
|
+
*/
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Options for {@link startHttpServer}.
|
|
82
|
+
*
|
|
83
|
+
* Mirrors the connection-oriented {@link RpcServerOptions} shape: the caller
|
|
84
|
+
* resolves the bind coordinates so the runtime stays free of any
|
|
85
|
+
* `@cleocode/paths` dependency.
|
|
86
|
+
*/
|
|
87
|
+
interface HttpServerOptions {
|
|
88
|
+
/**
|
|
89
|
+
* TCP port to bind. `0` selects an ephemeral port (the bound port is then
|
|
90
|
+
* readable from {@link HttpServerHandle.port}).
|
|
91
|
+
*/
|
|
92
|
+
port: number;
|
|
93
|
+
/**
|
|
94
|
+
* Host/interface to bind. Defaults to `127.0.0.1` (loopback only) — the
|
|
95
|
+
* gateway is local-process-facing; exposing it on a public interface is an
|
|
96
|
+
* explicit caller decision.
|
|
97
|
+
*/
|
|
98
|
+
host?: string;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* A live HTTP server handle returned by {@link startHttpServer}.
|
|
102
|
+
*
|
|
103
|
+
* Exposes the bound {@link Server}, the actually-bound `port` (resolved even
|
|
104
|
+
* when `0` was requested), and an idempotent {@link close}. The adapter never
|
|
105
|
+
* calls `process.exit` — lifecycle stays with the caller (R3 contract).
|
|
106
|
+
*/
|
|
107
|
+
interface HttpServerHandle {
|
|
108
|
+
/** The bound `node:http` server. */
|
|
109
|
+
server: Server;
|
|
110
|
+
/** The actually-bound TCP port (resolved when `0` was requested). */
|
|
111
|
+
port: number;
|
|
112
|
+
/** Host/interface the server is bound to. */
|
|
113
|
+
host: string;
|
|
114
|
+
/** Close the server + all live connections; resolves once fully closed. */
|
|
115
|
+
close(): Promise<void>;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* The result of parsing a request line into routing coordinates: either a
|
|
119
|
+
* resolved unary triple or a rejection reason for the wire edge.
|
|
120
|
+
*/
|
|
121
|
+
type ParsedRoute = {
|
|
122
|
+
ok: true;
|
|
123
|
+
gateway: Gateway;
|
|
124
|
+
domain: string;
|
|
125
|
+
operation: string;
|
|
126
|
+
} | {
|
|
127
|
+
ok: false;
|
|
128
|
+
status: number;
|
|
129
|
+
code: string;
|
|
130
|
+
message: string;
|
|
131
|
+
};
|
|
132
|
+
/**
|
|
133
|
+
* Parse `POST /<gateway>/<domain>/<operation>` into a routing triple.
|
|
134
|
+
*
|
|
135
|
+
* Rejects a non-POST method (`405`) and any path that does not resolve to a
|
|
136
|
+
* known `(gateway, domain, operation)` triple (`404`). The gateway segment is
|
|
137
|
+
* validated against the two CQRS gateways so a client cannot smuggle an
|
|
138
|
+
* arbitrary value into the dispatch request.
|
|
139
|
+
*
|
|
140
|
+
* @param method - The HTTP request method.
|
|
141
|
+
* @param url - The HTTP request URL (path + query).
|
|
142
|
+
* @returns The parsed route, or a typed rejection for the wire edge.
|
|
143
|
+
*/
|
|
144
|
+
declare function parseHttpRoute(method: string | undefined, url: string | undefined): ParsedRoute;
|
|
145
|
+
/**
|
|
146
|
+
* Start a `node:http` server that hosts the gateway over HTTP for external
|
|
147
|
+
* clients.
|
|
148
|
+
*
|
|
149
|
+
* Binds {@link HttpServerOptions.port} on {@link HttpServerOptions.host}
|
|
150
|
+
* (default loopback), routes every `POST /<gateway>/<domain>/<operation>`
|
|
151
|
+
* through the injected {@link GatewayHandler} via {@link routeUnary}, and
|
|
152
|
+
* resolves once listening. The returned {@link HttpServerHandle} exposes an
|
|
153
|
+
* idempotent `close()` for deterministic teardown (daemon shutdown, tests). The
|
|
154
|
+
* adapter never calls `process.exit`.
|
|
155
|
+
*
|
|
156
|
+
* The caller assembles the handler (via `createGatewayHandler` with its domain
|
|
157
|
+
* handlers + middleware) and injects it here — this server never builds
|
|
158
|
+
* handlers itself, keeping the runtime free of any `@cleocode/cleo` dependency.
|
|
159
|
+
*
|
|
160
|
+
* @param handler - The transport-neutral gateway handler to route through.
|
|
161
|
+
* @param opts - Bind coordinates.
|
|
162
|
+
* @returns A promise resolving to the live {@link HttpServerHandle}.
|
|
163
|
+
*/
|
|
164
|
+
declare function startHttpServer(handler: GatewayHandler, opts: HttpServerOptions): Promise<HttpServerHandle>;
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* NDJSON codec for the CLI-RPC transport.
|
|
168
|
+
*
|
|
169
|
+
* Implements the SAME framing discipline as the FROZEN `supervisor-ipc` v1.0
|
|
170
|
+
* contract — one zod-validated JSON envelope per newline-delimited line, with
|
|
171
|
+
* an explicit `protocol_version` negotiation step — but for the gateway-RPC
|
|
172
|
+
* frame set (`@cleocode/contracts/gateway/rpc`). The supervisor-ipc module is
|
|
173
|
+
* frozen and is NOT edited; this codec re-applies its pattern.
|
|
174
|
+
*
|
|
175
|
+
* The codec is intentionally socket-agnostic: it owns line buffering, JSON
|
|
176
|
+
* parse, zod decode, and version negotiation, returning typed results the
|
|
177
|
+
* server maps onto `node:net` reads/writes. This keeps the parse/validate logic
|
|
178
|
+
* unit-testable without binding a real socket.
|
|
179
|
+
*
|
|
180
|
+
* @task T11449
|
|
181
|
+
* @epic T11254
|
|
182
|
+
* @saga T11243
|
|
183
|
+
*/
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Encode any RPC frame to a single NDJSON line (JSON + trailing `\n`).
|
|
187
|
+
*
|
|
188
|
+
* @param frame - The response or error frame to serialize.
|
|
189
|
+
* @returns A newline-terminated JSON string ready to write to the socket.
|
|
190
|
+
*/
|
|
191
|
+
declare function encodeFrame(frame: GatewayRpcResponseFrame | GatewayRpcErrorFrame): string;
|
|
192
|
+
/**
|
|
193
|
+
* Build a protocol-level error frame.
|
|
194
|
+
*
|
|
195
|
+
* @param id - Correlation id (`'0'` when the originating frame's id is unknown).
|
|
196
|
+
* @param code - One of the frozen {@link GatewayRpcErrorCode} values.
|
|
197
|
+
* @param message - Human-readable detail.
|
|
198
|
+
* @returns A fully-formed {@link GatewayRpcErrorFrame}.
|
|
199
|
+
*/
|
|
200
|
+
declare function buildErrorFrame(id: string, code: GatewayRpcErrorCode, message: string): GatewayRpcErrorFrame;
|
|
201
|
+
/**
|
|
202
|
+
* The discriminated outcome of decoding a single inbound NDJSON line.
|
|
203
|
+
*
|
|
204
|
+
* - `request` — a valid, version-matched request frame ready to route.
|
|
205
|
+
* - `error` — a protocol-level error frame the server should write straight
|
|
206
|
+
* back (parse failure, version mismatch, or malformed frame). The
|
|
207
|
+
* server never routes these to the gateway.
|
|
208
|
+
*/
|
|
209
|
+
type DecodeResult = {
|
|
210
|
+
kind: 'request';
|
|
211
|
+
frame: GatewayRpcRequestFrame;
|
|
212
|
+
} | {
|
|
213
|
+
kind: 'error';
|
|
214
|
+
frame: GatewayRpcErrorFrame;
|
|
215
|
+
};
|
|
216
|
+
/**
|
|
217
|
+
* Decode + validate a single inbound NDJSON line into a routable request or a
|
|
218
|
+
* protocol-level error frame.
|
|
219
|
+
*
|
|
220
|
+
* Steps (the supervisor-ipc pattern, applied to gateway frames):
|
|
221
|
+
* 1. JSON parse — failure → `E_RPC_PARSE`.
|
|
222
|
+
* 2. zod decode against {@link GatewayRpcFrameSchema} — failure → `E_RPC_BAD_FRAME`.
|
|
223
|
+
* 3. version negotiation via {@link isFrozenGatewayRpcVersion} —
|
|
224
|
+
* mismatch → `E_RPC_BAD_VERSION`.
|
|
225
|
+
* 4. direction must be `request` (a server never receives response/error
|
|
226
|
+
* frames) — otherwise → `E_RPC_BAD_FRAME`.
|
|
227
|
+
*
|
|
228
|
+
* The best-effort correlation `id` is recovered from the raw JSON when possible
|
|
229
|
+
* so the client can match the error to its request even on a decode failure.
|
|
230
|
+
*
|
|
231
|
+
* @param line - One trimmed NDJSON line (no trailing newline).
|
|
232
|
+
* @returns A {@link DecodeResult}.
|
|
233
|
+
*/
|
|
234
|
+
declare function decodeLine(line: string): DecodeResult;
|
|
235
|
+
/**
|
|
236
|
+
* A streaming line splitter for the NDJSON wire.
|
|
237
|
+
*
|
|
238
|
+
* `node:net` sockets deliver arbitrary chunk boundaries; a single `data` event
|
|
239
|
+
* may carry a partial line or several lines. This buffer accumulates bytes and
|
|
240
|
+
* yields only complete lines (split on `\n`), matching the supervisor-ipc
|
|
241
|
+
* client's `readline`-style framing without depending on `readline` (so it can
|
|
242
|
+
* be driven directly off socket chunks).
|
|
243
|
+
*/
|
|
244
|
+
declare class LineBuffer {
|
|
245
|
+
/** Pending bytes that have not yet completed a line. */
|
|
246
|
+
private buffer;
|
|
247
|
+
/**
|
|
248
|
+
* Push a decoded UTF-8 chunk; return every complete line it completes.
|
|
249
|
+
*
|
|
250
|
+
* @param chunk - A UTF-8 string chunk from the socket.
|
|
251
|
+
* @returns Zero or more complete lines (trailing newline stripped, blanks
|
|
252
|
+
* skipped).
|
|
253
|
+
*/
|
|
254
|
+
push(chunk: string): string[];
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Wire + lifecycle types for the `@cleocode/runtime/gateway/rpc` adapter.
|
|
259
|
+
*
|
|
260
|
+
* The RPC wire frames themselves are defined (zod) in
|
|
261
|
+
* `@cleocode/contracts/gateway/rpc`; this module holds only the
|
|
262
|
+
* runtime-adapter-local option/handle shapes (no external dependency, mirroring
|
|
263
|
+
* the MCP adapter's `types.ts`).
|
|
264
|
+
*
|
|
265
|
+
* @task T11449
|
|
266
|
+
* @epic T11254
|
|
267
|
+
* @saga T11243
|
|
268
|
+
*/
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Options for {@link startRpcServer}.
|
|
272
|
+
*
|
|
273
|
+
* Mirrors the MCP adapter's `McpServerOptions` shape (a transport-local options
|
|
274
|
+
* bag), adapted for a connection-oriented unix-socket server rather than a
|
|
275
|
+
* single stdio stream.
|
|
276
|
+
*/
|
|
277
|
+
interface RpcServerOptions {
|
|
278
|
+
/**
|
|
279
|
+
* Absolute path to the unix-domain socket to bind. The caller resolves this
|
|
280
|
+
* (e.g. under the CLEO home using
|
|
281
|
+
* {@link GATEWAY_RPC_CHANNEL_BASENAME}) so the runtime stays free of any
|
|
282
|
+
* `@cleocode/paths` dependency.
|
|
283
|
+
*/
|
|
284
|
+
socketPath: string;
|
|
285
|
+
/**
|
|
286
|
+
* Whether to `unlink` a stale socket file at {@link socketPath} before
|
|
287
|
+
* binding. Defaults to `true` (a crashed prior server leaves a stale node).
|
|
288
|
+
*/
|
|
289
|
+
removeStaleSocket?: boolean;
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* A live RPC server handle returned by {@link startRpcServer}.
|
|
293
|
+
*
|
|
294
|
+
* Exposes the bound {@link Server} and an idempotent {@link close} so callers
|
|
295
|
+
* (daemon, tests) can tear down deterministically. The adapter never calls
|
|
296
|
+
* `process.exit` — lifecycle stays with the caller (R3 contract).
|
|
297
|
+
*/
|
|
298
|
+
interface RpcServerHandle {
|
|
299
|
+
/** The bound `node:net` server. */
|
|
300
|
+
server: Server$1;
|
|
301
|
+
/** Absolute path of the bound unix socket. */
|
|
302
|
+
socketPath: string;
|
|
303
|
+
/** Close the server + all live connections; resolves once fully closed. */
|
|
304
|
+
close(): Promise<void>;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* CLI-RPC unix-socket transport adapter — routes NDJSON frames through the gateway.
|
|
309
|
+
*
|
|
310
|
+
* Serves the gateway over a long-lived unix-domain socket using newline-delimited
|
|
311
|
+
* JSON (NDJSON): one zod-validated {@link GatewayRpcFrame} per line. Any local
|
|
312
|
+
* client (the `cleo` CLI talking to a warm daemon, Studio's server process, a
|
|
313
|
+
* test harness) can open the socket, write `request` frames, and read back
|
|
314
|
+
* `response`/`error` frames — without paying the per-invocation cold-start of
|
|
315
|
+
* spawning a fresh `cleo` process.
|
|
316
|
+
*
|
|
317
|
+
* This is the THIRD transport adapter, mirroring the just-merged MCP stdio
|
|
318
|
+
* adapter (`packages/runtime/src/gateway/mcp/`):
|
|
319
|
+
*
|
|
320
|
+
* RPC request frame { direction:'request', request:{…} }
|
|
321
|
+
* → decode + validate (`@cleocode/contracts/gateway/rpc`)
|
|
322
|
+
* → DispatchRequest { source: 'rpc', … } (re-validated at the boundary)
|
|
323
|
+
* → injected {@link GatewayHandler}.handle()
|
|
324
|
+
* → DispatchResponse (LAFS envelope) → RPC response frame
|
|
325
|
+
*
|
|
326
|
+
* The adapter owns ONLY wire concerns: socket lifecycle, line framing, version
|
|
327
|
+
* negotiation, error rendering, and per-connection cleanup. All domain logic
|
|
328
|
+
* (resolution, validation, middleware, handler execution) lives behind the
|
|
329
|
+
* {@link GatewayHandler} — exactly mirroring the CLI and MCP adapters. There is
|
|
330
|
+
* NO `process.exit` and NO error-render inside the handlers (R3 contract); the
|
|
331
|
+
* adapter never calls `process.exit` at all (lifecycle is the caller's).
|
|
332
|
+
*
|
|
333
|
+
* The request `source` is forced to `'rpc'` regardless of what the client sent,
|
|
334
|
+
* so a client cannot impersonate another transport.
|
|
335
|
+
*
|
|
336
|
+
* @task T11449
|
|
337
|
+
* @epic T11254
|
|
338
|
+
* @saga T11243
|
|
339
|
+
*/
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Route a single decoded request frame through the gateway handler and build
|
|
343
|
+
* the correlated response frame.
|
|
344
|
+
*
|
|
345
|
+
* The request's `source` is forced to `'rpc'`; the frame `id` is reused as the
|
|
346
|
+
* response correlation id (and, when the client omitted a `requestId` shaped
|
|
347
|
+
* for tracing, the frame id flows through as the dispatch `requestId`). Thrown
|
|
348
|
+
* handler errors are caught and rendered as a protocol-level `E_RPC_INTERNAL`
|
|
349
|
+
* error frame — never propagated as an unhandled rejection or `process.exit`.
|
|
350
|
+
*
|
|
351
|
+
* @param handler - The injected transport-neutral gateway handler.
|
|
352
|
+
* @param frame - A validated, version-matched request frame.
|
|
353
|
+
* @returns The response or error frame to write back on the same connection.
|
|
354
|
+
*/
|
|
355
|
+
declare function routeFrame(handler: GatewayHandler, frame: GatewayRpcRequestFrame): Promise<GatewayRpcResponseFrame | ReturnType<typeof buildErrorFrame>>;
|
|
356
|
+
/**
|
|
357
|
+
* Start the CLI-RPC unix-socket server over the gateway.
|
|
358
|
+
*
|
|
359
|
+
* Binds {@link RpcServerOptions.socketPath}, accepts connections, and routes
|
|
360
|
+
* each inbound NDJSON `request` frame through the injected {@link GatewayHandler}.
|
|
361
|
+
* The caller assembles the handler (via `createGatewayHandler` with its domain
|
|
362
|
+
* handlers + middleware) and injects it here — this adapter never builds
|
|
363
|
+
* handlers itself, keeping the runtime free of any `@cleocode/cleo` dependency.
|
|
364
|
+
*
|
|
365
|
+
* Returns once the socket is listening. The returned {@link RpcServerHandle}
|
|
366
|
+
* exposes an idempotent `close()` for deterministic teardown (daemon shutdown,
|
|
367
|
+
* tests). The adapter never calls `process.exit`.
|
|
368
|
+
*
|
|
369
|
+
* @param handler - The transport-neutral gateway handler to route through.
|
|
370
|
+
* @param opts - Socket path + lifecycle options.
|
|
371
|
+
* @returns A promise resolving to the live {@link RpcServerHandle}.
|
|
372
|
+
*/
|
|
373
|
+
declare function startRpcServer(handler: GatewayHandler, opts: RpcServerOptions): Promise<RpcServerHandle>;
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* The gateway as a supervised **daemon subsystem**.
|
|
377
|
+
*
|
|
378
|
+
* R3-T7 (this task) closes the loop between the two R-track halves: the R3
|
|
379
|
+
* gateway (dispatcher + the four transport adapters) and the R2 daemon
|
|
380
|
+
* (`defineSubsystem` + the `SubsystemRegistry`). It expresses the
|
|
381
|
+
* external-facing gateway as a uniform {@link Subsystem} so the R2 daemon
|
|
382
|
+
* registry drives its `start → healthProbe → shutdown` lifecycle exactly like
|
|
383
|
+
* every other long-running concern (Studio supervision, the GC cron, …). One
|
|
384
|
+
* gateway subsystem is registered per scope (project / global) → one
|
|
385
|
+
* external-facing gateway process per scope.
|
|
386
|
+
*
|
|
387
|
+
* Which transports the daemon hosts:
|
|
388
|
+
* - the **RPC unix socket** (`@cleocode/runtime/gateway/rpc`) — the canonical
|
|
389
|
+
* local-client wire: the `cleo` CLI, Studio's server process, and test
|
|
390
|
+
* harnesses open it to dispatch without paying a cold `cleo` spawn.
|
|
391
|
+
* - the **HTTP server** (`@cleocode/runtime/gateway/http` via the daemon-local
|
|
392
|
+
* `startHttpServer` embedder) — for external clients that speak HTTP rather
|
|
393
|
+
* than the unix-socket NDJSON wire.
|
|
394
|
+
*
|
|
395
|
+
* The CLI and MCP adapters are intentionally NOT hosted by the daemon: the CLI
|
|
396
|
+
* adapter is in-process to the `cleo` binary, and the MCP adapter is a stdio
|
|
397
|
+
* server an MCP client spawns and owns — neither is a long-lived,
|
|
398
|
+
* daemon-supervised listener. The two external-facing listeners (RPC + HTTP)
|
|
399
|
+
* are the ones the daemon owns.
|
|
400
|
+
*
|
|
401
|
+
* This subsystem adds NO behavior to the adapters themselves — it only wires
|
|
402
|
+
* their existing `start*Server` / `close()` lifecycle (and their already-merged
|
|
403
|
+
* routing) into the daemon registry's uniform lifecycle. It carries NO
|
|
404
|
+
* `@cleocode/cleo` dependency and NO drizzle-orm (the runtime invariant): the
|
|
405
|
+
* caller injects the assembled {@link GatewayHandler} and the resolved bind
|
|
406
|
+
* coordinates; this module only orchestrates listeners.
|
|
407
|
+
*
|
|
408
|
+
* @packageDocumentation
|
|
409
|
+
* @module @cleocode/runtime/gateway
|
|
410
|
+
*
|
|
411
|
+
* @task T11451 (R3-T7)
|
|
412
|
+
* @epic T11254
|
|
413
|
+
* @saga T11243 SG-RUNTIME-UNIFICATION
|
|
414
|
+
*/
|
|
415
|
+
|
|
416
|
+
/** The scope a single gateway process serves — one external-facing process per scope. */
|
|
417
|
+
type GatewayScope = 'project' | 'global';
|
|
418
|
+
/**
|
|
419
|
+
* Configuration for one scoped gateway subsystem.
|
|
420
|
+
*
|
|
421
|
+
* The caller injects the assembled, transport-neutral {@link GatewayHandler}
|
|
422
|
+
* (built with `createGatewayHandler` from its domain handlers + middleware) and
|
|
423
|
+
* the resolved bind coordinates for each hosted transport. The runtime never
|
|
424
|
+
* resolves paths/ports itself — that keeps it free of `@cleocode/paths` and of
|
|
425
|
+
* any policy decision about where a scope's socket/port lives.
|
|
426
|
+
*/
|
|
427
|
+
interface GatewaySubsystemOptions {
|
|
428
|
+
/** The scope this gateway process serves (`project` | `global`). */
|
|
429
|
+
scope: GatewayScope;
|
|
430
|
+
/** The transport-neutral gateway handler every hosted transport routes through. */
|
|
431
|
+
handler: GatewayHandler;
|
|
432
|
+
/**
|
|
433
|
+
* RPC unix-socket transport options. When omitted, the RPC transport is not
|
|
434
|
+
* hosted (a deployment that only wants HTTP). At least one transport MUST be
|
|
435
|
+
* configured.
|
|
436
|
+
*/
|
|
437
|
+
rpc?: RpcServerOptions;
|
|
438
|
+
/**
|
|
439
|
+
* HTTP transport options. When omitted, the HTTP transport is not hosted (a
|
|
440
|
+
* deployment that only wants the local RPC socket). At least one transport
|
|
441
|
+
* MUST be configured.
|
|
442
|
+
*/
|
|
443
|
+
http?: HttpServerOptions;
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* The live context a started gateway subsystem threads from `start` into
|
|
447
|
+
* `healthProbe`/`shutdown`: the bound transport handles plus the owning pid.
|
|
448
|
+
*
|
|
449
|
+
* `void`-typed contexts are common for subsystems, but the gateway needs to
|
|
450
|
+
* carry its bound handles so `shutdown` can close exactly what `start` opened
|
|
451
|
+
* (and `healthProbe` can report the bound coordinates).
|
|
452
|
+
*/
|
|
453
|
+
interface GatewaySubsystemContext {
|
|
454
|
+
/** The OS pid hosting the listeners (this daemon process). */
|
|
455
|
+
readonly pid: number;
|
|
456
|
+
/** The bound RPC server handle, when the RPC transport is hosted. */
|
|
457
|
+
readonly rpc?: RpcServerHandle;
|
|
458
|
+
/** The bound HTTP server handle, when the HTTP transport is hosted. */
|
|
459
|
+
readonly http?: HttpServerHandle;
|
|
460
|
+
}
|
|
461
|
+
/**
|
|
462
|
+
* The stable subsystem name (and supervised `child_id`) of a scoped gateway.
|
|
463
|
+
*
|
|
464
|
+
* Suffixed with the scope so a single daemon process supervising both scopes
|
|
465
|
+
* (project + global) registers two distinct, non-colliding subsystems. The
|
|
466
|
+
* `child_id` matches the id the Rust supervisor tracks the listener under.
|
|
467
|
+
*
|
|
468
|
+
* @param scope - The gateway scope.
|
|
469
|
+
* @returns The subsystem name, e.g. `gateway-project`.
|
|
470
|
+
*/
|
|
471
|
+
declare function gatewaySubsystemName(scope: GatewayScope): string;
|
|
472
|
+
/**
|
|
473
|
+
* Declare the external-facing gateway as a supervised daemon {@link Subsystem}.
|
|
474
|
+
*
|
|
475
|
+
* The returned subsystem (frozen by {@link defineSubsystem}) is registered with
|
|
476
|
+
* a `SubsystemRegistry`, which then drives its lifecycle uniformly:
|
|
477
|
+
*
|
|
478
|
+
* - `start()` — binds the configured transports (RPC unix socket and/or
|
|
479
|
+
* HTTP server) over the injected handler, in a fixed order (RPC then HTTP).
|
|
480
|
+
* If the second listener fails to bind, the first is rolled back so a failed
|
|
481
|
+
* start leaves no half-open listener — then the error is re-thrown for the
|
|
482
|
+
* registry's `onError` sink.
|
|
483
|
+
* - `healthProbe()` — reports a single {@link SubsystemHealth} row keyed on the
|
|
484
|
+
* scoped `child_id`, `running` while the configured listeners are live, with
|
|
485
|
+
* a `detail` summarizing the bound coordinates. Projects losslessly onto the
|
|
486
|
+
* supervisor's `ChildStatus`.
|
|
487
|
+
* - `shutdown()` — closes every bound listener (best-effort, idempotent) in
|
|
488
|
+
* reverse bind order (HTTP then RPC).
|
|
489
|
+
*
|
|
490
|
+
* One subsystem instance is declared per scope, so registering the project and
|
|
491
|
+
* global gateways yields two supervised listeners → one external-facing gateway
|
|
492
|
+
* process surface per scope (AC1).
|
|
493
|
+
*
|
|
494
|
+
* @param opts - The scoped gateway configuration (handler + per-transport bind
|
|
495
|
+
* coordinates).
|
|
496
|
+
* @returns A frozen {@link Subsystem} ready to `register()` with a
|
|
497
|
+
* `SubsystemRegistry`.
|
|
498
|
+
* @throws {TypeError} When neither the RPC nor the HTTP transport is configured
|
|
499
|
+
* (a gateway with no hosted transport serves nothing).
|
|
500
|
+
*
|
|
501
|
+
* @example
|
|
502
|
+
* ```ts
|
|
503
|
+
* const registry = new SubsystemRegistry();
|
|
504
|
+
* registry.register(
|
|
505
|
+
* defineGatewaySubsystem({
|
|
506
|
+
* scope: 'project',
|
|
507
|
+
* handler: createGatewayHandler(dispatcherConfig),
|
|
508
|
+
* rpc: { socketPath: '/run/cleo/cleo-gateway-rpc-project.sock' },
|
|
509
|
+
* http: { port: 7777 },
|
|
510
|
+
* }),
|
|
511
|
+
* );
|
|
512
|
+
* await registry.startAll(); // binds the RPC socket + HTTP server
|
|
513
|
+
* const health = await registry.aggregateHealth();
|
|
514
|
+
* await registry.shutdownAll(); // closes both listeners
|
|
515
|
+
* ```
|
|
516
|
+
*/
|
|
517
|
+
declare function defineGatewaySubsystem(opts: GatewaySubsystemOptions): Subsystem<GatewaySubsystemContext>;
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* Centralized engine error helper for the transport-agnostic gateway.
|
|
521
|
+
*
|
|
522
|
+
* Every domain handler (and the engine barrel) imports `engineError()` /
|
|
523
|
+
* `engineSuccess()` from this module to produce consistently typed error
|
|
524
|
+
* results with correct exit codes and pino logging.
|
|
525
|
+
*
|
|
526
|
+
* The STRING_TO_EXIT map is the canonical mapping from string error codes
|
|
527
|
+
* to numeric exit codes. It mirrors ERROR_CODE_TO_EXIT in cli.ts.
|
|
528
|
+
*
|
|
529
|
+
* @remarks
|
|
530
|
+
* Relocated from `packages/cleo/src/dispatch/engines/_error.ts` into
|
|
531
|
+
* `@cleocode/runtime/gateway` (R3-K1 · T11455 · SG-RUNTIME-UNIFICATION) so the
|
|
532
|
+
* runtime owns the shared handler dependencies and can assemble the handler map
|
|
533
|
+
* without importing `@cleocode/cleo`. A thin re-export shim remains at the old
|
|
534
|
+
* `engines/_error.ts` path so in-package import sites compile unchanged.
|
|
535
|
+
*
|
|
536
|
+
* Error codes are organized into numeric ranges:
|
|
537
|
+
* - 1-9: General errors (invalid input, file, dependency, validation)
|
|
538
|
+
* - 10-19: Hierarchy errors (parent, depth, siblings, circular deps)
|
|
539
|
+
* - 20-29: Concurrency errors (checksum, modification, collision)
|
|
540
|
+
* - 30-39: Session errors (scope, claims, active task)
|
|
541
|
+
* - 40-47: Verification errors (gates, agents, rounds)
|
|
542
|
+
* - 50-54: Context safeguard (warning through emergency)
|
|
543
|
+
* - 60-67: Orchestrator / protocol errors
|
|
544
|
+
* - 70-79: Nexus errors
|
|
545
|
+
* - 80-94: Lifecycle, artifact, provenance errors
|
|
546
|
+
* - 95-99: Adapter errors
|
|
547
|
+
* - 100+: Special informational codes (not errors)
|
|
548
|
+
*/
|
|
549
|
+
|
|
550
|
+
/**
|
|
551
|
+
* Canonical mapping from string error codes to numeric exit codes.
|
|
552
|
+
*
|
|
553
|
+
* Source of truth: src/types/exit-codes.ts (ExitCode enum).
|
|
554
|
+
* Must stay in sync with ERROR_CODE_TO_EXIT in src/dispatch/adapters/cli.ts.
|
|
555
|
+
*/
|
|
556
|
+
declare const STRING_TO_EXIT: Record<string, number>;
|
|
557
|
+
/**
|
|
558
|
+
* Create a typed engine error result with pino logging and correct exit code.
|
|
559
|
+
*
|
|
560
|
+
* @remarks
|
|
561
|
+
* The exit code is derived from the {@link STRING_TO_EXIT} mapping. If the
|
|
562
|
+
* code is not found in the map, exit code 1 (general error) is used.
|
|
563
|
+
* Pino logging is automatically triggered at the appropriate level
|
|
564
|
+
* (error for internal issues, warn for user/domain errors, debug for
|
|
565
|
+
* informational codes). Logging is suppressed under Vitest to keep
|
|
566
|
+
* test output clean.
|
|
567
|
+
*
|
|
568
|
+
* @param code - String error code (e.g., 'E_NOT_FOUND')
|
|
569
|
+
* @param message - Human-readable error message
|
|
570
|
+
* @param options - Optional details, fix command, and alternatives
|
|
571
|
+
* @returns EngineResult with success=false and properly structured error
|
|
572
|
+
*
|
|
573
|
+
* @example
|
|
574
|
+
* ```typescript
|
|
575
|
+
* import { engineError } from './_error.js';
|
|
576
|
+
*
|
|
577
|
+
* return engineError('E_NOT_FOUND', `Task ${id} not found`, {
|
|
578
|
+
* fix: `cleo show ${id}`,
|
|
579
|
+
* details: { taskId: id },
|
|
580
|
+
* });
|
|
581
|
+
* ```
|
|
582
|
+
*/
|
|
583
|
+
declare function engineError<T>(code: string, message: string, options?: {
|
|
584
|
+
exitCode?: number;
|
|
585
|
+
details?: Record<string, unknown>;
|
|
586
|
+
fix?: string;
|
|
587
|
+
alternatives?: Array<{
|
|
588
|
+
action: string;
|
|
589
|
+
command: string;
|
|
590
|
+
}>;
|
|
591
|
+
problemDetails?: ProblemDetails;
|
|
592
|
+
}): EngineResult<T>;
|
|
593
|
+
|
|
594
|
+
/**
|
|
595
|
+
* Minimal meta shape carrying the request ID for RFC 7807 `instance` population.
|
|
596
|
+
*
|
|
597
|
+
* Callers may pass the dispatch-layer `DispatchRequest.requestId` (or any
|
|
598
|
+
* other correlation identifier) so that the resulting `problemDetails.instance`
|
|
599
|
+
* URI uniquely identifies the specific error occurrence.
|
|
600
|
+
*/
|
|
601
|
+
interface ErrorMeta {
|
|
602
|
+
/** Unique request identifier (used as the RFC 7807 `instance` field). */
|
|
603
|
+
requestId?: string;
|
|
604
|
+
}
|
|
605
|
+
/**
|
|
606
|
+
* Convert any caught value into an {@link EngineResult}, forwarding the rich
|
|
607
|
+
* `fix`, `details`, and `alternatives` fields when the caught value is a
|
|
608
|
+
* `CleoError` (or structurally compatible object).
|
|
609
|
+
*
|
|
610
|
+
* @remarks
|
|
611
|
+
* This is the canonical catch-block helper for all dispatch engines. It
|
|
612
|
+
* replaces the previous pattern of:
|
|
613
|
+
* ```typescript
|
|
614
|
+
* const code = (err as { code?: number })?.code;
|
|
615
|
+
* if (code === 4) return engineError('E_NOT_FOUND', …);
|
|
616
|
+
* …
|
|
617
|
+
* ```
|
|
618
|
+
* with a single call that preserves every field the core layer attached.
|
|
619
|
+
*
|
|
620
|
+
* The `fallbackCode` is used when the numeric `err.code` is not present in
|
|
621
|
+
* the {@link STRING_TO_EXIT} mapping (e.g. unknown exit codes, plain `Error`
|
|
622
|
+
* instances, or values thrown by non-CLEO code).
|
|
623
|
+
*
|
|
624
|
+
* RFC 7807 `problemDetails` is always populated from the resolved code, message,
|
|
625
|
+
* and exit code. The optional `meta.requestId` is forwarded to the `instance`
|
|
626
|
+
* field so every error occurrence is uniquely identifiable.
|
|
627
|
+
*
|
|
628
|
+
* @param err - The caught value (unknown type)
|
|
629
|
+
* @param fallbackCode - String error code to use when mapping fails
|
|
630
|
+
* @param fallbackMessage - Human-readable message when `err.message` is absent
|
|
631
|
+
* @param meta - Optional dispatch meta carrying `requestId` for RFC 7807 `instance`
|
|
632
|
+
* @returns EngineResult with all available rich fields propagated
|
|
633
|
+
*
|
|
634
|
+
* @example
|
|
635
|
+
* ```typescript
|
|
636
|
+
* import { cleoErrorToEngineError } from './_error.js';
|
|
637
|
+
*
|
|
638
|
+
* } catch (err: unknown) {
|
|
639
|
+
* return cleoErrorToEngineError(err, 'E_NOT_INITIALIZED', 'Task database not initialized');
|
|
640
|
+
* }
|
|
641
|
+
* ```
|
|
642
|
+
*
|
|
643
|
+
* @example With requestId for RFC 7807 instance field:
|
|
644
|
+
* ```typescript
|
|
645
|
+
* } catch (err: unknown) {
|
|
646
|
+
* return cleoErrorToEngineError(err, 'E_NOT_FOUND', 'Not found', { requestId });
|
|
647
|
+
* }
|
|
648
|
+
* ```
|
|
649
|
+
*/
|
|
650
|
+
declare function cleoErrorToEngineError<T>(err: unknown, fallbackCode: string, fallbackMessage: string, meta?: ErrorMeta): EngineResult<T>;
|
|
651
|
+
|
|
652
|
+
/**
|
|
653
|
+
* Parse all templates from the repo's .github/ISSUE_TEMPLATE/ directory.
|
|
654
|
+
*
|
|
655
|
+
* @remarks
|
|
656
|
+
* Delegates to the core template parser and adapts the result to the
|
|
657
|
+
* dispatch EngineResult type.
|
|
658
|
+
*
|
|
659
|
+
* @param projectRoot - Absolute path to the project root
|
|
660
|
+
* @returns EngineResult containing the parsed template configuration
|
|
661
|
+
*
|
|
662
|
+
* @example
|
|
663
|
+
* ```typescript
|
|
664
|
+
* const result = parseIssueTemplates('/path/to/project');
|
|
665
|
+
* if (result.success) {
|
|
666
|
+
* console.log(result.data.templates);
|
|
667
|
+
* }
|
|
668
|
+
* ```
|
|
669
|
+
*/
|
|
670
|
+
declare function parseIssueTemplates(projectRoot: string): EngineResult<_cleocode_core_internal.TemplateConfig>;
|
|
671
|
+
/**
|
|
672
|
+
* Get template config for a specific subcommand (bug/feature/help).
|
|
673
|
+
*
|
|
674
|
+
* @remarks
|
|
675
|
+
* Looks up a specific template by subcommand name from the project's
|
|
676
|
+
* issue template directory.
|
|
677
|
+
*
|
|
678
|
+
* @param projectRoot - Absolute path to the project root
|
|
679
|
+
* @param subcommand - Template subcommand identifier (e.g. "bug", "feature")
|
|
680
|
+
* @returns EngineResult containing the matched issue template
|
|
681
|
+
*
|
|
682
|
+
* @example
|
|
683
|
+
* ```typescript
|
|
684
|
+
* const result = getTemplateForSubcommand('/path/to/project', 'bug');
|
|
685
|
+
* ```
|
|
686
|
+
*/
|
|
687
|
+
declare function getTemplateForSubcommand(projectRoot: string, subcommand: string): EngineResult<_cleocode_core_internal.IssueTemplate>;
|
|
688
|
+
/**
|
|
689
|
+
* Generate and cache the config as .cleo/issue-templates.json.
|
|
690
|
+
*
|
|
691
|
+
* @remarks
|
|
692
|
+
* Parses all templates and writes the resulting config to disk for caching.
|
|
693
|
+
*
|
|
694
|
+
* @param projectRoot - Absolute path to the project root
|
|
695
|
+
* @returns EngineResult containing the generated template configuration
|
|
696
|
+
*
|
|
697
|
+
* @example
|
|
698
|
+
* ```typescript
|
|
699
|
+
* const result = await generateTemplateConfig('/path/to/project');
|
|
700
|
+
* ```
|
|
701
|
+
*/
|
|
702
|
+
declare function generateTemplateConfig(projectRoot: string): Promise<EngineResult<_cleocode_core_internal.TemplateConfig>>;
|
|
703
|
+
/**
|
|
704
|
+
* Validate that labels exist on a GitHub repo.
|
|
705
|
+
*
|
|
706
|
+
* @remarks
|
|
707
|
+
* Compares a set of required labels against the labels found on the repo.
|
|
708
|
+
*
|
|
709
|
+
* @param labels - Labels required by the template
|
|
710
|
+
* @param repoLabels - Labels that exist on the GitHub repo
|
|
711
|
+
* @returns EngineResult with existing and missing label lists
|
|
712
|
+
*
|
|
713
|
+
* @example
|
|
714
|
+
* ```typescript
|
|
715
|
+
* const result = validateLabels(['bug', 'enhancement'], repoLabels);
|
|
716
|
+
* ```
|
|
717
|
+
*/
|
|
718
|
+
declare function validateLabels(labels: string[], repoLabels: string[]): EngineResult<{
|
|
719
|
+
existing: string[];
|
|
720
|
+
missing: string[];
|
|
721
|
+
}>;
|
|
722
|
+
|
|
723
|
+
/**
|
|
724
|
+
* Shared exit-code mapping utilities for the transport-agnostic gateway.
|
|
725
|
+
*
|
|
726
|
+
* Provides a canonical reverse lookup from numeric exit code to string error code,
|
|
727
|
+
* shared between the gateway engine helpers and every transport adapter (CLI,
|
|
728
|
+
* MCP, RPC, HTTP).
|
|
729
|
+
*
|
|
730
|
+
* @remarks
|
|
731
|
+
* Relocated from `packages/cleo/src/dispatch/lib/exit-codes.ts` into
|
|
732
|
+
* `@cleocode/runtime/gateway` (R3-K1 · T11455 · SG-RUNTIME-UNIFICATION) so the
|
|
733
|
+
* runtime can host the shared handler dependencies without importing
|
|
734
|
+
* `@cleocode/cleo`. A thin re-export shim remains at the old path.
|
|
735
|
+
*
|
|
736
|
+
* The forward mapping (string → number) lives in `engine-error.ts` as
|
|
737
|
+
* `STRING_TO_EXIT`. This module derives the inverse on first call and memoises
|
|
738
|
+
* it so engines can resolve a caught `CleoError.code` (numeric) to the
|
|
739
|
+
* string code expected by `engineError()`.
|
|
740
|
+
*
|
|
741
|
+
* @task T374
|
|
742
|
+
* @task T11455
|
|
743
|
+
* @epic T335
|
|
744
|
+
*/
|
|
745
|
+
/**
|
|
746
|
+
* Map a numeric CleoError exit code to the canonical string engine error code.
|
|
747
|
+
*
|
|
748
|
+
* @remarks
|
|
749
|
+
* Returns `undefined` when the numeric code is not present in the
|
|
750
|
+
* `STRING_TO_EXIT` map, allowing callers to fall back to their own default.
|
|
751
|
+
*
|
|
752
|
+
* @param numericCode - Numeric exit code from a caught `CleoError.code`
|
|
753
|
+
* @returns Canonical string error code (e.g. `'E_NOT_FOUND'`), or `undefined`
|
|
754
|
+
*
|
|
755
|
+
* @example
|
|
756
|
+
* ```typescript
|
|
757
|
+
* import { mapNumericExitCodeToString } from '../lib/exit-codes.js';
|
|
758
|
+
*
|
|
759
|
+
* const code = mapNumericExitCodeToString(4) ?? 'E_NOT_INITIALIZED';
|
|
760
|
+
* // → 'E_NOT_FOUND'
|
|
761
|
+
* ```
|
|
762
|
+
*/
|
|
763
|
+
declare function mapNumericExitCodeToString(numericCode: number | undefined): string | undefined;
|
|
764
|
+
|
|
765
|
+
/**
|
|
766
|
+
* Wire + lifecycle types for the `@cleocode/runtime/gateway/http` adapter.
|
|
767
|
+
*
|
|
768
|
+
* The HTTP transport carries the SAME gateway payloads as every other transport
|
|
769
|
+
* — request/response shapes are owned by `@cleocode/contracts/gateway` (R3-T2 ·
|
|
770
|
+
* T11446) and the streaming frame by `GatewayStreamEvent`. This module holds
|
|
771
|
+
* only the runtime-adapter-local option/handle shapes (mirroring the MCP and RPC
|
|
772
|
+
* adapters' `types.ts`), so the runtime stays framework-agnostic and free of any
|
|
773
|
+
* `@cleocode/cleo` / SvelteKit / drizzle dependency.
|
|
774
|
+
*
|
|
775
|
+
* @task T11450
|
|
776
|
+
* @epic T11254
|
|
777
|
+
* @saga T11243
|
|
778
|
+
*/
|
|
779
|
+
|
|
780
|
+
/**
|
|
781
|
+
* Where on the wire the `(gateway, domain, operation)` triple of an HTTP unary
|
|
782
|
+
* request is sourced from. The adapter is framework-agnostic, so the embedder
|
|
783
|
+
* (SvelteKit route, daemon HTTP server, test harness) supplies the resolved
|
|
784
|
+
* coordinates explicitly rather than the adapter parsing a URL itself.
|
|
785
|
+
*/
|
|
786
|
+
interface HttpUnaryRequest {
|
|
787
|
+
/** CQRS gateway — `'mutate'` (write) or `'query'` (read). */
|
|
788
|
+
gateway: Gateway;
|
|
789
|
+
/** Target canonical domain (e.g. `'tasks'`). */
|
|
790
|
+
domain: string;
|
|
791
|
+
/** Domain operation name (e.g. `'show'`). */
|
|
792
|
+
operation: string;
|
|
793
|
+
/** Decoded JSON request body → operation params. */
|
|
794
|
+
params?: Record<string, unknown>;
|
|
795
|
+
/**
|
|
796
|
+
* Optional caller-supplied request id for tracing. When omitted the adapter
|
|
797
|
+
* mints a fresh UUID.
|
|
798
|
+
*/
|
|
799
|
+
requestId?: string;
|
|
800
|
+
/** Bound session id, if any (forwarded into the dispatch request). */
|
|
801
|
+
sessionId?: string;
|
|
802
|
+
}
|
|
803
|
+
/**
|
|
804
|
+
* The result of routing one HTTP unary request through the gateway: the LAFS
|
|
805
|
+
* {@link DispatchResponse} JSON body plus the HTTP status the embedder should
|
|
806
|
+
* set (200 on success, 4xx/5xx mapped from the dispatch error).
|
|
807
|
+
*
|
|
808
|
+
* The adapter never writes the response itself (no framework coupling) — it
|
|
809
|
+
* returns this so the embedder serializes it onto its own `Response`.
|
|
810
|
+
*/
|
|
811
|
+
interface HttpUnaryResult {
|
|
812
|
+
/** HTTP status code derived from the dispatch outcome. */
|
|
813
|
+
status: number;
|
|
814
|
+
/** The LAFS envelope to serialize as the JSON response body. */
|
|
815
|
+
body: DispatchResponse;
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
/**
|
|
819
|
+
* HTTP transport adapter — serves gateway unary + SSE over HTTP.
|
|
820
|
+
*
|
|
821
|
+
* The fourth and final gateway transport (after CLI, MCP, RPC). Unlike the
|
|
822
|
+
* connection-oriented RPC/MCP adapters, the HTTP adapter is framework-agnostic
|
|
823
|
+
* by design: it does NOT bind a port or own a `Request`/`Response` lifecycle.
|
|
824
|
+
* Instead it exposes two pure routing primitives the embedder (a SvelteKit
|
|
825
|
+
* route handler, the daemon's HTTP server, a test harness) calls directly:
|
|
826
|
+
*
|
|
827
|
+
* - {@link routeUnary} — one POST → `source: 'http'` {@link DispatchRequest}
|
|
828
|
+
* → injected {@link GatewayHandler} → `{ status, body }` (LAFS envelope).
|
|
829
|
+
* - the SSE primitives in `./sse.js` (`createSseStream`) for streaming ops.
|
|
830
|
+
*
|
|
831
|
+
* This keeps the runtime free of any HTTP-server / SvelteKit / `@cleocode/cleo`
|
|
832
|
+
* dependency: the embedder owns wire concerns (parsing the URL into a
|
|
833
|
+
* `(gateway, domain, operation)` triple, setting headers, `process.exit`,
|
|
834
|
+
* error-render at the edge), exactly mirroring how the CLI adapter wraps the
|
|
835
|
+
* same handler. There is NO `process.exit` and NO error-render in the handlers
|
|
836
|
+
* (R3 contract); the adapter never calls `process.exit` at all.
|
|
837
|
+
*
|
|
838
|
+
* The request `source` is forced to `'http'` regardless of what the embedder
|
|
839
|
+
* passes, so a client cannot impersonate another transport.
|
|
840
|
+
*
|
|
841
|
+
* @packageDocumentation
|
|
842
|
+
* @module @cleocode/runtime/gateway/http
|
|
843
|
+
*
|
|
844
|
+
* @task T11450
|
|
845
|
+
* @epic T11254
|
|
846
|
+
* @saga T11243
|
|
847
|
+
*/
|
|
848
|
+
|
|
849
|
+
/**
|
|
850
|
+
* Map a LAFS dispatch error code to an HTTP status code.
|
|
851
|
+
*
|
|
852
|
+
* The canonical machine-readable error code (`E_NOT_FOUND`, `E_VALIDATION_…`,
|
|
853
|
+
* `E_FORBIDDEN`, …) is the source of truth; the numeric LAFS exit code is a
|
|
854
|
+
* secondary hint. Unknown error codes fall back to `500`. A successful envelope
|
|
855
|
+
* is always `200`.
|
|
856
|
+
*
|
|
857
|
+
* @param response - The dispatch response.
|
|
858
|
+
* @returns The HTTP status the embedder should set.
|
|
859
|
+
*/
|
|
860
|
+
declare function statusForResponse(response: DispatchResponse): number;
|
|
861
|
+
/**
|
|
862
|
+
* Route a single HTTP unary request through the gateway handler.
|
|
863
|
+
*
|
|
864
|
+
* Builds a `source: 'http'` {@link DispatchRequest} from the embedder-resolved
|
|
865
|
+
* `(gateway, domain, operation, params)` triple and delegates to the injected
|
|
866
|
+
* {@link GatewayHandler}. A thrown handler error is trapped and rendered as a
|
|
867
|
+
* `500` LAFS error envelope — never propagated as an unhandled rejection or
|
|
868
|
+
* `process.exit`. The `source` is always forced to `'http'`.
|
|
869
|
+
*
|
|
870
|
+
* @param handler - The injected transport-neutral gateway handler.
|
|
871
|
+
* @param req - The embedder-resolved unary request coordinates + body params.
|
|
872
|
+
* @returns The HTTP status + LAFS response body for the embedder to serialize.
|
|
873
|
+
*/
|
|
874
|
+
declare function routeUnary(handler: GatewayHandler, req: HttpUnaryRequest): Promise<HttpUnaryResult>;
|
|
875
|
+
|
|
876
|
+
/**
|
|
877
|
+
* Server-Sent Events (SSE) wire primitives for the `@cleocode/runtime/gateway/http`
|
|
878
|
+
* adapter.
|
|
879
|
+
*
|
|
880
|
+
* The HTTP transport is the only gateway transport that streams: long-running
|
|
881
|
+
* and subscription operations emit a sequence of {@link GatewayStreamEvent}
|
|
882
|
+
* frames terminated by a `done` or `error` frame. This module owns ONLY the SSE
|
|
883
|
+
* wire encoding + the `ReadableStream` lifecycle plumbing; the actual frame
|
|
884
|
+
* production (polling a DB, tailing a job, etc.) lives in the caller's async
|
|
885
|
+
* source. Keeping the encode/stream concern here lets BOTH the gateway-routed
|
|
886
|
+
* streaming ops AND the existing Studio SSE endpoints (which carry their own
|
|
887
|
+
* domain event shapes) share one tested, leak-free stream builder.
|
|
888
|
+
*
|
|
889
|
+
* Two encoders are provided so an existing stream can adopt this plumbing
|
|
890
|
+
* WITHOUT changing its observed bytes:
|
|
891
|
+
* - {@link encodeStreamEvent} — the canonical `data: <json>\n\n` form for
|
|
892
|
+
* {@link GatewayStreamEvent} frames (gateway-routed streaming ops).
|
|
893
|
+
* - {@link encodeSseFrame} — a generic `event:`/`id:`/`data:` writer for any
|
|
894
|
+
* payload, so endpoints with a bespoke wire (a named `event:` field, a
|
|
895
|
+
* `data:`-only frame) keep an IDENTICAL byte stream while still routing their
|
|
896
|
+
* lifecycle through the shared, abort-safe builder.
|
|
897
|
+
*
|
|
898
|
+
* @packageDocumentation
|
|
899
|
+
* @module @cleocode/runtime/gateway/http/sse
|
|
900
|
+
*
|
|
901
|
+
* @task T11450
|
|
902
|
+
* @epic T11254
|
|
903
|
+
* @saga T11243
|
|
904
|
+
*/
|
|
905
|
+
|
|
906
|
+
/**
|
|
907
|
+
* One generic SSE frame: an optional named `event`, an optional `id`, and a
|
|
908
|
+
* `data` payload that is JSON-serialized.
|
|
909
|
+
*
|
|
910
|
+
* This is intentionally NOT {@link GatewayStreamEvent} — it is the raw wire
|
|
911
|
+
* shape so a bespoke endpoint can reproduce its exact existing bytes (e.g. a
|
|
912
|
+
* named `event: task-updated` line, or a `data:`-only frame with no `event`).
|
|
913
|
+
*/
|
|
914
|
+
interface SseFrame {
|
|
915
|
+
/** Optional SSE `event:` field name (omitted → a `data:`-only frame). */
|
|
916
|
+
event?: string;
|
|
917
|
+
/** Optional SSE `id:` field for client-side last-event-id resumption. */
|
|
918
|
+
id?: string;
|
|
919
|
+
/** The frame payload; JSON-serialized into the `data:` field. */
|
|
920
|
+
data: unknown;
|
|
921
|
+
}
|
|
922
|
+
/**
|
|
923
|
+
* Encode a generic {@link SseFrame} to its SSE wire bytes.
|
|
924
|
+
*
|
|
925
|
+
* Field order is `event:` (if present) → `id:` (if present) → `data:`, matching
|
|
926
|
+
* the SSE spec record layout, then a blank line. When `event` is omitted the
|
|
927
|
+
* frame is a `data:`-only record — byte-identical to a handwritten
|
|
928
|
+
* `data: <json>\n\n`.
|
|
929
|
+
*
|
|
930
|
+
* @param frame - The frame to serialize.
|
|
931
|
+
* @returns The SSE record as a string (UTF-8 caller-encoded).
|
|
932
|
+
*/
|
|
933
|
+
declare function encodeSseFrame(frame: SseFrame): string;
|
|
934
|
+
/**
|
|
935
|
+
* Encode a canonical {@link GatewayStreamEvent} as a `data:`-only SSE record.
|
|
936
|
+
*
|
|
937
|
+
* Gateway-routed streaming ops carry the discriminator + sequence inside the
|
|
938
|
+
* JSON payload (`{ kind, seq, data, error, requestId }`), so no named SSE
|
|
939
|
+
* `event:` field is used — the client demultiplexes on `kind`/`seq`. The
|
|
940
|
+
* monotonic `seq` is also surfaced as the SSE `id:` so a reconnecting client can
|
|
941
|
+
* send `Last-Event-ID`.
|
|
942
|
+
*
|
|
943
|
+
* @param frame - The gateway stream event to serialize.
|
|
944
|
+
* @returns The SSE record as a string.
|
|
945
|
+
*/
|
|
946
|
+
declare function encodeStreamEvent(frame: GatewayStreamEvent): string;
|
|
947
|
+
/** The standard SSE response headers (no caching, no proxy buffering). */
|
|
948
|
+
declare const SSE_HEADERS: Readonly<Record<string, string>>;
|
|
949
|
+
/**
|
|
950
|
+
* A push-style SSE controller handed to an {@link SseSource} so it can emit
|
|
951
|
+
* frames and close the stream without touching the underlying
|
|
952
|
+
* `ReadableStreamDefaultController`.
|
|
953
|
+
*/
|
|
954
|
+
interface SseEmitter {
|
|
955
|
+
/** Emit one generic SSE frame; a no-op once the stream is closed. */
|
|
956
|
+
send(frame: SseFrame): void;
|
|
957
|
+
/** Emit one canonical {@link GatewayStreamEvent}; a no-op once closed. */
|
|
958
|
+
sendStreamEvent(frame: GatewayStreamEvent): void;
|
|
959
|
+
/**
|
|
960
|
+
* Emit a pre-encoded SSE record verbatim; a no-op once closed. For endpoints
|
|
961
|
+
* that own their own wire encoder (e.g. a bespoke `data: <json>\n\n`) and want
|
|
962
|
+
* to keep emitting byte-identical records while still routing lifecycle
|
|
963
|
+
* through this builder.
|
|
964
|
+
*/
|
|
965
|
+
sendRaw(record: string): void;
|
|
966
|
+
/** Whether the stream has been closed (client disconnect or `close()`). */
|
|
967
|
+
readonly closed: boolean;
|
|
968
|
+
/** Close the stream; idempotent. */
|
|
969
|
+
close(): void;
|
|
970
|
+
}
|
|
971
|
+
/**
|
|
972
|
+
* A teardown callback returned by an {@link SseSource}, invoked exactly once when
|
|
973
|
+
* the stream ends (client disconnect, `emitter.close()`, or producer completion).
|
|
974
|
+
*/
|
|
975
|
+
type SseTeardown = () => void;
|
|
976
|
+
/**
|
|
977
|
+
* A frame source for {@link createSseStream}. Receives an {@link SseEmitter} and
|
|
978
|
+
* MAY return an {@link SseTeardown} callback (return nothing to skip teardown).
|
|
979
|
+
*
|
|
980
|
+
* The source is started SYNCHRONOUSLY inside `ReadableStream.start`, mirroring
|
|
981
|
+
* the existing Studio endpoints (which schedule timers inside `start`). A source
|
|
982
|
+
* may push frames immediately and/or schedule async work.
|
|
983
|
+
*/
|
|
984
|
+
type SseSource = (emitter: SseEmitter) => SseTeardown | undefined;
|
|
985
|
+
/**
|
|
986
|
+
* Build a `text/event-stream` {@link ReadableStream} driven by an
|
|
987
|
+
* {@link SseSource}.
|
|
988
|
+
*
|
|
989
|
+
* This is the shared, abort-safe stream builder every SSE endpoint routes
|
|
990
|
+
* through. It guarantees:
|
|
991
|
+
* - frames are dropped (never throw) after the stream closes;
|
|
992
|
+
* - the source's teardown callback runs exactly once;
|
|
993
|
+
* - an optional {@link AbortSignal} (the request's) closes the stream on client
|
|
994
|
+
* disconnect, with its listener removed on teardown.
|
|
995
|
+
*
|
|
996
|
+
* The byte output is fully controlled by what the source emits — so an endpoint
|
|
997
|
+
* adopting this builder reproduces its prior wire exactly by emitting the same
|
|
998
|
+
* frames in the same order.
|
|
999
|
+
*
|
|
1000
|
+
* @param source - The frame producer.
|
|
1001
|
+
* @param signal - Optional request abort signal that closes the stream on
|
|
1002
|
+
* client disconnect.
|
|
1003
|
+
* @returns A ReadableStream of UTF-8 SSE bytes for a `Response` body.
|
|
1004
|
+
*/
|
|
1005
|
+
declare function createSseStream(source: SseSource, signal?: AbortSignal): ReadableStream<Uint8Array>;
|
|
1006
|
+
|
|
1007
|
+
/**
|
|
1008
|
+
* Singleton accessor for the gateway {@link BackgroundJobManager}.
|
|
1009
|
+
*
|
|
1010
|
+
* @remarks
|
|
1011
|
+
* Relocated from `packages/cleo/src/dispatch/lib/job-manager-accessor.ts` into
|
|
1012
|
+
* `@cleocode/runtime/gateway` (R3-K1 · T11455 · SG-RUNTIME-UNIFICATION) so the
|
|
1013
|
+
* `admin` domain handler resolves the job-manager singleton from the runtime
|
|
1014
|
+
* layer rather than a cleo-internal path. A thin re-export shim remains at the
|
|
1015
|
+
* old path so any existing import site compiles unchanged.
|
|
1016
|
+
*
|
|
1017
|
+
* Long-lived hosts (e.g. the daemon) call {@link setJobManager} once at startup;
|
|
1018
|
+
* short-lived CLI invocations leave the singleton `null`, and consumers handle
|
|
1019
|
+
* the absence gracefully.
|
|
1020
|
+
*
|
|
1021
|
+
* @task T4820
|
|
1022
|
+
* @task T11455
|
|
1023
|
+
*/
|
|
1024
|
+
|
|
1025
|
+
/**
|
|
1026
|
+
* Register the process-wide {@link BackgroundJobManager} singleton.
|
|
1027
|
+
*
|
|
1028
|
+
* @param manager - The manager instance to expose to gateway consumers.
|
|
1029
|
+
*/
|
|
1030
|
+
declare function setJobManager(manager: BackgroundJobManager): void;
|
|
1031
|
+
/**
|
|
1032
|
+
* Retrieve the registered {@link BackgroundJobManager} singleton.
|
|
1033
|
+
*
|
|
1034
|
+
* @returns The registered manager, or `null` when running in a short-lived
|
|
1035
|
+
* host (e.g. a one-shot CLI invocation) that never called {@link setJobManager}.
|
|
1036
|
+
*/
|
|
1037
|
+
declare function getJobManager(): BackgroundJobManager | null;
|
|
1038
|
+
|
|
1039
|
+
/**
|
|
1040
|
+
* MCP wire types for the `@cleocode/runtime/gateway/mcp` transport adapter.
|
|
1041
|
+
*
|
|
1042
|
+
* A deliberately-minimal subset of the Model Context Protocol shapes the
|
|
1043
|
+
* adapter needs — no external MCP SDK dependency. Mirrors the legacy
|
|
1044
|
+
* `@cleocode/mcp-adapter` `types.ts` so the wire surface is unchanged.
|
|
1045
|
+
*
|
|
1046
|
+
* @task T11448
|
|
1047
|
+
* @epic T11254
|
|
1048
|
+
* @saga T11243
|
|
1049
|
+
*/
|
|
1050
|
+
/** JSON Schema property descriptor surfaced for a single tool input field. */
|
|
1051
|
+
interface McpInputProperty {
|
|
1052
|
+
/** JSON Schema primitive type (`'string'`, `'number'`, …). */
|
|
1053
|
+
type: string;
|
|
1054
|
+
/** Human-readable description shown in MCP clients. */
|
|
1055
|
+
description: string;
|
|
1056
|
+
/** Allowed values (JSON Schema `enum`) for finite-domain string params. */
|
|
1057
|
+
enum?: readonly string[];
|
|
1058
|
+
}
|
|
1059
|
+
/**
|
|
1060
|
+
* MCP tool definition shape (minimal — no external MCP SDK dependency).
|
|
1061
|
+
* Structurally compatible with the legacy `@cleocode/mcp-adapter` `McpTool`.
|
|
1062
|
+
*/
|
|
1063
|
+
interface McpTool {
|
|
1064
|
+
/** Stable tool name (snake_case, e.g. `cleo_sentient_status`). */
|
|
1065
|
+
name: string;
|
|
1066
|
+
/** Human-readable description shown in MCP clients. */
|
|
1067
|
+
description: string;
|
|
1068
|
+
/** JSON Schema for the tool's input parameters. */
|
|
1069
|
+
inputSchema: {
|
|
1070
|
+
type: 'object';
|
|
1071
|
+
properties: Record<string, McpInputProperty>;
|
|
1072
|
+
required?: string[];
|
|
1073
|
+
};
|
|
1074
|
+
}
|
|
1075
|
+
/** A single content block returned by a tool handler. */
|
|
1076
|
+
interface McpContent {
|
|
1077
|
+
type: 'text';
|
|
1078
|
+
text: string;
|
|
1079
|
+
}
|
|
1080
|
+
/** Result returned by a tool handler over the MCP wire. */
|
|
1081
|
+
interface McpToolResult {
|
|
1082
|
+
content: McpContent[];
|
|
1083
|
+
isError?: boolean;
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
/**
|
|
1087
|
+
* MCP stdio transport adapter — routes `tools/call` through the gateway.
|
|
1088
|
+
*
|
|
1089
|
+
* Serves the Model Context Protocol over stdio (newline-delimited JSON-RPC 2.0)
|
|
1090
|
+
* so any MCP-compatible client (Claude Code, LLM-agnostic tools) can discover
|
|
1091
|
+
* and invoke CLEO operations. Unlike the legacy standalone `@cleocode/mcp-adapter`
|
|
1092
|
+
* — which called the `@cleocode/core` SDK directly — this adapter is a thin
|
|
1093
|
+
* TRANSPORT over the unified gateway:
|
|
1094
|
+
*
|
|
1095
|
+
* MCP `tools/call` { name, arguments }
|
|
1096
|
+
* → {@link GatewayOperationKey} (via the `mcpExposed` registry subset)
|
|
1097
|
+
* → DispatchRequest { source: 'mcp', gateway, domain, operation, params }
|
|
1098
|
+
* → injected {@link GatewayHandler}.handle()
|
|
1099
|
+
* → DispatchResponse (LAFS envelope) → MCP tool result (JSON text content)
|
|
1100
|
+
*
|
|
1101
|
+
* The adapter owns ONLY wire concerns: JSON-RPC framing, the stdio read loop,
|
|
1102
|
+
* `tools/list` generation, error rendering, and `process.exit` on stream close.
|
|
1103
|
+
* All domain logic (resolution, validation, middleware, handler execution) lives
|
|
1104
|
+
* behind the {@link GatewayHandler} — exactly mirroring how the CLI adapter
|
|
1105
|
+
* wraps the same handler. `process.exit` and error-render stay HERE, never in
|
|
1106
|
+
* the handlers (R3-T4 contract).
|
|
1107
|
+
*
|
|
1108
|
+
* `tools/list` is generated from the OPERATIONS registry behind the default-deny
|
|
1109
|
+
* `mcpExposed` flag, so the external tool surface is identical to the historical
|
|
1110
|
+
* standalone adapter (no behavior change).
|
|
1111
|
+
*
|
|
1112
|
+
* @task T11448
|
|
1113
|
+
* @epic T11254
|
|
1114
|
+
* @saga T11243
|
|
1115
|
+
*/
|
|
1116
|
+
|
|
1117
|
+
/**
|
|
1118
|
+
* Route a single MCP `tools/call` through the gateway handler.
|
|
1119
|
+
*
|
|
1120
|
+
* Resolves the tool name to a `(domain, operation)` key against the exposed
|
|
1121
|
+
* (`mcpExposed`) registry subset, builds a `source: 'mcp'` {@link DispatchRequest},
|
|
1122
|
+
* and delegates to the injected {@link GatewayHandler}. Unknown tool names and
|
|
1123
|
+
* thrown handler errors are rendered as MCP error results — never propagated as
|
|
1124
|
+
* `process.exit` from this layer.
|
|
1125
|
+
*
|
|
1126
|
+
* @param handler - The injected transport-neutral gateway handler.
|
|
1127
|
+
* @param toolName - The MCP `tools/call` tool name.
|
|
1128
|
+
* @param args - The parsed tool arguments (already JSON-decoded by the client).
|
|
1129
|
+
* @returns An MCP {@link McpToolResult}.
|
|
1130
|
+
*/
|
|
1131
|
+
declare function callTool(handler: GatewayHandler, toolName: string, args: Record<string, unknown>): Promise<McpToolResult>;
|
|
1132
|
+
/**
|
|
1133
|
+
* Options for {@link startMcpServer}.
|
|
1134
|
+
*/
|
|
1135
|
+
interface McpServerOptions {
|
|
1136
|
+
/** Inbound stream (defaults to `process.stdin`). */
|
|
1137
|
+
input?: NodeJS.ReadableStream;
|
|
1138
|
+
/** Outbound stream (defaults to `process.stdout`). */
|
|
1139
|
+
output?: NodeJS.WritableStream;
|
|
1140
|
+
/**
|
|
1141
|
+
* Whether to call `process.exit(0)` when the input stream closes.
|
|
1142
|
+
* Defaults to `true` (production stdio). Tests pass `false`.
|
|
1143
|
+
*/
|
|
1144
|
+
exitOnClose?: boolean;
|
|
1145
|
+
}
|
|
1146
|
+
/**
|
|
1147
|
+
* Start the MCP stdio server over the gateway.
|
|
1148
|
+
*
|
|
1149
|
+
* Reads newline-delimited JSON-RPC 2.0 requests from the input stream, routes
|
|
1150
|
+
* `tools/call` through the injected {@link GatewayHandler}, and writes responses
|
|
1151
|
+
* to the output stream. Each inbound line must be one complete JSON-RPC request.
|
|
1152
|
+
*
|
|
1153
|
+
* The caller assembles the {@link GatewayHandler} (via `createGatewayHandler`
|
|
1154
|
+
* with its domain handlers + middleware) and injects it here — this adapter
|
|
1155
|
+
* never builds handlers itself, keeping the runtime free of any
|
|
1156
|
+
* `@cleocode/cleo` dependency.
|
|
1157
|
+
*
|
|
1158
|
+
* @param handler - The transport-neutral gateway handler to route through.
|
|
1159
|
+
* @param opts - Optional stream + lifecycle overrides (defaults to stdio).
|
|
1160
|
+
*/
|
|
1161
|
+
declare function startMcpServer(handler: GatewayHandler, opts?: McpServerOptions): void;
|
|
1162
|
+
|
|
1163
|
+
/**
|
|
1164
|
+
* MCP tool-name <-> gateway-operation mapping.
|
|
1165
|
+
*
|
|
1166
|
+
* The MCP wire protocol identifies a tool by a single flat `name` string
|
|
1167
|
+
* (snake_case, e.g. `cleo_sentient_propose_list`), whereas the gateway routes
|
|
1168
|
+
* on a `(gateway, domain, operation)` triple (e.g.
|
|
1169
|
+
* `query / sentient / propose.list`). This module is the bijective bridge
|
|
1170
|
+
* between the two surfaces:
|
|
1171
|
+
*
|
|
1172
|
+
* gateway op → MCP tool name {@link operationToToolName}
|
|
1173
|
+
* MCP tool name → domain+operation {@link toolNameToOperationKey}
|
|
1174
|
+
*
|
|
1175
|
+
* ## Naming convention (frozen — matches the legacy `@cleocode/mcp-adapter`)
|
|
1176
|
+
*
|
|
1177
|
+
* `cleo_<domain>_<operation>` where every `.` and `-` in the dotted operation
|
|
1178
|
+
* name becomes `_`. The three historically-exposed tools therefore map as:
|
|
1179
|
+
*
|
|
1180
|
+
* | gateway op | MCP tool name |
|
|
1181
|
+
* |-----------------------------|--------------------------------|
|
|
1182
|
+
* | `sentient` · `status` | `cleo_sentient_status` |
|
|
1183
|
+
* | `sentient` · `propose.list` | `cleo_sentient_propose_list` |
|
|
1184
|
+
* | `sentient` · `propose.enable` | `cleo_sentient_propose_enable` |
|
|
1185
|
+
*
|
|
1186
|
+
* Preserving this exact mapping is what guarantees R3-T4's "no behavior change
|
|
1187
|
+
* to the MCP tool surface" acceptance criterion.
|
|
1188
|
+
*
|
|
1189
|
+
* @task T11448
|
|
1190
|
+
* @epic T11254
|
|
1191
|
+
* @saga T11243
|
|
1192
|
+
*/
|
|
1193
|
+
|
|
1194
|
+
/** Prefix every CLEO MCP tool name carries. */
|
|
1195
|
+
declare const MCP_TOOL_PREFIX = "cleo_";
|
|
1196
|
+
/**
|
|
1197
|
+
* Identifies a gateway operation by its `(domain, operation)` pair. The CQRS
|
|
1198
|
+
* gateway resolves which `Gateway` ('query' | 'mutate') the pair belongs to via
|
|
1199
|
+
* the operation registry, so the MCP adapter does not need to carry it.
|
|
1200
|
+
*/
|
|
1201
|
+
interface GatewayOperationKey {
|
|
1202
|
+
/** Canonical domain (e.g. `'sentient'`). */
|
|
1203
|
+
readonly domain: string;
|
|
1204
|
+
/** Dotted operation name (e.g. `'propose.list'`). */
|
|
1205
|
+
readonly operation: string;
|
|
1206
|
+
}
|
|
1207
|
+
/**
|
|
1208
|
+
* Derive the flat MCP tool name for a gateway operation.
|
|
1209
|
+
*
|
|
1210
|
+
* @param op - An {@link OperationDef} (or any object carrying `domain` +
|
|
1211
|
+
* `operation`) from the registry.
|
|
1212
|
+
* @returns The MCP tool name, e.g. `'cleo_sentient_propose_list'`.
|
|
1213
|
+
*/
|
|
1214
|
+
declare function operationToToolName(op: Pick<OperationDef, 'domain' | 'operation'>): string;
|
|
1215
|
+
/**
|
|
1216
|
+
* Resolve an MCP tool name back to its `(domain, operation)` gateway key.
|
|
1217
|
+
*
|
|
1218
|
+
* The reverse of {@link operationToToolName} cannot recover dots vs dashes from
|
|
1219
|
+
* the snake_case wire form, so resolution is performed against the provided
|
|
1220
|
+
* `exposed` set (the same `mcpExposed: true` subset used to build `tools/list`).
|
|
1221
|
+
* This keeps the mapping exact and rejects unknown tool names.
|
|
1222
|
+
*
|
|
1223
|
+
* @param toolName - The MCP `tools/call` tool name.
|
|
1224
|
+
* @param exposed - The exposed operation defs (the `mcpExposed` subset).
|
|
1225
|
+
* @returns The matching {@link GatewayOperationKey}, or `undefined` when the
|
|
1226
|
+
* tool name does not correspond to any exposed operation.
|
|
1227
|
+
*/
|
|
1228
|
+
declare function toolNameToOperationKey(toolName: string, exposed: readonly OperationDef[]): GatewayOperationKey | undefined;
|
|
1229
|
+
|
|
1230
|
+
/**
|
|
1231
|
+
* MCP `tools/list` generation from the OPERATIONS registry.
|
|
1232
|
+
*
|
|
1233
|
+
* The MCP tool surface is **default-deny**: only {@link OperationDef} entries
|
|
1234
|
+
* that explicitly set `mcpExposed: true` are surfaced. This replaces the legacy
|
|
1235
|
+
* `@cleocode/mcp-adapter`'s hand-maintained `ALL_TOOLS` array with a single
|
|
1236
|
+
* generation point driven by the canonical registry — promoting a new tool is
|
|
1237
|
+
* now one `mcpExposed: true` edit in `@cleocode/contracts`, not a fork-and-copy
|
|
1238
|
+
* into a standalone adapter.
|
|
1239
|
+
*
|
|
1240
|
+
* @task T11448
|
|
1241
|
+
* @epic T11254
|
|
1242
|
+
* @saga T11243
|
|
1243
|
+
*/
|
|
1244
|
+
|
|
1245
|
+
/**
|
|
1246
|
+
* The subset of OPERATIONS surfaced over MCP (default-deny `mcpExposed` filter).
|
|
1247
|
+
*
|
|
1248
|
+
* @returns Every {@link OperationDef} with `mcpExposed === true`, in registry
|
|
1249
|
+
* order. Empty when no operation opts in.
|
|
1250
|
+
*/
|
|
1251
|
+
declare function exposedOperations(): OperationDef[];
|
|
1252
|
+
/**
|
|
1253
|
+
* Convert a single exposed {@link OperationDef} into its MCP tool definition.
|
|
1254
|
+
*
|
|
1255
|
+
* @param op - An operation with `mcpExposed === true`.
|
|
1256
|
+
* @returns The MCP {@link McpTool} (name + description + inputSchema).
|
|
1257
|
+
*/
|
|
1258
|
+
declare function operationToMcpTool(op: OperationDef): McpTool;
|
|
1259
|
+
/**
|
|
1260
|
+
* Generate the full MCP `tools/list` payload from the registry.
|
|
1261
|
+
*
|
|
1262
|
+
* @returns Every exposed operation rendered as an {@link McpTool}, in registry
|
|
1263
|
+
* order (stable across calls).
|
|
1264
|
+
*/
|
|
1265
|
+
declare function buildToolsList(): McpTool[];
|
|
1266
|
+
|
|
1267
|
+
/**
|
|
1268
|
+
* Dispatch-local metadata factory.
|
|
1269
|
+
*
|
|
1270
|
+
* Generates LAFS-conformant metadata for dispatch layer responses.
|
|
1271
|
+
* Generates LAFS-conformant metadata for dispatch responses.
|
|
1272
|
+
*
|
|
1273
|
+
* @task T4772
|
|
1274
|
+
*/
|
|
1275
|
+
|
|
1276
|
+
/**
|
|
1277
|
+
* Create metadata for a dispatch response.
|
|
1278
|
+
*
|
|
1279
|
+
* @param gateway - Gateway name (e.g., 'query', 'mutate')
|
|
1280
|
+
* @param domain - Domain name (e.g., 'tasks', 'session')
|
|
1281
|
+
* @param operation - Operation name (e.g., 'show', 'list')
|
|
1282
|
+
* @param startTime - Timestamp from Date.now() at start of request
|
|
1283
|
+
* @param source - Where the request originated
|
|
1284
|
+
* @param requestId - Optional pre-generated request ID
|
|
1285
|
+
* @param sessionId - Optional session ID to include in metadata
|
|
1286
|
+
* @param originSessionId - Optional root workflow/saga session ID
|
|
1287
|
+
* @param executionSessionId - Optional execution-attempt session ID
|
|
1288
|
+
* @returns Metadata conforming to DispatchResponse['meta']
|
|
1289
|
+
*
|
|
1290
|
+
* @task T4772
|
|
1291
|
+
* @task T4959
|
|
1292
|
+
*/
|
|
1293
|
+
declare function createDispatchMeta(gateway: string, domain: string, operation: string, startTime: number, source?: Source, requestId?: string, sessionId?: string | null, originSessionId?: string | null, executionSessionId?: string | null): DispatchResponse['meta'];
|
|
1294
|
+
|
|
1295
|
+
/**
|
|
1296
|
+
* nexus-decorator — stamps `meta._nexus` on every nexus-domain dispatch response.
|
|
1297
|
+
*
|
|
1298
|
+
* Wave 2 of the Nexus Restructure (T9146). Reads `getNexusDescriptor(opId)` from
|
|
1299
|
+
* the W1 SSoT (`NEXUS_SCOPE_MAP`) and injects the namespaced `_nexus` block into
|
|
1300
|
+
* `DispatchResponse['meta']`.
|
|
1301
|
+
*
|
|
1302
|
+
* Usage:
|
|
1303
|
+
* ```ts
|
|
1304
|
+
* const decorated = stampNexusMeta(response, operation, params);
|
|
1305
|
+
* ```
|
|
1306
|
+
*
|
|
1307
|
+
* The `suggestedNext` field uses a structured form (not literal display strings).
|
|
1308
|
+
* Display strings are derived by `formatSuggestedNext()` for human/markdown renderers.
|
|
1309
|
+
*
|
|
1310
|
+
* Typed-registry gate: `validateSuggestedNext()` fails the build (throws at import-time
|
|
1311
|
+
* in test mode) if any `suggestedNext.op` is absent from `NEXUS_SCOPE_MAP`.
|
|
1312
|
+
*
|
|
1313
|
+
* @remarks
|
|
1314
|
+
* Relocated from `packages/cleo/src/dispatch/nexus-decorator.ts` into
|
|
1315
|
+
* `@cleocode/runtime/gateway` (R3-K1 · T11455 · SG-RUNTIME-UNIFICATION) so the
|
|
1316
|
+
* nexus domain handler can import it from the runtime layer rather than a
|
|
1317
|
+
* cleo-internal path. The module is pure (`@cleocode/contracts` only). A thin
|
|
1318
|
+
* re-export shim remains at the old path for the renderer / CLI import sites.
|
|
1319
|
+
*
|
|
1320
|
+
* @task T9146
|
|
1321
|
+
* @task T11455
|
|
1322
|
+
* @module runtime/gateway/nexus-decorator
|
|
1323
|
+
*/
|
|
1324
|
+
|
|
1325
|
+
/**
|
|
1326
|
+
* Assert that every `suggestedNext.op` in a list resolves to a known
|
|
1327
|
+
* NEXUS_SCOPE_MAP entry.
|
|
1328
|
+
*
|
|
1329
|
+
* Throws `TypeError` at runtime (caught by tests and CI) if an unknown op
|
|
1330
|
+
* is referenced — acting as a build-time gate when imported in test suites.
|
|
1331
|
+
*
|
|
1332
|
+
* @throws {TypeError} if any `op` is absent from `NEXUS_SCOPE_MAP`
|
|
1333
|
+
* @task T9146
|
|
1334
|
+
*/
|
|
1335
|
+
declare function validateSuggestedNext(suggestions: ReadonlyArray<SuggestedNextOp>): void;
|
|
1336
|
+
/**
|
|
1337
|
+
* Derive a human-readable display string from a {@link SuggestedNextOp}.
|
|
1338
|
+
*
|
|
1339
|
+
* Example: `"cleo nexus context --project-id myproj (read-only, no confirmation needed)"`
|
|
1340
|
+
*/
|
|
1341
|
+
declare function formatSuggestedNext(s: SuggestedNextOp): string;
|
|
1342
|
+
/**
|
|
1343
|
+
* Stamp `meta._nexus` onto a nexus-domain {@link DispatchResponse}.
|
|
1344
|
+
*
|
|
1345
|
+
* Call this after every nexus handler produces its `DispatchResponse`. The
|
|
1346
|
+
* function is a pure transformer — it returns a new object with `meta._nexus`
|
|
1347
|
+
* merged in.
|
|
1348
|
+
*
|
|
1349
|
+
* @param response - The original dispatch response from the nexus handler.
|
|
1350
|
+
* @param operation - The nexus operation key (must be a `keyof NexusOps`).
|
|
1351
|
+
* @param params - Raw params passed to the handler (used for binding-source resolution).
|
|
1352
|
+
* @param isTopLevel - Whether this is a top-level CLI invocation (not a sub-call).
|
|
1353
|
+
* @returns A new `DispatchResponse` with `meta._nexus` populated.
|
|
1354
|
+
*
|
|
1355
|
+
* @task T9146
|
|
1356
|
+
*/
|
|
1357
|
+
declare function stampNexusMeta(response: DispatchResponse, operation: string, params: Record<string, unknown>, isTopLevel?: boolean): DispatchResponse;
|
|
1358
|
+
/**
|
|
1359
|
+
* Pick the decorator-stamped fields (`_nexus`, `deprecated`) from a dispatch
|
|
1360
|
+
* response's `meta` so they can be forwarded via `cliOutput`'s `extensions`
|
|
1361
|
+
* option into the emitted LAFS envelope.
|
|
1362
|
+
*
|
|
1363
|
+
* Without this, `cliOutput(response.data, ...)` discards `response.meta._nexus`
|
|
1364
|
+
* + `response.meta.deprecated` (T9393 defect). Canonical CLI meta fields
|
|
1365
|
+
* (`operation`, `requestId`, `timestamp`) are intentionally NOT forwarded —
|
|
1366
|
+
* those are produced by {@link createCliMeta} on the CLI side.
|
|
1367
|
+
*
|
|
1368
|
+
* @task T9393
|
|
1369
|
+
*/
|
|
1370
|
+
declare function pickDecoratorMetaExtensions(responseMeta: DispatchResponseMeta | undefined): Record<string, unknown>;
|
|
1371
|
+
/**
|
|
1372
|
+
* Build CLI envelope extensions for nexus commands that bypass the dispatcher
|
|
1373
|
+
* (e.g. `cleo nexus status` SSoT-EXEMPT path). Runs the same {@link stampNexusMeta}
|
|
1374
|
+
* logic against a synthetic response so the resulting envelope still carries
|
|
1375
|
+
* `meta._nexus` (and `meta.deprecated` for alias shims).
|
|
1376
|
+
*
|
|
1377
|
+
* Use this only where the command does NOT call `dispatchRaw`. When you have a
|
|
1378
|
+
* real dispatch response, use {@link pickDecoratorMetaExtensions} on
|
|
1379
|
+
* `response.meta` instead — that path already ran the decorator.
|
|
1380
|
+
*
|
|
1381
|
+
* @task T9393
|
|
1382
|
+
*/
|
|
1383
|
+
declare function buildNexusMetaExtensions(operation: string, params?: Record<string, unknown>): Record<string, unknown>;
|
|
1384
|
+
|
|
1385
|
+
/**
|
|
1386
|
+
* Gateway-level path constants shared by domain handlers.
|
|
1387
|
+
*
|
|
1388
|
+
* @remarks
|
|
1389
|
+
* The `orchestrate` and `admin` (smoke-provider) domain handlers join a handful
|
|
1390
|
+
* of well-known `.cleo/` path segments when locating workflow definitions and
|
|
1391
|
+
* the BRAIN database. Those constants were previously defined in
|
|
1392
|
+
* `packages/cleo/src/cli/paths.ts` (a CLI-internal leaf), which coupled the
|
|
1393
|
+
* handler subgraph to `@cleocode/cleo`.
|
|
1394
|
+
*
|
|
1395
|
+
* To let `@cleocode/runtime` assemble the handler map WITHOUT importing
|
|
1396
|
+
* `@cleocode/cleo` (R3-K1 · T11455 · SG-RUNTIME-UNIFICATION), the constants the
|
|
1397
|
+
* handlers actually consume are hosted here as the canonical source. The
|
|
1398
|
+
* CLI-side `cli/paths.ts` re-exports them so the rest of the CLI (backup /
|
|
1399
|
+
* restore / doctor) keeps importing from its established surface — there is a
|
|
1400
|
+
* single definition, no drift.
|
|
1401
|
+
*
|
|
1402
|
+
* @task T090
|
|
1403
|
+
* @task T11455
|
|
1404
|
+
*/
|
|
1405
|
+
/** Name of the project-local CLEO data directory (`.cleo`). */
|
|
1406
|
+
declare const CLEO_DIR_NAME: ".cleo";
|
|
1407
|
+
/** Subdirectory under `.cleo/` that stores CANT workflow definitions. */
|
|
1408
|
+
declare const WORKFLOWS_SUBDIR: "workflows";
|
|
1409
|
+
/** Project-local SQLite database filename for cognitive memory (BRAIN). */
|
|
1410
|
+
declare const BRAIN_DB_FILENAME: "brain.db";
|
|
1411
|
+
|
|
1412
|
+
/**
|
|
1413
|
+
* Unified CQRS Dispatch Layer -- Middleware Pipeline
|
|
1414
|
+
*
|
|
1415
|
+
* Provides compose() to chain multiple Middleware functions together
|
|
1416
|
+
* into a single executable pipeline.
|
|
1417
|
+
*
|
|
1418
|
+
* @epic T4820
|
|
1419
|
+
* @task T4815
|
|
1420
|
+
*/
|
|
1421
|
+
|
|
1422
|
+
/**
|
|
1423
|
+
* Composes an array of Middleware functions into a single Middleware function.
|
|
1424
|
+
* Execution flows through the array from first to last, and returns bubble
|
|
1425
|
+
* back up from last to first.
|
|
1426
|
+
*
|
|
1427
|
+
* @remarks
|
|
1428
|
+
* Uses an index-tracked dispatch function to prevent `next()` from being
|
|
1429
|
+
* called multiple times within a single middleware. If the array is empty,
|
|
1430
|
+
* a passthrough middleware that immediately calls `next()` is returned.
|
|
1431
|
+
*
|
|
1432
|
+
* @param middlewares - Array of middleware functions to chain
|
|
1433
|
+
* @returns A single composed Middleware function
|
|
1434
|
+
*
|
|
1435
|
+
* @example
|
|
1436
|
+
* ```typescript
|
|
1437
|
+
* import { compose } from './pipeline.js';
|
|
1438
|
+
*
|
|
1439
|
+
* const pipeline = compose([sanitizer, rateLimiter, protocolEnforcement]);
|
|
1440
|
+
* const response = await pipeline(request, handler);
|
|
1441
|
+
* ```
|
|
1442
|
+
*/
|
|
1443
|
+
declare function compose(middlewares: Middleware[]): Middleware;
|
|
1444
|
+
|
|
1445
|
+
/**
|
|
1446
|
+
* Unified CQRS Dispatch Layer -- Operation Registry (shim)
|
|
1447
|
+
*
|
|
1448
|
+
* The `OPERATIONS` data array was relocated to
|
|
1449
|
+
* `@cleocode/contracts/dispatch/operations-registry` in T10061 / T9833b
|
|
1450
|
+
* (E-CLI-BOUNDARY · Saga T9831 SG-ARCH-SOLID). This file is now a thin
|
|
1451
|
+
* re-export shim that:
|
|
1452
|
+
*
|
|
1453
|
+
* 1. Re-exports `OPERATIONS` from contracts so every downstream consumer
|
|
1454
|
+
* that already imports `{ OPERATIONS }` from this path continues to
|
|
1455
|
+
* compile unchanged.
|
|
1456
|
+
* 2. Keeps the pure utility / derivation functions (`resolve`,
|
|
1457
|
+
* `validateRequiredParams`, `getByDomain`, etc.) that operate on
|
|
1458
|
+
* the data and are rightly colocated with the CLI package.
|
|
1459
|
+
*
|
|
1460
|
+
* @epic T4820
|
|
1461
|
+
* @task T4814, T5241, T5615, T10061
|
|
1462
|
+
*/
|
|
1463
|
+
|
|
1464
|
+
/** The single source of truth for all operations in CLEO (data lives in @cleocode/contracts). */
|
|
1465
|
+
declare const OPERATIONS: OperationDef[];
|
|
1466
|
+
/**
|
|
1467
|
+
* Derive a gateway operation matrix from the registry.
|
|
1468
|
+
*
|
|
1469
|
+
* Returns `Record<string, string[]>` containing:
|
|
1470
|
+
* - All canonical domains with their operations
|
|
1471
|
+
*
|
|
1472
|
+
* This is the SINGLE derivation point — gateways use this instead of
|
|
1473
|
+
* maintaining independent operation lists.
|
|
1474
|
+
*/
|
|
1475
|
+
declare function deriveGatewayMatrix(gateway: Gateway): Record<string, string[]>;
|
|
1476
|
+
/**
|
|
1477
|
+
* Get all accepted domain names for a gateway (canonical only).
|
|
1478
|
+
*/
|
|
1479
|
+
declare function getGatewayDomains(gateway: Gateway): string[];
|
|
1480
|
+
/**
|
|
1481
|
+
* Resolves a domain + operation to its registered definition.
|
|
1482
|
+
*/
|
|
1483
|
+
declare function resolve(gateway: Gateway, domain: string, operation: string): Resolution | undefined;
|
|
1484
|
+
/**
|
|
1485
|
+
* Validates that all required parameters are present in the request.
|
|
1486
|
+
* Returns an array of missing parameter keys.
|
|
1487
|
+
*/
|
|
1488
|
+
declare function validateRequiredParams(def: OperationDef, params?: Record<string, unknown>): string[];
|
|
1489
|
+
/** Get all operations for a specific canonical domain. */
|
|
1490
|
+
declare function getByDomain(domain: CanonicalDomain): OperationDef[];
|
|
1491
|
+
/** Get all operations for a specific gateway. */
|
|
1492
|
+
declare function getByGateway(gateway: Gateway): OperationDef[];
|
|
1493
|
+
/** Get all operations available at or below a specific tier. */
|
|
1494
|
+
declare function getByTier(tier: Tier): OperationDef[];
|
|
1495
|
+
/** Get a list of canonical domains that actually have operations registered. */
|
|
1496
|
+
declare function getActiveDomains(): CanonicalDomain[];
|
|
1497
|
+
/**
|
|
1498
|
+
* Returns summary counts of operations for module validation.
|
|
1499
|
+
*/
|
|
1500
|
+
declare function getCounts(): {
|
|
1501
|
+
query: number;
|
|
1502
|
+
mutate: number;
|
|
1503
|
+
total: number;
|
|
1504
|
+
};
|
|
1505
|
+
|
|
1506
|
+
/**
|
|
1507
|
+
* `@cleocode/runtime/gateway` — the transport-agnostic CQRS dispatch core.
|
|
1508
|
+
*
|
|
1509
|
+
* Relocated from `packages/cleo/src/dispatch` (R3-T3 · T11447 ·
|
|
1510
|
+
* SG-RUNTIME-UNIFICATION) so the dispatcher is owned by the runtime layer and
|
|
1511
|
+
* can be driven by ANY transport adapter — CLI, MCP, RPC, HTTP — not just the
|
|
1512
|
+
* CLI. Mirrors the `@cleocode/runtime/daemon` subpath (R2).
|
|
1513
|
+
*
|
|
1514
|
+
* The CLI adapter (`packages/cleo/src/dispatch/adapters/cli.ts`) and the
|
|
1515
|
+
* domain handlers + transport-specific middleware stay in `packages/cleo`; they
|
|
1516
|
+
* are INJECTED into the {@link Dispatcher} via {@link DispatcherConfig}. The
|
|
1517
|
+
* gateway contract itself lives in `@cleocode/contracts/gateway` (R3-T2).
|
|
1518
|
+
*
|
|
1519
|
+
* `packages/cleo/src/dispatch/{dispatcher,registry,lib/meta,middleware/pipeline}.ts`
|
|
1520
|
+
* remain as thin re-export shims pointing here, so the in-package import sites
|
|
1521
|
+
* (cli adapter, ~18 registry consumers, getCliDispatcher) compile unchanged.
|
|
1522
|
+
*
|
|
1523
|
+
* @epic T4820
|
|
1524
|
+
* @task T11447
|
|
1525
|
+
* @saga T11243
|
|
1526
|
+
*/
|
|
1527
|
+
|
|
1528
|
+
/**
|
|
1529
|
+
* Transport-agnostic gateway entrypoint. Wraps a configured {@link Dispatcher}
|
|
1530
|
+
* (`compose(middlewares) → DomainHandler`) behind a single `handle()` call that
|
|
1531
|
+
* every transport adapter (CLI/MCP/RPC/HTTP) invokes uniformly. The adapter
|
|
1532
|
+
* owns wire concerns (error-render, `process.exit`, serialization); the handler
|
|
1533
|
+
* only resolves, validates, runs middleware, and returns a {@link DispatchResponse}.
|
|
1534
|
+
*/
|
|
1535
|
+
interface GatewayHandler {
|
|
1536
|
+
/** Route one gateway request through the dispatch pipeline. */
|
|
1537
|
+
handle(req: DispatchRequest): Promise<DispatchResponse>;
|
|
1538
|
+
}
|
|
1539
|
+
/**
|
|
1540
|
+
* Build a {@link GatewayHandler} from a {@link DispatcherConfig} (injected
|
|
1541
|
+
* domain handlers + middleware). The returned handler is transport-neutral.
|
|
1542
|
+
*/
|
|
1543
|
+
declare function createGatewayHandler(config: DispatcherConfig): GatewayHandler;
|
|
1544
|
+
|
|
1545
|
+
export { getByDomain as $, type GatewayHandler as A, BRAIN_DB_FILENAME as B, CLEO_DIR_NAME as C, Dispatcher as D, type DecodeResult, type ErrorMeta as E, type GatewayScope as F, type GatewayOperationKey as G, type HttpServerHandle as H, type GatewaySubsystemContext as I, type GatewaySubsystemOptions as J, STRING_TO_EXIT as K, buildNexusMetaExtensions as L, LineBuffer, MCP_TOOL_PREFIX as M, cleoErrorToEngineError as N, OPERATIONS as O, compose as P, createDispatchMeta as Q, createGatewayHandler as R, type RpcServerHandle, type RpcServerOptions, SSE_HEADERS as S, defineGatewaySubsystem as T, deriveGatewayMatrix as U, engineError as V, WORKFLOWS_SUBDIR as W, formatSuggestedNext as X, gatewaySubsystemName as Y, generateTemplateConfig as Z, getActiveDomains as _, type HttpServerOptions as a, getByGateway as a0, getByTier as a1, getCounts as a2, getGatewayDomains as a3, getJobManager as a4, getTemplateForSubcommand as a5, mapNumericExitCodeToString as a6, parseIssueTemplates as a7, pickDecoratorMetaExtensions as a8, resolve as a9, setJobManager as aa, stampNexusMeta as ab, validateLabels as ac, validateRequiredParams as ad, validateSuggestedNext as ae, type HttpUnaryRequest as b, buildErrorFrame, type HttpUnaryResult as c, type SseEmitter as d, decodeLine, type SseFrame as e, encodeFrame, type SseSource as f, createSseStream as g, encodeSseFrame as h, encodeStreamEvent as i, statusForResponse as j, type McpContent as k, type McpInputProperty as l, type McpServerOptions as m, type McpTool as n, type McpToolResult as o, parseHttpRoute as p, buildToolsList as q, routeUnary as r, routeFrame, startHttpServer as s, startRpcServer, callTool as t, exposedOperations as u, operationToMcpTool as v, operationToToolName as w, startMcpServer as x, toolNameToOperationKey as y, type DispatcherConfig as z };
|