@senpi/runtime 3.0.102 → 3.0.106
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/action-events.d.ts +7 -21
- package/dist/actions/action-events.d.ts.map +1 -1
- package/dist/actions/action-events.js +25 -56
- package/dist/actions/action-events.js.map +1 -1
- package/dist/actions/action-runtime-module.d.ts.map +1 -1
- package/dist/actions/action-runtime-module.js +1 -18
- package/dist/actions/action-runtime-module.js.map +1 -1
- package/dist/actions/close-position/close-position.action.d.ts.map +1 -1
- package/dist/actions/close-position/close-position.action.js +12 -86
- package/dist/actions/close-position/close-position.action.js.map +1 -1
- package/dist/actions/event-derivation.d.ts +2 -40
- package/dist/actions/event-derivation.d.ts.map +1 -1
- package/dist/actions/event-derivation.js +9 -189
- package/dist/actions/event-derivation.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 +17 -115
- package/dist/actions/open-position/open-position.action.js.map +1 -1
- package/dist/actions/position-tracker/position-tracker.action.d.ts +1 -2
- package/dist/actions/position-tracker/position-tracker.action.d.ts.map +1 -1
- package/dist/actions/position-tracker/position-tracker.action.js +15 -50
- package/dist/actions/position-tracker/position-tracker.action.js.map +1 -1
- package/dist/dsl/engine-client/event-mapping.d.ts.map +1 -1
- package/dist/dsl/engine-client/event-mapping.js +0 -15
- package/dist/dsl/engine-client/event-mapping.js.map +1 -1
- package/dist/dsl/events/build-closed-event.d.ts +1 -16
- package/dist/dsl/events/build-closed-event.d.ts.map +1 -1
- package/dist/dsl/events/build-closed-event.js +0 -5
- package/dist/dsl/events/build-closed-event.js.map +1 -1
- package/dist/dsl/events/handlers.d.ts +0 -7
- package/dist/dsl/events/handlers.d.ts.map +1 -1
- package/dist/dsl/events/handlers.js +7 -49
- package/dist/dsl/events/handlers.js.map +1 -1
- package/dist/dsl/monitor/create-monitor.d.ts.map +1 -1
- package/dist/dsl/monitor/create-monitor.js +0 -7
- package/dist/dsl/monitor/create-monitor.js.map +1 -1
- package/dist/dsl/monitor/dsl-failure-events.d.ts +0 -2
- package/dist/dsl/monitor/dsl-failure-events.d.ts.map +1 -1
- package/dist/dsl/monitor/dsl-failure-events.js +1 -2
- package/dist/dsl/monitor/dsl-failure-events.js.map +1 -1
- package/dist/dsl/monitor/process-one-position.d.ts +0 -6
- package/dist/dsl/monitor/process-one-position.d.ts.map +1 -1
- package/dist/dsl/monitor/process-one-position.js +21 -55
- package/dist/dsl/monitor/process-one-position.js.map +1 -1
- package/dist/health/types.d.ts +0 -6
- package/dist/health/types.d.ts.map +1 -1
- package/dist/health/types.js.map +1 -1
- package/dist/index.d.ts +4 -16
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +47 -245
- package/dist/index.js.map +1 -1
- package/dist/runtime/domain-events.d.ts.map +1 -1
- package/dist/runtime/domain-events.js +0 -7
- package/dist/runtime/domain-events.js.map +1 -1
- package/dist/runtime/lifecycle-events.d.ts +1 -9
- package/dist/runtime/lifecycle-events.d.ts.map +1 -1
- package/dist/runtime/lifecycle-events.js +2 -11
- package/dist/runtime/lifecycle-events.js.map +1 -1
- package/dist/runtime/reconcile-run.d.ts +0 -2
- package/dist/runtime/reconcile-run.d.ts.map +1 -1
- package/dist/runtime/reconcile-run.js +1 -2
- package/dist/runtime/reconcile-run.js.map +1 -1
- package/dist/runtime/reconcile-state.d.ts +0 -8
- package/dist/runtime/reconcile-state.d.ts.map +1 -1
- package/dist/runtime/reconcile-state.js +1 -3
- package/dist/runtime/reconcile-state.js.map +1 -1
- package/dist/runtime/runtime.d.ts +5 -5
- package/dist/runtime/runtime.d.ts.map +1 -1
- package/dist/runtime/runtime.js +20 -44
- package/dist/runtime/runtime.js.map +1 -1
- package/dist/runtime/strategy-registry.d.ts +1 -0
- package/dist/runtime/strategy-registry.d.ts.map +1 -1
- package/dist/runtime/strategy-registry.js +19 -0
- package/dist/runtime/strategy-registry.js.map +1 -1
- package/dist/runtime/supervisor/long-running-supervisor.d.ts +0 -8
- package/dist/runtime/supervisor/long-running-supervisor.d.ts.map +1 -1
- package/dist/runtime/supervisor/long-running-supervisor.js +0 -1
- package/dist/runtime/supervisor/long-running-supervisor.js.map +1 -1
- package/dist/runtime/update-apply-deps.d.ts +6 -0
- package/dist/runtime/update-apply-deps.d.ts.map +1 -1
- package/dist/runtime/update-apply-deps.js +8 -19
- package/dist/runtime/update-apply-deps.js.map +1 -1
- package/dist/runtime-api/intake/intake-events.d.ts +3 -3
- package/dist/runtime-api/intake/intake-events.d.ts.map +1 -1
- package/dist/runtime-api/intake/intake-events.js +3 -21
- package/dist/runtime-api/intake/intake-events.js.map +1 -1
- package/dist/runtime-api/intake/intake-mount.d.ts +2 -0
- package/dist/runtime-api/intake/intake-mount.d.ts.map +1 -1
- package/dist/runtime-api/intake/intake-mount.js +10 -57
- package/dist/runtime-api/intake/intake-mount.js.map +1 -1
- package/dist/runtime-api/intake/process-signal-post.d.ts +0 -11
- package/dist/runtime-api/intake/process-signal-post.d.ts.map +1 -1
- package/dist/runtime-api/intake/process-signal-post.js +8 -39
- package/dist/runtime-api/intake/process-signal-post.js.map +1 -1
- package/dist/runtime-api/intake/scaffold-error-router.d.ts +43 -0
- package/dist/runtime-api/intake/scaffold-error-router.d.ts.map +1 -0
- package/dist/runtime-api/intake/scaffold-error-router.js +63 -0
- package/dist/runtime-api/intake/scaffold-error-router.js.map +1 -0
- package/dist/runtime-api/intake/scanner-launcher.d.ts.map +1 -1
- package/dist/runtime-api/intake/scanner-launcher.js +0 -12
- package/dist/runtime-api/intake/scanner-launcher.js.map +1 -1
- package/dist/runtime-api/intake/types.d.ts +1 -22
- package/dist/runtime-api/intake/types.d.ts.map +1 -1
- package/dist/runtime-api/routes/signals.d.ts.map +1 -1
- package/dist/runtime-api/routes/signals.js +1 -7
- package/dist/runtime-api/routes/signals.js.map +1 -1
- package/dist/runtime-api/routes/signals.schema.d.ts +0 -1
- package/dist/runtime-api/routes/signals.schema.d.ts.map +1 -1
- package/dist/runtime-api/routes/signals.schema.js +0 -6
- package/dist/runtime-api/routes/signals.schema.js.map +1 -1
- package/dist/scanners/engine/engine.d.ts +0 -5
- package/dist/scanners/engine/engine.d.ts.map +1 -1
- package/dist/scanners/engine/engine.js +2 -30
- package/dist/scanners/engine/engine.js.map +1 -1
- package/dist/scanners/external/scaffold/delivery.py +12 -79
- package/dist/scanners/external/scaffold/scaffold.py +9 -63
- package/dist/scanners/external/scaffold/senpi_mcp.py +81 -10
- package/dist/scanners/external-scanner-receiver.d.ts +0 -11
- package/dist/scanners/external-scanner-receiver.d.ts.map +1 -1
- package/dist/scanners/external-scanner-receiver.js.map +1 -1
- package/dist/scanners/runtime-module.d.ts.map +1 -1
- package/dist/scanners/runtime-module.js +0 -5
- package/dist/scanners/runtime-module.js.map +1 -1
- package/dist/senpi/client.d.ts.map +1 -1
- package/dist/senpi/client.js +4 -12
- package/dist/senpi/client.js.map +1 -1
- package/dist/senpi/types.d.ts +0 -5
- package/dist/senpi/types.d.ts.map +1 -1
- package/dist/skills-manager/manifest.d.ts.map +1 -1
- package/dist/skills-manager/manifest.js +3 -12
- package/dist/skills-manager/manifest.js.map +1 -1
- package/dist/skills-manager/skills-manifest.json +3 -3
- package/dist/telemetry/event-builders.d.ts +6 -74
- package/dist/telemetry/event-builders.d.ts.map +1 -1
- package/dist/telemetry/event-builders.js +11 -108
- package/dist/telemetry/event-builders.js.map +1 -1
- package/dist/telemetry/event-store.d.ts +0 -6
- package/dist/telemetry/event-store.d.ts.map +1 -1
- package/dist/telemetry/event-store.js +6 -6
- package/dist/telemetry/event-store.js.map +1 -1
- package/dist/telemetry/metrics.d.ts +2 -14
- package/dist/telemetry/metrics.d.ts.map +1 -1
- package/dist/telemetry/metrics.js +4 -19
- package/dist/telemetry/metrics.js.map +1 -1
- package/dist/telemetry/tracing.d.ts +0 -9
- package/dist/telemetry/tracing.d.ts.map +1 -1
- package/dist/telemetry/tracing.js +0 -36
- package/dist/telemetry/tracing.js.map +1 -1
- package/dist/types/dsl/index.d.ts +0 -11
- package/dist/types/dsl/index.d.ts.map +1 -1
- package/dist/types/scanner.d.ts +0 -9
- package/dist/types/scanner.d.ts.map +1 -1
- package/dist/utils/event-catalog.d.ts +0 -81
- package/dist/utils/event-catalog.d.ts.map +1 -1
- package/dist/utils/event-catalog.js +0 -1
- package/dist/utils/event-catalog.js.map +1 -1
- package/dist/utils/keyed-mutex.d.ts +1 -3
- package/dist/utils/keyed-mutex.d.ts.map +1 -1
- package/dist/utils/keyed-mutex.js +1 -3
- package/dist/utils/keyed-mutex.js.map +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/dist/actions/skipped-signal-results.d.ts +0 -16
- package/dist/actions/skipped-signal-results.d.ts.map +0 -1
- package/dist/actions/skipped-signal-results.js +0 -47
- package/dist/actions/skipped-signal-results.js.map +0 -1
- package/dist/scanners/external/scaffold/ulid.py +0 -58
- package/dist/telemetry/live-telemetry.d.ts +0 -158
- package/dist/telemetry/live-telemetry.d.ts.map +0 -1
- package/dist/telemetry/live-telemetry.js +0 -581
- package/dist/telemetry/live-telemetry.js.map +0 -1
- package/dist/telemetry/scanner-stats.d.ts +0 -136
- package/dist/telemetry/scanner-stats.d.ts.map +0 -1
- package/dist/telemetry/scanner-stats.js +0 -304
- package/dist/telemetry/scanner-stats.js.map +0 -1
- package/dist/telemetry/strategy-topology.d.ts +0 -99
- package/dist/telemetry/strategy-topology.d.ts.map +0 -1
- package/dist/telemetry/strategy-topology.js +0 -93
- package/dist/telemetry/strategy-topology.js.map +0 -1
- package/dist/telemetry/telemetry-publisher.d.ts +0 -63
- package/dist/telemetry/telemetry-publisher.d.ts.map +0 -1
- package/dist/telemetry/telemetry-publisher.js +0 -229
- package/dist/telemetry/telemetry-publisher.js.map +0 -1
- package/dist/telemetry/timeline-buffer.d.ts +0 -95
- package/dist/telemetry/timeline-buffer.d.ts.map +0 -1
- package/dist/telemetry/timeline-buffer.js +0 -237
- package/dist/telemetry/timeline-buffer.js.map +0 -1
- package/dist/utils/ulid.d.ts +0 -9
- package/dist/utils/ulid.d.ts.map +0 -1
- package/dist/utils/ulid.js +0 -43
- package/dist/utils/ulid.js.map +0 -1
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The `plugin.senpi.scanner.stats` payload: per-scanner activity counters plus the per-scanner state
|
|
3
|
-
* a summary screen needs, built as one pure function of its inputs.
|
|
4
|
-
*
|
|
5
|
-
* Two halves, from two sources:
|
|
6
|
-
* - The **counters** are computed on demand by scanning the recent-window buffer — no second data
|
|
7
|
-
* structure, no pre-aggregation on the write path, no retention setting of their own. Records are
|
|
8
|
-
* grouped by `(address, scannerId)` and bucketed by timestamp, because `scannerId` alone collides
|
|
9
|
-
* across strategies (the engine keys on the same pair).
|
|
10
|
-
* - The **state fields** are not derivable from records at all — health, schedule and liveness live in
|
|
11
|
-
* the health/system-state providers — so they arrive as {@link ScannerStateSnapshot} inputs and are
|
|
12
|
-
* joined on the same key. A scanner known to only one side still gets an entry: counters without
|
|
13
|
-
* state read `unknown`, state without counters carries an empty bucket list.
|
|
14
|
-
*
|
|
15
|
-
* The box never picks the window. It emits fixed-width buckets and the frontend sums however many it
|
|
16
|
-
* wants, so one payload serves every client and every window at once. `since` is what makes that sum
|
|
17
|
-
* honest: it is the stamp of the oldest record still held, and when nothing is held it is the moment
|
|
18
|
-
* this epoch began watching — "I have been watching since X and nothing happened" is a different
|
|
19
|
-
* statement from "no data", and only one of them is true.
|
|
20
|
-
*/
|
|
21
|
-
import type { ComponentHealth } from "../health/types.js";
|
|
22
|
-
import type { TimelineRecord } from "./timeline-buffer.js";
|
|
23
|
-
/**
|
|
24
|
-
* Bucket width in ms. It sets two things at once: the payload cost per active scanner, and the finest
|
|
25
|
-
* window a frontend can ask for (it can sum buckets, never split one).
|
|
26
|
-
*
|
|
27
|
-
* 15s divides the windows a summary screen actually shows — 1m is 4 buckets, 5m is 20, 15m is 60 — so
|
|
28
|
-
* no rendered window ever needs a partial bucket. It is also about two ticks of the fastest scanner
|
|
29
|
-
* the runtime will schedule (built-in cadence is floored at 7s in `runtime-schema.ts`), which keeps a
|
|
30
|
-
* bucket from hiding a run while still letting "it went quiet 30 seconds ago" show up. Buckets are
|
|
31
|
-
* emitted sparsely, so an idle scanner costs nothing regardless of this value.
|
|
32
|
-
*
|
|
33
|
-
* This is the number to change once real payload sizes are measured — see also the `bucketMs` input,
|
|
34
|
-
* which overrides it per call.
|
|
35
|
-
*/
|
|
36
|
-
export declare const DEFAULT_BUCKET_MS = 15000;
|
|
37
|
-
/**
|
|
38
|
-
* Ceiling on the buckets one payload may carry, across every scanner and every address.
|
|
39
|
-
*
|
|
40
|
-
* Buckets are the only part of this payload that grows without a bound of its own. A scanner
|
|
41
|
-
* producing one counted record per run fills one bucket per run, so a retention window of N records
|
|
42
|
-
* is N buckets on a single scanner, and every address the box runs adds its own series — the state
|
|
43
|
-
* rows do not, because there is one per registered scanner. One bucket serialises to about sixty
|
|
44
|
-
* bytes, so this budgets them at roughly 60 KiB and leaves the rest of the frame to the rows.
|
|
45
|
-
*
|
|
46
|
-
* Nothing downstream survives a breach: the bridge's north read limit is 256 KiB and exceeding it
|
|
47
|
-
* closes the client socket, which loses the stream rather than one sample.
|
|
48
|
-
*
|
|
49
|
-
* The trim cuts at a bucket boundary, so a payload may carry up to one extra bucket per scanner —
|
|
50
|
-
* splitting a timestamp across scanners would misalign the series a client sums.
|
|
51
|
-
*/
|
|
52
|
-
export declare const MAX_STATS_BUCKETS = 1000;
|
|
53
|
-
/**
|
|
54
|
-
* The four counters, which sum to the decisions counted in the bucket. `closes` is its own field
|
|
55
|
-
* because an accepted close is none of the other three: it opened nothing, it was not declined, and
|
|
56
|
-
* nothing failed — folding it into any of them replaces one wrong number with another.
|
|
57
|
-
*/
|
|
58
|
-
export interface ScannerStatsBucket {
|
|
59
|
-
/** Bucket start, epoch ms, aligned to a `bucketMs` grid so buckets line up across scanners. */
|
|
60
|
-
readonly t: number;
|
|
61
|
-
readonly opens: number;
|
|
62
|
-
readonly closes: number;
|
|
63
|
-
readonly skips: number;
|
|
64
|
-
readonly errors: number;
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* One scanner's state as the health/system-state providers report it. These fields have no
|
|
68
|
-
* representation in the buffer, so the builder takes them rather than reaching into the runtime.
|
|
69
|
-
*
|
|
70
|
-
* `inFlightSince` is a timestamp, not a boolean: a scan running for 200ms is normal and one running
|
|
71
|
-
* for four minutes on a 15s interval is hung, and a boolean cannot tell those apart.
|
|
72
|
-
*/
|
|
73
|
-
export interface ScannerStateSnapshot {
|
|
74
|
-
readonly address: string;
|
|
75
|
-
readonly scannerId: string;
|
|
76
|
-
readonly health: ComponentHealth;
|
|
77
|
-
readonly degradedReason?: string | null;
|
|
78
|
-
readonly enabled: boolean;
|
|
79
|
-
readonly inFlightSince: number | null;
|
|
80
|
-
readonly lastRunStartedAt: number | null;
|
|
81
|
-
readonly lastRunFinishedAt: number | null;
|
|
82
|
-
readonly lastRunStatus: string | null;
|
|
83
|
-
readonly lastRunReason: string | null;
|
|
84
|
-
readonly nextRunAt: number | null;
|
|
85
|
-
readonly lastAliveAt: number | null;
|
|
86
|
-
/** Null when no supervision row covers this scanner — a known zero is a different statement. */
|
|
87
|
-
readonly restartCount: number | null;
|
|
88
|
-
}
|
|
89
|
-
/**
|
|
90
|
-
* One scanner on the wire. `scannerId` is absent — not null — when the records could not be
|
|
91
|
-
* attributed to a scanner; the client buckets those separately rather than seeing them folded into a
|
|
92
|
-
* named scanner's numbers. Every state field is `null` when no snapshot covers this scanner, which is
|
|
93
|
-
* a different statement from a known `false` or a known `0`.
|
|
94
|
-
*/
|
|
95
|
-
export interface ScannerStatsEntry {
|
|
96
|
-
readonly address: string;
|
|
97
|
-
readonly scannerId?: string;
|
|
98
|
-
readonly health: ComponentHealth;
|
|
99
|
-
readonly degradedReason: string | null;
|
|
100
|
-
readonly enabled: boolean | null;
|
|
101
|
-
readonly inFlightSince: number | null;
|
|
102
|
-
readonly lastRunStartedAt: number | null;
|
|
103
|
-
readonly lastRunFinishedAt: number | null;
|
|
104
|
-
readonly lastRunStatus: string | null;
|
|
105
|
-
readonly lastRunReason: string | null;
|
|
106
|
-
readonly nextRunAt: number | null;
|
|
107
|
-
readonly lastAliveAt: number | null;
|
|
108
|
-
readonly restartCount: number | null;
|
|
109
|
-
/** Only buckets with activity, ascending by `t`. A gap means zero, not missing. */
|
|
110
|
-
readonly buckets: ScannerStatsBucket[];
|
|
111
|
-
}
|
|
112
|
-
export interface ScannerStatsPayload {
|
|
113
|
-
readonly v: 1;
|
|
114
|
-
readonly epoch: string;
|
|
115
|
-
readonly sampledAt: number;
|
|
116
|
-
/** Oldest record still held, or the buffer's start time when nothing is held. Never absent. */
|
|
117
|
-
readonly since: number;
|
|
118
|
-
readonly bucketMs: number;
|
|
119
|
-
readonly scanners: ScannerStatsEntry[];
|
|
120
|
-
}
|
|
121
|
-
export interface BuildScannerStatsInput {
|
|
122
|
-
/** The buffer's epoch ULID. A change means the box restarted. */
|
|
123
|
-
readonly epoch: string;
|
|
124
|
-
/** Every record the counters are derived from — the buffer's full held window. */
|
|
125
|
-
readonly records: readonly TimelineRecord[];
|
|
126
|
-
/** Per-scanner state from the health/system-state providers. */
|
|
127
|
-
readonly scannerState: readonly ScannerStateSnapshot[];
|
|
128
|
-
/** When this epoch began watching. Used as `since` only while the buffer holds nothing. */
|
|
129
|
-
readonly bufferStartedAt: number;
|
|
130
|
-
readonly sampledAt: number;
|
|
131
|
-
/** Overrides {@link DEFAULT_BUCKET_MS}. Ignored unless it is a finite width of at least 1ms. */
|
|
132
|
-
readonly bucketMs?: number;
|
|
133
|
-
}
|
|
134
|
-
/** Build one `plugin.senpi.scanner.stats` payload. Pure: no timers, no clocks, no runtime reads. */
|
|
135
|
-
export declare function buildScannerStats(input: BuildScannerStatsInput): ScannerStatsPayload;
|
|
136
|
-
//# sourceMappingURL=scanner-stats.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"scanner-stats.d.ts","sourceRoot":"","sources":["../../src/telemetry/scanner-stats.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,304 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The `plugin.senpi.scanner.stats` payload: per-scanner activity counters plus the per-scanner state
|
|
3
|
-
* a summary screen needs, built as one pure function of its inputs.
|
|
4
|
-
*
|
|
5
|
-
* Two halves, from two sources:
|
|
6
|
-
* - The **counters** are computed on demand by scanning the recent-window buffer — no second data
|
|
7
|
-
* structure, no pre-aggregation on the write path, no retention setting of their own. Records are
|
|
8
|
-
* grouped by `(address, scannerId)` and bucketed by timestamp, because `scannerId` alone collides
|
|
9
|
-
* across strategies (the engine keys on the same pair).
|
|
10
|
-
* - The **state fields** are not derivable from records at all — health, schedule and liveness live in
|
|
11
|
-
* the health/system-state providers — so they arrive as {@link ScannerStateSnapshot} inputs and are
|
|
12
|
-
* joined on the same key. A scanner known to only one side still gets an entry: counters without
|
|
13
|
-
* state read `unknown`, state without counters carries an empty bucket list.
|
|
14
|
-
*
|
|
15
|
-
* The box never picks the window. It emits fixed-width buckets and the frontend sums however many it
|
|
16
|
-
* wants, so one payload serves every client and every window at once. `since` is what makes that sum
|
|
17
|
-
* honest: it is the stamp of the oldest record still held, and when nothing is held it is the moment
|
|
18
|
-
* this epoch began watching — "I have been watching since X and nothing happened" is a different
|
|
19
|
-
* statement from "no data", and only one of them is true.
|
|
20
|
-
*/
|
|
21
|
-
import { normalizeStrategyAddress } from "./event-store.js";
|
|
22
|
-
import { createLogger } from "../utils/logger.js";
|
|
23
|
-
const logger = createLogger("[senpi-core][scanner-stats]");
|
|
24
|
-
/**
|
|
25
|
-
* Bucket width in ms. It sets two things at once: the payload cost per active scanner, and the finest
|
|
26
|
-
* window a frontend can ask for (it can sum buckets, never split one).
|
|
27
|
-
*
|
|
28
|
-
* 15s divides the windows a summary screen actually shows — 1m is 4 buckets, 5m is 20, 15m is 60 — so
|
|
29
|
-
* no rendered window ever needs a partial bucket. It is also about two ticks of the fastest scanner
|
|
30
|
-
* the runtime will schedule (built-in cadence is floored at 7s in `runtime-schema.ts`), which keeps a
|
|
31
|
-
* bucket from hiding a run while still letting "it went quiet 30 seconds ago" show up. Buckets are
|
|
32
|
-
* emitted sparsely, so an idle scanner costs nothing regardless of this value.
|
|
33
|
-
*
|
|
34
|
-
* This is the number to change once real payload sizes are measured — see also the `bucketMs` input,
|
|
35
|
-
* which overrides it per call.
|
|
36
|
-
*/
|
|
37
|
-
export const DEFAULT_BUCKET_MS = 15_000;
|
|
38
|
-
/**
|
|
39
|
-
* Ceiling on the buckets one payload may carry, across every scanner and every address.
|
|
40
|
-
*
|
|
41
|
-
* Buckets are the only part of this payload that grows without a bound of its own. A scanner
|
|
42
|
-
* producing one counted record per run fills one bucket per run, so a retention window of N records
|
|
43
|
-
* is N buckets on a single scanner, and every address the box runs adds its own series — the state
|
|
44
|
-
* rows do not, because there is one per registered scanner. One bucket serialises to about sixty
|
|
45
|
-
* bytes, so this budgets them at roughly 60 KiB and leaves the rest of the frame to the rows.
|
|
46
|
-
*
|
|
47
|
-
* Nothing downstream survives a breach: the bridge's north read limit is 256 KiB and exceeding it
|
|
48
|
-
* closes the client socket, which loses the stream rather than one sample.
|
|
49
|
-
*
|
|
50
|
-
* The trim cuts at a bucket boundary, so a payload may carry up to one extra bucket per scanner —
|
|
51
|
-
* splitting a timestamp across scanners would misalign the series a client sums.
|
|
52
|
-
*/
|
|
53
|
-
export const MAX_STATS_BUCKETS = 1_000;
|
|
54
|
-
/** The one event carrying an action decision, one record per evaluation. */
|
|
55
|
-
const OUTCOME_EVENT = "signal.outcome";
|
|
56
|
-
/** Scanner-side tick failures: the scanner produced nothing, and that is not a decision. */
|
|
57
|
-
const ERROR_EVENTS = new Set(["scaffold.tick_error", "scaffold.tick_timeout"]);
|
|
58
|
-
const ATTR_OUTCOME_RESULT = "senpi.outcome.result";
|
|
59
|
-
const ATTR_ACTION_TYPE = "senpi.action.type";
|
|
60
|
-
const ATTR_SCANNER_NAME = "senpi.scanner.name";
|
|
61
|
-
const ATTR_SCANNER_ID = "senpi.scanner.id";
|
|
62
|
-
/** The only disposition meaning an order was actually placed — of either kind. */
|
|
63
|
-
const RESULT_ACCEPTED = "accepted";
|
|
64
|
-
/** The disposition meaning the attempt itself failed. */
|
|
65
|
-
const RESULT_ERROR = "error";
|
|
66
|
-
/** The producer's closed disposition set; anything outside it is drift worth a warning. */
|
|
67
|
-
const KNOWN_RESULTS = new Set([RESULT_ACCEPTED, "rejected", "blocked", RESULT_ERROR]);
|
|
68
|
-
/** The `ActionType` values that tell an accepted order which bucket it belongs in. */
|
|
69
|
-
const ACTION_OPEN = "OPEN_POSITION";
|
|
70
|
-
const ACTION_CLOSE = "CLOSE_POSITION";
|
|
71
|
-
/** Separates the two halves of a group key. Never appears in a wallet or a scanner id. */
|
|
72
|
-
const KEY_SEP = "\u0000";
|
|
73
|
-
/**
|
|
74
|
-
* Distinct unrecognised dispositions already warned about, process-wide. Keyed by value rather than
|
|
75
|
-
* one-shot so a second kind of drift is still reported, and never per record — the flush tick can
|
|
76
|
-
* carry thousands of them.
|
|
77
|
-
*/
|
|
78
|
-
const warnedResults = new Set();
|
|
79
|
-
/** The same, for action types. The empty string keys an outcome that carried none. */
|
|
80
|
-
const warnedActionTypes = new Set();
|
|
81
|
-
/**
|
|
82
|
-
* The worst overflow warned about so far. A box permanently over the cap would otherwise warn on
|
|
83
|
-
* every sample tick, and one that is getting worse would otherwise say so only once.
|
|
84
|
-
*/
|
|
85
|
-
let warnedBucketOverflow = 0;
|
|
86
|
-
function key(address, scannerId) {
|
|
87
|
-
return `${address}${KEY_SEP}${scannerId ?? ""}`;
|
|
88
|
-
}
|
|
89
|
-
/**
|
|
90
|
-
* Rule 1: `senpi.scanner.id` carries the stable id on `signal.outcome` but the ephemeral, per-spawn id
|
|
91
|
-
* on the scaffold tick events, where the stable value rides `senpi.scanner.name`. Reading the id
|
|
92
|
-
* unqualified splits one scanner into two on every restart. The tap resolves this; the fallback here
|
|
93
|
-
* keeps a record whose tap did not from landing in the unattributed bucket.
|
|
94
|
-
*/
|
|
95
|
-
function resolveScannerId(record) {
|
|
96
|
-
if (typeof record.scannerId === "string" && record.scannerId !== "")
|
|
97
|
-
return record.scannerId;
|
|
98
|
-
const name = record.attrs?.[ATTR_SCANNER_NAME];
|
|
99
|
-
if (typeof name === "string" && name !== "")
|
|
100
|
-
return name;
|
|
101
|
-
const id = record.attrs?.[ATTR_SCANNER_ID];
|
|
102
|
-
if (typeof id === "string" && id !== "")
|
|
103
|
-
return id;
|
|
104
|
-
return undefined;
|
|
105
|
-
}
|
|
106
|
-
function warnUnknownResult(result) {
|
|
107
|
-
if (warnedResults.has(result))
|
|
108
|
-
return;
|
|
109
|
-
warnedResults.add(result);
|
|
110
|
-
logger.warn("signal.outcome carried an unrecognised disposition; counting it as a skip", {
|
|
111
|
-
[ATTR_OUTCOME_RESULT]: result,
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
function warnUnknownActionType(actionType) {
|
|
115
|
-
if (warnedActionTypes.has(actionType))
|
|
116
|
-
return;
|
|
117
|
-
warnedActionTypes.add(actionType);
|
|
118
|
-
logger.warn("an accepted signal.outcome named no action type this counter knows; counting it as an open", {
|
|
119
|
-
[ATTR_ACTION_TYPE]: actionType === "" ? "(absent)" : actionType,
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
/**
|
|
123
|
-
* Which of the two order buckets an accepted outcome feeds.
|
|
124
|
-
*
|
|
125
|
-
* `closes` is asserted positively — a close is the case that has to be proven, because everything
|
|
126
|
-
* that is not a close reads as it always did. An action type that is unrecognised, or absent (the
|
|
127
|
-
* producer omits the attribute rather than stamping a placeholder that would poison a counter),
|
|
128
|
-
* therefore counts as an open: it is the incumbent reading, it keeps the four counters summing to
|
|
129
|
-
* the decisions counted, and it is warned about once per distinct value so the drift is findable
|
|
130
|
-
* rather than dropped or silently folded away.
|
|
131
|
-
*/
|
|
132
|
-
function acceptedCounter(record) {
|
|
133
|
-
const raw = record.attrs?.[ATTR_ACTION_TYPE];
|
|
134
|
-
const actionType = typeof raw === "string" ? raw : "";
|
|
135
|
-
if (actionType === ACTION_CLOSE)
|
|
136
|
-
return "closes";
|
|
137
|
-
if (actionType !== ACTION_OPEN)
|
|
138
|
-
warnUnknownActionType(actionType);
|
|
139
|
-
return "opens";
|
|
140
|
-
}
|
|
141
|
-
/**
|
|
142
|
-
* Which counter a record feeds, or `null` when it feeds none.
|
|
143
|
-
*
|
|
144
|
-
* An order leaving the box is asserted positively: `accepted` is the single disposition that means
|
|
145
|
-
* one did (`disposition()` in `event-builders.ts` maps the executed result to it), and the action
|
|
146
|
-
* type then says whether it opened a position or closed one. Everything else that reached a decision
|
|
147
|
-
* placed no order, so `rejected` (actively gated) and `blocked` (not attempted) both read as a skip,
|
|
148
|
-
* and so does any value the producer adds later — an unrecognised disposition is warned about rather
|
|
149
|
-
* than silently dropped, because opens + closes + skips + errors must stay equal to the decisions
|
|
150
|
-
* counted or the four numbers cannot be read together.
|
|
151
|
-
*
|
|
152
|
-
* A failed attempt is an `error`, not an open or a close (nothing was placed) and not a skip (it was
|
|
153
|
-
* not declined) — which is as true of a close as of an open, so the action type splits the accepted
|
|
154
|
-
* case only. The scaffold tick failures land in the same counter: from the summary screen both mean
|
|
155
|
-
* the scanner lost work to a fault.
|
|
156
|
-
*/
|
|
157
|
-
function classify(record) {
|
|
158
|
-
if (ERROR_EVENTS.has(record.name))
|
|
159
|
-
return "errors";
|
|
160
|
-
if (record.name !== OUTCOME_EVENT)
|
|
161
|
-
return null;
|
|
162
|
-
const raw = record.attrs?.[ATTR_OUTCOME_RESULT];
|
|
163
|
-
const result = typeof raw === "string" ? raw : "";
|
|
164
|
-
if (result === RESULT_ACCEPTED)
|
|
165
|
-
return acceptedCounter(record);
|
|
166
|
-
if (result === RESULT_ERROR)
|
|
167
|
-
return "errors";
|
|
168
|
-
if (!KNOWN_RESULTS.has(result))
|
|
169
|
-
warnUnknownResult(result);
|
|
170
|
-
return "skips";
|
|
171
|
-
}
|
|
172
|
-
/**
|
|
173
|
-
* The oldest bucket start the payload may keep, or undefined when everything fits. Chosen so the
|
|
174
|
-
* kept buckets number at most {@link MAX_STATS_BUCKETS}, cut on a timestamp so every scanner's
|
|
175
|
-
* series starts at the same place.
|
|
176
|
-
*/
|
|
177
|
-
function trimCutoff(groups) {
|
|
178
|
-
const starts = [];
|
|
179
|
-
for (const group of groups)
|
|
180
|
-
for (const t of group.buckets.keys())
|
|
181
|
-
starts.push(t);
|
|
182
|
-
if (starts.length <= MAX_STATS_BUCKETS)
|
|
183
|
-
return undefined;
|
|
184
|
-
starts.sort((a, b) => b - a);
|
|
185
|
-
const cutoff = starts[MAX_STATS_BUCKETS - 1];
|
|
186
|
-
const dropped = starts.filter((t) => t < cutoff).length;
|
|
187
|
-
if (dropped > warnedBucketOverflow) {
|
|
188
|
-
warnedBucketOverflow = dropped;
|
|
189
|
-
logger.warn("scanner stats dropped its oldest buckets to keep the payload inside one frame", {
|
|
190
|
-
"senpi.telemetry.buckets_dropped": dropped,
|
|
191
|
-
"senpi.telemetry.buckets_kept": starts.length - dropped,
|
|
192
|
-
"senpi.telemetry.bucket_limit": MAX_STATS_BUCKETS,
|
|
193
|
-
});
|
|
194
|
-
}
|
|
195
|
-
return cutoff;
|
|
196
|
-
}
|
|
197
|
-
function resolveBucketMs(requested) {
|
|
198
|
-
if (requested === undefined)
|
|
199
|
-
return DEFAULT_BUCKET_MS;
|
|
200
|
-
// A zero or negative width makes every bucket key Infinity or NaN, which would collapse the whole
|
|
201
|
-
// series into one unreadable bucket rather than fail loudly.
|
|
202
|
-
return Number.isFinite(requested) && requested >= 1 ? Math.floor(requested) : DEFAULT_BUCKET_MS;
|
|
203
|
-
}
|
|
204
|
-
function groupFor(groups, address, scannerId) {
|
|
205
|
-
const k = key(address, scannerId);
|
|
206
|
-
let group = groups.get(k);
|
|
207
|
-
if (group === undefined) {
|
|
208
|
-
group = { address, ...(scannerId !== undefined ? { scannerId } : {}), buckets: new Map() };
|
|
209
|
-
groups.set(k, group);
|
|
210
|
-
}
|
|
211
|
-
return group;
|
|
212
|
-
}
|
|
213
|
-
/** Address, then scanner id, with the unattributed entry last so it reads as a footnote. */
|
|
214
|
-
function compareEntries(a, b) {
|
|
215
|
-
if (a.address !== b.address)
|
|
216
|
-
return a.address < b.address ? -1 : 1;
|
|
217
|
-
if (a.scannerId === b.scannerId)
|
|
218
|
-
return 0;
|
|
219
|
-
if (a.scannerId === undefined)
|
|
220
|
-
return 1;
|
|
221
|
-
if (b.scannerId === undefined)
|
|
222
|
-
return -1;
|
|
223
|
-
return a.scannerId < b.scannerId ? -1 : 1;
|
|
224
|
-
}
|
|
225
|
-
function toEntry(group, snapshot, cutoff) {
|
|
226
|
-
const buckets = [...group.buckets.entries()]
|
|
227
|
-
.filter(([t]) => cutoff === undefined || t >= cutoff)
|
|
228
|
-
.sort(([a], [b]) => a - b)
|
|
229
|
-
.map(([t, counts]) => ({
|
|
230
|
-
t,
|
|
231
|
-
opens: counts.opens,
|
|
232
|
-
closes: counts.closes,
|
|
233
|
-
skips: counts.skips,
|
|
234
|
-
errors: counts.errors,
|
|
235
|
-
}));
|
|
236
|
-
return {
|
|
237
|
-
address: group.address,
|
|
238
|
-
...(group.scannerId !== undefined ? { scannerId: group.scannerId } : {}),
|
|
239
|
-
health: snapshot?.health ?? "unknown",
|
|
240
|
-
degradedReason: snapshot?.degradedReason ?? null,
|
|
241
|
-
enabled: snapshot?.enabled ?? null,
|
|
242
|
-
inFlightSince: snapshot?.inFlightSince ?? null,
|
|
243
|
-
lastRunStartedAt: snapshot?.lastRunStartedAt ?? null,
|
|
244
|
-
lastRunFinishedAt: snapshot?.lastRunFinishedAt ?? null,
|
|
245
|
-
lastRunStatus: snapshot?.lastRunStatus ?? null,
|
|
246
|
-
lastRunReason: snapshot?.lastRunReason ?? null,
|
|
247
|
-
nextRunAt: snapshot?.nextRunAt ?? null,
|
|
248
|
-
lastAliveAt: snapshot?.lastAliveAt ?? null,
|
|
249
|
-
restartCount: snapshot?.restartCount ?? null,
|
|
250
|
-
buckets,
|
|
251
|
-
};
|
|
252
|
-
}
|
|
253
|
-
/** Build one `plugin.senpi.scanner.stats` payload. Pure: no timers, no clocks, no runtime reads. */
|
|
254
|
-
export function buildScannerStats(input) {
|
|
255
|
-
const bucketMs = resolveBucketMs(input.bucketMs);
|
|
256
|
-
const groups = new Map();
|
|
257
|
-
const snapshots = new Map();
|
|
258
|
-
// Seeded first so a scanner the providers know about still gets an entry when it has produced
|
|
259
|
-
// nothing — a silent scanner is a fact the summary screen must be able to show.
|
|
260
|
-
for (const snapshot of input.scannerState) {
|
|
261
|
-
const address = normalizeStrategyAddress(snapshot.address);
|
|
262
|
-
snapshots.set(key(address, snapshot.scannerId), snapshot);
|
|
263
|
-
groupFor(groups, address, snapshot.scannerId);
|
|
264
|
-
}
|
|
265
|
-
let oldest;
|
|
266
|
-
for (const record of input.records) {
|
|
267
|
-
const ts = record.ts;
|
|
268
|
-
if (!Number.isFinite(ts))
|
|
269
|
-
continue;
|
|
270
|
-
// `since` tracks every held record, not only the counted ones: it describes the window the client
|
|
271
|
-
// is reading, which is the whole buffer.
|
|
272
|
-
if (oldest === undefined || ts < oldest)
|
|
273
|
-
oldest = ts;
|
|
274
|
-
const counter = classify(record);
|
|
275
|
-
if (counter === null)
|
|
276
|
-
continue;
|
|
277
|
-
const group = groupFor(groups, normalizeStrategyAddress(record.address), resolveScannerId(record));
|
|
278
|
-
const t = Math.floor(ts / bucketMs) * bucketMs;
|
|
279
|
-
let counts = group.buckets.get(t);
|
|
280
|
-
if (counts === undefined) {
|
|
281
|
-
counts = { opens: 0, closes: 0, skips: 0, errors: 0 };
|
|
282
|
-
group.buckets.set(t, counts);
|
|
283
|
-
}
|
|
284
|
-
counts[counter] += 1;
|
|
285
|
-
}
|
|
286
|
-
const cutoff = trimCutoff(groups.values());
|
|
287
|
-
const scanners = [...groups.entries()]
|
|
288
|
-
.map(([k, group]) => toEntry(group, snapshots.get(k), cutoff))
|
|
289
|
-
.sort(compareEntries);
|
|
290
|
-
// A broken start time must not put a non-finite `since` on the wire — it serialises to null and
|
|
291
|
-
// the client's truncation check silently stops working.
|
|
292
|
-
const held = oldest ?? (Number.isFinite(input.bufferStartedAt) ? input.bufferStartedAt : input.sampledAt);
|
|
293
|
-
return {
|
|
294
|
-
v: 1,
|
|
295
|
-
epoch: input.epoch,
|
|
296
|
-
sampledAt: input.sampledAt,
|
|
297
|
-
// `since` labels the counters, so a trim moves it: the buckets no longer reach back to the
|
|
298
|
-
// oldest record held, and a client summing them from an older mark would read the gap as zeros.
|
|
299
|
-
since: cutoff !== undefined && cutoff > held ? cutoff : held,
|
|
300
|
-
bucketMs,
|
|
301
|
-
scanners,
|
|
302
|
-
};
|
|
303
|
-
}
|
|
304
|
-
//# sourceMappingURL=scanner-stats.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"scanner-stats.js","sourceRoot":"","sources":["../../src/telemetry/scanner-stats.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Builder for the `plugin.senpi.strategies` payload — the topology view of the live telemetry
|
|
3
|
-
* stream: which strategies exist, whether each is up, how healthy it is, and which scanners it
|
|
4
|
-
* declares.
|
|
5
|
-
*
|
|
6
|
-
* Pure. Every fact arrives as an input, nothing is read from the registry or a runtime handle here,
|
|
7
|
-
* so the shape on the wire is testable without booting a runtime.
|
|
8
|
-
*
|
|
9
|
-
* Two properties this module exists to hold:
|
|
10
|
-
* - `status` distinguishes a runtime that is down from one that is up but has no live entry
|
|
11
|
-
* scanners. The second reads as healthy unless it is named, and it is not.
|
|
12
|
-
* - The raw scanner `config` never reaches the wire. It is an unbounded passthrough record and it
|
|
13
|
-
* has been through `resolveEnvInObject`, so it may hold resolved secrets. Only the declared,
|
|
14
|
-
* typed fields below are copied, each by name.
|
|
15
|
-
*/
|
|
16
|
-
import type { ComponentHealth } from "../health/types.js";
|
|
17
|
-
import type { RuntimeScanner } from "../runtime/runtime-schema.js";
|
|
18
|
-
/** Payload schema version. Bump when a field changes meaning, not when one is added. */
|
|
19
|
-
export declare const STRATEGY_TOPOLOGY_VERSION = 1;
|
|
20
|
-
/**
|
|
21
|
-
* `stopped` — no runtime handle.
|
|
22
|
-
* `running_no_entry_scanners` — up, but its declared external scanners never wired, so it produces
|
|
23
|
-
* no entry signals.
|
|
24
|
-
* `running` — up and wired.
|
|
25
|
-
*/
|
|
26
|
-
export type StrategyStatus = "running" | "stopped" | "running_no_entry_scanners";
|
|
27
|
-
/** Membership test satisfied by both a `Map` and a `Set` — the two shapes the plugin holds. */
|
|
28
|
-
export interface IdMembership {
|
|
29
|
-
has(id: string): boolean;
|
|
30
|
-
}
|
|
31
|
-
/** Engine registration facts for one scanner. Absent when the runtime has no live handle. */
|
|
32
|
-
export interface TopologyScannerRegistration {
|
|
33
|
-
enabled: boolean;
|
|
34
|
-
scheduleMode: "interval" | "external";
|
|
35
|
-
intervalSeconds: number | null;
|
|
36
|
-
configVersion: number | null;
|
|
37
|
-
}
|
|
38
|
-
export interface TopologyScannerSource {
|
|
39
|
-
/**
|
|
40
|
-
* The parsed YAML scanner entry. Only `name`, `type`, `interval_seconds` and
|
|
41
|
-
* `signal_data_schema` are ever read from it — `config`, `advanced`, `inputs` and any
|
|
42
|
-
* passthrough key stay off the wire.
|
|
43
|
-
*/
|
|
44
|
-
entry: RuntimeScanner;
|
|
45
|
-
registration?: TopologyScannerRegistration;
|
|
46
|
-
}
|
|
47
|
-
export interface TopologyStrategySource {
|
|
48
|
-
runtimeId: string;
|
|
49
|
-
/** The config wallet as written. Normalized to the wire form by the builder — see below. */
|
|
50
|
-
address: string;
|
|
51
|
-
name: string;
|
|
52
|
-
scanners: TopologyScannerSource[];
|
|
53
|
-
}
|
|
54
|
-
/** The slice of `RuntimeHealthStatus` the topology reads. `startedAt` is ISO, as health emits it. */
|
|
55
|
-
export interface RuntimeHealthLike {
|
|
56
|
-
health: ComponentHealth;
|
|
57
|
-
startedAt?: string;
|
|
58
|
-
}
|
|
59
|
-
export interface BuildStrategyTopologyInput {
|
|
60
|
-
/** The buffer's epoch ULID. A change means the plugin restarted. */
|
|
61
|
-
epoch: string;
|
|
62
|
-
strategies: TopologyStrategySource[];
|
|
63
|
-
/** Runtimes holding a live handle. Everything else is stopped. */
|
|
64
|
-
runningRuntimeIds: IdMembership;
|
|
65
|
-
/** Runtimes that are up but whose entry scanners never wired. */
|
|
66
|
-
unwiredRuntimeIds: IdMembership;
|
|
67
|
-
/** Per-runtime health lookup, keyed on `runtimeId` the way `senpi.getHealthStatus` keys. */
|
|
68
|
-
readHealth?: (runtimeId: string) => RuntimeHealthLike | undefined;
|
|
69
|
-
/** Injectable clock for deterministic tests. */
|
|
70
|
-
now?: () => number;
|
|
71
|
-
}
|
|
72
|
-
export interface StrategyTopologyScanner {
|
|
73
|
-
/** The engine id — the YAML `name:` field. Unique within a runtime, not across runtimes. */
|
|
74
|
-
scannerId: string;
|
|
75
|
-
scheduleMode: "interval" | "external";
|
|
76
|
-
enabled: boolean;
|
|
77
|
-
intervalSeconds: number | null;
|
|
78
|
-
configVersion: number | null;
|
|
79
|
-
signalDataSchema: Record<string, unknown> | null;
|
|
80
|
-
}
|
|
81
|
-
export interface StrategyTopologyEntry {
|
|
82
|
-
runtimeId: string;
|
|
83
|
-
address: string;
|
|
84
|
-
name: string;
|
|
85
|
-
status: StrategyStatus;
|
|
86
|
-
health: ComponentHealth;
|
|
87
|
-
/** Epoch ms, converted from health's ISO stamp; null when health is unavailable. */
|
|
88
|
-
startedAt: number | null;
|
|
89
|
-
scanners: StrategyTopologyScanner[];
|
|
90
|
-
}
|
|
91
|
-
export interface StrategyTopologyPayload {
|
|
92
|
-
v: number;
|
|
93
|
-
epoch: string;
|
|
94
|
-
generatedAt: number;
|
|
95
|
-
strategies: StrategyTopologyEntry[];
|
|
96
|
-
}
|
|
97
|
-
/** Builds one `plugin.senpi.strategies` payload from an already-gathered set of facts. */
|
|
98
|
-
export declare function buildStrategyTopology(input: BuildStrategyTopologyInput): StrategyTopologyPayload;
|
|
99
|
-
//# sourceMappingURL=strategy-topology.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"strategy-topology.d.ts","sourceRoot":"","sources":["../../src/telemetry/strategy-topology.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAInE,wFAAwF;AACxF,eAAO,MAAM,yBAAyB,IAAI,CAAC;AAE3C;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,SAAS,GAAG,2BAA2B,CAAC;AAEjF,+FAA+F;AAC/F,MAAM,WAAW,YAAY;IAC3B,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;CAC1B;AAED,6FAA6F;AAC7F,MAAM,WAAW,2BAA2B;IAC1C,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,UAAU,GAAG,UAAU,CAAC;IACtC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED,MAAM,WAAW,qBAAqB;IACpC;;;;OAIG;IACH,KAAK,EAAE,cAAc,CAAC;IACtB,YAAY,CAAC,EAAE,2BAA2B,CAAC;CAC5C;AAED,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,4FAA4F;IAC5F,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,qBAAqB,EAAE,CAAC;CACnC;AAED,qGAAqG;AACrG,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,eAAe,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,0BAA0B;IACzC,oEAAoE;IACpE,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,sBAAsB,EAAE,CAAC;IACrC,kEAAkE;IAClE,iBAAiB,EAAE,YAAY,CAAC;IAChC,iEAAiE;IACjE,iBAAiB,EAAE,YAAY,CAAC;IAChC,4FAA4F;IAC5F,UAAU,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,iBAAiB,GAAG,SAAS,CAAC;IAClE,gDAAgD;IAChD,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,uBAAuB;IACtC,4FAA4F;IAC5F,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,UAAU,GAAG,UAAU,CAAC;IACtC,OAAO,EAAE,OAAO,CAAC;IACjB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAClD;AAED,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,cAAc,CAAC;IACvB,MAAM,EAAE,eAAe,CAAC;IACxB,oFAAoF;IACpF,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,EAAE,uBAAuB,EAAE,CAAC;CACrC;AAED,MAAM,WAAW,uBAAuB;IACtC,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,qBAAqB,EAAE,CAAC;CACrC;AAwDD,0FAA0F;AAC1F,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,0BAA0B,GAChC,uBAAuB,CAwBzB"}
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Builder for the `plugin.senpi.strategies` payload — the topology view of the live telemetry
|
|
3
|
-
* stream: which strategies exist, whether each is up, how healthy it is, and which scanners it
|
|
4
|
-
* declares.
|
|
5
|
-
*
|
|
6
|
-
* Pure. Every fact arrives as an input, nothing is read from the registry or a runtime handle here,
|
|
7
|
-
* so the shape on the wire is testable without booting a runtime.
|
|
8
|
-
*
|
|
9
|
-
* Two properties this module exists to hold:
|
|
10
|
-
* - `status` distinguishes a runtime that is down from one that is up but has no live entry
|
|
11
|
-
* scanners. The second reads as healthy unless it is named, and it is not.
|
|
12
|
-
* - The raw scanner `config` never reaches the wire. It is an unbounded passthrough record and it
|
|
13
|
-
* has been through `resolveEnvInObject`, so it may hold resolved secrets. Only the declared,
|
|
14
|
-
* typed fields below are copied, each by name.
|
|
15
|
-
*/
|
|
16
|
-
import { createLogger } from "../utils/logger.js";
|
|
17
|
-
import { normalizeStrategyAddress } from "./event-store.js";
|
|
18
|
-
const logger = createLogger("[senpi-core][strategy-topology]");
|
|
19
|
-
/** Payload schema version. Bump when a field changes meaning, not when one is added. */
|
|
20
|
-
export const STRATEGY_TOPOLOGY_VERSION = 1;
|
|
21
|
-
/**
|
|
22
|
-
* Mirrors the three-way distinction `senpi.listRuntimes` draws: no handle is stopped, a handle in
|
|
23
|
-
* the unwired set is running blind, anything else is running. The wire carries the machine token —
|
|
24
|
-
* the operator-facing string is the frontend's to choose.
|
|
25
|
-
*/
|
|
26
|
-
function resolveStatus(runtimeId, running, unwired) {
|
|
27
|
-
if (!running.has(runtimeId))
|
|
28
|
-
return "stopped";
|
|
29
|
-
return unwired.has(runtimeId) ? "running_no_entry_scanners" : "running";
|
|
30
|
-
}
|
|
31
|
-
/** Health is advisory here: a lookup that fails must degrade the row, never the payload. */
|
|
32
|
-
function resolveHealth(runtimeId, readHealth) {
|
|
33
|
-
let status;
|
|
34
|
-
try {
|
|
35
|
-
status = readHealth?.(runtimeId);
|
|
36
|
-
}
|
|
37
|
-
catch (err) {
|
|
38
|
-
logger.warn("strategy topology could not read runtime health; reporting unknown", err, {
|
|
39
|
-
runtimeId,
|
|
40
|
-
});
|
|
41
|
-
return { health: "unknown", startedAt: null };
|
|
42
|
-
}
|
|
43
|
-
if (!status)
|
|
44
|
-
return { health: "unknown", startedAt: null };
|
|
45
|
-
const parsed = status.startedAt === undefined ? NaN : Date.parse(status.startedAt);
|
|
46
|
-
return {
|
|
47
|
-
health: status.health,
|
|
48
|
-
startedAt: Number.isFinite(parsed) ? parsed : null,
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Copies the declared scanner fields one by one. Never spread `entry` and never copy `config` —
|
|
53
|
-
* see the module note.
|
|
54
|
-
*/
|
|
55
|
-
function buildScanner(source) {
|
|
56
|
-
const { entry, registration } = source;
|
|
57
|
-
const declaredSchema = entry.signal_data_schema;
|
|
58
|
-
return {
|
|
59
|
-
scannerId: entry.name,
|
|
60
|
-
scheduleMode: registration?.scheduleMode ?? (entry.type === "external_scanner" ? "external" : "interval"),
|
|
61
|
-
enabled: registration?.enabled ?? true,
|
|
62
|
-
intervalSeconds: registration?.intervalSeconds ?? entry.interval_seconds ?? null,
|
|
63
|
-
configVersion: registration?.configVersion ?? null,
|
|
64
|
-
signalDataSchema: declaredSchema ?? null,
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
/** Builds one `plugin.senpi.strategies` payload from an already-gathered set of facts. */
|
|
68
|
-
export function buildStrategyTopology(input) {
|
|
69
|
-
const now = input.now ?? Date.now;
|
|
70
|
-
const strategies = input.strategies.map((source) => {
|
|
71
|
-
const { health, startedAt } = resolveHealth(source.runtimeId, input.readHealth);
|
|
72
|
-
return {
|
|
73
|
-
runtimeId: source.runtimeId,
|
|
74
|
-
// `(address, scannerId)` is the join key across all four payloads, and the other three
|
|
75
|
-
// normalize their own address input. A checksummed EIP-55 wallet reaching the wire raw would
|
|
76
|
-
// spell this strategy differently from its own scanner rows and timeline rows, and nothing
|
|
77
|
-
// downstream rejects that — the join just returns nothing.
|
|
78
|
-
address: normalizeStrategyAddress(source.address),
|
|
79
|
-
name: source.name,
|
|
80
|
-
status: resolveStatus(source.runtimeId, input.runningRuntimeIds, input.unwiredRuntimeIds),
|
|
81
|
-
health,
|
|
82
|
-
startedAt,
|
|
83
|
-
scanners: source.scanners.map(buildScanner),
|
|
84
|
-
};
|
|
85
|
-
});
|
|
86
|
-
return {
|
|
87
|
-
v: STRATEGY_TOPOLOGY_VERSION,
|
|
88
|
-
epoch: input.epoch,
|
|
89
|
-
generatedAt: now(),
|
|
90
|
-
strategies,
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
//# sourceMappingURL=strategy-topology.js.map
|