@senpi/runtime 1.0.95-dev.runtime-phase-2.20260506193916 → 1.0.95-dev.runtime-phase-2-api.20260507130312
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/README.md +13 -0
- package/dist/index.d.ts +35 -100
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +33 -930
- package/dist/index.js.map +1 -1
- package/dist/plugin.d.ts +110 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +1055 -0
- package/dist/plugin.js.map +1 -0
- package/dist/runtime/__tests__/helpers/runtime-gateway-harness.d.ts +3 -3
- package/dist/runtime/audit-query.types.d.ts +113 -0
- package/dist/runtime/audit-query.types.d.ts.map +1 -0
- package/dist/runtime/audit-query.types.js +35 -0
- package/dist/runtime/audit-query.types.js.map +1 -0
- package/dist/runtime/auto-update/coordinator.d.ts +12 -8
- package/dist/runtime/auto-update/coordinator.d.ts.map +1 -1
- package/dist/runtime/auto-update/coordinator.js +18 -17
- package/dist/runtime/auto-update/coordinator.js.map +1 -1
- package/dist/runtime/auto-update/gateway-restart.d.ts +9 -2
- package/dist/runtime/auto-update/gateway-restart.d.ts.map +1 -1
- package/dist/runtime/auto-update/gateway-restart.js +23 -13
- package/dist/runtime/auto-update/gateway-restart.js.map +1 -1
- package/dist/runtime/create-actions.d.ts.map +1 -1
- package/dist/runtime/create-actions.js +13 -2
- package/dist/runtime/create-actions.js.map +1 -1
- package/dist/runtime/run.d.ts +52 -1
- package/dist/runtime/run.d.ts.map +1 -1
- package/dist/runtime/run.js +9 -0
- package/dist/runtime/run.js.map +1 -1
- package/dist/runtime/runtime.d.ts +56 -0
- package/dist/runtime/runtime.d.ts.map +1 -1
- package/dist/runtime/runtime.js +169 -1
- package/dist/runtime/runtime.js.map +1 -1
- package/dist/runtime-api/config.d.ts +47 -0
- package/dist/runtime-api/config.d.ts.map +1 -0
- package/dist/runtime-api/config.js +74 -0
- package/dist/runtime-api/config.js.map +1 -0
- package/dist/runtime-api/handlers/audit.d.ts +78 -0
- package/dist/runtime-api/handlers/audit.d.ts.map +1 -0
- package/dist/runtime-api/handlers/audit.js +65 -0
- package/dist/runtime-api/handlers/audit.js.map +1 -0
- package/dist/runtime-api/handlers/group-by-scanner.d.ts +43 -0
- package/dist/runtime-api/handlers/group-by-scanner.d.ts.map +1 -0
- package/dist/runtime-api/handlers/group-by-scanner.js +43 -0
- package/dist/runtime-api/handlers/group-by-scanner.js.map +1 -0
- package/dist/runtime-api/handlers/ingest-signals.d.ts +54 -0
- package/dist/runtime-api/handlers/ingest-signals.d.ts.map +1 -0
- package/dist/runtime-api/handlers/ingest-signals.js +141 -0
- package/dist/runtime-api/handlers/ingest-signals.js.map +1 -0
- package/dist/runtime-api/handlers/lookup.d.ts +36 -0
- package/dist/runtime-api/handlers/lookup.d.ts.map +1 -0
- package/dist/runtime-api/handlers/lookup.js +31 -0
- package/dist/runtime-api/handlers/lookup.js.map +1 -0
- package/dist/runtime-api/handlers/types.d.ts +85 -0
- package/dist/runtime-api/handlers/types.d.ts.map +1 -0
- package/dist/runtime-api/handlers/types.js +8 -0
- package/dist/runtime-api/handlers/types.js.map +1 -0
- package/dist/runtime-api/lifecycle.d.ts +98 -0
- package/dist/runtime-api/lifecycle.d.ts.map +1 -0
- package/dist/runtime-api/lifecycle.js +137 -0
- package/dist/runtime-api/lifecycle.js.map +1 -0
- package/dist/runtime-api/readiness.d.ts +37 -0
- package/dist/runtime-api/readiness.d.ts.map +1 -0
- package/dist/runtime-api/readiness.js +35 -0
- package/dist/runtime-api/readiness.js.map +1 -0
- package/dist/runtime-api/routes/audit.d.ts +40 -0
- package/dist/runtime-api/routes/audit.d.ts.map +1 -0
- package/dist/runtime-api/routes/audit.js +90 -0
- package/dist/runtime-api/routes/audit.js.map +1 -0
- package/dist/runtime-api/routes/audit.schema.d.ts +75 -0
- package/dist/runtime-api/routes/audit.schema.d.ts.map +1 -0
- package/dist/runtime-api/routes/audit.schema.js +78 -0
- package/dist/runtime-api/routes/audit.schema.js.map +1 -0
- package/dist/runtime-api/routes/health.d.ts +53 -0
- package/dist/runtime-api/routes/health.d.ts.map +1 -0
- package/dist/runtime-api/routes/health.js +48 -0
- package/dist/runtime-api/routes/health.js.map +1 -0
- package/dist/runtime-api/routes/signals.d.ts +50 -0
- package/dist/runtime-api/routes/signals.d.ts.map +1 -0
- package/dist/runtime-api/routes/signals.js +90 -0
- package/dist/runtime-api/routes/signals.js.map +1 -0
- package/dist/runtime-api/routes/signals.schema.d.ts +94 -0
- package/dist/runtime-api/routes/signals.schema.d.ts.map +1 -0
- package/dist/runtime-api/routes/signals.schema.js +58 -0
- package/dist/runtime-api/routes/signals.schema.js.map +1 -0
- package/dist/runtime-api/server.d.ts +79 -0
- package/dist/runtime-api/server.d.ts.map +1 -0
- package/dist/runtime-api/server.js +109 -0
- package/dist/runtime-api/server.js.map +1 -0
- package/dist/senpi/client.d.ts +92 -2
- package/dist/senpi/client.d.ts.map +1 -1
- package/dist/senpi/client.js +181 -8
- package/dist/senpi/client.js.map +1 -1
- package/openclaw.plugin.json +24 -0
- package/package.json +6 -2
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transport-agnostic type definitions shared by all `src/runtime-api/handlers/`
|
|
3
|
+
* modules and the route layer that consumes them.
|
|
4
|
+
*
|
|
5
|
+
* No Fastify imports. No side-effects.
|
|
6
|
+
*/
|
|
7
|
+
import type { ExternalScannerIngestSignalItem, ExternalScannerIngestErrorCode } from "../../scanners/external-scanner-receiver.js";
|
|
8
|
+
import type { Signal } from "../../scanners/types.js";
|
|
9
|
+
/**
|
|
10
|
+
* Per-item shape accepted by `POST /signals`.
|
|
11
|
+
*
|
|
12
|
+
* Mirrors {@link ExternalScannerIngestRequest} with `address` lifted to the
|
|
13
|
+
* item level so a single request body can target multiple wallets.
|
|
14
|
+
*
|
|
15
|
+
* Only one of `data` (single-signal shortcut) or `signals[]` (batch within
|
|
16
|
+
* one address+scanner) may be present — the handler enforces mutual exclusion
|
|
17
|
+
* at runtime in addition to the TypeBox schema check.
|
|
18
|
+
*/
|
|
19
|
+
export interface SignalItem {
|
|
20
|
+
/** Wallet address of the target runtime. Lowercased server-side. */
|
|
21
|
+
address: string;
|
|
22
|
+
/** Configured scanner id. */
|
|
23
|
+
scanner: string;
|
|
24
|
+
/** Asset for a single ingested signal. */
|
|
25
|
+
asset?: string;
|
|
26
|
+
/** Direction for a single ingested signal. */
|
|
27
|
+
direction?: Signal["direction"];
|
|
28
|
+
/** Confidence score for a single ingested signal (0–1). */
|
|
29
|
+
score?: number;
|
|
30
|
+
/** Signal classification override for a single ingested signal. */
|
|
31
|
+
signal_type?: string;
|
|
32
|
+
/**
|
|
33
|
+
* Single ingest payload for signal-producing or context-producing scanners.
|
|
34
|
+
* Mutually exclusive with `signals[]`.
|
|
35
|
+
*/
|
|
36
|
+
data?: Record<string, unknown>;
|
|
37
|
+
/**
|
|
38
|
+
* Batch signal payloads for signal-producing external scanners.
|
|
39
|
+
* Mutually exclusive with `data`.
|
|
40
|
+
* Prefer this over multiple top-level items for the same address+scanner
|
|
41
|
+
* to avoid the per-(address, scanner) ingest lock.
|
|
42
|
+
*/
|
|
43
|
+
signals?: ExternalScannerIngestSignalItem[];
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Per-item result returned by {@link ingestSignalsHandler}.
|
|
47
|
+
*
|
|
48
|
+
* INTERNAL handler shape — kept flat so the gateway shim
|
|
49
|
+
* (`senpi.ingestExternalScannerData` in `src/plugin.ts`) can read fields
|
|
50
|
+
* directly without a transform. The HTTP route layer
|
|
51
|
+
* (`routes/signals.ts`) reshapes this into the senpi-stack wire format
|
|
52
|
+
* `{ success, address, scanner, data | error }` before sending on the wire.
|
|
53
|
+
*
|
|
54
|
+
* Discriminator is `success` (matching MCP and the rest of the runtime-api
|
|
55
|
+
* surface).
|
|
56
|
+
*
|
|
57
|
+
* Error codes reuse the existing
|
|
58
|
+
* {@link ExternalScannerIngestErrorCode} taxonomy:
|
|
59
|
+
* `INVALID_REQUEST | NOT_FOUND | UNAVAILABLE`. No `INTERNAL` code is added —
|
|
60
|
+
* unexpected throws map to `UNAVAILABLE`.
|
|
61
|
+
*/
|
|
62
|
+
export type SignalResult = {
|
|
63
|
+
success: true;
|
|
64
|
+
/** Lowercased wallet address. */
|
|
65
|
+
address: string;
|
|
66
|
+
/** Configured scanner id. */
|
|
67
|
+
scanner: string;
|
|
68
|
+
/** Unix epoch ms timestamp from the ingest result. */
|
|
69
|
+
timestamp: number;
|
|
70
|
+
/** Number of committed signals; `0` for context-only ingests. */
|
|
71
|
+
signalCount: number;
|
|
72
|
+
/** Whether retained context was updated. */
|
|
73
|
+
contextUpdated: boolean;
|
|
74
|
+
} | {
|
|
75
|
+
success: false;
|
|
76
|
+
/** Lowercased wallet address. */
|
|
77
|
+
address: string;
|
|
78
|
+
/** Configured scanner id. */
|
|
79
|
+
scanner: string;
|
|
80
|
+
/** Machine-readable failure code. */
|
|
81
|
+
code: ExternalScannerIngestErrorCode;
|
|
82
|
+
/** Human-readable failure detail. */
|
|
83
|
+
message: string;
|
|
84
|
+
};
|
|
85
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/runtime-api/handlers/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,+BAA+B,EAC/B,8BAA8B,EAC/B,MAAM,6CAA6C,CAAC;AACrD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAEtD;;;;;;;;;GASG;AACH,MAAM,WAAW,UAAU;IACzB,oEAAoE;IACpE,OAAO,EAAE,MAAM,CAAC;IAChB,6BAA6B;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,0CAA0C;IAC1C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8CAA8C;IAC9C,SAAS,CAAC,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC;IAChC,2DAA2D;IAC3D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mEAAmE;IACnE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B;;;;;OAKG;IACH,OAAO,CAAC,EAAE,+BAA+B,EAAE,CAAC;CAC7C;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,YAAY,GACpB;IACE,OAAO,EAAE,IAAI,CAAC;IACd,iCAAiC;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,6BAA6B;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,sDAAsD;IACtD,SAAS,EAAE,MAAM,CAAC;IAClB,iEAAiE;IACjE,WAAW,EAAE,MAAM,CAAC;IACpB,4CAA4C;IAC5C,cAAc,EAAE,OAAO,CAAC;CACzB,GACD;IACE,OAAO,EAAE,KAAK,CAAC;IACf,iCAAiC;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,6BAA6B;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,qCAAqC;IACrC,IAAI,EAAE,8BAA8B,CAAC;IACrC,qCAAqC;IACrC,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/runtime-api/handlers/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Start/stop helpers for the runtime HTTP API.
|
|
3
|
+
*
|
|
4
|
+
* This module owns the Fastify server lifecycle: binding the port,
|
|
5
|
+
* flipping readiness states, and draining in-flight requests on shutdown
|
|
6
|
+
* with a hard 15s timeout so teardown always proceeds.
|
|
7
|
+
*
|
|
8
|
+
* The plugin glue layer (S6 in `src/index.ts`) calls {@link startApi} during
|
|
9
|
+
* `api.lifecycle.start` and {@link ApiHandle.close} during `api.lifecycle.stop`.
|
|
10
|
+
* This module does NOT modify `src/index.ts`.
|
|
11
|
+
*/
|
|
12
|
+
import type { FastifyInstance } from "fastify";
|
|
13
|
+
import { type CreateApiDeps, type ApiArtifacts } from "./server.js";
|
|
14
|
+
import type { Logger } from "../utils/logger.js";
|
|
15
|
+
/**
|
|
16
|
+
* Hard timeout for Fastify drain.
|
|
17
|
+
*
|
|
18
|
+
* After this many milliseconds, {@link stopApi} logs a warning and proceeds
|
|
19
|
+
* to runtime teardown regardless of in-flight requests. 15s is enough
|
|
20
|
+
* headroom for any Telegram notification or close-resolver tick that may
|
|
21
|
+
* be mid-flight on the underlying runtime.
|
|
22
|
+
*/
|
|
23
|
+
export declare const DRAIN_HARD_TIMEOUT_MS = 15000;
|
|
24
|
+
/**
|
|
25
|
+
* Handle returned by {@link startApi}.
|
|
26
|
+
*
|
|
27
|
+
* The plugin lifecycle layer holds this and calls `ready()` once runtimes
|
|
28
|
+
* are confirmed healthy, then `close()` at shutdown.
|
|
29
|
+
*/
|
|
30
|
+
export interface ApiHandle {
|
|
31
|
+
/** The bound Fastify instance (exposed for tests that need `fastify.inject`). */
|
|
32
|
+
fastify: FastifyInstance;
|
|
33
|
+
/**
|
|
34
|
+
* Flip readiness from `"starting"` to `"ok"`.
|
|
35
|
+
*
|
|
36
|
+
* Call immediately after {@link startApi} resolves unless you need a
|
|
37
|
+
* deliberate warm-up window before accepting traffic.
|
|
38
|
+
*/
|
|
39
|
+
ready: () => void;
|
|
40
|
+
/**
|
|
41
|
+
* Drain in-flight requests and close the HTTP server.
|
|
42
|
+
*
|
|
43
|
+
* Sets readiness to `"draining"` (so `/health` starts returning 503),
|
|
44
|
+
* then awaits `fastify.close()` with a hard {@link DRAIN_HARD_TIMEOUT_MS}
|
|
45
|
+
* timeout. If the timeout fires, a warning is logged and teardown proceeds
|
|
46
|
+
* without waiting further.
|
|
47
|
+
*
|
|
48
|
+
* Never throws — all errors are logged and swallowed so the caller's
|
|
49
|
+
* teardown sequence is not interrupted.
|
|
50
|
+
*/
|
|
51
|
+
close: () => Promise<void>;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Start the HTTP API server.
|
|
55
|
+
*
|
|
56
|
+
* Calls {@link createApi} to build the Fastify instance, binds it to
|
|
57
|
+
* `deps.apiConfig.host:deps.apiConfig.port`, and returns an {@link ApiHandle}.
|
|
58
|
+
*
|
|
59
|
+
* The server is NOT yet marked ready after this call — `/health` will return
|
|
60
|
+
* 503 until the caller invokes `handle.ready()`. Call `ready()` immediately
|
|
61
|
+
* after the runtimes are confirmed up, which in the plugin lifecycle is right
|
|
62
|
+
* after `startApi` returns.
|
|
63
|
+
*
|
|
64
|
+
* A boot-time warning is logged when `deps.apiConfig.host !== "127.0.0.1"` so
|
|
65
|
+
* any operator who opts into Docker port mapping sees it in the logs.
|
|
66
|
+
*
|
|
67
|
+
* @param deps Same dependencies passed to {@link createApi}: runtime handles,
|
|
68
|
+
* validated API config, logger, and plugin version string.
|
|
69
|
+
* @returns An {@link ApiHandle} whose `ready()` and `close()` methods drive
|
|
70
|
+
* the lifecycle.
|
|
71
|
+
* @throws Re-throws any `fastify.listen()` error (e.g. `EADDRINUSE`) so
|
|
72
|
+
* the plugin lifecycle layer can perform its rollback and fail the
|
|
73
|
+
* openclaw start cleanly.
|
|
74
|
+
*/
|
|
75
|
+
export declare function startApi(deps: CreateApiDeps): Promise<ApiHandle>;
|
|
76
|
+
/**
|
|
77
|
+
* Drain in-flight requests and close the HTTP server.
|
|
78
|
+
*
|
|
79
|
+
* Sets readiness to `"draining"` immediately so `/health` stops returning 200,
|
|
80
|
+
* then races `fastify.close()` against {@link DRAIN_HARD_TIMEOUT_MS}. If the
|
|
81
|
+
* hard timeout fires first, a warning is logged and a best-effort force-close
|
|
82
|
+
* is issued (fire-and-forget). The function always resolves — it never rejects.
|
|
83
|
+
*
|
|
84
|
+
* @param artifacts The {@link ApiArtifacts} returned by `createApi`.
|
|
85
|
+
* @param logger Logger for the startup/drain messages.
|
|
86
|
+
*/
|
|
87
|
+
declare function stopApi(artifacts: ApiArtifacts, logger: Logger): Promise<void>;
|
|
88
|
+
/**
|
|
89
|
+
* Exported for tests (S8) so they can assert drain timeout behavior without
|
|
90
|
+
* going through the full `startApi` path.
|
|
91
|
+
*
|
|
92
|
+
* @internal Not part of the public API surface — import only in `__tests__/`.
|
|
93
|
+
*/
|
|
94
|
+
export declare const __test_only: {
|
|
95
|
+
stopApi: typeof stopApi;
|
|
96
|
+
};
|
|
97
|
+
export {};
|
|
98
|
+
//# sourceMappingURL=lifecycle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../src/runtime-api/lifecycle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,EAAa,KAAK,aAAa,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAC/E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAEjD;;;;;;;GAOG;AACH,eAAO,MAAM,qBAAqB,QAAS,CAAC;AAE5C;;;;;GAKG;AACH,MAAM,WAAW,SAAS;IACxB,iFAAiF;IACjF,OAAO,EAAE,eAAe,CAAC;IACzB;;;;;OAKG;IACH,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB;;;;;;;;;;OAUG;IACH,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,SAAS,CAAC,CA+BtE;AAED;;;;;;;;;;GAUG;AACH,iBAAe,OAAO,CAAC,SAAS,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAuD7E;AAED;;;;;GAKG;AACH,eAAO,MAAM,WAAW;;CAAc,CAAC"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Start/stop helpers for the runtime HTTP API.
|
|
3
|
+
*
|
|
4
|
+
* This module owns the Fastify server lifecycle: binding the port,
|
|
5
|
+
* flipping readiness states, and draining in-flight requests on shutdown
|
|
6
|
+
* with a hard 15s timeout so teardown always proceeds.
|
|
7
|
+
*
|
|
8
|
+
* The plugin glue layer (S6 in `src/index.ts`) calls {@link startApi} during
|
|
9
|
+
* `api.lifecycle.start` and {@link ApiHandle.close} during `api.lifecycle.stop`.
|
|
10
|
+
* This module does NOT modify `src/index.ts`.
|
|
11
|
+
*/
|
|
12
|
+
import { createApi } from "./server.js";
|
|
13
|
+
/**
|
|
14
|
+
* Hard timeout for Fastify drain.
|
|
15
|
+
*
|
|
16
|
+
* After this many milliseconds, {@link stopApi} logs a warning and proceeds
|
|
17
|
+
* to runtime teardown regardless of in-flight requests. 15s is enough
|
|
18
|
+
* headroom for any Telegram notification or close-resolver tick that may
|
|
19
|
+
* be mid-flight on the underlying runtime.
|
|
20
|
+
*/
|
|
21
|
+
export const DRAIN_HARD_TIMEOUT_MS = 15_000;
|
|
22
|
+
/**
|
|
23
|
+
* Start the HTTP API server.
|
|
24
|
+
*
|
|
25
|
+
* Calls {@link createApi} to build the Fastify instance, binds it to
|
|
26
|
+
* `deps.apiConfig.host:deps.apiConfig.port`, and returns an {@link ApiHandle}.
|
|
27
|
+
*
|
|
28
|
+
* The server is NOT yet marked ready after this call — `/health` will return
|
|
29
|
+
* 503 until the caller invokes `handle.ready()`. Call `ready()` immediately
|
|
30
|
+
* after the runtimes are confirmed up, which in the plugin lifecycle is right
|
|
31
|
+
* after `startApi` returns.
|
|
32
|
+
*
|
|
33
|
+
* A boot-time warning is logged when `deps.apiConfig.host !== "127.0.0.1"` so
|
|
34
|
+
* any operator who opts into Docker port mapping sees it in the logs.
|
|
35
|
+
*
|
|
36
|
+
* @param deps Same dependencies passed to {@link createApi}: runtime handles,
|
|
37
|
+
* validated API config, logger, and plugin version string.
|
|
38
|
+
* @returns An {@link ApiHandle} whose `ready()` and `close()` methods drive
|
|
39
|
+
* the lifecycle.
|
|
40
|
+
* @throws Re-throws any `fastify.listen()` error (e.g. `EADDRINUSE`) so
|
|
41
|
+
* the plugin lifecycle layer can perform its rollback and fail the
|
|
42
|
+
* openclaw start cleanly.
|
|
43
|
+
*/
|
|
44
|
+
export async function startApi(deps) {
|
|
45
|
+
const artifacts = createApi(deps);
|
|
46
|
+
await artifacts.fastify.listen({
|
|
47
|
+
host: deps.apiConfig.host,
|
|
48
|
+
port: deps.apiConfig.port,
|
|
49
|
+
});
|
|
50
|
+
deps.logger.info("[runtime-api] api server started", {
|
|
51
|
+
host: deps.apiConfig.host,
|
|
52
|
+
port: deps.apiConfig.port,
|
|
53
|
+
});
|
|
54
|
+
// Friction is a feature: anyone who flips host to 0.0.0.0 sees this in logs.
|
|
55
|
+
if (deps.apiConfig.host !== "127.0.0.1") {
|
|
56
|
+
deps.logger.warn("[runtime-api] api.host is bound to \"" +
|
|
57
|
+
deps.apiConfig.host +
|
|
58
|
+
"\". " +
|
|
59
|
+
"The API is reachable from anything on this container's Docker network. " +
|
|
60
|
+
"If you set this for Postman testing, make sure your docker-compose port mapping " +
|
|
61
|
+
"uses the \"127.0.0.1:\" prefix so the API is not exposed beyond your laptop's loopback. " +
|
|
62
|
+
"See docs/runtime-docs/runtime-api.md \"Postman testing recipe\".");
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
fastify: artifacts.fastify,
|
|
66
|
+
ready: () => artifacts.readiness.setOk(),
|
|
67
|
+
close: () => stopApi(artifacts, deps.logger),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Drain in-flight requests and close the HTTP server.
|
|
72
|
+
*
|
|
73
|
+
* Sets readiness to `"draining"` immediately so `/health` stops returning 200,
|
|
74
|
+
* then races `fastify.close()` against {@link DRAIN_HARD_TIMEOUT_MS}. If the
|
|
75
|
+
* hard timeout fires first, a warning is logged and a best-effort force-close
|
|
76
|
+
* is issued (fire-and-forget). The function always resolves — it never rejects.
|
|
77
|
+
*
|
|
78
|
+
* @param artifacts The {@link ApiArtifacts} returned by `createApi`.
|
|
79
|
+
* @param logger Logger for the startup/drain messages.
|
|
80
|
+
*/
|
|
81
|
+
async function stopApi(artifacts, logger) {
|
|
82
|
+
artifacts.readiness.setDraining();
|
|
83
|
+
// Capture the close promise once. Two reasons:
|
|
84
|
+
// 1. We need to mute its rejection unconditionally — when the drain
|
|
85
|
+
// timeout wins the race, the close promise is still in flight and
|
|
86
|
+
// may reject later, producing an unhandled rejection (the second
|
|
87
|
+
// .close() call in the timeout branch was a *new* promise, not a
|
|
88
|
+
// handler for the original).
|
|
89
|
+
// 2. We need a stable reference to race against the timeout below.
|
|
90
|
+
const closePromise = artifacts.fastify.close();
|
|
91
|
+
// Pre-attach a no-op catch so a late rejection on the original promise
|
|
92
|
+
// is always considered handled. The catch returns a new derived promise
|
|
93
|
+
// that we ignore — this does NOT prevent Promise.race below from seeing
|
|
94
|
+
// the rejection, since handlers on the same promise are independent.
|
|
95
|
+
closePromise.catch(() => { });
|
|
96
|
+
// Capture the timeout handle so we can clear it on the happy path.
|
|
97
|
+
// Otherwise, when fastify.close() wins the race, the still-pending
|
|
98
|
+
// setTimeout fires DRAIN_HARD_TIMEOUT_MS later and rejects an unwatched
|
|
99
|
+
// promise — an unhandled rejection on every clean shutdown.
|
|
100
|
+
let timeoutHandle;
|
|
101
|
+
try {
|
|
102
|
+
await Promise.race([
|
|
103
|
+
closePromise,
|
|
104
|
+
new Promise((_resolve, reject) => {
|
|
105
|
+
timeoutHandle = setTimeout(() => reject(new Error("DRAIN_TIMEOUT")), DRAIN_HARD_TIMEOUT_MS);
|
|
106
|
+
}),
|
|
107
|
+
]);
|
|
108
|
+
logger.info("[runtime-api] api server stopped");
|
|
109
|
+
}
|
|
110
|
+
catch (err) {
|
|
111
|
+
if (err.message === "DRAIN_TIMEOUT") {
|
|
112
|
+
logger.warn("[runtime-api] api server drain exceeded hard timeout; proceeding to teardown", { timeoutMs: DRAIN_HARD_TIMEOUT_MS });
|
|
113
|
+
// The original closePromise is still in flight; any future rejection
|
|
114
|
+
// is already muted by the .catch() above. Caller proceeds to
|
|
115
|
+
// runtime teardown.
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
// Unexpected close error — log and swallow so teardown sequence continues.
|
|
119
|
+
logger.error("[runtime-api] unexpected error during api server close", {
|
|
120
|
+
err,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
finally {
|
|
124
|
+
// Always clear so the timeout cannot fire after stopApi returns.
|
|
125
|
+
if (timeoutHandle !== undefined) {
|
|
126
|
+
clearTimeout(timeoutHandle);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Exported for tests (S8) so they can assert drain timeout behavior without
|
|
132
|
+
* going through the full `startApi` path.
|
|
133
|
+
*
|
|
134
|
+
* @internal Not part of the public API surface — import only in `__tests__/`.
|
|
135
|
+
*/
|
|
136
|
+
export const __test_only = { stopApi };
|
|
137
|
+
//# sourceMappingURL=lifecycle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lifecycle.js","sourceRoot":"","sources":["../../src/runtime-api/lifecycle.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Three-state readiness contract for `GET /health`.
|
|
3
|
+
*
|
|
4
|
+
* S5 (lifecycle layer) wires the lifecycle to flip these states:
|
|
5
|
+
* - `"starting"` — initial state; server is binding or warming up.
|
|
6
|
+
* - `"ok"` — server is bound and ready to handle traffic.
|
|
7
|
+
* - `"draining"` — graceful-shutdown in progress; stop routing new requests.
|
|
8
|
+
*
|
|
9
|
+
* `GET /health` returns `200` for `"ok"` and `503` for the other two states
|
|
10
|
+
* so load balancers stop routing before the process exits.
|
|
11
|
+
*/
|
|
12
|
+
/** Three valid readiness states. */
|
|
13
|
+
export type ReadinessState = "starting" | "ok" | "draining";
|
|
14
|
+
/**
|
|
15
|
+
* Read-only interface consumed by `GET /health` and any other component that
|
|
16
|
+
* needs to observe readiness without being able to mutate it.
|
|
17
|
+
*/
|
|
18
|
+
export interface Readiness {
|
|
19
|
+
/** Return the current readiness state synchronously. */
|
|
20
|
+
state(): ReadinessState;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Create a mutable readiness object.
|
|
24
|
+
*
|
|
25
|
+
* The returned object satisfies both the read-only {@link Readiness} interface
|
|
26
|
+
* and exposes `setOk()` / `setDraining()` mutators for the lifecycle layer
|
|
27
|
+
* (S5) to call at the appropriate moments.
|
|
28
|
+
*
|
|
29
|
+
* Initial state is `"starting"`.
|
|
30
|
+
*
|
|
31
|
+
* @returns A combined readiness + mutator object.
|
|
32
|
+
*/
|
|
33
|
+
export declare function createReadiness(): Readiness & {
|
|
34
|
+
setOk(): void;
|
|
35
|
+
setDraining(): void;
|
|
36
|
+
};
|
|
37
|
+
//# sourceMappingURL=readiness.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"readiness.d.ts","sourceRoot":"","sources":["../../src/runtime-api/readiness.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,oCAAoC;AACpC,MAAM,MAAM,cAAc,GAAG,UAAU,GAAG,IAAI,GAAG,UAAU,CAAC;AAE5D;;;GAGG;AACH,MAAM,WAAW,SAAS;IACxB,wDAAwD;IACxD,KAAK,IAAI,cAAc,CAAC;CACzB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,IAAI,SAAS,GAAG;IAC7C,KAAK,IAAI,IAAI,CAAC;IACd,WAAW,IAAI,IAAI,CAAC;CACrB,CAWA"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Three-state readiness contract for `GET /health`.
|
|
3
|
+
*
|
|
4
|
+
* S5 (lifecycle layer) wires the lifecycle to flip these states:
|
|
5
|
+
* - `"starting"` — initial state; server is binding or warming up.
|
|
6
|
+
* - `"ok"` — server is bound and ready to handle traffic.
|
|
7
|
+
* - `"draining"` — graceful-shutdown in progress; stop routing new requests.
|
|
8
|
+
*
|
|
9
|
+
* `GET /health` returns `200` for `"ok"` and `503` for the other two states
|
|
10
|
+
* so load balancers stop routing before the process exits.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Create a mutable readiness object.
|
|
14
|
+
*
|
|
15
|
+
* The returned object satisfies both the read-only {@link Readiness} interface
|
|
16
|
+
* and exposes `setOk()` / `setDraining()` mutators for the lifecycle layer
|
|
17
|
+
* (S5) to call at the appropriate moments.
|
|
18
|
+
*
|
|
19
|
+
* Initial state is `"starting"`.
|
|
20
|
+
*
|
|
21
|
+
* @returns A combined readiness + mutator object.
|
|
22
|
+
*/
|
|
23
|
+
export function createReadiness() {
|
|
24
|
+
let s = "starting";
|
|
25
|
+
return {
|
|
26
|
+
state: () => s,
|
|
27
|
+
setOk: () => {
|
|
28
|
+
s = "ok";
|
|
29
|
+
},
|
|
30
|
+
setDraining: () => {
|
|
31
|
+
s = "draining";
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=readiness.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"readiness.js","sourceRoot":"","sources":["../../src/runtime-api/readiness.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAcH;;;;;;;;;;GAUG;AACH,MAAM,UAAU,eAAe;IAI7B,IAAI,CAAC,GAAmB,UAAU,CAAC;IACnC,OAAO;QACL,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;QACd,KAAK,EAAE,GAAG,EAAE;YACV,CAAC,GAAG,IAAI,CAAC;QACX,CAAC;QACD,WAAW,EAAE,GAAG,EAAE;YAChB,CAAC,GAAG,UAAU,CAAC;QACjB,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fastify route: `GET /audit`.
|
|
3
|
+
*
|
|
4
|
+
* Proxies a `audit_query` MCP call through the runtime handle matching the
|
|
5
|
+
* requested wallet address. AbortSignal is wired to the HTTP request lifetime
|
|
6
|
+
* so MCP cancels in-flight work when the client disconnects.
|
|
7
|
+
*
|
|
8
|
+
* Business logic lives entirely in `handlers/audit.ts`. This file contains
|
|
9
|
+
* only Fastify wiring and query-string coercion.
|
|
10
|
+
*/
|
|
11
|
+
import type { FastifyInstance } from "fastify";
|
|
12
|
+
import type { RuntimeHandle } from "../../runtime/run.js";
|
|
13
|
+
import type { Logger } from "../../utils/logger.js";
|
|
14
|
+
/**
|
|
15
|
+
* Dependencies injected into the `/audit` route at registration time.
|
|
16
|
+
*/
|
|
17
|
+
export interface AuditRouteDeps {
|
|
18
|
+
/** Live runtime handles maintained by the plugin, keyed by lowercased address. */
|
|
19
|
+
runtimeHandles: Map<string, RuntimeHandle>;
|
|
20
|
+
/** Logger instance for unexpected errors. */
|
|
21
|
+
logger: Logger;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Register `GET /audit` on the given Fastify instance.
|
|
25
|
+
*
|
|
26
|
+
* Coerces string query-string values to their typed counterparts before
|
|
27
|
+
* forwarding to `auditHandler`. The `success` field is `"true" | "false"` from
|
|
28
|
+
* the schema; converted to `boolean`. Integer fields are parsed with
|
|
29
|
+
* `parseInt`. Missing optional fields are left as `undefined`.
|
|
30
|
+
*
|
|
31
|
+
* HTTP status mapping from `AuditQueryError.code`:
|
|
32
|
+
* - `NOT_FOUND` → 404
|
|
33
|
+
* - `ABORTED` → 504 (gateway timeout — client disconnected)
|
|
34
|
+
* - `UPSTREAM` → 502 (bad gateway — MCP error)
|
|
35
|
+
*
|
|
36
|
+
* @param fastify Fastify instance to register the route on.
|
|
37
|
+
* @param deps Route dependencies injected at plugin start.
|
|
38
|
+
*/
|
|
39
|
+
export declare function registerAuditRoute(fastify: FastifyInstance, deps: AuditRouteDeps): void;
|
|
40
|
+
//# sourceMappingURL=audit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../../src/runtime-api/routes/audit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAK/C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAEpD;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,kFAAkF;IAClF,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IAC3C,6CAA6C;IAC7C,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,eAAe,EACxB,IAAI,EAAE,cAAc,GACnB,IAAI,CAoEN"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fastify route: `GET /audit`.
|
|
3
|
+
*
|
|
4
|
+
* Proxies a `audit_query` MCP call through the runtime handle matching the
|
|
5
|
+
* requested wallet address. AbortSignal is wired to the HTTP request lifetime
|
|
6
|
+
* so MCP cancels in-flight work when the client disconnects.
|
|
7
|
+
*
|
|
8
|
+
* Business logic lives entirely in `handlers/audit.ts`. This file contains
|
|
9
|
+
* only Fastify wiring and query-string coercion.
|
|
10
|
+
*/
|
|
11
|
+
import { AuditQuery } from "./audit.schema.js";
|
|
12
|
+
import { auditHandler } from "../handlers/audit.js";
|
|
13
|
+
/**
|
|
14
|
+
* Register `GET /audit` on the given Fastify instance.
|
|
15
|
+
*
|
|
16
|
+
* Coerces string query-string values to their typed counterparts before
|
|
17
|
+
* forwarding to `auditHandler`. The `success` field is `"true" | "false"` from
|
|
18
|
+
* the schema; converted to `boolean`. Integer fields are parsed with
|
|
19
|
+
* `parseInt`. Missing optional fields are left as `undefined`.
|
|
20
|
+
*
|
|
21
|
+
* HTTP status mapping from `AuditQueryError.code`:
|
|
22
|
+
* - `NOT_FOUND` → 404
|
|
23
|
+
* - `ABORTED` → 504 (gateway timeout — client disconnected)
|
|
24
|
+
* - `UPSTREAM` → 502 (bad gateway — MCP error)
|
|
25
|
+
*
|
|
26
|
+
* @param fastify Fastify instance to register the route on.
|
|
27
|
+
* @param deps Route dependencies injected at plugin start.
|
|
28
|
+
*/
|
|
29
|
+
export function registerAuditRoute(fastify, deps) {
|
|
30
|
+
fastify.get("/audit", { schema: { querystring: AuditQuery } }, async (req, reply) => {
|
|
31
|
+
const q = req.query;
|
|
32
|
+
// Coerce string query-string values to typed handler args.
|
|
33
|
+
const args = {
|
|
34
|
+
...(q.tool_name !== undefined && { tool_name: q.tool_name }),
|
|
35
|
+
...(q.action_type !== undefined && { action_type: q.action_type }),
|
|
36
|
+
...(q.success !== undefined && { success: q.success === "true" }),
|
|
37
|
+
...(q.start_time !== undefined && { start_time: q.start_time }),
|
|
38
|
+
...(q.end_time !== undefined && { end_time: q.end_time }),
|
|
39
|
+
...(q.min_duration_ms !== undefined && {
|
|
40
|
+
min_duration_ms: parseInt(q.min_duration_ms, 10),
|
|
41
|
+
}),
|
|
42
|
+
...(q.max_duration_ms !== undefined && {
|
|
43
|
+
max_duration_ms: parseInt(q.max_duration_ms, 10),
|
|
44
|
+
}),
|
|
45
|
+
...(q.limit !== undefined
|
|
46
|
+
? { limit: parseInt(q.limit, 10) }
|
|
47
|
+
: { limit: 100 }),
|
|
48
|
+
...(q.offset !== undefined
|
|
49
|
+
? { offset: parseInt(q.offset, 10) }
|
|
50
|
+
: { offset: 0 }),
|
|
51
|
+
...(q.cursor_requested_at !== undefined && {
|
|
52
|
+
cursor_requested_at: q.cursor_requested_at,
|
|
53
|
+
}),
|
|
54
|
+
...(q.cursor_id !== undefined && { cursor_id: q.cursor_id }),
|
|
55
|
+
};
|
|
56
|
+
// Wire AbortSignal to the request close event so MCP cancellation works
|
|
57
|
+
// when the HTTP client disconnects before the MCP response arrives.
|
|
58
|
+
const ac = new AbortController();
|
|
59
|
+
req.raw.on("close", () => {
|
|
60
|
+
if (req.raw.destroyed)
|
|
61
|
+
ac.abort();
|
|
62
|
+
});
|
|
63
|
+
const result = await auditHandler(q.address, args, deps, ac.signal);
|
|
64
|
+
if (result.success) {
|
|
65
|
+
// Spread MCP fields first, then explicit `address` / `strategyId` —
|
|
66
|
+
// ours take precedence if MCP ever introduces a colliding field name.
|
|
67
|
+
// Without this order, a future MCP schema bump could silently shadow
|
|
68
|
+
// our routing identifiers.
|
|
69
|
+
return reply.send({
|
|
70
|
+
success: true,
|
|
71
|
+
data: {
|
|
72
|
+
...result.result,
|
|
73
|
+
address: result.address,
|
|
74
|
+
strategyId: result.strategyId,
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
const statusMap = {
|
|
79
|
+
NOT_FOUND: 404,
|
|
80
|
+
ABORTED: 504,
|
|
81
|
+
UPSTREAM: 502,
|
|
82
|
+
};
|
|
83
|
+
const status = statusMap[result.code] ?? 502;
|
|
84
|
+
return reply.status(status).send({
|
|
85
|
+
success: false,
|
|
86
|
+
error: { code: result.code, message: result.message },
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=audit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../../../src/runtime-api/routes/audit.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TypeBox schema for the `GET /audit` query string.
|
|
3
|
+
*
|
|
4
|
+
* All Fastify query-string values arrive as strings. Integer fields
|
|
5
|
+
* (`min_duration_ms`, `max_duration_ms`, `limit`, `offset`) and the `success`
|
|
6
|
+
* boolean are declared as strings here so TypeBox validation passes on the raw
|
|
7
|
+
* HTTP input. The route handler coerces them to the appropriate types before
|
|
8
|
+
* forwarding to `auditHandler`.
|
|
9
|
+
*
|
|
10
|
+
* Fields excluded from the caller-visible surface (per plan Section 6):
|
|
11
|
+
* - `resource_type` — always `"strategy"`, set by `RuntimeHandle.auditQuery`.
|
|
12
|
+
* - `resource_id` — resolved wallet → strategyId, set by the handle.
|
|
13
|
+
* - `user_ids` — server-controlled (the MCP API-key user).
|
|
14
|
+
* - `usernames` — server-controlled (the MCP API-key user).
|
|
15
|
+
*
|
|
16
|
+
* @see senpi-hyperliquid-mcp/src/tools/audit.tools.ts `auditQueryInputSchema`
|
|
17
|
+
* for the upstream MCP Zod schema this mirrors.
|
|
18
|
+
*/
|
|
19
|
+
import { type Static } from "@sinclair/typebox";
|
|
20
|
+
/** TypeBox schema for `GET /audit` query-string parameters. */
|
|
21
|
+
export declare const AuditQuery: import("@sinclair/typebox").TObject<{
|
|
22
|
+
/**
|
|
23
|
+
* Wallet address to scope the audit query.
|
|
24
|
+
* Required — used to look up the matching runtime handle.
|
|
25
|
+
*/
|
|
26
|
+
address: import("@sinclair/typebox").TString;
|
|
27
|
+
/** Filter by exact tool name (e.g. `create_position`). */
|
|
28
|
+
tool_name: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
29
|
+
/**
|
|
30
|
+
* Filter by action classification.
|
|
31
|
+
* One of: `read`, `create`, `update`, `delete`.
|
|
32
|
+
*/
|
|
33
|
+
action_type: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"read">, import("@sinclair/typebox").TLiteral<"create">, import("@sinclair/typebox").TLiteral<"update">, import("@sinclair/typebox").TLiteral<"delete">]>>;
|
|
34
|
+
/**
|
|
35
|
+
* Filter by success/failure.
|
|
36
|
+
* Query strings arrive as text — coerce `"true"` / `"false"` in the route.
|
|
37
|
+
*/
|
|
38
|
+
success: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"true">, import("@sinclair/typebox").TLiteral<"false">]>>;
|
|
39
|
+
/** ISO 8601 inclusive lower bound on `requested_at`. */
|
|
40
|
+
start_time: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
41
|
+
/** ISO 8601 inclusive upper bound on `requested_at`. */
|
|
42
|
+
end_time: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
43
|
+
/**
|
|
44
|
+
* Minimum end-to-end duration in ms.
|
|
45
|
+
* Arrives as a string; pattern restricts to non-negative integers so
|
|
46
|
+
* `parseInt` never produces NaN. Coerced to integer in the route handler.
|
|
47
|
+
*/
|
|
48
|
+
min_duration_ms: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
49
|
+
/**
|
|
50
|
+
* Maximum end-to-end duration in ms.
|
|
51
|
+
* Arrives as a string; pattern restricts to non-negative integers so
|
|
52
|
+
* `parseInt` never produces NaN. Coerced to integer in the route handler.
|
|
53
|
+
*/
|
|
54
|
+
max_duration_ms: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
55
|
+
/**
|
|
56
|
+
* Page size (1–1000, MCP default 100).
|
|
57
|
+
* Arrives as a string; pattern restricts to non-negative integers.
|
|
58
|
+
* Coerced to integer in the route handler. Default `100` applied on
|
|
59
|
+
* missing value. MCP enforces the 1–1000 range.
|
|
60
|
+
*/
|
|
61
|
+
limit: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
62
|
+
/**
|
|
63
|
+
* Offset-based pagination skip count (≥0, MCP default 0).
|
|
64
|
+
* Arrives as a string; pattern restricts to non-negative integers.
|
|
65
|
+
* Coerced to integer in the route handler.
|
|
66
|
+
*/
|
|
67
|
+
offset: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
68
|
+
/** Cursor timestamp for keyset pagination (ISO 8601). Must pair with `cursor_id`. */
|
|
69
|
+
cursor_requested_at: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
70
|
+
/** Cursor UUID for keyset pagination. Must pair with `cursor_requested_at`. */
|
|
71
|
+
cursor_id: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
72
|
+
}>;
|
|
73
|
+
/** TypeScript type for the `GET /audit` query string. */
|
|
74
|
+
export type AuditQueryT = Static<typeof AuditQuery>;
|
|
75
|
+
//# sourceMappingURL=audit.schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.schema.d.ts","sourceRoot":"","sources":["../../../src/runtime-api/routes/audit.schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAQ,KAAK,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAEtD,+DAA+D;AAC/D,eAAO,MAAM,UAAU;IAEnB;;;OAGG;;IAGH,0DAA0D;;IAG1D;;;OAGG;;IAUH;;;OAGG;;IAKH,wDAAwD;;IAGxD,wDAAwD;;IAGxD;;;;OAIG;;IAGH;;;;OAIG;;IAGH;;;;;OAKG;;IAGH;;;;OAIG;;IAGH,qFAAqF;;IAGrF,+EAA+E;;EAIlF,CAAC;AAEF,yDAAyD;AACzD,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,UAAU,CAAC,CAAC"}
|