@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,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>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/senpi/client.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EACV,YAAY,EACZ,iBAAiB,EACjB,qBAAqB,EACrB,oBAAoB,EAGpB,kBAAkB,EAClB,iBAAiB,EACjB,YAAY,EACZ,iBAAiB,EACjB,mBAAmB,EACnB,uBAAuB,EACvB,kBAAkB,EAClB,qBAAqB,EACrB,iBAAiB,EAGjB,kBAAkB,EAClB,qBAAqB,EACrB,oBAAoB,EACpB,qBAAqB,EACrB,eAAe,EACf,mBAAmB,EACnB,gBAAgB,EAChB,mBAAmB,EACnB,oBAAoB,EACpB,sBAAsB,EACtB,sBAAsB,EACtB,2BAA2B,EAC5B,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/senpi/client.ts"],"names":[],"mappings":"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"}
|
package/dist/senpi/client.js
CHANGED
|
@@ -6,6 +6,99 @@ import { extractMinClearingHouseTime, createClearingHouseStalenessGuard } from "
|
|
|
6
6
|
import { normalizeDslCoinDex, reconcileDexFromClearingHouseKey } from "../utils/perp-coin.js";
|
|
7
7
|
import { createLogger } from "../utils/logger.js";
|
|
8
8
|
const logger = createLogger("[senpi][client]");
|
|
9
|
+
/**
|
|
10
|
+
* Typed error thrown by `SenpiClient` mutation methods when the response
|
|
11
|
+
* reports a failure at either layer (outer MCP envelope or inner engine
|
|
12
|
+
* result). Callers that today only catch `throw` will fire on real
|
|
13
|
+
* failures instead of silently treating phantom-success responses as
|
|
14
|
+
* success — preventing the orphaned-DSL-state class of bug.
|
|
15
|
+
*
|
|
16
|
+
* Codes:
|
|
17
|
+
* - `UNEXPECTED_RESPONSE` — response shape is not the expected object.
|
|
18
|
+
* - `UPSTREAM_ERROR` — outer MCP envelope `success === false`.
|
|
19
|
+
* - `ENGINE_FAILURE` — outer envelope ok, but inner engine result
|
|
20
|
+
* reported `success === false` (e.g. CREATE_ORDER_RESTING).
|
|
21
|
+
*/
|
|
22
|
+
export class SenpiClientError extends Error {
|
|
23
|
+
code;
|
|
24
|
+
details;
|
|
25
|
+
constructor(code, message, details) {
|
|
26
|
+
super(message);
|
|
27
|
+
this.name = "SenpiClientError";
|
|
28
|
+
this.code = code;
|
|
29
|
+
this.details = details;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Best-effort engine error message extraction. Mirrors the resolver used
|
|
34
|
+
* inside the MCP server's mutation handlers (Task 2 fix shape) so DSL/
|
|
35
|
+
* action logs surface the same string the operator sees in audit logs.
|
|
36
|
+
*/
|
|
37
|
+
function extractEngineMessage(data) {
|
|
38
|
+
const err = data.error;
|
|
39
|
+
if (err && typeof err === "object") {
|
|
40
|
+
const errObj = err;
|
|
41
|
+
const description = typeof errObj.description === "string" ? errObj.description : undefined;
|
|
42
|
+
const errMessage = typeof errObj.message === "string" ? errObj.message : undefined;
|
|
43
|
+
const errCode = typeof errObj.code === "string" ? errObj.code : undefined;
|
|
44
|
+
const text = description ?? errMessage;
|
|
45
|
+
if (text && errCode)
|
|
46
|
+
return `${errCode}: ${text}`;
|
|
47
|
+
if (text)
|
|
48
|
+
return text;
|
|
49
|
+
if (errCode)
|
|
50
|
+
return errCode;
|
|
51
|
+
}
|
|
52
|
+
else if (typeof err === "string" && err.length > 0) {
|
|
53
|
+
return err;
|
|
54
|
+
}
|
|
55
|
+
if (typeof data.message === "string" && data.message.length > 0)
|
|
56
|
+
return data.message;
|
|
57
|
+
if (typeof data.errorCode === "string" && data.errorCode.length > 0)
|
|
58
|
+
return data.errorCode;
|
|
59
|
+
return "Trading engine reported failure";
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Enforces success at both response layers and returns the unwrapped
|
|
63
|
+
* inner data. Throws `SenpiClientError` on any reported failure.
|
|
64
|
+
*
|
|
65
|
+
* Resolution order:
|
|
66
|
+
* 1. `raw` is not an object → `UNEXPECTED_RESPONSE`.
|
|
67
|
+
* 2. Outer envelope `success === false` → `UPSTREAM_ERROR` with the
|
|
68
|
+
* envelope's error.code / error.message preserved.
|
|
69
|
+
* 3. Unwrap envelope to inner data; if not an object → `UNEXPECTED_RESPONSE`.
|
|
70
|
+
* 4. Inner data carries a boolean `success === false` → `ENGINE_FAILURE`
|
|
71
|
+
* with a best-effort engine message.
|
|
72
|
+
* 5. Otherwise return the inner data.
|
|
73
|
+
*
|
|
74
|
+
* Used by every mutation method (createPosition, editPosition,
|
|
75
|
+
* closePosition, etc.) so DSL/actions can rely on `throw` semantics
|
|
76
|
+
* matching real failures, instead of trusting an outer-envelope-only
|
|
77
|
+
* "success" that may mask an engine rejection.
|
|
78
|
+
*/
|
|
79
|
+
function requireMutationSuccess(tool, raw) {
|
|
80
|
+
if (!isRecord(raw)) {
|
|
81
|
+
throw new SenpiClientError("UNEXPECTED_RESPONSE", `${tool}: response is not an object`, raw);
|
|
82
|
+
}
|
|
83
|
+
// Outer envelope check — only present on direct MCP responses.
|
|
84
|
+
if (raw.success === false) {
|
|
85
|
+
const errObj = isRecord(raw.error) ? raw.error : undefined;
|
|
86
|
+
const code = (errObj && typeof errObj.code === "string") ? errObj.code : "UPSTREAM_ERROR";
|
|
87
|
+
const message = (errObj && typeof errObj.message === "string")
|
|
88
|
+
? errObj.message
|
|
89
|
+
: `${tool}: outer envelope reported failure`;
|
|
90
|
+
throw new SenpiClientError("UPSTREAM_ERROR", `${tool}: ${code}: ${message}`, raw);
|
|
91
|
+
}
|
|
92
|
+
const data = unwrapEnvelope(raw) ?? raw;
|
|
93
|
+
if (!isRecord(data)) {
|
|
94
|
+
throw new SenpiClientError("UNEXPECTED_RESPONSE", `${tool}: inner data is not an object`, raw);
|
|
95
|
+
}
|
|
96
|
+
// Inner engine-level success check (only when the field is explicitly boolean).
|
|
97
|
+
if (typeof data.success === "boolean" && data.success === false) {
|
|
98
|
+
throw new SenpiClientError("ENGINE_FAILURE", `${tool}: ${extractEngineMessage(data)}`, data);
|
|
99
|
+
}
|
|
100
|
+
return data;
|
|
101
|
+
}
|
|
9
102
|
/**
|
|
10
103
|
* execution_get_order_status payloads vary: flat `{ filled, status }` vs Hyperliquid-style
|
|
11
104
|
* `{ status: "order", order: { status: "filled", order: {...} } }`. Top-level `status` may be a
|
|
@@ -125,7 +218,7 @@ export class SenpiClient {
|
|
|
125
218
|
this.mcpClient = null;
|
|
126
219
|
}
|
|
127
220
|
}
|
|
128
|
-
async callTool(toolName, args) {
|
|
221
|
+
async callTool(toolName, args, opts) {
|
|
129
222
|
// Injected mode: delegate directly
|
|
130
223
|
if (this.injectedTransport) {
|
|
131
224
|
return this.injectedTransport(toolName, args);
|
|
@@ -134,7 +227,9 @@ export class SenpiClient {
|
|
|
134
227
|
if (!this.mcpClient) {
|
|
135
228
|
throw new Error('SenpiClient not connected — call connect() first');
|
|
136
229
|
}
|
|
137
|
-
const result =
|
|
230
|
+
const result = opts?.signal
|
|
231
|
+
? await this.mcpClient.callTool({ name: toolName, arguments: args }, undefined, { signal: opts.signal })
|
|
232
|
+
: await this.mcpClient.callTool({ name: toolName, arguments: args });
|
|
138
233
|
if (result.content && Array.isArray(result.content) && result.content.length > 0) {
|
|
139
234
|
const part = result.content[0];
|
|
140
235
|
if (part && 'text' in part && typeof part.text === 'string') {
|
|
@@ -148,6 +243,28 @@ export class SenpiClient {
|
|
|
148
243
|
}
|
|
149
244
|
return result;
|
|
150
245
|
}
|
|
246
|
+
/**
|
|
247
|
+
* Run an MCP `audit_query` and return the unwrapped response payload.
|
|
248
|
+
*
|
|
249
|
+
* Stays a thin pass-through so the runtime layer (which owns the
|
|
250
|
+
* wallet→strategyId resolution and error mapping) can compose the call
|
|
251
|
+
* with full type-safety. Callers must already have set
|
|
252
|
+
* `resource_type` / `resource_id` if scoping is desired — this method
|
|
253
|
+
* does not interpret arguments.
|
|
254
|
+
*
|
|
255
|
+
* @param args Arbitrary key/value map forwarded verbatim as the MCP
|
|
256
|
+
* tool input. Caller is responsible for matching the
|
|
257
|
+
* MCP `audit_query` schema.
|
|
258
|
+
* @param opts Optional AbortSignal; when fired, the underlying MCP
|
|
259
|
+
* `callTool` is cancelled (the SDK rejects with an
|
|
260
|
+
* AbortError).
|
|
261
|
+
* @returns The `.data` field of the MCP envelope when present;
|
|
262
|
+
* otherwise the raw response.
|
|
263
|
+
*/
|
|
264
|
+
async auditQuery(args, opts) {
|
|
265
|
+
const raw = await this.callTool('audit_query', args, opts);
|
|
266
|
+
return unwrapEnvelope(raw) ?? raw;
|
|
267
|
+
}
|
|
151
268
|
/**
|
|
152
269
|
* Returns an adapter compatible with SenpiToolClient so the same connection
|
|
153
270
|
* can be used for scanner provider MCP tool calls (e.g. leaderboard_get_markets).
|
|
@@ -158,26 +275,48 @@ export class SenpiClient {
|
|
|
158
275
|
};
|
|
159
276
|
}
|
|
160
277
|
// ─── Ratchet stop (DSL backend engine) ──────────────────────────────────────
|
|
278
|
+
/**
|
|
279
|
+
* Register a backend ratchet-stop (DSL Phase 2 handoff).
|
|
280
|
+
*
|
|
281
|
+
* Throws `SenpiClientError` on outer envelope failure or inner engine
|
|
282
|
+
* `success: false`. Callers' existing `if (result.success && result.position)`
|
|
283
|
+
* branches now treat the success path only — failures land in the
|
|
284
|
+
* surrounding try/catch instead of falling through.
|
|
285
|
+
*/
|
|
161
286
|
async addRatchetStop(input) {
|
|
162
287
|
const raw = await this.callTool("ratchet_stop_add", {
|
|
163
288
|
...input,
|
|
164
289
|
asset: dexQualifiedAsset(input.asset, input.dex),
|
|
165
290
|
});
|
|
166
|
-
return
|
|
291
|
+
return requireMutationSuccess("ratchet_stop_add", raw);
|
|
167
292
|
}
|
|
293
|
+
/**
|
|
294
|
+
* Update an existing backend ratchet-stop.
|
|
295
|
+
*
|
|
296
|
+
* Throws `SenpiClientError` on outer envelope failure or inner engine
|
|
297
|
+
* `success: false`.
|
|
298
|
+
*/
|
|
168
299
|
async editRatchetStop(input) {
|
|
169
300
|
const raw = await this.callTool("ratchet_stop_edit", {
|
|
170
301
|
...input,
|
|
171
302
|
asset: dexQualifiedAsset(input.asset, input.dex),
|
|
172
303
|
});
|
|
173
|
-
return
|
|
304
|
+
return requireMutationSuccess("ratchet_stop_edit", raw);
|
|
174
305
|
}
|
|
306
|
+
/**
|
|
307
|
+
* Delete a backend ratchet-stop. Used during DSL teardown / handoff close.
|
|
308
|
+
*
|
|
309
|
+
* Throws `SenpiClientError` on outer envelope failure or inner engine
|
|
310
|
+
* `success: false`. Previously this method's `{ success: boolean }` return
|
|
311
|
+
* was ignored by every caller (only `throw` was caught), which left
|
|
312
|
+
* zombie ratchet-stops on the backend when the engine reported failure.
|
|
313
|
+
*/
|
|
175
314
|
async deleteRatchetStop(input) {
|
|
176
315
|
const raw = await this.callTool("ratchet_stop_delete", {
|
|
177
316
|
...input,
|
|
178
317
|
asset: dexQualifiedAsset(input.asset, input.dex),
|
|
179
318
|
});
|
|
180
|
-
return
|
|
319
|
+
return requireMutationSuccess("ratchet_stop_delete", raw);
|
|
181
320
|
}
|
|
182
321
|
async queryRatchetStops(input) {
|
|
183
322
|
const qualifiedAsset = input.asset ? dexQualifiedAsset(input.asset, input.dex) : undefined;
|
|
@@ -247,6 +386,21 @@ export class SenpiClient {
|
|
|
247
386
|
* Set/update SL (and optionally TP) on an open position. Uses MCP edit_position.
|
|
248
387
|
* For XYZ DEX pass dex: "xyz" so coin is sent as "xyz:COIN"; otherwise main DEX uses plain coin.
|
|
249
388
|
*/
|
|
389
|
+
/**
|
|
390
|
+
* Set / update an exchange-side stop loss (or perform other position
|
|
391
|
+
* edits) on an open position.
|
|
392
|
+
*
|
|
393
|
+
* Throws `SenpiClientError` when either response layer reports failure:
|
|
394
|
+
* - `UPSTREAM_ERROR` — MCP envelope `success: false`.
|
|
395
|
+
* - `ENGINE_FAILURE` — outer ok, inner engine `success: false`
|
|
396
|
+
* (e.g. position not found, SL placement rejected).
|
|
397
|
+
*
|
|
398
|
+
* Returning normally means the engine accepted the edit. The returned
|
|
399
|
+
* `EditPositionResult` carries `slOrderId` when an SL was placed/updated;
|
|
400
|
+
* a `null` slOrderId on a non-throwing return indicates the engine reported
|
|
401
|
+
* success without the expected SL artifact — caller should treat that as
|
|
402
|
+
* a soft failure (logged as a warn) and re-attempt on the next sync tick.
|
|
403
|
+
*/
|
|
250
404
|
async editPosition(wallet, payload) {
|
|
251
405
|
const coin = payload.dex === "xyz" && payload.coin && !payload.coin.startsWith("xyz:")
|
|
252
406
|
? `xyz:${payload.coin}`
|
|
@@ -266,7 +420,10 @@ export class SenpiClient {
|
|
|
266
420
|
rawKeys: isRecord(raw) ? Object.keys(raw) : [],
|
|
267
421
|
rawShape: isRecord(raw) ? JSON.stringify(raw).slice(0, 500) : String(raw),
|
|
268
422
|
});
|
|
269
|
-
|
|
423
|
+
// Throws on outer/inner success: false. Returning means the engine
|
|
424
|
+
// accepted the edit. Older callers that only catch `throw` will now
|
|
425
|
+
// see real failures instead of silently treating them as success.
|
|
426
|
+
const result = requireMutationSuccess("edit_position", raw);
|
|
270
427
|
if (result && typeof result === "object" && !Array.isArray(result)) {
|
|
271
428
|
const ro = result;
|
|
272
429
|
const slOrderId = extractSlOrderId(ro, raw);
|
|
@@ -537,17 +694,33 @@ export class SenpiClient {
|
|
|
537
694
|
return { success: false };
|
|
538
695
|
return { success: data.success === true, message: asString(data.message) ?? undefined };
|
|
539
696
|
}
|
|
697
|
+
/**
|
|
698
|
+
* Submit a batch close_positions MCP call.
|
|
699
|
+
*
|
|
700
|
+
* Throws `SenpiClientError` on outer envelope or inner engine failure
|
|
701
|
+
* (same shape as {@link closePosition}). Successful return means the
|
|
702
|
+
* batch was accepted; per-position confirmation must still come from
|
|
703
|
+
* position-tracker scanner events.
|
|
704
|
+
*/
|
|
540
705
|
async closePositions(wallet, positions) {
|
|
541
|
-
|
|
706
|
+
const raw = await this.callTool("strategy_close_positions", {
|
|
542
707
|
strategy_wallet: wallet,
|
|
543
708
|
positions,
|
|
544
709
|
});
|
|
710
|
+
return requireMutationSuccess("strategy_close_positions", raw);
|
|
545
711
|
}
|
|
712
|
+
/**
|
|
713
|
+
* Cancel an outstanding order by id.
|
|
714
|
+
*
|
|
715
|
+
* Throws `SenpiClientError` on outer envelope or inner engine failure
|
|
716
|
+
* (same shape as {@link closePosition}).
|
|
717
|
+
*/
|
|
546
718
|
async cancelOrder(wallet, orderId) {
|
|
547
|
-
|
|
719
|
+
const raw = await this.callTool("cancel_order", {
|
|
548
720
|
strategyWalletAddress: wallet,
|
|
549
721
|
orderId,
|
|
550
722
|
});
|
|
723
|
+
return requireMutationSuccess("cancel_order", raw);
|
|
551
724
|
}
|
|
552
725
|
async getAssetTradingLimits(wallet, coin, dex) {
|
|
553
726
|
try {
|