@senpi/runtime 1.0.95-dev.runtime-phase-2.20260506193916 → 1.0.95-dev.runtime-phase-2-api.20260507133904
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.mts +38 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +38 -0
- package/dist/index.mjs.map +1 -0
- package/dist/{index.d.ts → plugin.d.ts} +8 -1
- package/dist/plugin.d.ts.map +1 -0
- package/dist/{index.js → plugin.js} +152 -32
- 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 +8 -4
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fastify route: `POST /signals`.
|
|
3
|
+
*
|
|
4
|
+
* Accepts a batch of signal items targeting one or more runtime wallets,
|
|
5
|
+
* groups them by `(address, scanner)` to avoid the per-key ingest lock, fans
|
|
6
|
+
* out groups in parallel, processes items within each group sequentially, and
|
|
7
|
+
* returns per-item results in input order.
|
|
8
|
+
*
|
|
9
|
+
* Business logic lives entirely in:
|
|
10
|
+
* - `handlers/ingest-signals.ts` — per-item dispatch + error translation.
|
|
11
|
+
* - `handlers/group-by-scanner.ts` — lock-aware grouping helper.
|
|
12
|
+
*
|
|
13
|
+
* This file contains only Fastify wiring and the handler-level cap check.
|
|
14
|
+
*/
|
|
15
|
+
import type { FastifyInstance } from "fastify";
|
|
16
|
+
import type { RuntimeHandle } from "../../runtime/run.js";
|
|
17
|
+
import type { Logger } from "../../utils/logger.js";
|
|
18
|
+
/**
|
|
19
|
+
* Dependencies injected into the `/signals` route at registration time.
|
|
20
|
+
*/
|
|
21
|
+
export interface SignalsRouteDeps {
|
|
22
|
+
/** Live runtime handles maintained by the plugin, keyed by lowercased address. */
|
|
23
|
+
runtimeHandles: Map<string, RuntimeHandle>;
|
|
24
|
+
/** Logger instance for unexpected errors. */
|
|
25
|
+
logger: Logger;
|
|
26
|
+
/**
|
|
27
|
+
* Maximum number of items allowed in a single request.
|
|
28
|
+
* Sourced from `config.api.maxItemsPerSignalsRequest` (default 10).
|
|
29
|
+
*/
|
|
30
|
+
maxItems: number;
|
|
31
|
+
/**
|
|
32
|
+
* Transport tag forwarded to the ingest handler for telemetry.
|
|
33
|
+
* Fixed as `"http"` for this route; the gateway shim passes `"gateway"`.
|
|
34
|
+
*/
|
|
35
|
+
transport: "http" | "gateway";
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Register `POST /signals` on the given Fastify instance.
|
|
39
|
+
*
|
|
40
|
+
* The route validates the body against the `SignalsBody` TypeBox schema, then:
|
|
41
|
+
* 1. Rejects oversized batches (handler-level cap, configurable).
|
|
42
|
+
* 2. Groups items by `(address, scanner)` via `groupByAddressScanner`.
|
|
43
|
+
* 3. Runs groups in parallel; items within each group run sequentially.
|
|
44
|
+
* 4. Re-flattens results back to input order before responding.
|
|
45
|
+
*
|
|
46
|
+
* @param fastify Fastify instance to register the route on.
|
|
47
|
+
* @param deps Route dependencies injected at plugin start.
|
|
48
|
+
*/
|
|
49
|
+
export declare function registerSignalsRoute(fastify: FastifyInstance, deps: SignalsRouteDeps): void;
|
|
50
|
+
//# sourceMappingURL=signals.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signals.d.ts","sourceRoot":"","sources":["../../../src/runtime-api/routes/signals.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAM/C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAwDpD;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,kFAAkF;IAClF,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IAC3C,6CAA6C;IAC7C,MAAM,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;CAC/B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,eAAe,EACxB,IAAI,EAAE,gBAAgB,GACrB,IAAI,CA+CN"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fastify route: `POST /signals`.
|
|
3
|
+
*
|
|
4
|
+
* Accepts a batch of signal items targeting one or more runtime wallets,
|
|
5
|
+
* groups them by `(address, scanner)` to avoid the per-key ingest lock, fans
|
|
6
|
+
* out groups in parallel, processes items within each group sequentially, and
|
|
7
|
+
* returns per-item results in input order.
|
|
8
|
+
*
|
|
9
|
+
* Business logic lives entirely in:
|
|
10
|
+
* - `handlers/ingest-signals.ts` — per-item dispatch + error translation.
|
|
11
|
+
* - `handlers/group-by-scanner.ts` — lock-aware grouping helper.
|
|
12
|
+
*
|
|
13
|
+
* This file contains only Fastify wiring and the handler-level cap check.
|
|
14
|
+
*/
|
|
15
|
+
import { SignalsBody } from "./signals.schema.js";
|
|
16
|
+
import { ingestSignalsHandler } from "../handlers/ingest-signals.js";
|
|
17
|
+
import { groupByAddressScanner } from "../handlers/group-by-scanner.js";
|
|
18
|
+
function toWire(r) {
|
|
19
|
+
if (r.success) {
|
|
20
|
+
return {
|
|
21
|
+
success: true,
|
|
22
|
+
address: r.address,
|
|
23
|
+
scanner: r.scanner,
|
|
24
|
+
data: {
|
|
25
|
+
timestamp: r.timestamp,
|
|
26
|
+
signalCount: r.signalCount,
|
|
27
|
+
contextUpdated: r.contextUpdated,
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
success: false,
|
|
33
|
+
address: r.address,
|
|
34
|
+
scanner: r.scanner,
|
|
35
|
+
error: {
|
|
36
|
+
code: r.code,
|
|
37
|
+
message: r.message,
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Register `POST /signals` on the given Fastify instance.
|
|
43
|
+
*
|
|
44
|
+
* The route validates the body against the `SignalsBody` TypeBox schema, then:
|
|
45
|
+
* 1. Rejects oversized batches (handler-level cap, configurable).
|
|
46
|
+
* 2. Groups items by `(address, scanner)` via `groupByAddressScanner`.
|
|
47
|
+
* 3. Runs groups in parallel; items within each group run sequentially.
|
|
48
|
+
* 4. Re-flattens results back to input order before responding.
|
|
49
|
+
*
|
|
50
|
+
* @param fastify Fastify instance to register the route on.
|
|
51
|
+
* @param deps Route dependencies injected at plugin start.
|
|
52
|
+
*/
|
|
53
|
+
export function registerSignalsRoute(fastify, deps) {
|
|
54
|
+
fastify.post("/signals", { schema: { body: SignalsBody } }, async (req, reply) => {
|
|
55
|
+
const items = req.body;
|
|
56
|
+
// Handler-level cap — operator-tunable, not encoded in the TypeBox schema.
|
|
57
|
+
if (items.length > deps.maxItems) {
|
|
58
|
+
return reply.status(400).send({
|
|
59
|
+
success: false,
|
|
60
|
+
error: {
|
|
61
|
+
code: "INVALID_REQUEST",
|
|
62
|
+
message: `Exceeded api.maxItemsPerSignalsRequest=${deps.maxItems}; got ${items.length}.`,
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
// Group by (address, scanner) — each group is a SignalGroup (IndexedSignalItem[]).
|
|
67
|
+
const groups = groupByAddressScanner(items);
|
|
68
|
+
// Fan out across groups in parallel; process items within each group sequentially.
|
|
69
|
+
const grouped = await Promise.all(groups.map(async (group) => {
|
|
70
|
+
const out = [];
|
|
71
|
+
for (const { index, item } of group) {
|
|
72
|
+
out.push({
|
|
73
|
+
index,
|
|
74
|
+
result: await ingestSignalsHandler(item, {
|
|
75
|
+
runtimeHandles: deps.runtimeHandles,
|
|
76
|
+
logger: deps.logger,
|
|
77
|
+
transport: deps.transport,
|
|
78
|
+
}),
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
return out;
|
|
82
|
+
}));
|
|
83
|
+
// Re-flatten in input order.
|
|
84
|
+
const flat = grouped.flat();
|
|
85
|
+
flat.sort((a, b) => a.index - b.index);
|
|
86
|
+
const results = flat.map((entry) => toWire(entry.result));
|
|
87
|
+
return reply.send({ success: true, data: { results } });
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=signals.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signals.js","sourceRoot":"","sources":["../../../src/runtime-api/routes/signals.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TypeBox schema for the `POST /signals` request body.
|
|
3
|
+
*
|
|
4
|
+
* `SignalItemT` is derived from TypeBox `Static<>` and is structurally
|
|
5
|
+
* compatible with the hand-written {@link SignalItem} interface in
|
|
6
|
+
* `handlers/types.ts`. The interface in `handlers/types.ts` is kept as the
|
|
7
|
+
* canonical description (referenced by JSDoc across the codebase); this module
|
|
8
|
+
* derives the runtime-validatable form from TypeBox so both shapes stay in
|
|
9
|
+
* sync at compile-time.
|
|
10
|
+
*
|
|
11
|
+
* Note on `direction`: the TypeBox schema accepts any string so
|
|
12
|
+
* `SignalItemT["direction"]` is `string | undefined`, matching
|
|
13
|
+
* `Signal["direction"]` in `scanners/types.ts`. The tighter
|
|
14
|
+
* `"long" | "short" | null` constraint lives in the MCP layer; the runtime
|
|
15
|
+
* applies it implicitly during scanner-engine ingest.
|
|
16
|
+
*
|
|
17
|
+
* Note on `maxItems`: enforced at handler level using the configured
|
|
18
|
+
* `api.maxItemsPerSignalsRequest` value (default 10). Schema-level cap is
|
|
19
|
+
* kept open-ended so the shape works in tests without recompiling.
|
|
20
|
+
*/
|
|
21
|
+
import { type Static } from "@sinclair/typebox";
|
|
22
|
+
/** TypeBox schema for one item in the `POST /signals` body. */
|
|
23
|
+
export declare const SignalItem: import("@sinclair/typebox").TObject<{
|
|
24
|
+
/** Wallet address of the target runtime. Lowercased server-side. */
|
|
25
|
+
address: import("@sinclair/typebox").TString;
|
|
26
|
+
/** Configured scanner id. */
|
|
27
|
+
scanner: import("@sinclair/typebox").TString;
|
|
28
|
+
/** Asset for a single ingested signal. */
|
|
29
|
+
asset: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
30
|
+
/**
|
|
31
|
+
* Direction for a single ingested signal.
|
|
32
|
+
* Accepts any string — `"long"` / `"short"` are the conventional values.
|
|
33
|
+
*/
|
|
34
|
+
direction: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
35
|
+
/** Confidence score for a single ingested signal (0–1). */
|
|
36
|
+
score: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
|
|
37
|
+
/** Signal classification override for a single ingested signal. */
|
|
38
|
+
signal_type: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
39
|
+
/**
|
|
40
|
+
* Single ingest payload for signal-producing or context-producing scanners.
|
|
41
|
+
* Mutually exclusive with `signals[]`.
|
|
42
|
+
*/
|
|
43
|
+
data: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TRecord<import("@sinclair/typebox").TString, import("@sinclair/typebox").TUnknown>>;
|
|
44
|
+
/**
|
|
45
|
+
* Batch signal payloads for signal-producing external scanners.
|
|
46
|
+
* Mutually exclusive with `data`.
|
|
47
|
+
*/
|
|
48
|
+
signals: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TArray<import("@sinclair/typebox").TObject<{
|
|
49
|
+
asset: import("@sinclair/typebox").TString;
|
|
50
|
+
direction: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
51
|
+
score: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
|
|
52
|
+
signal_type: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
53
|
+
data: import("@sinclair/typebox").TRecord<import("@sinclair/typebox").TString, import("@sinclair/typebox").TUnknown>;
|
|
54
|
+
}>>>;
|
|
55
|
+
}>;
|
|
56
|
+
/** TypeBox schema for the full `POST /signals` request body array. */
|
|
57
|
+
export declare const SignalsBody: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TObject<{
|
|
58
|
+
/** Wallet address of the target runtime. Lowercased server-side. */
|
|
59
|
+
address: import("@sinclair/typebox").TString;
|
|
60
|
+
/** Configured scanner id. */
|
|
61
|
+
scanner: import("@sinclair/typebox").TString;
|
|
62
|
+
/** Asset for a single ingested signal. */
|
|
63
|
+
asset: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
64
|
+
/**
|
|
65
|
+
* Direction for a single ingested signal.
|
|
66
|
+
* Accepts any string — `"long"` / `"short"` are the conventional values.
|
|
67
|
+
*/
|
|
68
|
+
direction: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
69
|
+
/** Confidence score for a single ingested signal (0–1). */
|
|
70
|
+
score: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
|
|
71
|
+
/** Signal classification override for a single ingested signal. */
|
|
72
|
+
signal_type: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
73
|
+
/**
|
|
74
|
+
* Single ingest payload for signal-producing or context-producing scanners.
|
|
75
|
+
* Mutually exclusive with `signals[]`.
|
|
76
|
+
*/
|
|
77
|
+
data: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TRecord<import("@sinclair/typebox").TString, import("@sinclair/typebox").TUnknown>>;
|
|
78
|
+
/**
|
|
79
|
+
* Batch signal payloads for signal-producing external scanners.
|
|
80
|
+
* Mutually exclusive with `data`.
|
|
81
|
+
*/
|
|
82
|
+
signals: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TArray<import("@sinclair/typebox").TObject<{
|
|
83
|
+
asset: import("@sinclair/typebox").TString;
|
|
84
|
+
direction: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
85
|
+
score: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
|
|
86
|
+
signal_type: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
87
|
+
data: import("@sinclair/typebox").TRecord<import("@sinclair/typebox").TString, import("@sinclair/typebox").TUnknown>;
|
|
88
|
+
}>>>;
|
|
89
|
+
}>>;
|
|
90
|
+
/** TypeScript type for one signal item — derived from TypeBox, single source of truth. */
|
|
91
|
+
export type SignalItemT = Static<typeof SignalItem>;
|
|
92
|
+
/** TypeScript type for the `POST /signals` request body. */
|
|
93
|
+
export type SignalsBodyT = Static<typeof SignalsBody>;
|
|
94
|
+
//# sourceMappingURL=signals.schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signals.schema.d.ts","sourceRoot":"","sources":["../../../src/runtime-api/routes/signals.schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAQ,KAAK,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAEtD,+DAA+D;AAC/D,eAAO,MAAM,UAAU;IAEnB,oEAAoE;;IAEpE,6BAA6B;;IAE7B,0CAA0C;;IAE1C;;;OAGG;;IAEH,2DAA2D;;IAE3D,mEAAmE;;IAEnE;;;OAGG;;IAEH;;;OAGG;;;;;;;;EAcN,CAAC;AAEF,sEAAsE;AACtE,eAAO,MAAM,WAAW;IAxCpB,oEAAoE;;IAEpE,6BAA6B;;IAE7B,0CAA0C;;IAE1C;;;OAGG;;IAEH,2DAA2D;;IAE3D,mEAAmE;;IAEnE;;;OAGG;;IAEH;;;OAGG;;;;;;;;GAiB2D,CAAC;AAEnE,0FAA0F;AAC1F,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,UAAU,CAAC,CAAC;AAEpD,4DAA4D;AAC5D,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,OAAO,WAAW,CAAC,CAAC"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TypeBox schema for the `POST /signals` request body.
|
|
3
|
+
*
|
|
4
|
+
* `SignalItemT` is derived from TypeBox `Static<>` and is structurally
|
|
5
|
+
* compatible with the hand-written {@link SignalItem} interface in
|
|
6
|
+
* `handlers/types.ts`. The interface in `handlers/types.ts` is kept as the
|
|
7
|
+
* canonical description (referenced by JSDoc across the codebase); this module
|
|
8
|
+
* derives the runtime-validatable form from TypeBox so both shapes stay in
|
|
9
|
+
* sync at compile-time.
|
|
10
|
+
*
|
|
11
|
+
* Note on `direction`: the TypeBox schema accepts any string so
|
|
12
|
+
* `SignalItemT["direction"]` is `string | undefined`, matching
|
|
13
|
+
* `Signal["direction"]` in `scanners/types.ts`. The tighter
|
|
14
|
+
* `"long" | "short" | null` constraint lives in the MCP layer; the runtime
|
|
15
|
+
* applies it implicitly during scanner-engine ingest.
|
|
16
|
+
*
|
|
17
|
+
* Note on `maxItems`: enforced at handler level using the configured
|
|
18
|
+
* `api.maxItemsPerSignalsRequest` value (default 10). Schema-level cap is
|
|
19
|
+
* kept open-ended so the shape works in tests without recompiling.
|
|
20
|
+
*/
|
|
21
|
+
import { Type } from "@sinclair/typebox";
|
|
22
|
+
/** TypeBox schema for one item in the `POST /signals` body. */
|
|
23
|
+
export const SignalItem = Type.Object({
|
|
24
|
+
/** Wallet address of the target runtime. Lowercased server-side. */
|
|
25
|
+
address: Type.String({ minLength: 1 }),
|
|
26
|
+
/** Configured scanner id. */
|
|
27
|
+
scanner: Type.String({ minLength: 1 }),
|
|
28
|
+
/** Asset for a single ingested signal. */
|
|
29
|
+
asset: Type.Optional(Type.String()),
|
|
30
|
+
/**
|
|
31
|
+
* Direction for a single ingested signal.
|
|
32
|
+
* Accepts any string — `"long"` / `"short"` are the conventional values.
|
|
33
|
+
*/
|
|
34
|
+
direction: Type.Optional(Type.String()),
|
|
35
|
+
/** Confidence score for a single ingested signal (0–1). */
|
|
36
|
+
score: Type.Optional(Type.Number({ minimum: 0, maximum: 1 })),
|
|
37
|
+
/** Signal classification override for a single ingested signal. */
|
|
38
|
+
signal_type: Type.Optional(Type.String()),
|
|
39
|
+
/**
|
|
40
|
+
* Single ingest payload for signal-producing or context-producing scanners.
|
|
41
|
+
* Mutually exclusive with `signals[]`.
|
|
42
|
+
*/
|
|
43
|
+
data: Type.Optional(Type.Record(Type.String(), Type.Unknown())),
|
|
44
|
+
/**
|
|
45
|
+
* Batch signal payloads for signal-producing external scanners.
|
|
46
|
+
* Mutually exclusive with `data`.
|
|
47
|
+
*/
|
|
48
|
+
signals: Type.Optional(Type.Array(Type.Object({
|
|
49
|
+
asset: Type.String(),
|
|
50
|
+
direction: Type.Optional(Type.String()),
|
|
51
|
+
score: Type.Optional(Type.Number({ minimum: 0, maximum: 1 })),
|
|
52
|
+
signal_type: Type.Optional(Type.String()),
|
|
53
|
+
data: Type.Record(Type.String(), Type.Unknown()),
|
|
54
|
+
}))),
|
|
55
|
+
}, { additionalProperties: false });
|
|
56
|
+
/** TypeBox schema for the full `POST /signals` request body array. */
|
|
57
|
+
export const SignalsBody = Type.Array(SignalItem, { minItems: 1 });
|
|
58
|
+
//# sourceMappingURL=signals.schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signals.schema.js","sourceRoot":"","sources":["../../../src/runtime-api/routes/signals.schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,IAAI,EAAe,MAAM,mBAAmB,CAAC;AAEtD,+DAA+D;AAC/D,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,CACnC;IACE,oEAAoE;IACpE,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC;IACtC,6BAA6B;IAC7B,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC;IACtC,0CAA0C;IAC1C,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;IACnC;;;OAGG;IACH,SAAS,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;IACvC,2DAA2D;IAC3D,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;IAC7D,mEAAmE;IACnE,WAAW,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;IACzC;;;OAGG;IACH,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;IAC/D;;;OAGG;IACH,OAAO,EAAE,IAAI,CAAC,QAAQ,CACpB,IAAI,CAAC,KAAK,CACR,IAAI,CAAC,MAAM,CAAC;QACV,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE;QACpB,SAAS,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;QACvC,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;QAC7D,WAAW,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;QACzC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC;KACjD,CAAC,CACH,CACF;CACF,EACD,EAAE,oBAAoB,EAAE,KAAK,EAAE,CAChC,CAAC;AAEF,sEAAsE;AACtE,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAC"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `createApi()` — Fastify server factory for the runtime HTTP API.
|
|
3
|
+
*
|
|
4
|
+
* Wires the Fastify instance with:
|
|
5
|
+
* - `POST /signals` — ingest signals for one or more wallets.
|
|
6
|
+
* - `GET /audit` — MCP audit-query proxy scoped to one wallet.
|
|
7
|
+
* - `GET /health` — three-state readiness + per-runtime queue depth.
|
|
8
|
+
*
|
|
9
|
+
* Returns the Fastify instance, the mutable readiness flag, and the boot
|
|
10
|
+
* timestamp. The lifecycle layer (S5) calls `fastify.listen()` and flips
|
|
11
|
+
* `readiness.setOk()` / `readiness.setDraining()` around start/stop.
|
|
12
|
+
*
|
|
13
|
+
* This module owns only wiring — no business logic. All route logic
|
|
14
|
+
* delegates to the shared handlers in `handlers/`.
|
|
15
|
+
*/
|
|
16
|
+
import { type FastifyInstance } from "fastify";
|
|
17
|
+
import { createReadiness } from "./readiness.js";
|
|
18
|
+
import type { RuntimeHandle } from "../runtime/run.js";
|
|
19
|
+
import type { Logger } from "../utils/logger.js";
|
|
20
|
+
import type { ApiConfig } from "./config.js";
|
|
21
|
+
/**
|
|
22
|
+
* Dependencies passed to {@link createApi}.
|
|
23
|
+
*/
|
|
24
|
+
export interface CreateApiDeps {
|
|
25
|
+
/** Live runtime handles maintained by the plugin, keyed by lowercased address. */
|
|
26
|
+
runtimeHandles: Map<string, RuntimeHandle>;
|
|
27
|
+
/** Validated `api` config block (host, port, maxItemsPerSignalsRequest). */
|
|
28
|
+
apiConfig: ApiConfig;
|
|
29
|
+
/** Logger instance — forwarded to shared handlers for structured error logging. */
|
|
30
|
+
logger: Logger;
|
|
31
|
+
/**
|
|
32
|
+
* Plugin version string read from `openclaw.plugin.json` at boot.
|
|
33
|
+
* Exposed in `GET /health` so callers can correlate requests with deployments.
|
|
34
|
+
*/
|
|
35
|
+
version: string;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Artifacts returned by {@link createApi}.
|
|
39
|
+
*
|
|
40
|
+
* Ownership:
|
|
41
|
+
* - `fastify` — call `.listen()` and `.close()` in the lifecycle layer (S5).
|
|
42
|
+
* - `readiness` — call `setOk()` after `.listen()` resolves; `setDraining()`
|
|
43
|
+
* before `.close()` during graceful shutdown.
|
|
44
|
+
* - `bootedAt` — Unix epoch ms recorded when `createApi()` was called;
|
|
45
|
+
* used by `GET /health` to compute `uptime_ms`.
|
|
46
|
+
*/
|
|
47
|
+
export interface ApiArtifacts {
|
|
48
|
+
fastify: FastifyInstance;
|
|
49
|
+
readiness: ReturnType<typeof createReadiness>;
|
|
50
|
+
bootedAt: number;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Create and configure the Fastify HTTP server.
|
|
54
|
+
*
|
|
55
|
+
* Does NOT start listening. Call `artifacts.fastify.listen(...)` from the
|
|
56
|
+
* lifecycle layer (S5) after `createApi()` returns.
|
|
57
|
+
*
|
|
58
|
+
* Error handler behaviour:
|
|
59
|
+
* - `400` (schema validation), `413` (body too large), `415` (wrong
|
|
60
|
+
* content-type) are passed through as `INVALID_REQUEST` with the original
|
|
61
|
+
* Fastify message so callers can read the validation detail.
|
|
62
|
+
* - All other errors are logged and returned as `500 / UNAVAILABLE`.
|
|
63
|
+
*
|
|
64
|
+
* Request timeout strategy (resolved in S5):
|
|
65
|
+
* A single server-wide `requestTimeout: 30_000` covers all routes.
|
|
66
|
+
* The plan called for per-route values (10s for `/signals` + `/health`,
|
|
67
|
+
* 30s for `/audit`), but Fastify v5 does not expose a declarative per-route
|
|
68
|
+
* `requestTimeout` and wiring an `onRequest` hook with manual `setTimeout`
|
|
69
|
+
* calls adds meaningful complexity for a localhost-only server where requests
|
|
70
|
+
* that genuinely hang are almost always MCP-side. The 30s ceiling is driven
|
|
71
|
+
* entirely by `/audit` (MCP round-trip); `/signals` and `/health` both
|
|
72
|
+
* complete in well under 1s in practice. If per-route enforcement becomes
|
|
73
|
+
* necessary, add an `onRequest` hook in this file.
|
|
74
|
+
*
|
|
75
|
+
* @param deps Dependencies injected by the plugin entry point.
|
|
76
|
+
* @returns `{ fastify, readiness, bootedAt }`.
|
|
77
|
+
*/
|
|
78
|
+
export declare function createApi(deps: CreateApiDeps): ApiArtifacts;
|
|
79
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/runtime-api/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAgB,EAAE,KAAK,eAAe,EAAE,MAAM,SAAS,CAAC;AAIxD,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,kFAAkF;IAClF,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IAC3C,4EAA4E;IAC5E,SAAS,EAAE,SAAS,CAAC;IACrB,mFAAmF;IACnF,MAAM,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,eAAe,CAAC;IACzB,SAAS,EAAE,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC;IAC9C,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,aAAa,GAAG,YAAY,CAqE3D"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `createApi()` — Fastify server factory for the runtime HTTP API.
|
|
3
|
+
*
|
|
4
|
+
* Wires the Fastify instance with:
|
|
5
|
+
* - `POST /signals` — ingest signals for one or more wallets.
|
|
6
|
+
* - `GET /audit` — MCP audit-query proxy scoped to one wallet.
|
|
7
|
+
* - `GET /health` — three-state readiness + per-runtime queue depth.
|
|
8
|
+
*
|
|
9
|
+
* Returns the Fastify instance, the mutable readiness flag, and the boot
|
|
10
|
+
* timestamp. The lifecycle layer (S5) calls `fastify.listen()` and flips
|
|
11
|
+
* `readiness.setOk()` / `readiness.setDraining()` around start/stop.
|
|
12
|
+
*
|
|
13
|
+
* This module owns only wiring — no business logic. All route logic
|
|
14
|
+
* delegates to the shared handlers in `handlers/`.
|
|
15
|
+
*/
|
|
16
|
+
import Fastify from "fastify";
|
|
17
|
+
import { registerSignalsRoute } from "./routes/signals.js";
|
|
18
|
+
import { registerAuditRoute } from "./routes/audit.js";
|
|
19
|
+
import { registerHealthRoute } from "./routes/health.js";
|
|
20
|
+
import { createReadiness } from "./readiness.js";
|
|
21
|
+
/**
|
|
22
|
+
* Create and configure the Fastify HTTP server.
|
|
23
|
+
*
|
|
24
|
+
* Does NOT start listening. Call `artifacts.fastify.listen(...)` from the
|
|
25
|
+
* lifecycle layer (S5) after `createApi()` returns.
|
|
26
|
+
*
|
|
27
|
+
* Error handler behaviour:
|
|
28
|
+
* - `400` (schema validation), `413` (body too large), `415` (wrong
|
|
29
|
+
* content-type) are passed through as `INVALID_REQUEST` with the original
|
|
30
|
+
* Fastify message so callers can read the validation detail.
|
|
31
|
+
* - All other errors are logged and returned as `500 / UNAVAILABLE`.
|
|
32
|
+
*
|
|
33
|
+
* Request timeout strategy (resolved in S5):
|
|
34
|
+
* A single server-wide `requestTimeout: 30_000` covers all routes.
|
|
35
|
+
* The plan called for per-route values (10s for `/signals` + `/health`,
|
|
36
|
+
* 30s for `/audit`), but Fastify v5 does not expose a declarative per-route
|
|
37
|
+
* `requestTimeout` and wiring an `onRequest` hook with manual `setTimeout`
|
|
38
|
+
* calls adds meaningful complexity for a localhost-only server where requests
|
|
39
|
+
* that genuinely hang are almost always MCP-side. The 30s ceiling is driven
|
|
40
|
+
* entirely by `/audit` (MCP round-trip); `/signals` and `/health` both
|
|
41
|
+
* complete in well under 1s in practice. If per-route enforcement becomes
|
|
42
|
+
* necessary, add an `onRequest` hook in this file.
|
|
43
|
+
*
|
|
44
|
+
* @param deps Dependencies injected by the plugin entry point.
|
|
45
|
+
* @returns `{ fastify, readiness, bootedAt }`.
|
|
46
|
+
*/
|
|
47
|
+
export function createApi(deps) {
|
|
48
|
+
const bootedAt = Date.now();
|
|
49
|
+
const readiness = createReadiness();
|
|
50
|
+
const fastify = Fastify({
|
|
51
|
+
logger: { level: "info" },
|
|
52
|
+
bodyLimit: 256 * 1024, // 256 KB — generous for signal batches, hard cap on abuse.
|
|
53
|
+
disableRequestLogging: false,
|
|
54
|
+
/**
|
|
55
|
+
* 30s request ceiling — driven by `/audit` (MCP round-trip).
|
|
56
|
+
* `/signals` and `/health` complete far faster; we accept the unified
|
|
57
|
+
* ceiling to avoid per-route hook complexity on a localhost-only server.
|
|
58
|
+
* Documented in docs/runtime-docs/runtime-api.md.
|
|
59
|
+
*/
|
|
60
|
+
requestTimeout: 30_000,
|
|
61
|
+
});
|
|
62
|
+
// Custom error handler — normalises Fastify validation / payload errors into
|
|
63
|
+
// the senpi-stack envelope `{ success: false, error: { code, message } }`.
|
|
64
|
+
fastify.setErrorHandler((error, _req, reply) => {
|
|
65
|
+
// Fastify v5 types setErrorHandler with `unknown`; cast once for the handler.
|
|
66
|
+
const err = error;
|
|
67
|
+
const statusCode = err.statusCode;
|
|
68
|
+
if (statusCode === 400 ||
|
|
69
|
+
statusCode === 413 ||
|
|
70
|
+
statusCode === 415) {
|
|
71
|
+
return reply.status(statusCode).send({
|
|
72
|
+
success: false,
|
|
73
|
+
error: {
|
|
74
|
+
code: "INVALID_REQUEST",
|
|
75
|
+
message: err.message,
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
deps.logger.error("[senpi-core][runtime-api] Unhandled error", {
|
|
80
|
+
err,
|
|
81
|
+
});
|
|
82
|
+
return reply.status(500).send({
|
|
83
|
+
success: false,
|
|
84
|
+
error: {
|
|
85
|
+
code: "UNAVAILABLE",
|
|
86
|
+
message: err.message,
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
registerSignalsRoute(fastify, {
|
|
91
|
+
runtimeHandles: deps.runtimeHandles,
|
|
92
|
+
logger: deps.logger,
|
|
93
|
+
maxItems: deps.apiConfig.maxItemsPerSignalsRequest,
|
|
94
|
+
transport: "http",
|
|
95
|
+
});
|
|
96
|
+
registerAuditRoute(fastify, {
|
|
97
|
+
runtimeHandles: deps.runtimeHandles,
|
|
98
|
+
logger: deps.logger,
|
|
99
|
+
});
|
|
100
|
+
registerHealthRoute(fastify, {
|
|
101
|
+
runtimeHandles: deps.runtimeHandles,
|
|
102
|
+
apiConfig: { host: deps.apiConfig.host, port: deps.apiConfig.port },
|
|
103
|
+
readiness,
|
|
104
|
+
version: deps.version,
|
|
105
|
+
bootedAt,
|
|
106
|
+
});
|
|
107
|
+
return { fastify, readiness, bootedAt };
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/runtime-api/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,OAAiC,MAAM,SAAS,CAAC;AACxD,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAsCjD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,UAAU,SAAS,CAAC,IAAmB;IAC3C,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC5B,MAAM,SAAS,GAAG,eAAe,EAAE,CAAC;IAEpC,MAAM,OAAO,GAAG,OAAO,CAAC;QACtB,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE;QACzB,SAAS,EAAE,GAAG,GAAG,IAAI,EAAE,2DAA2D;QAClF,qBAAqB,EAAE,KAAK;QAC5B;;;;;WAKG;QACH,cAAc,EAAE,MAAM;KACvB,CAAC,CAAC;IAEH,6EAA6E;IAC7E,2EAA2E;IAC3E,OAAO,CAAC,eAAe,CAAC,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE;QAC7C,8EAA8E;QAC9E,MAAM,GAAG,GAAG,KAAwC,CAAC;QACrD,MAAM,UAAU,GAAG,GAAG,CAAC,UAAU,CAAC;QAClC,IACE,UAAU,KAAK,GAAG;YAClB,UAAU,KAAK,GAAG;YAClB,UAAU,KAAK,GAAG,EAClB,CAAC;YACD,OAAO,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC;gBACnC,OAAO,EAAE,KAAK;gBACd,KAAK,EAAE;oBACL,IAAI,EAAE,iBAAiB;oBACvB,OAAO,EAAE,GAAG,CAAC,OAAO;iBACrB;aACF,CAAC,CAAC;QACL,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,2CAA2C,EAAE;YAC7D,GAAG;SACJ,CAAC,CAAC;QACH,OAAO,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;YAC5B,OAAO,EAAE,KAAK;YACd,KAAK,EAAE;gBACL,IAAI,EAAE,aAAa;gBACnB,OAAO,EAAE,GAAG,CAAC,OAAO;aACrB;SACF,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,oBAAoB,CAAC,OAAO,EAAE;QAC5B,cAAc,EAAE,IAAI,CAAC,cAAc;QACnC,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,yBAAyB;QAClD,SAAS,EAAE,MAAM;KAClB,CAAC,CAAC;IAEH,kBAAkB,CAAC,OAAO,EAAE;QAC1B,cAAc,EAAE,IAAI,CAAC,cAAc;QACnC,MAAM,EAAE,IAAI,CAAC,MAAM;KACpB,CAAC,CAAC;IAEH,mBAAmB,CAAC,OAAO,EAAE;QAC3B,cAAc,EAAE,IAAI,CAAC,cAAc;QACnC,SAAS,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE;QACnE,SAAS;QACT,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,QAAQ;KACT,CAAC,CAAC;IAEH,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC;AAC1C,CAAC"}
|
package/dist/senpi/client.d.ts
CHANGED
|
@@ -1,4 +1,22 @@
|
|
|
1
1
|
import type { McpTransport, SenpiClientConfig, CreatePositionRequest, CreatePositionResult, EditPositionResult, GetPositionResult, OpenPosition, GetStrategyResult, GetOpenOrdersResult, GetTraderHistoryOptions, TraderHistoryEntry, GetTraderStateOptions, TraderStateResult, StrategyFundResult, ClosePositionsRequest, GetPnlHistoryOptions, ListStrategiesFilters, StrategySummary, RatchetStopPosition, RatchetStopEvent, AddRatchetStopInput, EditRatchetStopInput, DeleteRatchetStopInput, QueryRatchetStopsInput, QueryRatchetStopEventsInput } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Typed error thrown by `SenpiClient` mutation methods when the response
|
|
4
|
+
* reports a failure at either layer (outer MCP envelope or inner engine
|
|
5
|
+
* result). Callers that today only catch `throw` will fire on real
|
|
6
|
+
* failures instead of silently treating phantom-success responses as
|
|
7
|
+
* success — preventing the orphaned-DSL-state class of bug.
|
|
8
|
+
*
|
|
9
|
+
* Codes:
|
|
10
|
+
* - `UNEXPECTED_RESPONSE` — response shape is not the expected object.
|
|
11
|
+
* - `UPSTREAM_ERROR` — outer MCP envelope `success === false`.
|
|
12
|
+
* - `ENGINE_FAILURE` — outer envelope ok, but inner engine result
|
|
13
|
+
* reported `success === false` (e.g. CREATE_ORDER_RESTING).
|
|
14
|
+
*/
|
|
15
|
+
export declare class SenpiClientError extends Error {
|
|
16
|
+
readonly code: "UNEXPECTED_RESPONSE" | "UPSTREAM_ERROR" | "ENGINE_FAILURE";
|
|
17
|
+
readonly details: unknown;
|
|
18
|
+
constructor(code: "UNEXPECTED_RESPONSE" | "UPSTREAM_ERROR" | "ENGINE_FAILURE", message: string, details?: unknown);
|
|
19
|
+
}
|
|
2
20
|
export declare class SenpiClient {
|
|
3
21
|
private readonly injectedTransport;
|
|
4
22
|
private readonly config;
|
|
@@ -10,6 +28,27 @@ export declare class SenpiClient {
|
|
|
10
28
|
connect(): Promise<void>;
|
|
11
29
|
disconnect(): Promise<void>;
|
|
12
30
|
private callTool;
|
|
31
|
+
/**
|
|
32
|
+
* Run an MCP `audit_query` and return the unwrapped response payload.
|
|
33
|
+
*
|
|
34
|
+
* Stays a thin pass-through so the runtime layer (which owns the
|
|
35
|
+
* wallet→strategyId resolution and error mapping) can compose the call
|
|
36
|
+
* with full type-safety. Callers must already have set
|
|
37
|
+
* `resource_type` / `resource_id` if scoping is desired — this method
|
|
38
|
+
* does not interpret arguments.
|
|
39
|
+
*
|
|
40
|
+
* @param args Arbitrary key/value map forwarded verbatim as the MCP
|
|
41
|
+
* tool input. Caller is responsible for matching the
|
|
42
|
+
* MCP `audit_query` schema.
|
|
43
|
+
* @param opts Optional AbortSignal; when fired, the underlying MCP
|
|
44
|
+
* `callTool` is cancelled (the SDK rejects with an
|
|
45
|
+
* AbortError).
|
|
46
|
+
* @returns The `.data` field of the MCP envelope when present;
|
|
47
|
+
* otherwise the raw response.
|
|
48
|
+
*/
|
|
49
|
+
auditQuery(args: Record<string, unknown>, opts?: {
|
|
50
|
+
signal?: AbortSignal;
|
|
51
|
+
}): Promise<unknown>;
|
|
13
52
|
/**
|
|
14
53
|
* Returns an adapter compatible with SenpiToolClient so the same connection
|
|
15
54
|
* can be used for scanner provider MCP tool calls (e.g. leaderboard_get_markets).
|
|
@@ -17,14 +56,36 @@ export declare class SenpiClient {
|
|
|
17
56
|
asToolClient(): {
|
|
18
57
|
callTool(name: string, args: Record<string, unknown>): Promise<unknown>;
|
|
19
58
|
};
|
|
59
|
+
/**
|
|
60
|
+
* Register a backend ratchet-stop (DSL Phase 2 handoff).
|
|
61
|
+
*
|
|
62
|
+
* Throws `SenpiClientError` on outer envelope failure or inner engine
|
|
63
|
+
* `success: false`. Callers' existing `if (result.success && result.position)`
|
|
64
|
+
* branches now treat the success path only — failures land in the
|
|
65
|
+
* surrounding try/catch instead of falling through.
|
|
66
|
+
*/
|
|
20
67
|
addRatchetStop(input: AddRatchetStopInput): Promise<{
|
|
21
68
|
success: boolean;
|
|
22
69
|
position?: RatchetStopPosition;
|
|
23
70
|
}>;
|
|
71
|
+
/**
|
|
72
|
+
* Update an existing backend ratchet-stop.
|
|
73
|
+
*
|
|
74
|
+
* Throws `SenpiClientError` on outer envelope failure or inner engine
|
|
75
|
+
* `success: false`.
|
|
76
|
+
*/
|
|
24
77
|
editRatchetStop(input: EditRatchetStopInput): Promise<{
|
|
25
78
|
success: boolean;
|
|
26
79
|
position?: RatchetStopPosition;
|
|
27
80
|
}>;
|
|
81
|
+
/**
|
|
82
|
+
* Delete a backend ratchet-stop. Used during DSL teardown / handoff close.
|
|
83
|
+
*
|
|
84
|
+
* Throws `SenpiClientError` on outer envelope failure or inner engine
|
|
85
|
+
* `success: false`. Previously this method's `{ success: boolean }` return
|
|
86
|
+
* was ignored by every caller (only `throw` was caught), which left
|
|
87
|
+
* zombie ratchet-stops on the backend when the engine reported failure.
|
|
88
|
+
*/
|
|
28
89
|
deleteRatchetStop(input: DeleteRatchetStopInput): Promise<{
|
|
29
90
|
success: boolean;
|
|
30
91
|
}>;
|
|
@@ -44,6 +105,21 @@ export declare class SenpiClient {
|
|
|
44
105
|
* Set/update SL (and optionally TP) on an open position. Uses MCP edit_position.
|
|
45
106
|
* For XYZ DEX pass dex: "xyz" so coin is sent as "xyz:COIN"; otherwise main DEX uses plain coin.
|
|
46
107
|
*/
|
|
108
|
+
/**
|
|
109
|
+
* Set / update an exchange-side stop loss (or perform other position
|
|
110
|
+
* edits) on an open position.
|
|
111
|
+
*
|
|
112
|
+
* Throws `SenpiClientError` when either response layer reports failure:
|
|
113
|
+
* - `UPSTREAM_ERROR` — MCP envelope `success: false`.
|
|
114
|
+
* - `ENGINE_FAILURE` — outer ok, inner engine `success: false`
|
|
115
|
+
* (e.g. position not found, SL placement rejected).
|
|
116
|
+
*
|
|
117
|
+
* Returning normally means the engine accepted the edit. The returned
|
|
118
|
+
* `EditPositionResult` carries `slOrderId` when an SL was placed/updated;
|
|
119
|
+
* a `null` slOrderId on a non-throwing return indicates the engine reported
|
|
120
|
+
* success without the expected SL artifact — caller should treat that as
|
|
121
|
+
* a soft failure (logged as a warn) and re-attempt on the next sync tick.
|
|
122
|
+
*/
|
|
47
123
|
editPosition(wallet: string, payload: {
|
|
48
124
|
coin: string;
|
|
49
125
|
dex?: string;
|
|
@@ -105,8 +181,22 @@ export declare class SenpiClient {
|
|
|
105
181
|
topUp(wallet: string, amount: number): Promise<StrategyFundResult>;
|
|
106
182
|
withdrawFunds(wallet: string, amount: number): Promise<StrategyFundResult>;
|
|
107
183
|
closeStrategy(wallet: string): Promise<StrategyFundResult>;
|
|
108
|
-
|
|
109
|
-
|
|
184
|
+
/**
|
|
185
|
+
* Submit a batch close_positions MCP call.
|
|
186
|
+
*
|
|
187
|
+
* Throws `SenpiClientError` on outer envelope or inner engine failure
|
|
188
|
+
* (same shape as {@link closePosition}). Successful return means the
|
|
189
|
+
* batch was accepted; per-position confirmation must still come from
|
|
190
|
+
* position-tracker scanner events.
|
|
191
|
+
*/
|
|
192
|
+
closePositions(wallet: string, positions: ClosePositionsRequest[]): Promise<Record<string, unknown>>;
|
|
193
|
+
/**
|
|
194
|
+
* Cancel an outstanding order by id.
|
|
195
|
+
*
|
|
196
|
+
* Throws `SenpiClientError` on outer envelope or inner engine failure
|
|
197
|
+
* (same shape as {@link closePosition}).
|
|
198
|
+
*/
|
|
199
|
+
cancelOrder(wallet: string, orderId: number): Promise<Record<string, unknown>>;
|
|
110
200
|
getAssetTradingLimits(wallet: string, coin: string, dex?: string): Promise<{
|
|
111
201
|
maxLeverage: number;
|
|
112
202
|
} | null>;
|