@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.
Files changed (95) hide show
  1. package/README.md +13 -0
  2. package/dist/index.d.ts +35 -100
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +33 -930
  5. package/dist/index.js.map +1 -1
  6. package/dist/plugin.d.ts +110 -0
  7. package/dist/plugin.d.ts.map +1 -0
  8. package/dist/plugin.js +1055 -0
  9. package/dist/plugin.js.map +1 -0
  10. package/dist/runtime/__tests__/helpers/runtime-gateway-harness.d.ts +3 -3
  11. package/dist/runtime/audit-query.types.d.ts +113 -0
  12. package/dist/runtime/audit-query.types.d.ts.map +1 -0
  13. package/dist/runtime/audit-query.types.js +35 -0
  14. package/dist/runtime/audit-query.types.js.map +1 -0
  15. package/dist/runtime/auto-update/coordinator.d.ts +12 -8
  16. package/dist/runtime/auto-update/coordinator.d.ts.map +1 -1
  17. package/dist/runtime/auto-update/coordinator.js +18 -17
  18. package/dist/runtime/auto-update/coordinator.js.map +1 -1
  19. package/dist/runtime/auto-update/gateway-restart.d.ts +9 -2
  20. package/dist/runtime/auto-update/gateway-restart.d.ts.map +1 -1
  21. package/dist/runtime/auto-update/gateway-restart.js +23 -13
  22. package/dist/runtime/auto-update/gateway-restart.js.map +1 -1
  23. package/dist/runtime/create-actions.d.ts.map +1 -1
  24. package/dist/runtime/create-actions.js +13 -2
  25. package/dist/runtime/create-actions.js.map +1 -1
  26. package/dist/runtime/run.d.ts +52 -1
  27. package/dist/runtime/run.d.ts.map +1 -1
  28. package/dist/runtime/run.js +9 -0
  29. package/dist/runtime/run.js.map +1 -1
  30. package/dist/runtime/runtime.d.ts +56 -0
  31. package/dist/runtime/runtime.d.ts.map +1 -1
  32. package/dist/runtime/runtime.js +169 -1
  33. package/dist/runtime/runtime.js.map +1 -1
  34. package/dist/runtime-api/config.d.ts +47 -0
  35. package/dist/runtime-api/config.d.ts.map +1 -0
  36. package/dist/runtime-api/config.js +74 -0
  37. package/dist/runtime-api/config.js.map +1 -0
  38. package/dist/runtime-api/handlers/audit.d.ts +78 -0
  39. package/dist/runtime-api/handlers/audit.d.ts.map +1 -0
  40. package/dist/runtime-api/handlers/audit.js +65 -0
  41. package/dist/runtime-api/handlers/audit.js.map +1 -0
  42. package/dist/runtime-api/handlers/group-by-scanner.d.ts +43 -0
  43. package/dist/runtime-api/handlers/group-by-scanner.d.ts.map +1 -0
  44. package/dist/runtime-api/handlers/group-by-scanner.js +43 -0
  45. package/dist/runtime-api/handlers/group-by-scanner.js.map +1 -0
  46. package/dist/runtime-api/handlers/ingest-signals.d.ts +54 -0
  47. package/dist/runtime-api/handlers/ingest-signals.d.ts.map +1 -0
  48. package/dist/runtime-api/handlers/ingest-signals.js +141 -0
  49. package/dist/runtime-api/handlers/ingest-signals.js.map +1 -0
  50. package/dist/runtime-api/handlers/lookup.d.ts +36 -0
  51. package/dist/runtime-api/handlers/lookup.d.ts.map +1 -0
  52. package/dist/runtime-api/handlers/lookup.js +31 -0
  53. package/dist/runtime-api/handlers/lookup.js.map +1 -0
  54. package/dist/runtime-api/handlers/types.d.ts +85 -0
  55. package/dist/runtime-api/handlers/types.d.ts.map +1 -0
  56. package/dist/runtime-api/handlers/types.js +8 -0
  57. package/dist/runtime-api/handlers/types.js.map +1 -0
  58. package/dist/runtime-api/lifecycle.d.ts +98 -0
  59. package/dist/runtime-api/lifecycle.d.ts.map +1 -0
  60. package/dist/runtime-api/lifecycle.js +137 -0
  61. package/dist/runtime-api/lifecycle.js.map +1 -0
  62. package/dist/runtime-api/readiness.d.ts +37 -0
  63. package/dist/runtime-api/readiness.d.ts.map +1 -0
  64. package/dist/runtime-api/readiness.js +35 -0
  65. package/dist/runtime-api/readiness.js.map +1 -0
  66. package/dist/runtime-api/routes/audit.d.ts +40 -0
  67. package/dist/runtime-api/routes/audit.d.ts.map +1 -0
  68. package/dist/runtime-api/routes/audit.js +90 -0
  69. package/dist/runtime-api/routes/audit.js.map +1 -0
  70. package/dist/runtime-api/routes/audit.schema.d.ts +75 -0
  71. package/dist/runtime-api/routes/audit.schema.d.ts.map +1 -0
  72. package/dist/runtime-api/routes/audit.schema.js +78 -0
  73. package/dist/runtime-api/routes/audit.schema.js.map +1 -0
  74. package/dist/runtime-api/routes/health.d.ts +53 -0
  75. package/dist/runtime-api/routes/health.d.ts.map +1 -0
  76. package/dist/runtime-api/routes/health.js +48 -0
  77. package/dist/runtime-api/routes/health.js.map +1 -0
  78. package/dist/runtime-api/routes/signals.d.ts +50 -0
  79. package/dist/runtime-api/routes/signals.d.ts.map +1 -0
  80. package/dist/runtime-api/routes/signals.js +90 -0
  81. package/dist/runtime-api/routes/signals.js.map +1 -0
  82. package/dist/runtime-api/routes/signals.schema.d.ts +94 -0
  83. package/dist/runtime-api/routes/signals.schema.d.ts.map +1 -0
  84. package/dist/runtime-api/routes/signals.schema.js +58 -0
  85. package/dist/runtime-api/routes/signals.schema.js.map +1 -0
  86. package/dist/runtime-api/server.d.ts +79 -0
  87. package/dist/runtime-api/server.d.ts.map +1 -0
  88. package/dist/runtime-api/server.js +109 -0
  89. package/dist/runtime-api/server.js.map +1 -0
  90. package/dist/senpi/client.d.ts +92 -2
  91. package/dist/senpi/client.d.ts.map +1 -1
  92. package/dist/senpi/client.js +181 -8
  93. package/dist/senpi/client.js.map +1 -1
  94. package/openclaw.plugin.json +24 -0
  95. package/package.json +6 -2
@@ -0,0 +1,74 @@
1
+ /**
2
+ * TypeBox schema and loader for the `api` block in the openclaw plugin config.
3
+ *
4
+ * The `api` block is an optional top-level property inside `openclaw.json`
5
+ * `plugins[*].config`. If the operator omits it entirely — or omits individual
6
+ * fields — this module fills in the documented defaults so the rest of the
7
+ * runtime-api code can rely on a fully-typed, always-present value.
8
+ */
9
+ import { Type } from "@sinclair/typebox";
10
+ import { Value } from "@sinclair/typebox/value";
11
+ /** TypeBox schema for the `api` config block. Mirrors openclaw.plugin.json. */
12
+ export const ApiConfigSchema = Type.Object({
13
+ /**
14
+ * Bind interface for the HTTP server inside the openclaw container.
15
+ * Default `127.0.0.1` — container loopback only (most secure).
16
+ * Set to `0.0.0.0` to enable Docker port mapping for Postman testing.
17
+ * A boot-time warning is logged when this is not `127.0.0.1`.
18
+ */
19
+ host: Type.String({ default: "127.0.0.1" }),
20
+ /**
21
+ * TCP port the HTTP server listens on.
22
+ * Default `8787`. Must be in [1, 65535].
23
+ */
24
+ port: Type.Integer({ minimum: 1, maximum: 65535, default: 8787 }),
25
+ /**
26
+ * Maximum number of items allowed in a single `POST /signals` request body.
27
+ * Conservative default of `10` — operators raise it deliberately when
28
+ * broadcasting to many wallets at once.
29
+ */
30
+ maxItemsPerSignalsRequest: Type.Integer({
31
+ minimum: 1,
32
+ maximum: 1000,
33
+ default: 10,
34
+ }),
35
+ }, { additionalProperties: false });
36
+ /** Default values applied when the operator omits the `api` block or individual fields. */
37
+ const API_CONFIG_DEFAULTS = {
38
+ host: "127.0.0.1",
39
+ port: 8787,
40
+ maxItemsPerSignalsRequest: 10,
41
+ };
42
+ /**
43
+ * Load and validate the `api` config block from the full plugin config object.
44
+ *
45
+ * Merges operator-supplied values over the defaults, then validates the result
46
+ * against {@link ApiConfigSchema}. Throws if any field is out of range or the
47
+ * wrong type (e.g. a port value of 99999).
48
+ *
49
+ * @param pluginConfig - The entire plugin config object as passed by openclaw to
50
+ * `register(api)`. May be `undefined`, `null`, or a plain object. The function
51
+ * reads only the `api` sub-key; all other keys are ignored.
52
+ * @returns A fully-typed {@link ApiConfig} with all defaults applied.
53
+ * @throws {Error} If the merged config fails TypeBox validation (e.g. port out of range).
54
+ */
55
+ export function loadApiConfig(pluginConfig) {
56
+ const raw = pluginConfig !== null &&
57
+ typeof pluginConfig === "object" &&
58
+ "api" in pluginConfig
59
+ ? pluginConfig["api"]
60
+ : undefined;
61
+ const merged = {
62
+ ...API_CONFIG_DEFAULTS,
63
+ ...(raw !== null && typeof raw === "object" ? raw : {}),
64
+ };
65
+ if (!Value.Check(ApiConfigSchema, merged)) {
66
+ const firstError = Value.Errors(ApiConfigSchema, merged).First();
67
+ const detail = firstError
68
+ ? `${firstError.path}: ${firstError.message}`
69
+ : "unknown validation error";
70
+ throw new Error(`Invalid runtime api config — ${detail}`);
71
+ }
72
+ return merged;
73
+ }
74
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/runtime-api/config.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,IAAI,EAAe,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAC;AAEhD,+EAA+E;AAC/E,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,CAAC,MAAM,CACxC;IACE;;;;;OAKG;IACH,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC;IAE3C;;;OAGG;IACH,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAEjE;;;;OAIG;IACH,yBAAyB,EAAE,IAAI,CAAC,OAAO,CAAC;QACtC,OAAO,EAAE,CAAC;QACV,OAAO,EAAE,IAAI;QACb,OAAO,EAAE,EAAE;KACZ,CAAC;CACH,EACD,EAAE,oBAAoB,EAAE,KAAK,EAAE,CAChC,CAAC;AAKF,2FAA2F;AAC3F,MAAM,mBAAmB,GAAc;IACrC,IAAI,EAAE,WAAW;IACjB,IAAI,EAAE,IAAI;IACV,yBAAyB,EAAE,EAAE;CAC9B,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,aAAa,CAAC,YAAqB;IACjD,MAAM,GAAG,GACP,YAAY,KAAK,IAAI;QACrB,OAAO,YAAY,KAAK,QAAQ;QAChC,KAAK,IAAK,YAAwC;QAChD,CAAC,CAAE,YAAwC,CAAC,KAAK,CAAC;QAClD,CAAC,CAAC,SAAS,CAAC;IAEhB,MAAM,MAAM,GAAc;QACxB,GAAG,mBAAmB;QACtB,GAAG,CAAC,GAAG,KAAK,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAE,GAA0B,CAAC,CAAC,CAAC,EAAE,CAAC;KAChF,CAAC;IAEF,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,eAAe,EAAE,MAAM,CAAC,EAAE,CAAC;QAC1C,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC,KAAK,EAAE,CAAC;QACjE,MAAM,MAAM,GAAG,UAAU;YACvB,CAAC,CAAC,GAAG,UAAU,CAAC,IAAI,KAAK,UAAU,CAAC,OAAO,EAAE;YAC7C,CAAC,CAAC,0BAA0B,CAAC;QAC/B,MAAM,IAAI,KAAK,CAAC,gCAAgC,MAAM,EAAE,CAAC,CAAC;IAC5D,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Shared audit handler for `GET /audit`.
3
+ *
4
+ * Delegates to `RuntimeHandle.auditQuery` and translates `AuditQueryError`
5
+ * codes into the HTTP-layer result shape. Called by the Fastify route (S4).
6
+ *
7
+ * The discriminator field is `success` (matching MCP and the rest of the
8
+ * runtime-api surface). The route layer translates a successful result
9
+ * into the wire shape `{ success: true, data: {...} }` and a failure into
10
+ * `{ success: false, error: { code, message } }` per the senpi-stack
11
+ * convention.
12
+ */
13
+ import type { AuditQueryArgs, AuditQueryResult } from "../../runtime/audit-query.types.js";
14
+ import type { RuntimeHandle } from "../../runtime/run.js";
15
+ import type { Logger } from "../../utils/logger.js";
16
+ /**
17
+ * Dependencies injected into {@link auditHandler}.
18
+ */
19
+ export interface AuditDeps {
20
+ /** Live runtime handles maintained by the plugin. */
21
+ runtimeHandles: Map<string, RuntimeHandle>;
22
+ /** Logger instance — unexpected errors logged at `error` level. */
23
+ logger: Logger;
24
+ }
25
+ /**
26
+ * Result returned by {@link auditHandler}.
27
+ *
28
+ * Discriminated on `success`:
29
+ * - `true` → audit query succeeded; `address`, `strategyId`, and `result` are present.
30
+ * - `false` → failure; `code` is one of `NOT_FOUND | UPSTREAM | ABORTED`.
31
+ *
32
+ * `NOT_FOUND` — no running runtime for the given address, or `listStrategies`
33
+ * returned 0 matches (wallet not owned by the API-key user).
34
+ * `UPSTREAM` — MCP returned an error (network, auth, schema mismatch, runtime
35
+ * not started).
36
+ * `ABORTED` — caller cancelled via `AbortSignal` before MCP responded.
37
+ */
38
+ export type AuditHandlerResult = {
39
+ success: true;
40
+ /** Lowercased wallet address. */
41
+ address: string;
42
+ /**
43
+ * Strategy UUID resolved from the wallet by `listStrategies`.
44
+ * May be `null` on the first successful call in the unlikely window
45
+ * between `auditQuery` resolving and `getStrategyId()` being read —
46
+ * in practice the cache is populated synchronously before the
47
+ * Promise resolves.
48
+ */
49
+ strategyId: string | null;
50
+ /** Pass-through `audit_query` response payload. */
51
+ result: AuditQueryResult;
52
+ } | {
53
+ success: false;
54
+ code: "NOT_FOUND" | "UPSTREAM" | "ABORTED";
55
+ message: string;
56
+ };
57
+ /**
58
+ * Execute an audit query for one wallet via its runtime handle.
59
+ *
60
+ * Steps:
61
+ * 1. Resolve the runtime handle by `address` (case-insensitive lookup).
62
+ * Returns `NOT_FOUND` on miss.
63
+ * 2. Call `handle.auditQuery(args, { signal })`.
64
+ * 3. Read `handle.getStrategyId()` after a successful call — the id is cached
65
+ * on the runtime instance from step 2's `listStrategies` resolution.
66
+ * 4. On `AuditQueryError`: map `code` straight through to the result.
67
+ * 5. On any other throw: log + return `UPSTREAM`.
68
+ *
69
+ * @param address Wallet address to audit (normalised to lowercase internally).
70
+ * @param args Audit query filters forwarded to `RuntimeHandle.auditQuery`.
71
+ * @param deps Injected dependencies.
72
+ * @param signal Optional `AbortSignal` tied to the Fastify request lifetime.
73
+ * Threads through to the underlying MCP call so the runtime
74
+ * can cancel in-flight work when the HTTP client disconnects.
75
+ * @returns A resolved `AuditHandlerResult` — never rejects.
76
+ */
77
+ export declare function auditHandler(address: string, args: AuditQueryArgs, deps: AuditDeps, signal?: AbortSignal): Promise<AuditHandlerResult>;
78
+ //# sourceMappingURL=audit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../../src/runtime-api/handlers/audit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,KAAK,EACV,cAAc,EACd,gBAAgB,EACjB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAEpD;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,qDAAqD;IACrD,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IAC3C,mEAAmE;IACnE,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,kBAAkB,GAC1B;IACE,OAAO,EAAE,IAAI,CAAC;IACd,iCAAiC;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB;;;;;;OAMG;IACH,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,mDAAmD;IACnD,MAAM,EAAE,gBAAgB,CAAC;CAC1B,GACD;IACE,OAAO,EAAE,KAAK,CAAC;IACf,IAAI,EAAE,WAAW,GAAG,UAAU,GAAG,SAAS,CAAC;IAC3C,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEN;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,YAAY,CAChC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,cAAc,EACpB,IAAI,EAAE,SAAS,EACf,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,kBAAkB,CAAC,CAsC7B"}
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Shared audit handler for `GET /audit`.
3
+ *
4
+ * Delegates to `RuntimeHandle.auditQuery` and translates `AuditQueryError`
5
+ * codes into the HTTP-layer result shape. Called by the Fastify route (S4).
6
+ *
7
+ * The discriminator field is `success` (matching MCP and the rest of the
8
+ * runtime-api surface). The route layer translates a successful result
9
+ * into the wire shape `{ success: true, data: {...} }` and a failure into
10
+ * `{ success: false, error: { code, message } }` per the senpi-stack
11
+ * convention.
12
+ */
13
+ import { findRuntimeHandleByAddress } from "./lookup.js";
14
+ import { AuditQueryError } from "../../runtime/audit-query.types.js";
15
+ /**
16
+ * Execute an audit query for one wallet via its runtime handle.
17
+ *
18
+ * Steps:
19
+ * 1. Resolve the runtime handle by `address` (case-insensitive lookup).
20
+ * Returns `NOT_FOUND` on miss.
21
+ * 2. Call `handle.auditQuery(args, { signal })`.
22
+ * 3. Read `handle.getStrategyId()` after a successful call — the id is cached
23
+ * on the runtime instance from step 2's `listStrategies` resolution.
24
+ * 4. On `AuditQueryError`: map `code` straight through to the result.
25
+ * 5. On any other throw: log + return `UPSTREAM`.
26
+ *
27
+ * @param address Wallet address to audit (normalised to lowercase internally).
28
+ * @param args Audit query filters forwarded to `RuntimeHandle.auditQuery`.
29
+ * @param deps Injected dependencies.
30
+ * @param signal Optional `AbortSignal` tied to the Fastify request lifetime.
31
+ * Threads through to the underlying MCP call so the runtime
32
+ * can cancel in-flight work when the HTTP client disconnects.
33
+ * @returns A resolved `AuditHandlerResult` — never rejects.
34
+ */
35
+ export async function auditHandler(address, args, deps, signal) {
36
+ const normalizedAddress = address.toLowerCase();
37
+ const lookup = findRuntimeHandleByAddress(deps.runtimeHandles, normalizedAddress);
38
+ if (!lookup.found) {
39
+ return { success: false, code: "NOT_FOUND", message: lookup.message };
40
+ }
41
+ const { handle } = lookup;
42
+ try {
43
+ const result = await handle.auditQuery(args, { signal });
44
+ // getStrategyId() is synchronous — the cache is populated by auditQuery
45
+ // before the awaited Promise resolves, so this is always set on success.
46
+ const strategyId = handle.getStrategyId();
47
+ return {
48
+ success: true,
49
+ address: normalizedAddress,
50
+ strategyId,
51
+ result,
52
+ };
53
+ }
54
+ catch (err) {
55
+ if (err instanceof AuditQueryError) {
56
+ return { success: false, code: err.code, message: err.message };
57
+ }
58
+ const message = err instanceof Error
59
+ ? err.message
60
+ : `Unexpected audit error: ${String(err)}`;
61
+ deps.logger.error("[senpi-core][runtime-api][audit] Unexpected error during audit query", { address: normalizedAddress, err });
62
+ return { success: false, code: "UPSTREAM", message };
63
+ }
64
+ }
65
+ //# sourceMappingURL=audit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.js","sourceRoot":"","sources":["../../../src/runtime-api/handlers/audit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,0BAA0B,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,oCAAoC,CAAC;AAqDrE;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,OAAe,EACf,IAAoB,EACpB,IAAe,EACf,MAAoB;IAEpB,MAAM,iBAAiB,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAEhD,MAAM,MAAM,GAAG,0BAA0B,CAAC,IAAI,CAAC,cAAc,EAAE,iBAAiB,CAAC,CAAC;IAClF,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QAClB,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC;IACxE,CAAC;IAED,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC;IAE1B,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;QAEzD,wEAAwE;QACxE,yEAAyE;QACzE,MAAM,UAAU,GAAG,MAAM,CAAC,aAAa,EAAE,CAAC;QAE1C,OAAO;YACL,OAAO,EAAE,IAAI;YACb,OAAO,EAAE,iBAAiB;YAC1B,UAAU;YACV,MAAM;SACP,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,eAAe,EAAE,CAAC;YACnC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC;QAClE,CAAC;QAED,MAAM,OAAO,GACX,GAAG,YAAY,KAAK;YAClB,CAAC,CAAC,GAAG,CAAC,OAAO;YACb,CAAC,CAAC,2BAA2B,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;QAC/C,IAAI,CAAC,MAAM,CAAC,KAAK,CACf,sEAAsE,EACtE,EAAE,OAAO,EAAE,iBAAiB,EAAE,GAAG,EAAE,CACpC,CAAC;QACF,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC;IACvD,CAAC;AACH,CAAC"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Pure helper that groups a flat `SignalItem[]` by `(address, scanner)` so
3
+ * the route handler can fan-out groups in parallel while keeping items within
4
+ * a group sequential — dodging the per-(address, scanner) ingest lock held by
5
+ * `ScannerEngine` during async ingest
6
+ * (`src/scanners/engine/engine.ts:282-287`).
7
+ */
8
+ import type { SignalItem } from "./types.js";
9
+ /**
10
+ * A `SignalItem` paired with its original zero-based index in the input array.
11
+ * The index is used to re-flatten grouped results back into the caller's
12
+ * requested order after `Promise.all` group settlement.
13
+ */
14
+ export interface IndexedSignalItem {
15
+ /** Zero-based position in the original input array. */
16
+ index: number;
17
+ /** The original signal item. */
18
+ item: SignalItem;
19
+ }
20
+ /**
21
+ * A sequence of items that share the same `(lowercased address, scanner)` key
22
+ * and must be processed sequentially to avoid the per-(address, scanner) lock.
23
+ */
24
+ export type SignalGroup = IndexedSignalItem[];
25
+ /**
26
+ * Group signal items by `(lowercased address, scanner)`.
27
+ *
28
+ * Items within a group must be processed sequentially — the runtime's scanner
29
+ * engine holds a per-(address, scanner) mutex during the full async ingest
30
+ * chain and throws `UNAVAILABLE` on concurrent calls for the same key.
31
+ *
32
+ * Groups with different keys (different address or different scanner) can be
33
+ * processed in parallel — they do not share the lock.
34
+ *
35
+ * Each item carries its original index so the route handler can sort results
36
+ * back to input order after `Promise.all` resolves.
37
+ *
38
+ * @param items Flat array of signal items as received in the request body.
39
+ * @returns Array of groups; order of groups is not guaranteed, but item
40
+ * order within each group is preserved.
41
+ */
42
+ export declare function groupByAddressScanner(items: SignalItem[]): SignalGroup[];
43
+ //# sourceMappingURL=group-by-scanner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"group-by-scanner.d.ts","sourceRoot":"","sources":["../../../src/runtime-api/handlers/group-by-scanner.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC,uDAAuD;IACvD,KAAK,EAAE,MAAM,CAAC;IACd,gCAAgC;IAChC,IAAI,EAAE,UAAU,CAAC;CAClB;AAED;;;GAGG;AACH,MAAM,MAAM,WAAW,GAAG,iBAAiB,EAAE,CAAC;AAE9C;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,WAAW,EAAE,CAkBxE"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Pure helper that groups a flat `SignalItem[]` by `(address, scanner)` so
3
+ * the route handler can fan-out groups in parallel while keeping items within
4
+ * a group sequential — dodging the per-(address, scanner) ingest lock held by
5
+ * `ScannerEngine` during async ingest
6
+ * (`src/scanners/engine/engine.ts:282-287`).
7
+ */
8
+ /**
9
+ * Group signal items by `(lowercased address, scanner)`.
10
+ *
11
+ * Items within a group must be processed sequentially — the runtime's scanner
12
+ * engine holds a per-(address, scanner) mutex during the full async ingest
13
+ * chain and throws `UNAVAILABLE` on concurrent calls for the same key.
14
+ *
15
+ * Groups with different keys (different address or different scanner) can be
16
+ * processed in parallel — they do not share the lock.
17
+ *
18
+ * Each item carries its original index so the route handler can sort results
19
+ * back to input order after `Promise.all` resolves.
20
+ *
21
+ * @param items Flat array of signal items as received in the request body.
22
+ * @returns Array of groups; order of groups is not guaranteed, but item
23
+ * order within each group is preserved.
24
+ */
25
+ export function groupByAddressScanner(items) {
26
+ const grouped = new Map();
27
+ for (let index = 0; index < items.length; index++) {
28
+ const item = items[index];
29
+ // item is always defined inside bounds; the cast avoids a superfluous
30
+ // non-null assertion in strict mode.
31
+ const safeItem = item;
32
+ const key = `${safeItem.address.toLowerCase()}::${safeItem.scanner}`;
33
+ const existing = grouped.get(key);
34
+ if (existing !== undefined) {
35
+ existing.push({ index, item: safeItem });
36
+ }
37
+ else {
38
+ grouped.set(key, [{ index, item: safeItem }]);
39
+ }
40
+ }
41
+ return Array.from(grouped.values());
42
+ }
43
+ //# sourceMappingURL=group-by-scanner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"group-by-scanner.js","sourceRoot":"","sources":["../../../src/runtime-api/handlers/group-by-scanner.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAsBH;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,qBAAqB,CAAC,KAAmB;IACvD,MAAM,OAAO,GAAG,IAAI,GAAG,EAAuB,CAAC;IAE/C,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;QAClD,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;QAC1B,sEAAsE;QACtE,qCAAqC;QACrC,MAAM,QAAQ,GAAG,IAAkB,CAAC;QACpC,MAAM,GAAG,GAAG,GAAG,QAAQ,CAAC,OAAO,CAAC,WAAW,EAAE,KAAK,QAAQ,CAAC,OAAO,EAAE,CAAC;QACrE,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAClC,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC3C,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC;QAChD,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;AACtC,CAAC"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Shared ingest handler for `POST /signals`.
3
+ *
4
+ * Called by the HTTP route (S4) and the gateway-method shim (S6). Contains
5
+ * all business logic for one ingest item — lookup, validation, execution, and
6
+ * error translation — so neither transport duplicates logic.
7
+ */
8
+ import type { SignalItem, SignalResult } from "./types.js";
9
+ import type { RuntimeHandle } from "../../runtime/run.js";
10
+ import type { Logger } from "../../utils/logger.js";
11
+ /**
12
+ * Dependencies injected into {@link ingestSignalsHandler}.
13
+ *
14
+ * Keeping deps explicit (rather than closing over module-level state) makes
15
+ * unit-testing straightforward and keeps the function pure with respect to
16
+ * side effects outside the deps object.
17
+ */
18
+ export interface IngestSignalsDeps {
19
+ /** Live runtime handles maintained by the plugin. */
20
+ runtimeHandles: Map<string, RuntimeHandle>;
21
+ /** Logger instance — errors logged at `error`, unexpected throws at `error`. */
22
+ logger: Logger;
23
+ /**
24
+ * Transport tag forwarded to future tracking events so the telemetry
25
+ * pipeline can distinguish HTTP-originated ingests from gateway-shim
26
+ * ingests on the migration dashboard.
27
+ */
28
+ transport: "http" | "gateway";
29
+ }
30
+ /**
31
+ * Ingest one signal item.
32
+ *
33
+ * Looks up the runtime by address, validates handler-level constraints, calls
34
+ * `RuntimeHandle.ingestExternalScannerData`, and translates all error shapes
35
+ * into the uniform `{ success: false, code, message }` discriminant. Never
36
+ * throws — all error paths return a `SignalResult`.
37
+ *
38
+ * Validation rules (beyond TypeBox):
39
+ * - At least one of `asset`, `data`, or `signals` must be present; an item
40
+ * with none of those fields has nothing to ingest → `INVALID_REQUEST`.
41
+ * - `data` and `signals` are mutually exclusive → `INVALID_REQUEST`.
42
+ * (The runtime also enforces this, but we check early to surface a clear
43
+ * message before the engine lock is acquired.)
44
+ *
45
+ * Per-(address, scanner) ingest lock is the engine's concern; callers should
46
+ * use {@link groupByAddressScanner} or nest multiple signals in `signals[]`
47
+ * rather than submitting concurrent top-level items for the same key.
48
+ *
49
+ * @param item A single element from the `POST /signals` request body.
50
+ * @param deps Injected dependencies.
51
+ * @returns A resolved `SignalResult` — never rejects.
52
+ */
53
+ export declare function ingestSignalsHandler(item: SignalItem, deps: IngestSignalsDeps): Promise<SignalResult>;
54
+ //# sourceMappingURL=ingest-signals.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ingest-signals.d.ts","sourceRoot":"","sources":["../../../src/runtime-api/handlers/ingest-signals.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAOH,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC3D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAEpD;;;;;;GAMG;AACH,MAAM,WAAW,iBAAiB;IAChC,qDAAqD;IACrD,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IAC3C,gFAAgF;IAChF,MAAM,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;CAC/B;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,UAAU,EAChB,IAAI,EAAE,iBAAiB,GACtB,OAAO,CAAC,YAAY,CAAC,CAuIvB"}
@@ -0,0 +1,141 @@
1
+ /**
2
+ * Shared ingest handler for `POST /signals`.
3
+ *
4
+ * Called by the HTTP route (S4) and the gateway-method shim (S6). Contains
5
+ * all business logic for one ingest item — lookup, validation, execution, and
6
+ * error translation — so neither transport duplicates logic.
7
+ */
8
+ import { ExternalScannerIngestError, } from "../../scanners/external-scanner-receiver.js";
9
+ import { findRuntimeHandleByAddress } from "./lookup.js";
10
+ /**
11
+ * Ingest one signal item.
12
+ *
13
+ * Looks up the runtime by address, validates handler-level constraints, calls
14
+ * `RuntimeHandle.ingestExternalScannerData`, and translates all error shapes
15
+ * into the uniform `{ success: false, code, message }` discriminant. Never
16
+ * throws — all error paths return a `SignalResult`.
17
+ *
18
+ * Validation rules (beyond TypeBox):
19
+ * - At least one of `asset`, `data`, or `signals` must be present; an item
20
+ * with none of those fields has nothing to ingest → `INVALID_REQUEST`.
21
+ * - `data` and `signals` are mutually exclusive → `INVALID_REQUEST`.
22
+ * (The runtime also enforces this, but we check early to surface a clear
23
+ * message before the engine lock is acquired.)
24
+ *
25
+ * Per-(address, scanner) ingest lock is the engine's concern; callers should
26
+ * use {@link groupByAddressScanner} or nest multiple signals in `signals[]`
27
+ * rather than submitting concurrent top-level items for the same key.
28
+ *
29
+ * @param item A single element from the `POST /signals` request body.
30
+ * @param deps Injected dependencies.
31
+ * @returns A resolved `SignalResult` — never rejects.
32
+ */
33
+ export async function ingestSignalsHandler(item, deps) {
34
+ const address = item.address.toLowerCase();
35
+ const { scanner } = item;
36
+ // --- Handler-level validation ---
37
+ const hasAsset = item.asset !== undefined;
38
+ const hasData = item.data !== undefined;
39
+ // `signals: []` is treated as no signals — the caller intended a batch but
40
+ // sent nothing to ingest. Falls through to the at-least-one rule below.
41
+ const hasSignals = item.signals !== undefined && item.signals.length > 0;
42
+ if (!hasAsset && !hasData && !hasSignals) {
43
+ return {
44
+ success: false,
45
+ address,
46
+ scanner,
47
+ code: "INVALID_REQUEST",
48
+ message: "Signal item must include at least one of: asset, data, or signals.",
49
+ };
50
+ }
51
+ if (hasData && hasSignals) {
52
+ return {
53
+ success: false,
54
+ address,
55
+ scanner,
56
+ code: "INVALID_REQUEST",
57
+ message: "Signal item must not include both data and signals — they are mutually exclusive.",
58
+ };
59
+ }
60
+ // --- Runtime lookup ---
61
+ const lookup = findRuntimeHandleByAddress(deps.runtimeHandles, address);
62
+ if (!lookup.found) {
63
+ return {
64
+ success: false,
65
+ address,
66
+ scanner,
67
+ code: "NOT_FOUND",
68
+ message: lookup.message,
69
+ };
70
+ }
71
+ const { handle } = lookup;
72
+ // --- Build the ingest request (address stays at the routing layer) ---
73
+ //
74
+ // `signals` only forwarded when non-empty — mirrors the validation rule
75
+ // above. An empty array would have failed the at-least-one check (unless
76
+ // accompanied by asset/data), so we strip it here for symmetry: don't
77
+ // forward what the validator just rejected as meaningful payload.
78
+ const request = {
79
+ scanner: item.scanner,
80
+ ...(item.asset !== undefined && { asset: item.asset }),
81
+ ...(item.direction !== undefined && { direction: item.direction }),
82
+ ...(item.score !== undefined && { score: item.score }),
83
+ ...(item.signal_type !== undefined && { signal_type: item.signal_type }),
84
+ ...(item.data !== undefined && { data: item.data }),
85
+ ...(hasSignals && { signals: item.signals }),
86
+ };
87
+ // --- Dispatch ---
88
+ console.info("[senpi-runtime] [ingest-trace] /signals handler dispatching", JSON.stringify({
89
+ address,
90
+ scanner,
91
+ transport: deps.transport,
92
+ hasAsset,
93
+ hasData,
94
+ hasSignals,
95
+ dataKeys: hasData ? Object.keys(item.data) : [],
96
+ }));
97
+ try {
98
+ const result = await handle.ingestExternalScannerData(request);
99
+ // TODO(s11): emit runtime.signals_ingested with transport=deps.transport
100
+ console.info("[senpi-runtime] [ingest-trace] /signals handler dispatch ok", JSON.stringify({ address, scanner, signalCount: result.signalCount, contextUpdated: result.contextUpdated }));
101
+ return {
102
+ success: true,
103
+ address,
104
+ scanner,
105
+ timestamp: result.timestamp,
106
+ signalCount: result.signalCount,
107
+ contextUpdated: result.contextUpdated,
108
+ };
109
+ }
110
+ catch (err) {
111
+ console.info("[senpi-runtime] [ingest-trace] /signals handler dispatch THREW", JSON.stringify({
112
+ address,
113
+ scanner,
114
+ errType: err instanceof Error ? err.constructor.name : typeof err,
115
+ message: err instanceof Error ? err.message : String(err),
116
+ }));
117
+ if (err instanceof ExternalScannerIngestError) {
118
+ return {
119
+ success: false,
120
+ address,
121
+ scanner,
122
+ code: err.code,
123
+ message: err.message,
124
+ };
125
+ }
126
+ // Unexpected throw — do not add a new INTERNAL code; map to UNAVAILABLE
127
+ // per Section 5 plan: "reuse INVALID_REQUEST | NOT_FOUND | UNAVAILABLE".
128
+ const message = err instanceof Error
129
+ ? err.message
130
+ : `Unexpected ingest error: ${String(err)}`;
131
+ deps.logger.error("[senpi-core][runtime-api][ingest-signals] Unexpected error during ingest", { address, scanner, transport: deps.transport, err });
132
+ return {
133
+ success: false,
134
+ address,
135
+ scanner,
136
+ code: "UNAVAILABLE",
137
+ message,
138
+ };
139
+ }
140
+ }
141
+ //# sourceMappingURL=ingest-signals.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ingest-signals.js","sourceRoot":"","sources":["../../../src/runtime-api/handlers/ingest-signals.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Pure helper for resolving a running runtime handle by wallet address.
3
+ *
4
+ * Extracted from `getRuntimeHandleByAddressOrRespond` in `src/index.ts` so
5
+ * both the HTTP route layer (S4) and the gateway shim (S6) can share the
6
+ * lookup logic without the respond-on-error side-effect.
7
+ */
8
+ import type { RuntimeHandle } from "../../runtime/run.js";
9
+ /**
10
+ * Discriminated result of a handle lookup.
11
+ *
12
+ * Use `found` to narrow:
13
+ * - `true` → `runtimeId` and `handle` are available.
14
+ * - `false` → `code` is always `"NOT_FOUND"` and `message` describes the miss.
15
+ */
16
+ export type LookupResult = {
17
+ found: true;
18
+ runtimeId: string;
19
+ handle: RuntimeHandle;
20
+ } | {
21
+ found: false;
22
+ code: "NOT_FOUND";
23
+ message: string;
24
+ };
25
+ /**
26
+ * Find a running runtime by lowercased wallet address.
27
+ *
28
+ * Matches case-insensitively — wallet addresses from the runtime's strategy
29
+ * config may differ in capitalisation from caller-supplied values.
30
+ *
31
+ * @param handles The live map maintained by the plugin (`runtimeHandles`).
32
+ * @param address Wallet address to search for. Case is normalised internally.
33
+ * @returns A discriminated union; never throws.
34
+ */
35
+ export declare function findRuntimeHandleByAddress(handles: Map<string, RuntimeHandle>, address: string): LookupResult;
36
+ //# sourceMappingURL=lookup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lookup.d.ts","sourceRoot":"","sources":["../../../src/runtime-api/handlers/lookup.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE1D;;;;;;GAMG;AACH,MAAM,MAAM,YAAY,GACpB;IAAE,KAAK,EAAE,IAAI,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,aAAa,CAAA;CAAE,GACzD;IAAE,KAAK,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,WAAW,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzD;;;;;;;;;GASG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,EACnC,OAAO,EAAE,MAAM,GACd,YAAY,CAYd"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Pure helper for resolving a running runtime handle by wallet address.
3
+ *
4
+ * Extracted from `getRuntimeHandleByAddressOrRespond` in `src/index.ts` so
5
+ * both the HTTP route layer (S4) and the gateway shim (S6) can share the
6
+ * lookup logic without the respond-on-error side-effect.
7
+ */
8
+ /**
9
+ * Find a running runtime by lowercased wallet address.
10
+ *
11
+ * Matches case-insensitively — wallet addresses from the runtime's strategy
12
+ * config may differ in capitalisation from caller-supplied values.
13
+ *
14
+ * @param handles The live map maintained by the plugin (`runtimeHandles`).
15
+ * @param address Wallet address to search for. Case is normalised internally.
16
+ * @returns A discriminated union; never throws.
17
+ */
18
+ export function findRuntimeHandleByAddress(handles, address) {
19
+ const normalizedAddress = address.toLowerCase();
20
+ for (const [runtimeId, handle] of handles) {
21
+ if (handle.wallet.toLowerCase() === normalizedAddress) {
22
+ return { found: true, runtimeId, handle };
23
+ }
24
+ }
25
+ return {
26
+ found: false,
27
+ code: "NOT_FOUND",
28
+ message: `No running runtime found for wallet address "${address}".`,
29
+ };
30
+ }
31
+ //# sourceMappingURL=lookup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lookup.js","sourceRoot":"","sources":["../../../src/runtime-api/handlers/lookup.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAeH;;;;;;;;;GASG;AACH,MAAM,UAAU,0BAA0B,CACxC,OAAmC,EACnC,OAAe;IAEf,MAAM,iBAAiB,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAChD,KAAK,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QAC1C,IAAI,MAAM,CAAC,MAAM,CAAC,WAAW,EAAE,KAAK,iBAAiB,EAAE,CAAC;YACtD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC;QAC5C,CAAC;IACH,CAAC;IACD,OAAO;QACL,KAAK,EAAE,KAAK;QACZ,IAAI,EAAE,WAAW;QACjB,OAAO,EAAE,gDAAgD,OAAO,IAAI;KACrE,CAAC;AACJ,CAAC"}