@senpi/runtime 3.0.6-dev.runtime-phase-2.20260627082846 → 3.0.6-dev.runtime-phase-2.20260630134307
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/dist/actions/close-position/close-position.action.d.ts.map +1 -1
- package/dist/actions/close-position/close-position.action.js +95 -65
- package/dist/actions/close-position/close-position.action.js.map +1 -1
- package/dist/actions/decision-engine.d.ts.map +1 -1
- package/dist/actions/decision-engine.js +40 -28
- package/dist/actions/decision-engine.js.map +1 -1
- package/dist/actions/open-position/open-position.action.d.ts.map +1 -1
- package/dist/actions/open-position/open-position.action.js +261 -202
- package/dist/actions/open-position/open-position.action.js.map +1 -1
- package/dist/dsl/monitor/create-monitor.d.ts +3 -0
- package/dist/dsl/monitor/create-monitor.d.ts.map +1 -1
- package/dist/dsl/monitor/create-monitor.js +76 -28
- package/dist/dsl/monitor/create-monitor.js.map +1 -1
- package/dist/dsl/monitor/process-one-position.d.ts +2 -1
- package/dist/dsl/monitor/process-one-position.d.ts.map +1 -1
- package/dist/dsl/monitor/process-one-position.js +84 -38
- package/dist/dsl/monitor/process-one-position.js.map +1 -1
- package/dist/dsl/plugin/index.d.ts +2 -0
- package/dist/dsl/plugin/index.d.ts.map +1 -1
- package/dist/dsl/plugin/index.js +6 -2
- package/dist/dsl/plugin/index.js.map +1 -1
- package/dist/dsl/plugin/options.d.ts +3 -0
- package/dist/dsl/plugin/options.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -12
- package/dist/index.js.map +1 -1
- package/dist/runtime/create-actions.d.ts.map +1 -1
- package/dist/runtime/create-actions.js +40 -5
- package/dist/runtime/create-actions.js.map +1 -1
- package/dist/runtime/create-scanner-module.d.ts +2 -1
- package/dist/runtime/create-scanner-module.d.ts.map +1 -1
- package/dist/runtime/create-scanner-module.js +2 -1
- package/dist/runtime/create-scanner-module.js.map +1 -1
- package/dist/runtime/runtime.d.ts +10 -0
- package/dist/runtime/runtime.d.ts.map +1 -1
- package/dist/runtime/runtime.js +143 -73
- package/dist/runtime/runtime.js.map +1 -1
- package/dist/scanners/engine/data-providers.d.ts +3 -0
- package/dist/scanners/engine/data-providers.d.ts.map +1 -1
- package/dist/scanners/engine/data-providers.js.map +1 -1
- package/dist/scanners/engine/engine.d.ts +18 -0
- package/dist/scanners/engine/engine.d.ts.map +1 -1
- package/dist/scanners/engine/engine.js +93 -2
- package/dist/scanners/engine/engine.js.map +1 -1
- package/dist/scanners/engine/input-resolver.d.ts.map +1 -1
- package/dist/scanners/engine/input-resolver.js +9 -7
- package/dist/scanners/engine/input-resolver.js.map +1 -1
- package/dist/scanners/runtime-module.d.ts +3 -0
- package/dist/scanners/runtime-module.d.ts.map +1 -1
- package/dist/scanners/runtime-module.js +1 -0
- package/dist/scanners/runtime-module.js.map +1 -1
- package/dist/telemetry/otel-sdk.d.ts +105 -0
- package/dist/telemetry/otel-sdk.d.ts.map +1 -0
- package/dist/telemetry/otel-sdk.js +268 -0
- package/dist/telemetry/otel-sdk.js.map +1 -0
- package/dist/telemetry/trace-identity.d.ts +31 -0
- package/dist/telemetry/trace-identity.d.ts.map +1 -0
- package/dist/telemetry/trace-identity.js +58 -0
- package/dist/telemetry/trace-identity.js.map +1 -0
- package/dist/telemetry/tracing.d.ts +32 -0
- package/dist/telemetry/tracing.d.ts.map +1 -0
- package/dist/telemetry/tracing.js +70 -0
- package/dist/telemetry/tracing.js.map +1 -0
- package/dist/types/scanner.d.ts +2 -0
- package/dist/types/scanner.d.ts.map +1 -1
- package/package.json +6 -2
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenTelemetry SDK stand-up: initialises HyperDX's OTel distribution
|
|
3
|
+
* (`@hyperdx/node-opentelemetry`) — auto-instrumentation, console-capture logs,
|
|
4
|
+
* uncaught-exception capture — against a self-hosted OTLP collector, with a
|
|
5
|
+
* standardised resource-attribute set.
|
|
6
|
+
*
|
|
7
|
+
* Init runs from `Runtime.start()`, not at process entry: resource attributes
|
|
8
|
+
* are immutable after `init()` and are built from the recipe/strategy identity
|
|
9
|
+
* and the `telemetry.enabled` gate, none of which exist before a runtime starts.
|
|
10
|
+
* On Node >= 22 the diagnostics_channel HTTP instrumentation hooks lazily, so
|
|
11
|
+
* deferring init still instruments outbound HTTP.
|
|
12
|
+
*
|
|
13
|
+
* The package is imported dynamically (not at top level) because it captures the
|
|
14
|
+
* OTLP transport config from the `OTEL_EXPORTER_OTLP_*` env vars at module-load time
|
|
15
|
+
* and exposes no endpoint/header option on `init()` — so those vars must be set before
|
|
16
|
+
* it loads.
|
|
17
|
+
*
|
|
18
|
+
* Under openclaw the operator must set ALL of these in the container env (the SDK does
|
|
19
|
+
* not derive them; this module only defaults the base endpoint below, for standalone runs):
|
|
20
|
+
* OTEL_EXPORTER_OTLP_ENDPOINT (base, e.g. http://host.docker.internal:4318)
|
|
21
|
+
* OTEL_EXPORTER_OTLP_TRACES_ENDPOINT (.../v1/traces)
|
|
22
|
+
* OTEL_EXPORTER_OTLP_LOGS_ENDPOINT (.../v1/logs)
|
|
23
|
+
* OTEL_EXPORTER_OTLP_METRICS_ENDPOINT (.../v1/metrics)
|
|
24
|
+
* OTEL_EXPORTER_OTLP_HEADERS (e.g. Authorization=<collector-token>)
|
|
25
|
+
* See docs/runtime-docs/runtime-api.md (Tracing) for why each is required.
|
|
26
|
+
*/
|
|
27
|
+
import { randomUUID } from "node:crypto";
|
|
28
|
+
import { createLogger } from "../utils/logger.js";
|
|
29
|
+
const logger = createLogger("telemetry/otel-sdk");
|
|
30
|
+
/** Base OTLP endpoint defaulted for standalone Node runs only; ignored under openclaw (see module header). */
|
|
31
|
+
export const DEFAULT_OTLP_ENDPOINT = "http://localhost:4318";
|
|
32
|
+
/**
|
|
33
|
+
* Process-level instance id for `service.instance.id`. Distinct from the
|
|
34
|
+
* per-runtime `IdentityEnvelope.bootId`: resource attributes are process-global,
|
|
35
|
+
* so this must identify the PROCESS (disambiguating restarts), not a single
|
|
36
|
+
* runtime — N runtimes share one of these.
|
|
37
|
+
*/
|
|
38
|
+
const PROCESS_INSTANCE_ID = randomUUID();
|
|
39
|
+
/**
|
|
40
|
+
* Re-enable the undici (global `fetch`) auto-instrumentation after `hyperdx.init()`.
|
|
41
|
+
*
|
|
42
|
+
* HyperDX's `init()` leaves undici disabled: across init it calls
|
|
43
|
+
* `setConfig → enable (subscribe undici:request:*) → disable`, with no re-enable
|
|
44
|
+
* (observed by instrumenting the class). So `undici:request:create` ends with no
|
|
45
|
+
* subscribers and no `fetch()` produces an HTTP client span — untracing everything on
|
|
46
|
+
* global `fetch`: the Senpi MCP client (`StreamableHTTPClientTransport`), the LLM
|
|
47
|
+
* client, etc. (`net`/`dns`/`tls` are independent and still emit, so connections show
|
|
48
|
+
* up while the requests riding them don't.)
|
|
49
|
+
*
|
|
50
|
+
* Fix: enable our own `UndiciInstrumentation`; with no provider set it resolves the
|
|
51
|
+
* global one `init()` registered, and HyperDX's instance stays disabled (no double
|
|
52
|
+
* instrumentation). Best-effort — a failure must not fail boot. Also a prerequisite
|
|
53
|
+
* for the manual `mcp.*`/`dsl.*` spans (Task 9b), which assume outbound `fetch` calls
|
|
54
|
+
* auto-nest as HTTP children.
|
|
55
|
+
*
|
|
56
|
+
* Seen with `@hyperdx/node-opentelemetry` 0.11 / `instrumentation-undici` 0.28, Node 24
|
|
57
|
+
* — re-check on upgrade; drop if HyperDX stops disabling undici.
|
|
58
|
+
*/
|
|
59
|
+
async function reinstrumentUndici() {
|
|
60
|
+
try {
|
|
61
|
+
const mod = (await import("@opentelemetry/instrumentation-undici"));
|
|
62
|
+
new mod.UndiciInstrumentation().enable();
|
|
63
|
+
logger.debug("undici (fetch) instrumentation re-enabled post-init");
|
|
64
|
+
}
|
|
65
|
+
catch (err) {
|
|
66
|
+
logger.warn("failed to re-enable undici instrumentation; fetch client spans will be missing", {
|
|
67
|
+
err: err instanceof Error ? err.message : String(err),
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/** Process-global once-only guard; resource attributes are immutable after init(). */
|
|
72
|
+
let initialized = false;
|
|
73
|
+
/**
|
|
74
|
+
* Reference to the loaded HyperDX module, captured at init so {@link shutdownOtelSdk}
|
|
75
|
+
* can flush the SDK without a second dynamic import. Process-global like the SDK itself.
|
|
76
|
+
*/
|
|
77
|
+
let hyperdxModule;
|
|
78
|
+
/**
|
|
79
|
+
* Whether {@link initOtelSdk} has completed. Code that loads `@hyperdx/node-opentelemetry`
|
|
80
|
+
* lazily (e.g. per-trace attribute stamping) gates on this so the package — which captures
|
|
81
|
+
* its OTLP transport env at module-load — never loads when telemetry is disabled or under test.
|
|
82
|
+
*/
|
|
83
|
+
export function isOtelInitialized() {
|
|
84
|
+
return initialized;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Decode the `senpi_user_id` claim from a JWT without verifying its signature.
|
|
88
|
+
* Returns `undefined` for any non-JWT / malformed / missing-claim input; never throws.
|
|
89
|
+
*
|
|
90
|
+
* The claim name is assumed, not confirmed against a real token — if the id lives
|
|
91
|
+
* under a different claim (e.g. `sub`), this must change before `user.id` is trusted.
|
|
92
|
+
*/
|
|
93
|
+
export function decodeSenpiUserId(token) {
|
|
94
|
+
if (typeof token !== "string" || token.length === 0)
|
|
95
|
+
return undefined;
|
|
96
|
+
try {
|
|
97
|
+
const segments = token.split(".");
|
|
98
|
+
if (segments.length < 2)
|
|
99
|
+
return undefined;
|
|
100
|
+
const payloadSegment = segments[1];
|
|
101
|
+
if (!payloadSegment)
|
|
102
|
+
return undefined;
|
|
103
|
+
const json = Buffer.from(payloadSegment, "base64url").toString("utf8");
|
|
104
|
+
if (json.length === 0)
|
|
105
|
+
return undefined;
|
|
106
|
+
const payload = JSON.parse(json);
|
|
107
|
+
const claim = payload?.senpi_user_id;
|
|
108
|
+
// Only string/number; anything else (object/array) would coerce to junk.
|
|
109
|
+
if (typeof claim !== "string" && typeof claim !== "number")
|
|
110
|
+
return undefined;
|
|
111
|
+
const asString = String(claim);
|
|
112
|
+
return asString.length > 0 ? asString : undefined;
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
return undefined;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Build the `OTEL_RESOURCE_ATTRIBUTES` value as a `key=value,key=value` string.
|
|
120
|
+
* Values are percent-encoded so spaces/commas stay valid; empty keys are omitted.
|
|
121
|
+
*
|
|
122
|
+
* SCOPING (multi-runtime): resource attributes are process-global and immutable
|
|
123
|
+
* after the first init(), but N runtimes (one per wallet) share one process — so
|
|
124
|
+
* only PROCESS-STABLE values belong here. We carry:
|
|
125
|
+
* - service.name, service.version, deployment.environment — process-stable.
|
|
126
|
+
* - service.instance.id — a PROCESS-level id ({@link PROCESS_INSTANCE_ID}), not
|
|
127
|
+
* the per-runtime `IdentityEnvelope.bootId`.
|
|
128
|
+
* - user.id — process-stable: single-tenant, one SENPI_API_KEY = one user for
|
|
129
|
+
* the whole process, so it is the same across every runtime here.
|
|
130
|
+
*
|
|
131
|
+
* The per-runtime keys (senpi.strategy.address, senpi.recipe, senpi.recipe.version,
|
|
132
|
+
* senpi.runtime, senpi.config.hash) are deliberately OMITTED — on a process-global
|
|
133
|
+
* resource they would stamp the first runtime's value onto every other runtime's
|
|
134
|
+
* telemetry. They return as per-trace/per-log attributes in 9b (`setTraceAttributes`)
|
|
135
|
+
* / 9c (LogRecordProcessor).
|
|
136
|
+
*/
|
|
137
|
+
export function buildOtelResourceAttributes(serviceName, extras) {
|
|
138
|
+
const pairs = [
|
|
139
|
+
["service.name", serviceName],
|
|
140
|
+
["service.instance.id", extras.serviceInstanceId],
|
|
141
|
+
["service.version", extras.serviceVersion],
|
|
142
|
+
["deployment.environment", extras.deploymentEnvironment],
|
|
143
|
+
["user.id", extras.userId],
|
|
144
|
+
];
|
|
145
|
+
return pairs
|
|
146
|
+
.filter(([, value]) => value !== undefined && String(value).length > 0)
|
|
147
|
+
.map(([key, value]) => `${key}=${encodeURIComponent(String(value))}`)
|
|
148
|
+
.join(",");
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Resolve the OTLP endpoint: `OTEL_EXPORTER_OTLP_ENDPOINT` env >
|
|
152
|
+
* {@link DEFAULT_OTLP_ENDPOINT}.
|
|
153
|
+
*/
|
|
154
|
+
export function resolveOtlpEndpoint() {
|
|
155
|
+
const envOverride = process.env.OTEL_EXPORTER_OTLP_ENDPOINT?.trim();
|
|
156
|
+
if (envOverride)
|
|
157
|
+
return envOverride;
|
|
158
|
+
return DEFAULT_OTLP_ENDPOINT;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Initialise the OTel SDK. Idempotent, side-effecting, and never crashes boot:
|
|
162
|
+
* no-ops when disabled, already initialised, or under test.
|
|
163
|
+
*/
|
|
164
|
+
export async function initOtelSdk(params) {
|
|
165
|
+
if (initialized)
|
|
166
|
+
return;
|
|
167
|
+
if (!params.enabled) {
|
|
168
|
+
logger.debug("OTel SDK init skipped: telemetry disabled");
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
// Skip under test: the enable gate defaults to true, so without this the SDK
|
|
172
|
+
// would init (start exporters, patch core modules) during the vitest run.
|
|
173
|
+
if (process.env.VITEST || process.env.NODE_ENV === "test") {
|
|
174
|
+
logger.debug("OTel SDK init skipped: running under test");
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
try {
|
|
178
|
+
const identity = params.identity();
|
|
179
|
+
// Commit the guard before any fallible work: a failed init must not let a
|
|
180
|
+
// later Runtime.start() re-enter and double-append OTEL_RESOURCE_ATTRIBUTES.
|
|
181
|
+
// identity() above threw before this, so env is still untouched on that path.
|
|
182
|
+
initialized = true;
|
|
183
|
+
// Source the environment label from NODE_ENV (Node-standard). Pass the value
|
|
184
|
+
// through verbatim (so `staging`/custom envs are faithful), defaulting to
|
|
185
|
+
// "development" when unset/empty — a label must never claim production by
|
|
186
|
+
// accident; prod deployments set NODE_ENV=production as standard practice.
|
|
187
|
+
const nodeEnv = process.env.NODE_ENV?.trim();
|
|
188
|
+
const deploymentEnvironment = nodeEnv && nodeEnv.length > 0 ? nodeEnv : "development";
|
|
189
|
+
const userId = decodeSenpiUserId(params.userToken);
|
|
190
|
+
// Append to any host-set value rather than clobber operator attrs.
|
|
191
|
+
const ourAttrs = buildOtelResourceAttributes(identity.service, {
|
|
192
|
+
serviceInstanceId: PROCESS_INSTANCE_ID,
|
|
193
|
+
serviceVersion: params.serviceVersion,
|
|
194
|
+
deploymentEnvironment,
|
|
195
|
+
userId,
|
|
196
|
+
});
|
|
197
|
+
const existingAttrs = process.env.OTEL_RESOURCE_ATTRIBUTES?.trim();
|
|
198
|
+
process.env.OTEL_RESOURCE_ATTRIBUTES = existingAttrs
|
|
199
|
+
? `${existingAttrs},${ourAttrs}`
|
|
200
|
+
: ourAttrs;
|
|
201
|
+
// Must be set before the import below — HyperDX reads it at module-load.
|
|
202
|
+
if (!process.env.OTEL_EXPORTER_OTLP_ENDPOINT) {
|
|
203
|
+
process.env.OTEL_EXPORTER_OTLP_ENDPOINT = resolveOtlpEndpoint();
|
|
204
|
+
}
|
|
205
|
+
// Dynamic import: loads only now, after the env var above is set.
|
|
206
|
+
const hyperdx = (await import("@hyperdx/node-opentelemetry"));
|
|
207
|
+
hyperdxModule = hyperdx;
|
|
208
|
+
hyperdx.init({
|
|
209
|
+
// No apiKey on init(): the exporter authenticates to the collector via the
|
|
210
|
+
// Authorization header in OTEL_EXPORTER_OTLP_HEADERS (read from env at
|
|
211
|
+
// module-load), not through the SDK's HyperDX apiKey field.
|
|
212
|
+
service: identity.service,
|
|
213
|
+
// Routes createLogger() → console.* output into OTel logs.
|
|
214
|
+
consoleCapture: true,
|
|
215
|
+
experimentalExceptionCapture: true,
|
|
216
|
+
// Don't let HyperDX install its own SIGTERM/SIGINT handlers. Its default
|
|
217
|
+
// handler calls process.exit() once its own _shutdown() settles, without
|
|
218
|
+
// awaiting our teardown. OpenClaw owns process lifecycle and drives
|
|
219
|
+
// Runtime.stop() → disposeRecording(), whose recording-batcher drain is an
|
|
220
|
+
// async network flush; HyperDX's exit would preempt that and drop buffered
|
|
221
|
+
// rows. Leaving this false hands shutdown ordering back to the gateway.
|
|
222
|
+
stopOnTerminationSignals: false,
|
|
223
|
+
// Arms setTraceAttributes: without it the span processor that copies per-trace
|
|
224
|
+
// identity onto every span and log stays disabled, and the stamping no-ops.
|
|
225
|
+
betaMode: true,
|
|
226
|
+
});
|
|
227
|
+
await reinstrumentUndici();
|
|
228
|
+
logger.info("OTel SDK initialised", {
|
|
229
|
+
service: identity.service,
|
|
230
|
+
endpoint: process.env.OTEL_EXPORTER_OTLP_ENDPOINT,
|
|
231
|
+
deploymentEnvironment,
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
catch (err) {
|
|
235
|
+
logger.warn("OTel SDK init failed; continuing without telemetry transport", {
|
|
236
|
+
err: err instanceof Error ? err.message : String(err),
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Flush and stop the OTel SDK. Best-effort, idempotent, never throws.
|
|
242
|
+
*
|
|
243
|
+
* Required because {@link initOtelSdk} disables HyperDX's own SIGTERM handler
|
|
244
|
+
* (`stopOnTerminationSignals: false`) — that handler was the SDK's only on-exit
|
|
245
|
+
* flush, so without an explicit shutdown the span/log batch processors' buffers
|
|
246
|
+
* are dropped at process exit. The SDK is process-global (one per process,
|
|
247
|
+
* shared across all hosted runtimes), so this must be called ONCE at process /
|
|
248
|
+
* plugin teardown, after every span has been emitted — never per-runtime, which
|
|
249
|
+
* would cut off telemetry for sibling runtimes still running.
|
|
250
|
+
*/
|
|
251
|
+
export async function shutdownOtelSdk() {
|
|
252
|
+
if (!initialized || !hyperdxModule)
|
|
253
|
+
return;
|
|
254
|
+
// Flip the guard first so a concurrent/duplicate teardown can't double-shutdown.
|
|
255
|
+
initialized = false;
|
|
256
|
+
const mod = hyperdxModule;
|
|
257
|
+
hyperdxModule = undefined;
|
|
258
|
+
try {
|
|
259
|
+
await mod.shutdown();
|
|
260
|
+
logger.debug("OTel SDK shut down (final flush complete)");
|
|
261
|
+
}
|
|
262
|
+
catch (err) {
|
|
263
|
+
logger.warn("OTel SDK shutdown failed; buffered spans/logs may be lost", {
|
|
264
|
+
err: err instanceof Error ? err.message : String(err),
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
//# sourceMappingURL=otel-sdk.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"otel-sdk.js","sourceRoot":"","sources":["../../src/telemetry/otel-sdk.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure, vendor-neutral mapping `IdentityEnvelope → senpi.* attributes`, set per-trace
|
|
3
|
+
* at each root span's birth. Per-runtime identity can't live on the process-global OTel
|
|
4
|
+
* resource — N wallets share one process, so a resource attribute would stamp the first
|
|
5
|
+
* runtime's value onto everyone's telemetry (see `otel-sdk.ts` SCOPING note).
|
|
6
|
+
*
|
|
7
|
+
* The actual stamping is HyperDX's `setTraceAttributes` (rides every span in the trace),
|
|
8
|
+
* called at the root-span seams — NOT here: importing `@hyperdx/node-opentelemetry`
|
|
9
|
+
* captures its OTLP transport env at module-load, so it must stay behind code that runs
|
|
10
|
+
* only after `initOtelSdk()`.
|
|
11
|
+
*/
|
|
12
|
+
import type { Attributes } from "@opentelemetry/api";
|
|
13
|
+
import type { IdentityEnvelope } from "../recording/identity.js";
|
|
14
|
+
/**
|
|
15
|
+
* Map the runtime {@link IdentityEnvelope} to its `senpi.*` trace attributes. Optional
|
|
16
|
+
* envelope fields are omitted when unset rather than emitted as empty/undefined, so a
|
|
17
|
+
* trace never carries a hollow `senpi.config.hash=""`.
|
|
18
|
+
*/
|
|
19
|
+
export declare function identityTraceAttributes(identity: IdentityEnvelope): Attributes;
|
|
20
|
+
/**
|
|
21
|
+
* Stamp the runtime's `senpi.*` identity onto the active trace via HyperDX's
|
|
22
|
+
* `setTraceAttributes`, which rides every span in the trace. Call it from a root-span
|
|
23
|
+
* seam (inside an active span's context) so the whole trace carries the identity.
|
|
24
|
+
*
|
|
25
|
+
* No-ops until the SDK is initialised: the import is dynamic so `@hyperdx/node-opentelemetry`
|
|
26
|
+
* (which captures its OTLP env at module-load) only loads after `initOtelSdk()`, and there is
|
|
27
|
+
* no trace to stamp before then. Best-effort: a failure is logged and swallowed — tracing must
|
|
28
|
+
* never break trading.
|
|
29
|
+
*/
|
|
30
|
+
export declare function setTraceIdentityAttributes(identity: IdentityEnvelope): Promise<void>;
|
|
31
|
+
//# sourceMappingURL=trace-identity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trace-identity.d.ts","sourceRoot":"","sources":["../../src/telemetry/trace-identity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAMjE;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,gBAAgB,GAAG,UAAU,CAc9E;AAED;;;;;;;;;GASG;AACH,wBAAsB,0BAA0B,CAAC,QAAQ,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAY1F"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure, vendor-neutral mapping `IdentityEnvelope → senpi.* attributes`, set per-trace
|
|
3
|
+
* at each root span's birth. Per-runtime identity can't live on the process-global OTel
|
|
4
|
+
* resource — N wallets share one process, so a resource attribute would stamp the first
|
|
5
|
+
* runtime's value onto everyone's telemetry (see `otel-sdk.ts` SCOPING note).
|
|
6
|
+
*
|
|
7
|
+
* The actual stamping is HyperDX's `setTraceAttributes` (rides every span in the trace),
|
|
8
|
+
* called at the root-span seams — NOT here: importing `@hyperdx/node-opentelemetry`
|
|
9
|
+
* captures its OTLP transport env at module-load, so it must stay behind code that runs
|
|
10
|
+
* only after `initOtelSdk()`.
|
|
11
|
+
*/
|
|
12
|
+
import { isOtelInitialized } from "./otel-sdk.js";
|
|
13
|
+
import { createLogger } from "../utils/logger.js";
|
|
14
|
+
const logger = createLogger("telemetry/trace-identity");
|
|
15
|
+
/**
|
|
16
|
+
* Map the runtime {@link IdentityEnvelope} to its `senpi.*` trace attributes. Optional
|
|
17
|
+
* envelope fields are omitted when unset rather than emitted as empty/undefined, so a
|
|
18
|
+
* trace never carries a hollow `senpi.config.hash=""`.
|
|
19
|
+
*/
|
|
20
|
+
export function identityTraceAttributes(identity) {
|
|
21
|
+
const attributes = {
|
|
22
|
+
"senpi.strategy.address": identity.strategyAddress,
|
|
23
|
+
"senpi.recipe": identity.recipe,
|
|
24
|
+
"senpi.runtime": identity.runtime,
|
|
25
|
+
"senpi.boot.id": identity.bootId,
|
|
26
|
+
};
|
|
27
|
+
if (identity.recipeVersion !== undefined) {
|
|
28
|
+
attributes["senpi.recipe.version"] = String(identity.recipeVersion);
|
|
29
|
+
}
|
|
30
|
+
if (identity.configHash !== undefined) {
|
|
31
|
+
attributes["senpi.config.hash"] = identity.configHash;
|
|
32
|
+
}
|
|
33
|
+
return attributes;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Stamp the runtime's `senpi.*` identity onto the active trace via HyperDX's
|
|
37
|
+
* `setTraceAttributes`, which rides every span in the trace. Call it from a root-span
|
|
38
|
+
* seam (inside an active span's context) so the whole trace carries the identity.
|
|
39
|
+
*
|
|
40
|
+
* No-ops until the SDK is initialised: the import is dynamic so `@hyperdx/node-opentelemetry`
|
|
41
|
+
* (which captures its OTLP env at module-load) only loads after `initOtelSdk()`, and there is
|
|
42
|
+
* no trace to stamp before then. Best-effort: a failure is logged and swallowed — tracing must
|
|
43
|
+
* never break trading.
|
|
44
|
+
*/
|
|
45
|
+
export async function setTraceIdentityAttributes(identity) {
|
|
46
|
+
if (!isOtelInitialized())
|
|
47
|
+
return;
|
|
48
|
+
try {
|
|
49
|
+
const hyperdx = (await import("@hyperdx/node-opentelemetry"));
|
|
50
|
+
hyperdx.setTraceAttributes(identityTraceAttributes(identity));
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
logger.warn("failed to stamp per-trace identity attributes", {
|
|
54
|
+
err: err instanceof Error ? err.message : String(err),
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=trace-identity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trace-identity.js","sourceRoot":"","sources":["../../src/telemetry/trace-identity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,MAAM,MAAM,GAAG,YAAY,CAAC,0BAA0B,CAAC,CAAC;AAExD;;;;GAIG;AACH,MAAM,UAAU,uBAAuB,CAAC,QAA0B;IAChE,MAAM,UAAU,GAAe;QAC7B,wBAAwB,EAAE,QAAQ,CAAC,eAAe;QAClD,cAAc,EAAE,QAAQ,CAAC,MAAM;QAC/B,eAAe,EAAE,QAAQ,CAAC,OAAO;QACjC,eAAe,EAAE,QAAQ,CAAC,MAAM;KACjC,CAAC;IACF,IAAI,QAAQ,CAAC,aAAa,KAAK,SAAS,EAAE,CAAC;QACzC,UAAU,CAAC,sBAAsB,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;IACtE,CAAC;IACD,IAAI,QAAQ,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;QACtC,UAAU,CAAC,mBAAmB,CAAC,GAAG,QAAQ,CAAC,UAAU,CAAC;IACxD,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAAC,QAA0B;IACzE,IAAI,CAAC,iBAAiB,EAAE;QAAE,OAAO;IACjC,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,CAAC,MAAM,MAAM,CAAC,6BAA6B,CAAC,CAE3D,CAAC;QACF,OAAO,CAAC,kBAAkB,CAAC,uBAAuB,CAAC,QAAQ,CAAC,CAAC,CAAC;IAChE,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,CAAC,IAAI,CAAC,+CAA+C,EAAE;YAC3D,GAAG,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;SACtD,CAAC,CAAC;IACL,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one place runtime code touches the OTel trace API: manual spans are created
|
|
3
|
+
* exclusively through {@link withSpan}. Vendor-neutral (depends only on
|
|
4
|
+
* `@opentelemetry/api`); the SDK itself is stood up in `otel-sdk.ts`. When the SDK
|
|
5
|
+
* is not initialised (telemetry off, or under test) the API resolves a no-op tracer,
|
|
6
|
+
* so `withSpan` runs the callback with near-zero overhead — no `enabled` gate needed.
|
|
7
|
+
*/
|
|
8
|
+
import { type Span, type Attributes, type Tracer } from "@opentelemetry/api";
|
|
9
|
+
/** The single tracer for our manual spans. Resolves the no-op tracer pre-init. */
|
|
10
|
+
export declare function getTracer(): Tracer;
|
|
11
|
+
/**
|
|
12
|
+
* Run `fn` inside a new active span, standardising the lifecycle:
|
|
13
|
+
*
|
|
14
|
+
* - `startActiveSpan`, so the span is the active context: auto-instrumented HTTP
|
|
15
|
+
* children and nested {@link withSpan} calls nest under it while work stays on the
|
|
16
|
+
* synchronous `await` chain. (`startSpan` would detach children into roots.)
|
|
17
|
+
* - Passes the span **handle** into `fn` — use it for `addEvent`/`setAttribute`/
|
|
18
|
+
* `recordException`, never `trace.getActiveSpan()` (ambiguous under concurrent
|
|
19
|
+
* sibling spans, e.g. a `Promise.all` fan-out).
|
|
20
|
+
* - On throw: records the exception, sets ERROR status, then **rethrows** — tracing
|
|
21
|
+
* observes, never alters control flow. Pass `options.expected` to mark control-flow
|
|
22
|
+
* throws (e.g. `SkipScan`) that should propagate *without* painting the span red.
|
|
23
|
+
* - Always ends the span (`finally`), so a span can never leak.
|
|
24
|
+
*
|
|
25
|
+
* Tracing must never break runtime code: the span operations are guarded so a throw
|
|
26
|
+
* from the SDK — or from a caller's `expected` predicate — can neither mask `fn`'s
|
|
27
|
+
* error nor corrupt its result.
|
|
28
|
+
*/
|
|
29
|
+
export declare function withSpan<T>(name: string, attributes: Attributes, fn: (span: Span) => Promise<T>, options?: {
|
|
30
|
+
expected?: (err: unknown) => boolean;
|
|
31
|
+
}): Promise<T>;
|
|
32
|
+
//# sourceMappingURL=tracing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tracing.d.ts","sourceRoot":"","sources":["../../src/telemetry/tracing.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAGL,KAAK,IAAI,EACT,KAAK,UAAU,EACf,KAAK,MAAM,EACZ,MAAM,oBAAoB,CAAC;AAK5B,kFAAkF;AAClF,wBAAgB,SAAS,IAAI,MAAM,CAElC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EACxB,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,UAAU,EACtB,EAAE,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,EAC9B,OAAO,CAAC,EAAE;IAAE,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAA;CAAE,GACjD,OAAO,CAAC,CAAC,CAAC,CA8BZ"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one place runtime code touches the OTel trace API: manual spans are created
|
|
3
|
+
* exclusively through {@link withSpan}. Vendor-neutral (depends only on
|
|
4
|
+
* `@opentelemetry/api`); the SDK itself is stood up in `otel-sdk.ts`. When the SDK
|
|
5
|
+
* is not initialised (telemetry off, or under test) the API resolves a no-op tracer,
|
|
6
|
+
* so `withSpan` runs the callback with near-zero overhead — no `enabled` gate needed.
|
|
7
|
+
*/
|
|
8
|
+
import { trace, SpanStatusCode, } from "@opentelemetry/api";
|
|
9
|
+
/** Instrumentation scope for every span this runtime creates. */
|
|
10
|
+
const TRACER_NAME = "senpi-trading-runtime";
|
|
11
|
+
/** The single tracer for our manual spans. Resolves the no-op tracer pre-init. */
|
|
12
|
+
export function getTracer() {
|
|
13
|
+
return trace.getTracer(TRACER_NAME);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Run `fn` inside a new active span, standardising the lifecycle:
|
|
17
|
+
*
|
|
18
|
+
* - `startActiveSpan`, so the span is the active context: auto-instrumented HTTP
|
|
19
|
+
* children and nested {@link withSpan} calls nest under it while work stays on the
|
|
20
|
+
* synchronous `await` chain. (`startSpan` would detach children into roots.)
|
|
21
|
+
* - Passes the span **handle** into `fn` — use it for `addEvent`/`setAttribute`/
|
|
22
|
+
* `recordException`, never `trace.getActiveSpan()` (ambiguous under concurrent
|
|
23
|
+
* sibling spans, e.g. a `Promise.all` fan-out).
|
|
24
|
+
* - On throw: records the exception, sets ERROR status, then **rethrows** — tracing
|
|
25
|
+
* observes, never alters control flow. Pass `options.expected` to mark control-flow
|
|
26
|
+
* throws (e.g. `SkipScan`) that should propagate *without* painting the span red.
|
|
27
|
+
* - Always ends the span (`finally`), so a span can never leak.
|
|
28
|
+
*
|
|
29
|
+
* Tracing must never break runtime code: the span operations are guarded so a throw
|
|
30
|
+
* from the SDK — or from a caller's `expected` predicate — can neither mask `fn`'s
|
|
31
|
+
* error nor corrupt its result.
|
|
32
|
+
*/
|
|
33
|
+
export function withSpan(name, attributes, fn, options) {
|
|
34
|
+
return getTracer().startActiveSpan(name, { attributes }, async (span) => {
|
|
35
|
+
try {
|
|
36
|
+
return await fn(span);
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
let expected = false;
|
|
40
|
+
try {
|
|
41
|
+
expected = options?.expected?.(err) ?? false;
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
// A throwing predicate must never mask the real error; treat as unexpected.
|
|
45
|
+
}
|
|
46
|
+
if (expected)
|
|
47
|
+
throw err;
|
|
48
|
+
try {
|
|
49
|
+
span.recordException(err);
|
|
50
|
+
span.setStatus({
|
|
51
|
+
code: SpanStatusCode.ERROR,
|
|
52
|
+
message: err instanceof Error ? err.message : String(err),
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
// A tracing failure must never replace the real error the caller needs.
|
|
57
|
+
}
|
|
58
|
+
throw err;
|
|
59
|
+
}
|
|
60
|
+
finally {
|
|
61
|
+
try {
|
|
62
|
+
span.end();
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
// Span teardown must never break the caller's result or error.
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=tracing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tracing.js","sourceRoot":"","sources":["../../src/telemetry/tracing.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACL,KAAK,EACL,cAAc,GAIf,MAAM,oBAAoB,CAAC;AAE5B,iEAAiE;AACjE,MAAM,WAAW,GAAG,uBAAuB,CAAC;AAE5C,kFAAkF;AAClF,MAAM,UAAU,SAAS;IACvB,OAAO,KAAK,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;AACtC,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,QAAQ,CACtB,IAAY,EACZ,UAAsB,EACtB,EAA8B,EAC9B,OAAkD;IAElD,OAAO,SAAS,EAAE,CAAC,eAAe,CAAC,IAAI,EAAE,EAAE,UAAU,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;QACtE,IAAI,CAAC;YACH,OAAO,MAAM,EAAE,CAAC,IAAI,CAAC,CAAC;QACxB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,QAAQ,GAAG,KAAK,CAAC;YACrB,IAAI,CAAC;gBACH,QAAQ,GAAG,OAAO,EAAE,QAAQ,EAAE,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC;YAC/C,CAAC;YAAC,MAAM,CAAC;gBACP,4EAA4E;YAC9E,CAAC;YACD,IAAI,QAAQ;gBAAE,MAAM,GAAG,CAAC;YACxB,IAAI,CAAC;gBACH,IAAI,CAAC,eAAe,CAAC,GAAY,CAAC,CAAC;gBACnC,IAAI,CAAC,SAAS,CAAC;oBACb,IAAI,EAAE,cAAc,CAAC,KAAK;oBAC1B,OAAO,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;iBAC1D,CAAC,CAAC;YACL,CAAC;YAAC,MAAM,CAAC;gBACP,wEAAwE;YAC1E,CAAC;YACD,MAAM,GAAG,CAAC;QACZ,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC;gBACH,IAAI,CAAC,GAAG,EAAE,CAAC;YACb,CAAC;YAAC,MAAM,CAAC;gBACP,+DAA+D;YACjE,CAAC;QACH,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC"}
|
package/dist/types/scanner.d.ts
CHANGED
|
@@ -71,5 +71,7 @@ export interface ScanResult {
|
|
|
71
71
|
reason?: string;
|
|
72
72
|
/** Aggregated run metrics/details. */
|
|
73
73
|
summary: Record<string, unknown>;
|
|
74
|
+
/** Telemetry-owned per-run id; join key linking scanner.run → signal.process. */
|
|
75
|
+
runId?: string;
|
|
74
76
|
}
|
|
75
77
|
//# sourceMappingURL=scanner.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../../src/types/scanner.ts"],"names":[],"mappings":"AAEA,6EAA6E;AAC7E,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC;AAC/B,2CAA2C;AAC3C,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC;AAIhC,uEAAuE;AACvE,oBAAY,UAAU;IACpB,EAAE,OAAO;IACT,SAAS,cAAc;IACvB,OAAO,YAAY;IACnB,MAAM,WAAW;IACjB,QAAQ,aAAa;IACrB,KAAK,UAAU;CAChB;AAED;;;GAGG;AACH,MAAM,WAAW,MAAM;IACrB,0DAA0D;IAC1D,OAAO,EAAE,MAAM,CAAC;IAChB,mDAAmD;IACnD,SAAS,EAAE,SAAS,CAAC;IACrB,mEAAmE;IACnE,UAAU,EAAE,MAAM,CAAC;IACnB,8CAA8C;IAC9C,KAAK,EAAE,MAAM,CAAC;IACd,4DAA4D;IAC5D,SAAS,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;IACnC,6CAA6C;IAC7C,KAAK,EAAE,MAAM,CAAC;IACd,sCAAsC;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,0DAA0D;IAC1D,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,gEAAgE;IAChE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iFAAiF;IACjF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,mEAAmE;AACnE,MAAM,WAAW,UAAU;IACzB,qDAAqD;IACrD,OAAO,EAAE,MAAM,CAAC;IAChB,oCAAoC;IACpC,SAAS,EAAE,SAAS,CAAC;IACrB,0DAA0D;IAC1D,MAAM,EAAE,UAAU,CAAC;IACnB,6CAA6C;IAC7C,SAAS,EAAE,MAAM,CAAC;IAClB,iCAAiC;IACjC,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,oDAAoD;IACpD,eAAe,EAAE,MAAM,CAAC;IACxB,sCAAsC;IACtC,YAAY,EAAE,MAAM,CAAC;IACrB,8CAA8C;IAC9C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sCAAsC;IACtC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../../src/types/scanner.ts"],"names":[],"mappings":"AAEA,6EAA6E;AAC7E,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC;AAC/B,2CAA2C;AAC3C,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC;AAIhC,uEAAuE;AACvE,oBAAY,UAAU;IACpB,EAAE,OAAO;IACT,SAAS,cAAc;IACvB,OAAO,YAAY;IACnB,MAAM,WAAW;IACjB,QAAQ,aAAa;IACrB,KAAK,UAAU;CAChB;AAED;;;GAGG;AACH,MAAM,WAAW,MAAM;IACrB,0DAA0D;IAC1D,OAAO,EAAE,MAAM,CAAC;IAChB,mDAAmD;IACnD,SAAS,EAAE,SAAS,CAAC;IACrB,mEAAmE;IACnE,UAAU,EAAE,MAAM,CAAC;IACnB,8CAA8C;IAC9C,KAAK,EAAE,MAAM,CAAC;IACd,4DAA4D;IAC5D,SAAS,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;IACnC,6CAA6C;IAC7C,KAAK,EAAE,MAAM,CAAC;IACd,sCAAsC;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,0DAA0D;IAC1D,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,gEAAgE;IAChE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iFAAiF;IACjF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,mEAAmE;AACnE,MAAM,WAAW,UAAU;IACzB,qDAAqD;IACrD,OAAO,EAAE,MAAM,CAAC;IAChB,oCAAoC;IACpC,SAAS,EAAE,SAAS,CAAC;IACrB,0DAA0D;IAC1D,MAAM,EAAE,UAAU,CAAC;IACnB,6CAA6C;IAC7C,SAAS,EAAE,MAAM,CAAC;IAClB,iCAAiC;IACjC,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,oDAAoD;IACpD,eAAe,EAAE,MAAM,CAAC;IACxB,sCAAsC;IACtC,YAAY,EAAE,MAAM,CAAC;IACrB,8CAA8C;IAC9C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sCAAsC;IACtC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,iFAAiF;IACjF,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@senpi/runtime",
|
|
3
|
-
"version": "3.0.6-dev.runtime-phase-2.
|
|
3
|
+
"version": "3.0.6-dev.runtime-phase-2.20260630134307",
|
|
4
4
|
"description": "Senpi runtime plugin for OpenClaw",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -32,11 +32,13 @@
|
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"@anthropic-ai/sdk": "^0.39.0",
|
|
35
|
+
"@hyperdx/node-opentelemetry": "^0.11.0",
|
|
35
36
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
37
|
+
"@opentelemetry/api": "^1.9.1",
|
|
38
|
+
"@opentelemetry/instrumentation-undici": "^0.28.0",
|
|
36
39
|
"@sinclair/typebox": "^0.34.0",
|
|
37
40
|
"async-retry": "^1.3.3",
|
|
38
41
|
"bip39": "^3.1.0",
|
|
39
|
-
"dd-trace": "^5.99.1",
|
|
40
42
|
"dotenv": "^16.4.5",
|
|
41
43
|
"fastify": "^5.8.5",
|
|
42
44
|
"mcporter": "^0.7.3",
|
|
@@ -48,6 +50,8 @@
|
|
|
48
50
|
"openclaw": ">=2026.5.0"
|
|
49
51
|
},
|
|
50
52
|
"devDependencies": {
|
|
53
|
+
"@opentelemetry/context-async-hooks": "^2.8.0",
|
|
54
|
+
"@opentelemetry/sdk-trace-base": "^2.8.0",
|
|
51
55
|
"@types/async-retry": "^1.4.9",
|
|
52
56
|
"@types/node": "^22.0.0",
|
|
53
57
|
"tsx": "^4.21.0",
|