@raintree-technology/perps 0.1.0

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.
Files changed (316) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/LICENSE +21 -0
  3. package/README.md +175 -0
  4. package/dist/adapters/aevo.d.ts +64 -0
  5. package/dist/adapters/aevo.js +899 -0
  6. package/dist/adapters/certification.d.ts +33 -0
  7. package/dist/adapters/certification.js +99 -0
  8. package/dist/adapters/decibel/order-manager.d.ts +45 -0
  9. package/dist/adapters/decibel/order-manager.js +140 -0
  10. package/dist/adapters/decibel/rest-client.d.ts +176 -0
  11. package/dist/adapters/decibel/rest-client.js +155 -0
  12. package/dist/adapters/decibel/ws-feed.d.ts +28 -0
  13. package/dist/adapters/decibel/ws-feed.js +166 -0
  14. package/dist/adapters/decibel.d.ts +108 -0
  15. package/dist/adapters/decibel.js +1377 -0
  16. package/dist/adapters/hyperliquid.d.ts +63 -0
  17. package/dist/adapters/hyperliquid.js +797 -0
  18. package/dist/adapters/index.d.ts +11 -0
  19. package/dist/adapters/index.js +12 -0
  20. package/dist/adapters/interface.d.ts +310 -0
  21. package/dist/adapters/interface.js +15 -0
  22. package/dist/adapters/orderly.d.ts +70 -0
  23. package/dist/adapters/orderly.js +936 -0
  24. package/dist/adapters/paradex.d.ts +69 -0
  25. package/dist/adapters/paradex.js +862 -0
  26. package/dist/adapters/utils.d.ts +17 -0
  27. package/dist/adapters/utils.js +122 -0
  28. package/dist/cli/command-metadata.d.ts +2 -0
  29. package/dist/cli/command-metadata.js +44 -0
  30. package/dist/cli/context.d.ts +14 -0
  31. package/dist/cli/context.js +59 -0
  32. package/dist/cli/experience.d.ts +48 -0
  33. package/dist/cli/experience.js +243 -0
  34. package/dist/cli/ink/app/AppShell.d.ts +12 -0
  35. package/dist/cli/ink/app/AppShell.js +32 -0
  36. package/dist/cli/ink/app/MetricStrip.d.ts +6 -0
  37. package/dist/cli/ink/app/MetricStrip.js +14 -0
  38. package/dist/cli/ink/app/Panel.d.ts +9 -0
  39. package/dist/cli/ink/app/Panel.js +7 -0
  40. package/dist/cli/ink/app/ascii.d.ts +2 -0
  41. package/dist/cli/ink/app/ascii.js +46 -0
  42. package/dist/cli/ink/app/index.d.ts +5 -0
  43. package/dist/cli/ink/app/index.js +4 -0
  44. package/dist/cli/ink/app/types.d.ts +15 -0
  45. package/dist/cli/ink/app/types.js +1 -0
  46. package/dist/cli/ink/components/PnL.d.ts +12 -0
  47. package/dist/cli/ink/components/PnL.js +23 -0
  48. package/dist/cli/ink/components/Spinner.d.ts +13 -0
  49. package/dist/cli/ink/components/Spinner.js +13 -0
  50. package/dist/cli/ink/components/Table.d.ts +14 -0
  51. package/dist/cli/ink/components/Table.js +42 -0
  52. package/dist/cli/ink/components/WatchHeader.d.ts +10 -0
  53. package/dist/cli/ink/components/WatchHeader.js +18 -0
  54. package/dist/cli/ink/components/index.d.ts +4 -0
  55. package/dist/cli/ink/components/index.js +4 -0
  56. package/dist/cli/ink/index.d.ts +4 -0
  57. package/dist/cli/ink/index.js +4 -0
  58. package/dist/cli/ink/render.d.ts +12 -0
  59. package/dist/cli/ink/render.js +21 -0
  60. package/dist/cli/ink/theme.d.ts +29 -0
  61. package/dist/cli/ink/theme.js +40 -0
  62. package/dist/cli/network-defaults.d.ts +10 -0
  63. package/dist/cli/network-defaults.js +35 -0
  64. package/dist/cli/output.d.ts +11 -0
  65. package/dist/cli/output.js +115 -0
  66. package/dist/cli/program.d.ts +18 -0
  67. package/dist/cli/program.js +164 -0
  68. package/dist/cli/watch.d.ts +19 -0
  69. package/dist/cli/watch.js +35 -0
  70. package/dist/client/index.d.ts +55 -0
  71. package/dist/client/index.js +157 -0
  72. package/dist/commands/account/add.d.ts +2 -0
  73. package/dist/commands/account/add.js +510 -0
  74. package/dist/commands/account/balances-simple.d.ts +5 -0
  75. package/dist/commands/account/balances-simple.js +63 -0
  76. package/dist/commands/account/index.d.ts +2 -0
  77. package/dist/commands/account/index.js +17 -0
  78. package/dist/commands/account/ls.d.ts +2 -0
  79. package/dist/commands/account/ls.js +95 -0
  80. package/dist/commands/account/positions-simple.d.ts +5 -0
  81. package/dist/commands/account/positions-simple.js +77 -0
  82. package/dist/commands/account/remove.d.ts +2 -0
  83. package/dist/commands/account/remove.js +47 -0
  84. package/dist/commands/account/set-default.d.ts +2 -0
  85. package/dist/commands/account/set-default.js +47 -0
  86. package/dist/commands/agent/index.d.ts +2 -0
  87. package/dist/commands/agent/index.js +126 -0
  88. package/dist/commands/arb/alert.d.ts +6 -0
  89. package/dist/commands/arb/alert.js +88 -0
  90. package/dist/commands/arb/basis-execute.d.ts +6 -0
  91. package/dist/commands/arb/basis-execute.js +332 -0
  92. package/dist/commands/arb/basis.d.ts +6 -0
  93. package/dist/commands/arb/basis.js +181 -0
  94. package/dist/commands/arb/compare.d.ts +6 -0
  95. package/dist/commands/arb/compare.js +216 -0
  96. package/dist/commands/arb/execute.d.ts +6 -0
  97. package/dist/commands/arb/execute.js +467 -0
  98. package/dist/commands/arb/funding.d.ts +6 -0
  99. package/dist/commands/arb/funding.js +201 -0
  100. package/dist/commands/arb/history.d.ts +6 -0
  101. package/dist/commands/arb/history.js +153 -0
  102. package/dist/commands/arb/index.d.ts +6 -0
  103. package/dist/commands/arb/index.js +29 -0
  104. package/dist/commands/arb/positions.d.ts +6 -0
  105. package/dist/commands/arb/positions.js +158 -0
  106. package/dist/commands/arb/spread.d.ts +6 -0
  107. package/dist/commands/arb/spread.js +253 -0
  108. package/dist/commands/arb/track.d.ts +6 -0
  109. package/dist/commands/arb/track.js +259 -0
  110. package/dist/commands/asset/book-simple.d.ts +5 -0
  111. package/dist/commands/asset/book-simple.js +77 -0
  112. package/dist/commands/asset/index.d.ts +2 -0
  113. package/dist/commands/asset/index.js +5 -0
  114. package/dist/commands/completion.d.ts +2 -0
  115. package/dist/commands/completion.js +161 -0
  116. package/dist/commands/config/index.d.ts +5 -0
  117. package/dist/commands/config/index.js +109 -0
  118. package/dist/commands/data/index.d.ts +31 -0
  119. package/dist/commands/data/index.js +1466 -0
  120. package/dist/commands/doctor.d.ts +2 -0
  121. package/dist/commands/doctor.js +201 -0
  122. package/dist/commands/exchange/index.d.ts +2 -0
  123. package/dist/commands/exchange/index.js +107 -0
  124. package/dist/commands/index.d.ts +2 -0
  125. package/dist/commands/index.js +48 -0
  126. package/dist/commands/markets/index.d.ts +2 -0
  127. package/dist/commands/markets/index.js +5 -0
  128. package/dist/commands/markets/ls-simple.d.ts +7 -0
  129. package/dist/commands/markets/ls-simple.js +277 -0
  130. package/dist/commands/operator/index.d.ts +2 -0
  131. package/dist/commands/operator/index.js +146 -0
  132. package/dist/commands/order/cancel-simple.d.ts +5 -0
  133. package/dist/commands/order/cancel-simple.js +104 -0
  134. package/dist/commands/order/index.d.ts +2 -0
  135. package/dist/commands/order/index.js +13 -0
  136. package/dist/commands/order/limit-simple.d.ts +5 -0
  137. package/dist/commands/order/limit-simple.js +195 -0
  138. package/dist/commands/order/market-simple.d.ts +5 -0
  139. package/dist/commands/order/market-simple.js +190 -0
  140. package/dist/commands/order/shared.d.ts +17 -0
  141. package/dist/commands/order/shared.js +51 -0
  142. package/dist/commands/order/trigger-simple.d.ts +5 -0
  143. package/dist/commands/order/trigger-simple.js +246 -0
  144. package/dist/commands/referral/index.d.ts +2 -0
  145. package/dist/commands/referral/index.js +7 -0
  146. package/dist/commands/referral/set.d.ts +2 -0
  147. package/dist/commands/referral/set.js +26 -0
  148. package/dist/commands/referral/status.d.ts +2 -0
  149. package/dist/commands/referral/status.js +31 -0
  150. package/dist/commands/replay/index.d.ts +2 -0
  151. package/dist/commands/replay/index.js +152 -0
  152. package/dist/commands/risk/analytics.d.ts +2 -0
  153. package/dist/commands/risk/analytics.js +64 -0
  154. package/dist/commands/risk/audit.d.ts +2 -0
  155. package/dist/commands/risk/audit.js +52 -0
  156. package/dist/commands/risk/index.d.ts +2 -0
  157. package/dist/commands/risk/index.js +9 -0
  158. package/dist/commands/risk/rules.d.ts +2 -0
  159. package/dist/commands/risk/rules.js +102 -0
  160. package/dist/commands/server.d.ts +2 -0
  161. package/dist/commands/server.js +208 -0
  162. package/dist/commands/setup/index.d.ts +2 -0
  163. package/dist/commands/setup/index.js +478 -0
  164. package/dist/commands/signal/index.d.ts +2 -0
  165. package/dist/commands/signal/index.js +129 -0
  166. package/dist/commands/state/index.d.ts +2 -0
  167. package/dist/commands/state/index.js +5 -0
  168. package/dist/commands/state/show.d.ts +2 -0
  169. package/dist/commands/state/show.js +105 -0
  170. package/dist/commands/strategy/index.d.ts +4 -0
  171. package/dist/commands/strategy/index.js +73 -0
  172. package/dist/commands/traces/index.d.ts +2 -0
  173. package/dist/commands/traces/index.js +76 -0
  174. package/dist/commands/ui/demo.d.ts +9 -0
  175. package/dist/commands/ui/demo.js +195 -0
  176. package/dist/commands/ui/index.d.ts +2 -0
  177. package/dist/commands/ui/index.js +7 -0
  178. package/dist/commands/ui/terminal.d.ts +2 -0
  179. package/dist/commands/ui/terminal.js +255 -0
  180. package/dist/commands/upgrade.d.ts +2 -0
  181. package/dist/commands/upgrade.js +98 -0
  182. package/dist/index.d.ts +2 -0
  183. package/dist/index.js +4 -0
  184. package/dist/lib/agent/audit.d.ts +12 -0
  185. package/dist/lib/agent/audit.js +13 -0
  186. package/dist/lib/agent/gateway.d.ts +13 -0
  187. package/dist/lib/agent/gateway.js +598 -0
  188. package/dist/lib/agent/metrics.d.ts +33 -0
  189. package/dist/lib/agent/metrics.js +175 -0
  190. package/dist/lib/agent/signature.d.ts +8 -0
  191. package/dist/lib/agent/signature.js +28 -0
  192. package/dist/lib/agent/tools.d.ts +28 -0
  193. package/dist/lib/agent/tools.js +453 -0
  194. package/dist/lib/agent/x402.d.ts +23 -0
  195. package/dist/lib/agent/x402.js +62 -0
  196. package/dist/lib/api-wallet.d.ts +69 -0
  197. package/dist/lib/api-wallet.js +101 -0
  198. package/dist/lib/balance-watcher.d.ts +25 -0
  199. package/dist/lib/balance-watcher.js +83 -0
  200. package/dist/lib/book-watcher.d.ts +25 -0
  201. package/dist/lib/book-watcher.js +48 -0
  202. package/dist/lib/config.d.ts +88 -0
  203. package/dist/lib/config.js +427 -0
  204. package/dist/lib/constants.d.ts +50 -0
  205. package/dist/lib/constants.js +84 -0
  206. package/dist/lib/contracts.d.ts +7 -0
  207. package/dist/lib/contracts.js +8 -0
  208. package/dist/lib/credential-vault.d.ts +22 -0
  209. package/dist/lib/credential-vault.js +109 -0
  210. package/dist/lib/db/accounts.d.ts +83 -0
  211. package/dist/lib/db/accounts.js +203 -0
  212. package/dist/lib/db/funding-history.d.ts +69 -0
  213. package/dist/lib/db/funding-history.js +183 -0
  214. package/dist/lib/db/index.d.ts +11 -0
  215. package/dist/lib/db/index.js +272 -0
  216. package/dist/lib/events/bus.d.ts +10 -0
  217. package/dist/lib/events/bus.js +17 -0
  218. package/dist/lib/events/types.d.ts +51 -0
  219. package/dist/lib/events/types.js +1 -0
  220. package/dist/lib/exchange.d.ts +30 -0
  221. package/dist/lib/exchange.js +84 -0
  222. package/dist/lib/execution/journal.d.ts +25 -0
  223. package/dist/lib/execution/journal.js +158 -0
  224. package/dist/lib/execution/safety.d.ts +34 -0
  225. package/dist/lib/execution/safety.js +197 -0
  226. package/dist/lib/exit-codes.d.ts +18 -0
  227. package/dist/lib/exit-codes.js +60 -0
  228. package/dist/lib/fetch.d.ts +18 -0
  229. package/dist/lib/fetch.js +66 -0
  230. package/dist/lib/fs-security.d.ts +10 -0
  231. package/dist/lib/fs-security.js +26 -0
  232. package/dist/lib/funding-tracker.d.ts +40 -0
  233. package/dist/lib/funding-tracker.js +118 -0
  234. package/dist/lib/logger.d.ts +27 -0
  235. package/dist/lib/logger.js +82 -0
  236. package/dist/lib/network-model.d.ts +13 -0
  237. package/dist/lib/network-model.js +30 -0
  238. package/dist/lib/onboarding.d.ts +133 -0
  239. package/dist/lib/onboarding.js +1459 -0
  240. package/dist/lib/operator-state.d.ts +25 -0
  241. package/dist/lib/operator-state.js +82 -0
  242. package/dist/lib/orders-watcher.d.ts +24 -0
  243. package/dist/lib/orders-watcher.js +74 -0
  244. package/dist/lib/paths.d.ts +20 -0
  245. package/dist/lib/paths.js +23 -0
  246. package/dist/lib/portfolio-watcher.d.ts +33 -0
  247. package/dist/lib/portfolio-watcher.js +95 -0
  248. package/dist/lib/position-watcher.d.ts +16 -0
  249. package/dist/lib/position-watcher.js +44 -0
  250. package/dist/lib/price-watcher.d.ts +15 -0
  251. package/dist/lib/price-watcher.js +84 -0
  252. package/dist/lib/prompts.d.ts +32 -0
  253. package/dist/lib/prompts.js +105 -0
  254. package/dist/lib/rate-limit.d.ts +32 -0
  255. package/dist/lib/rate-limit.js +88 -0
  256. package/dist/lib/risk/analytics.d.ts +39 -0
  257. package/dist/lib/risk/analytics.js +98 -0
  258. package/dist/lib/risk/drawdown.d.ts +18 -0
  259. package/dist/lib/risk/drawdown.js +49 -0
  260. package/dist/lib/risk/evaluation-log.d.ts +29 -0
  261. package/dist/lib/risk/evaluation-log.js +61 -0
  262. package/dist/lib/risk/index.d.ts +4 -0
  263. package/dist/lib/risk/index.js +4 -0
  264. package/dist/lib/risk/limits.d.ts +23 -0
  265. package/dist/lib/risk/limits.js +27 -0
  266. package/dist/lib/risk/manager.d.ts +32 -0
  267. package/dist/lib/risk/manager.js +85 -0
  268. package/dist/lib/risk/policy-middleware.d.ts +33 -0
  269. package/dist/lib/risk/policy-middleware.js +267 -0
  270. package/dist/lib/risk/position-sizer.d.ts +9 -0
  271. package/dist/lib/risk/position-sizer.js +14 -0
  272. package/dist/lib/risk/rules-store.d.ts +16 -0
  273. package/dist/lib/risk/rules-store.js +47 -0
  274. package/dist/lib/schema.d.ts +254 -0
  275. package/dist/lib/schema.js +199 -0
  276. package/dist/lib/secrets.d.ts +3 -0
  277. package/dist/lib/secrets.js +62 -0
  278. package/dist/lib/settings.d.ts +24 -0
  279. package/dist/lib/settings.js +86 -0
  280. package/dist/lib/signals.d.ts +73 -0
  281. package/dist/lib/signals.js +136 -0
  282. package/dist/lib/stable-stringify.d.ts +6 -0
  283. package/dist/lib/stable-stringify.js +17 -0
  284. package/dist/lib/state-context.d.ts +44 -0
  285. package/dist/lib/state-context.js +133 -0
  286. package/dist/lib/strategy/basis-trade.d.ts +2 -0
  287. package/dist/lib/strategy/basis-trade.js +24 -0
  288. package/dist/lib/strategy/funding-arb.d.ts +2 -0
  289. package/dist/lib/strategy/funding-arb.js +23 -0
  290. package/dist/lib/strategy/interface.d.ts +23 -0
  291. package/dist/lib/strategy/interface.js +1 -0
  292. package/dist/lib/strategy/registry.d.ts +4 -0
  293. package/dist/lib/strategy/registry.js +10 -0
  294. package/dist/lib/telemetry.d.ts +25 -0
  295. package/dist/lib/telemetry.js +101 -0
  296. package/dist/lib/trace-queries.d.ts +20 -0
  297. package/dist/lib/trace-queries.js +133 -0
  298. package/dist/lib/trace.d.ts +1 -0
  299. package/dist/lib/trace.js +4 -0
  300. package/dist/lib/trade-reputation.d.ts +6 -0
  301. package/dist/lib/trade-reputation.js +99 -0
  302. package/dist/lib/ui-tokens.d.ts +21 -0
  303. package/dist/lib/ui-tokens.js +26 -0
  304. package/dist/lib/validate.d.ts +39 -0
  305. package/dist/lib/validate.js +108 -0
  306. package/dist/lib/validation.d.ts +9 -0
  307. package/dist/lib/validation.js +64 -0
  308. package/dist/server/cache.d.ts +38 -0
  309. package/dist/server/cache.js +56 -0
  310. package/dist/server/index.d.ts +2 -0
  311. package/dist/server/index.js +89 -0
  312. package/dist/server/ipc.d.ts +18 -0
  313. package/dist/server/ipc.js +159 -0
  314. package/dist/server/subscriptions.d.ts +18 -0
  315. package/dist/server/subscriptions.js +114 -0
  316. package/package.json +124 -0
@@ -0,0 +1,598 @@
1
+ import { createServer } from "node:http";
2
+ import { getAvailableExchanges, getExchangeAdapterById } from "../exchange.js";
3
+ import { getExchangeCredentials, loadConfig } from "../config.js";
4
+ import { withJsonContract } from "../contracts.js";
5
+ import { inferExitCode } from "../exit-codes.js";
6
+ import { RiskPolicyMiddleware } from "../risk/policy-middleware.js";
7
+ import { executeOrderWithSafety } from "../execution/safety.js";
8
+ import { runWithExecutionJournal } from "../execution/journal.js";
9
+ import { appendAgentAuditEvent } from "./audit.js";
10
+ import { generateTraceId } from "../trace.js";
11
+ import { getAgentMetricsSnapshot, getAgentReputationSummary, recordAgentRouteMetrics } from "./metrics.js";
12
+ import { signAgentResponse } from "./signature.js";
13
+ import { getX402Requirement, validateX402Payment } from "./x402.js";
14
+ import { buildStateContext } from "../state-context.js";
15
+ import { getTelemetrySnapshot, summarizeFailureRates } from "../telemetry.js";
16
+ import { getToolDefinitions } from "./tools.js";
17
+ import { addSignal } from "../signals.js";
18
+ const JSON_HEADERS = {
19
+ "content-type": "application/json; charset=utf-8",
20
+ "cache-control": "no-store",
21
+ };
22
+ function writeJson(res, status, data) {
23
+ res.statusCode = status;
24
+ res.setHeader("content-type", JSON_HEADERS["content-type"]);
25
+ res.setHeader("cache-control", JSON_HEADERS["cache-control"]);
26
+ res.end(`${JSON.stringify(data, null, 2)}\n`);
27
+ }
28
+ async function readBody(req) {
29
+ const chunks = [];
30
+ for await (const chunk of req) {
31
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
32
+ }
33
+ const raw = Buffer.concat(chunks).toString("utf8").trim();
34
+ if (!raw) {
35
+ return {};
36
+ }
37
+ return JSON.parse(raw);
38
+ }
39
+ function toMarket(input) {
40
+ const raw = (input.market ?? input.symbol ?? "").trim().toUpperCase();
41
+ if (!raw) {
42
+ throw new Error("market or symbol is required");
43
+ }
44
+ if (raw.includes("-")) {
45
+ return raw;
46
+ }
47
+ return `${raw}-PERP`;
48
+ }
49
+ function toSide(value) {
50
+ const normalized = value.trim().toLowerCase();
51
+ if (normalized === "buy" || normalized === "long") {
52
+ return "long";
53
+ }
54
+ if (normalized === "sell" || normalized === "short") {
55
+ return "short";
56
+ }
57
+ throw new Error("side must be one of: buy, sell, long, short");
58
+ }
59
+ function toSize(value) {
60
+ const num = typeof value === "number" ? value : Number.parseFloat(value);
61
+ if (!Number.isFinite(num) || num <= 0) {
62
+ throw new Error("size must be a positive number");
63
+ }
64
+ return num;
65
+ }
66
+ function toExchange(defaultExchange, value) {
67
+ const selected = (value ?? defaultExchange).trim().toLowerCase();
68
+ if (!getAvailableExchanges().includes(selected)) {
69
+ throw new Error(`unknown exchange: ${selected}`);
70
+ }
71
+ return selected;
72
+ }
73
+ function toTestnet(defaultTestnet, value) {
74
+ if (typeof value === "boolean") {
75
+ return value;
76
+ }
77
+ return defaultTestnet;
78
+ }
79
+ function normalizeExchangeHint(value, fallback) {
80
+ if (typeof value === "string") {
81
+ const normalized = value.trim().toLowerCase();
82
+ if (normalized.length > 0) {
83
+ return normalized;
84
+ }
85
+ }
86
+ return fallback;
87
+ }
88
+ function inferExchangeForRoute(route, requestPayload, defaultExchange) {
89
+ if (route === "quote" || route === "execute") {
90
+ const payload = requestPayload;
91
+ return normalizeExchangeHint(payload?.exchange, defaultExchange);
92
+ }
93
+ if (route === "acp.intent") {
94
+ const payload = requestPayload;
95
+ return normalizeExchangeHint(payload?.payload?.exchange, defaultExchange);
96
+ }
97
+ return "system";
98
+ }
99
+ function bindRouteMeta(route, requestPayload, responsePayload) {
100
+ const signature = signAgentResponse(requestPayload, responsePayload);
101
+ return {
102
+ ...responsePayload,
103
+ contract: {
104
+ route,
105
+ signature,
106
+ },
107
+ };
108
+ }
109
+ function requirePayment(req) {
110
+ const validated = validateX402Payment(req);
111
+ if (!validated.ok || !validated.receipt) {
112
+ const error = new Error(validated.error ?? "payment_required");
113
+ error.requirement =
114
+ validated.requirement ?? getX402Requirement();
115
+ throw error;
116
+ }
117
+ return validated.receipt;
118
+ }
119
+ async function handleQuote(req, options, requestPayload) {
120
+ const exchange = toExchange(options.defaultExchange, requestPayload.exchange);
121
+ const testnet = toTestnet(options.defaultTestnet, requestPayload.testnet);
122
+ const market = toMarket(requestPayload);
123
+ const side = toSide(requestPayload.side);
124
+ const size = toSize(requestPayload.size);
125
+ const payment = requestPayload.premium ? requirePayment(req) : undefined;
126
+ const config = loadConfig(testnet, exchange);
127
+ const credentials = getExchangeCredentials(config, exchange);
128
+ const adapter = getExchangeAdapterById(exchange);
129
+ await adapter.connect({
130
+ testnet: config.testnet,
131
+ rpcUrl: credentials.fullnodeUrl,
132
+ wsUrl: credentials.wsUrl,
133
+ credentials,
134
+ });
135
+ try {
136
+ const ticker = await adapter.getTicker(market);
137
+ const book = await adapter.getOrderBook(market, 3);
138
+ const bid = Number.parseFloat(book.bids[0]?.price ?? "");
139
+ const ask = Number.parseFloat(book.asks[0]?.price ?? "");
140
+ const mark = Number.parseFloat(ticker.markPrice);
141
+ const last = Number.parseFloat(ticker.lastPrice);
142
+ const reference = side === "long"
143
+ ? (Number.isFinite(ask) && ask > 0 ? ask : mark)
144
+ : (Number.isFinite(bid) && bid > 0 ? bid : mark);
145
+ const effective = Number.isFinite(reference) && reference > 0 ? reference : last;
146
+ if (!Number.isFinite(effective) || effective <= 0) {
147
+ throw new Error("unable to compute reference price");
148
+ }
149
+ const spread = Number.isFinite(ask) && Number.isFinite(bid) && ask > 0 && bid > 0
150
+ ? ask - bid
151
+ : undefined;
152
+ const payload = withJsonContract("agent.quote.result", bindRouteMeta("quote", requestPayload, {
153
+ status: "ok",
154
+ exchange,
155
+ testnet,
156
+ market,
157
+ side,
158
+ size,
159
+ quote: {
160
+ referencePrice: effective,
161
+ notionalUsd: effective * size,
162
+ spread,
163
+ bid: Number.isFinite(bid) ? bid : null,
164
+ ask: Number.isFinite(ask) ? ask : null,
165
+ },
166
+ payment: payment
167
+ ? {
168
+ id: payment.paymentId,
169
+ protocol: "x402",
170
+ }
171
+ : null,
172
+ generatedAt: Date.now(),
173
+ }));
174
+ return payload;
175
+ }
176
+ finally {
177
+ await adapter.disconnect();
178
+ }
179
+ }
180
+ async function handleExecute(req, options, requestPayload, traceId) {
181
+ const exchange = toExchange(options.defaultExchange, requestPayload.exchange);
182
+ const testnet = toTestnet(options.defaultTestnet, requestPayload.testnet);
183
+ const market = toMarket(requestPayload);
184
+ const side = toSide(requestPayload.side);
185
+ const size = toSize(requestPayload.size);
186
+ if (!testnet && !options.allowMainnetExecution) {
187
+ throw new Error("mainnet execution is disabled for agent gateway. set PERPS_AGENT_ALLOW_MAINNET=1 to enable");
188
+ }
189
+ const idempotencyKey = requestPayload.idempotencyKey?.trim();
190
+ if (!idempotencyKey) {
191
+ throw new Error("idempotencyKey is required");
192
+ }
193
+ const requirePaidExecution = requestPayload.requirePayment !== false;
194
+ const payment = requirePaidExecution ? requirePayment(req) : undefined;
195
+ const config = loadConfig(testnet, exchange);
196
+ const credentials = getExchangeCredentials(config, exchange, {
197
+ requireTrading: true,
198
+ });
199
+ const adapter = getExchangeAdapterById(exchange);
200
+ await adapter.connect({
201
+ testnet: config.testnet,
202
+ rpcUrl: credentials.fullnodeUrl,
203
+ wsUrl: credentials.wsUrl,
204
+ credentials,
205
+ });
206
+ try {
207
+ const riskPolicy = new RiskPolicyMiddleware(config, exchange);
208
+ const confidence = Number.isFinite(requestPayload.confidence) ? requestPayload.confidence : 1;
209
+ const riskDecision = await riskPolicy.evaluateOrder(adapter, {
210
+ market,
211
+ side,
212
+ requestedSizeBase: size,
213
+ confidence,
214
+ reason: "agent-gateway:execute",
215
+ }, traceId);
216
+ if (!riskDecision.allowed || riskDecision.adjustedSizeBase <= 0) {
217
+ throw new Error(riskDecision.reason ?? "order blocked by risk policy");
218
+ }
219
+ const finalSize = riskDecision.adjustedSizeBase;
220
+ const journaled = await runWithExecutionJournal({
221
+ idempotencyKey,
222
+ traceId,
223
+ metadata: {
224
+ command: "agent.execute.market",
225
+ exchange,
226
+ testnet: config.testnet,
227
+ market,
228
+ side,
229
+ orderType: "market",
230
+ },
231
+ request: {
232
+ exchange,
233
+ testnet: config.testnet,
234
+ market,
235
+ side,
236
+ requestedSize: size,
237
+ finalSize,
238
+ confidence,
239
+ reduceOnly: requestPayload.reduceOnly ?? false,
240
+ paymentId: payment?.paymentId ?? null,
241
+ },
242
+ execute: async () => {
243
+ const execution = await executeOrderWithSafety(adapter, {
244
+ market,
245
+ side,
246
+ type: "market",
247
+ size: finalSize.toFixed(8),
248
+ reduceOnly: requestPayload.reduceOnly,
249
+ }, {
250
+ closeThenFlip: true,
251
+ spreadAwarePricing: true,
252
+ spreadOffset: config.executionSafety.spreadOffset,
253
+ });
254
+ return {
255
+ order: execution.order,
256
+ safety: execution,
257
+ risk: {
258
+ requestedSize: size,
259
+ adjustedSize: finalSize,
260
+ adjustedNotionalUsd: riskDecision.adjustedSizeUsd,
261
+ referencePrice: riskDecision.referencePriceUsd,
262
+ },
263
+ };
264
+ },
265
+ });
266
+ const resultPayload = withJsonContract("agent.execute.result", bindRouteMeta("execute", requestPayload, {
267
+ status: journaled.replayed ? "replayed" : "executed",
268
+ exchange,
269
+ testnet: config.testnet,
270
+ market,
271
+ idempotency: {
272
+ key: journaled.idempotencyKey,
273
+ replayed: journaled.replayed,
274
+ autoGenerated: journaled.autoGeneratedKey,
275
+ journalId: journaled.journalId,
276
+ },
277
+ payment: payment
278
+ ? {
279
+ id: payment.paymentId,
280
+ protocol: "x402",
281
+ }
282
+ : null,
283
+ result: journaled.result,
284
+ generatedAt: Date.now(),
285
+ }));
286
+ appendAgentAuditEvent({
287
+ action: "execute",
288
+ status: "ok",
289
+ exchange,
290
+ market,
291
+ idempotencyKey: journaled.idempotencyKey,
292
+ paymentId: payment?.paymentId,
293
+ requestHash: resultPayload.data.contract.signature.requestHash,
294
+ traceId,
295
+ details: {
296
+ replayed: journaled.replayed,
297
+ testnet: config.testnet,
298
+ },
299
+ });
300
+ return resultPayload;
301
+ }
302
+ finally {
303
+ await adapter.disconnect();
304
+ }
305
+ }
306
+ async function handleAcpIntent(req, options, requestPayload, traceId) {
307
+ const role = requestPayload.role?.trim().toLowerCase();
308
+ const action = requestPayload.action?.trim().toLowerCase();
309
+ const correlationId = requestPayload.correlationId?.trim() || `acp-${Date.now()}`;
310
+ if (!role) {
311
+ throw new Error("role is required");
312
+ }
313
+ if (!action) {
314
+ throw new Error("action is required");
315
+ }
316
+ // ACP flows are treated as paid orchestration by default.
317
+ const payment = requirePayment(req);
318
+ let result;
319
+ if (action === "quote" || action === "price_quote") {
320
+ result = await handleQuote(req, options, {
321
+ ...requestPayload.payload,
322
+ premium: false,
323
+ });
324
+ }
325
+ else if (action === "execute" || action === "trade_execute") {
326
+ result = await handleExecute(req, options, requestPayload.payload, traceId);
327
+ }
328
+ else {
329
+ throw new Error(`unsupported ACP action: ${action}`);
330
+ }
331
+ const payload = withJsonContract("agent.acp.intent.result", bindRouteMeta("acp.intent", requestPayload, {
332
+ status: "accepted",
333
+ correlationId,
334
+ role,
335
+ action,
336
+ payment: {
337
+ id: payment.paymentId,
338
+ protocol: "x402",
339
+ },
340
+ ackAt: Date.now(),
341
+ result,
342
+ }));
343
+ appendAgentAuditEvent({
344
+ action: "acp.intent",
345
+ status: "ok",
346
+ traceId,
347
+ details: {
348
+ role,
349
+ action,
350
+ correlationId,
351
+ paymentId: payment.paymentId,
352
+ },
353
+ });
354
+ return payload;
355
+ }
356
+ function toErrorPayload(route, requestPayload, err) {
357
+ const message = err instanceof Error ? err.message : String(err);
358
+ const requirement = err.requirement;
359
+ if (message === "missing_payment_proof" || message === "invalid_payment_proof") {
360
+ return {
361
+ status: 402,
362
+ body: withJsonContract("agent.payment.required", bindRouteMeta(route, requestPayload, {
363
+ status: "payment_required",
364
+ error: message,
365
+ requirement: requirement ?? getX402Requirement(),
366
+ })),
367
+ };
368
+ }
369
+ const exitCode = inferExitCode(err);
370
+ return {
371
+ status: 400,
372
+ body: withJsonContract("agent.error", bindRouteMeta(route, requestPayload, {
373
+ status: "error",
374
+ error: {
375
+ message,
376
+ exitCode,
377
+ },
378
+ })),
379
+ };
380
+ }
381
+ export function createAgentGateway(options) {
382
+ const startedAt = Date.now();
383
+ const server = createServer(async (req, res) => {
384
+ const routeStart = Date.now();
385
+ const method = req.method ?? "GET";
386
+ const url = req.url ?? "/";
387
+ let parsedBody = {};
388
+ const traceId = generateTraceId();
389
+ res.setHeader("X-Trace-Id", traceId);
390
+ const finalize = (route, ok, exchange = "system") => {
391
+ recordAgentRouteMetrics(route, ok, Date.now() - routeStart, exchange);
392
+ };
393
+ try {
394
+ if (method === "GET" && url === "/status") {
395
+ const payload = withJsonContract("agent.status", {
396
+ status: "ok",
397
+ startedAt,
398
+ uptimeMs: Date.now() - startedAt,
399
+ defaultExchange: options.defaultExchange,
400
+ defaultTestnet: options.defaultTestnet,
401
+ allowMainnetExecution: options.allowMainnetExecution,
402
+ });
403
+ writeJson(res, 200, payload);
404
+ finalize("status", true, "system");
405
+ return;
406
+ }
407
+ if (method === "GET" && url === "/metrics") {
408
+ writeJson(res, 200, withJsonContract("agent.metrics", getAgentMetricsSnapshot()));
409
+ finalize("metrics", true, "system");
410
+ return;
411
+ }
412
+ if (method === "GET" && url === "/reputation") {
413
+ const telemetry = getTelemetrySnapshot();
414
+ writeJson(res, 200, withJsonContract("agent.reputation", {
415
+ ...getAgentReputationSummary(),
416
+ telemetry: summarizeFailureRates(telemetry),
417
+ }));
418
+ finalize("reputation", true, "system");
419
+ return;
420
+ }
421
+ if (method === "GET" && url === "/capabilities") {
422
+ const payload = withJsonContract("agent.capabilities", {
423
+ agent: "perps-agent-gateway",
424
+ schemaVersion: 1,
425
+ defaults: {
426
+ exchange: options.defaultExchange,
427
+ testnet: options.defaultTestnet,
428
+ },
429
+ exchanges: getAvailableExchanges(),
430
+ endpoints: [
431
+ { method: "GET", path: "/capabilities", scope: "public" },
432
+ { method: "GET", path: "/context", scope: "authenticated" },
433
+ { method: "POST", path: "/quote", scope: "public|paid" },
434
+ { method: "POST", path: "/execute", scope: "paid" },
435
+ { method: "POST", path: "/acp/intent", scope: "paid|orchestrated" },
436
+ { method: "POST", path: "/signal", scope: "authenticated" },
437
+ { method: "GET", path: "/tools", scope: "public" },
438
+ { method: "GET", path: "/status", scope: "public" },
439
+ { method: "GET", path: "/metrics", scope: "operator" },
440
+ { method: "GET", path: "/reputation", scope: "public" },
441
+ ],
442
+ x402: getX402Requirement(),
443
+ observability: {
444
+ metricsDimensions: ["route", "exchange"],
445
+ dailyReportCommand: "corepack pnpm agent:telemetry:daily -- --json",
446
+ },
447
+ acp: {
448
+ enabled: true,
449
+ actions: ["quote", "price_quote", "execute", "trade_execute"],
450
+ roles: ["buyer", "seller", "evaluator", "facilitator"],
451
+ },
452
+ });
453
+ writeJson(res, 200, payload);
454
+ finalize("capabilities", true, "system");
455
+ return;
456
+ }
457
+ if (method === "GET" && url === "/tools") {
458
+ const tools = getToolDefinitions();
459
+ const payload = withJsonContract("agent.tools", { tools });
460
+ writeJson(res, 200, payload);
461
+ finalize("tools", true, "system");
462
+ return;
463
+ }
464
+ if (method === "GET" && url === "/context") {
465
+ const exchangeId = options.defaultExchange ?? "hyperliquid";
466
+ try {
467
+ const exchange = toExchange(options.defaultExchange, exchangeId);
468
+ const config = loadConfig(toTestnet(options.defaultTestnet), exchange);
469
+ const credentials = getExchangeCredentials(config, exchange, {
470
+ requireReadAccess: true,
471
+ });
472
+ const adapter = getExchangeAdapterById(exchange);
473
+ await adapter.connect({
474
+ testnet: config.testnet,
475
+ rpcUrl: credentials.fullnodeUrl,
476
+ wsUrl: credentials.wsUrl,
477
+ credentials,
478
+ });
479
+ try {
480
+ const context = await buildStateContext(adapter, exchange, config);
481
+ const signed = signAgentResponse({}, context);
482
+ const payload = withJsonContract("agent.context", {
483
+ ...context,
484
+ contract: {
485
+ route: "context",
486
+ signature: signed,
487
+ },
488
+ });
489
+ writeJson(res, 200, payload);
490
+ finalize("context", true, exchange);
491
+ }
492
+ finally {
493
+ await adapter.disconnect();
494
+ }
495
+ }
496
+ catch (ctxErr) {
497
+ const failure = toErrorPayload("context", {}, ctxErr);
498
+ writeJson(res, failure.status, failure.body);
499
+ finalize("context", false, exchangeId);
500
+ }
501
+ return;
502
+ }
503
+ if (method === "POST" && url === "/quote") {
504
+ parsedBody = await readBody(req);
505
+ const body = parsedBody;
506
+ const payload = await handleQuote(req, options, body);
507
+ writeJson(res, 200, payload);
508
+ finalize("quote", true, inferExchangeForRoute("quote", body, options.defaultExchange));
509
+ return;
510
+ }
511
+ if (method === "POST" && url === "/execute") {
512
+ parsedBody = await readBody(req);
513
+ const body = parsedBody;
514
+ const payload = await handleExecute(req, options, body, traceId);
515
+ writeJson(res, 200, payload);
516
+ finalize("execute", true, inferExchangeForRoute("execute", body, options.defaultExchange));
517
+ return;
518
+ }
519
+ if (method === "POST" && url === "/acp/intent") {
520
+ parsedBody = await readBody(req);
521
+ const body = parsedBody;
522
+ const payload = await handleAcpIntent(req, options, body, traceId);
523
+ writeJson(res, 200, payload);
524
+ finalize("acp.intent", true, inferExchangeForRoute("acp.intent", body, options.defaultExchange));
525
+ return;
526
+ }
527
+ if (method === "POST" && url === "/signal") {
528
+ parsedBody = await readBody(req);
529
+ const body = parsedBody;
530
+ const exchange = body.exchange;
531
+ const signalValue = body.signal;
532
+ if (!exchange || typeof exchange !== "string") {
533
+ throw new Error("exchange is required");
534
+ }
535
+ if (signalValue !== "up" && signalValue !== "down") {
536
+ throw new Error('signal must be "up" or "down"');
537
+ }
538
+ const id = addSignal({
539
+ traceId: typeof body.traceId === "string" ? body.traceId : undefined,
540
+ exchange,
541
+ market: typeof body.market === "string" ? body.market : undefined,
542
+ strategy: typeof body.strategy === "string" ? body.strategy : undefined,
543
+ signal: signalValue,
544
+ reason: typeof body.reason === "string" ? body.reason : undefined,
545
+ pnlUsd: typeof body.pnlUsd === "number" ? body.pnlUsd : undefined,
546
+ metadata: typeof body.metadata === "object" && body.metadata !== null
547
+ ? body.metadata
548
+ : undefined,
549
+ });
550
+ writeJson(res, 200, withJsonContract("agent.signal.recorded", { id }));
551
+ finalize("signal", true, exchange);
552
+ return;
553
+ }
554
+ writeJson(res, 404, withJsonContract("agent.not_found", {
555
+ status: "not_found",
556
+ method,
557
+ path: url,
558
+ }));
559
+ finalize("not_found", false, "system");
560
+ }
561
+ catch (err) {
562
+ const routeName = url === "/quote" ? "quote" :
563
+ url === "/execute" ? "execute" :
564
+ url === "/acp/intent" ? "acp.intent" :
565
+ url === "/signal" ? "signal" : "unknown";
566
+ const requestPayload = method === "POST" ? parsedBody : {};
567
+ const failure = toErrorPayload(routeName, requestPayload, err);
568
+ writeJson(res, failure.status, failure.body);
569
+ appendAgentAuditEvent({
570
+ action: routeName,
571
+ status: "error",
572
+ traceId,
573
+ details: {
574
+ message: err instanceof Error ? err.message : String(err),
575
+ method,
576
+ path: url,
577
+ },
578
+ });
579
+ finalize(routeName, false, inferExchangeForRoute(routeName, requestPayload, options.defaultExchange));
580
+ }
581
+ });
582
+ return {
583
+ server,
584
+ start: () => new Promise((resolve, reject) => {
585
+ server.once("error", reject);
586
+ server.listen(options.port, options.host, () => resolve());
587
+ }),
588
+ stop: () => new Promise((resolve, reject) => {
589
+ server.close((err) => {
590
+ if (err) {
591
+ reject(err);
592
+ return;
593
+ }
594
+ resolve();
595
+ });
596
+ }),
597
+ };
598
+ }
@@ -0,0 +1,33 @@
1
+ interface RouteMetrics {
2
+ total: number;
3
+ success: number;
4
+ failure: number;
5
+ latencyMsTotal: number;
6
+ }
7
+ export interface AgentMetricsSnapshot {
8
+ startedAt: number;
9
+ routes: Record<string, RouteMetrics>;
10
+ exchanges: Record<string, RouteMetrics>;
11
+ }
12
+ export declare function resetAgentMetrics(): void;
13
+ export declare function recordAgentRouteMetrics(route: string, ok: boolean, latencyMs: number, exchange?: string): void;
14
+ export declare function getAgentMetricsSnapshot(): AgentMetricsSnapshot;
15
+ export declare function getAgentReputationSummary(): {
16
+ uptimeMs: number;
17
+ successRate: number;
18
+ totalRequests: number;
19
+ routes: Array<{
20
+ route: string;
21
+ total: number;
22
+ successRate: number;
23
+ p50LatencyEstimateMs: number;
24
+ avgLatencyMs: number;
25
+ }>;
26
+ exchanges: Array<{
27
+ exchange: string;
28
+ total: number;
29
+ successRate: number;
30
+ avgLatencyMs: number;
31
+ }>;
32
+ };
33
+ export {};