@senpi/runtime 3.0.6-dev.runtime-phase-2.20260630134451 → 3.0.6-dev.runtime-phase-2.20260630134536
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.map +1 -1
- package/dist/actions/action-events.js +39 -5
- package/dist/actions/action-events.js.map +1 -1
- package/dist/actions/event-derivation.d.ts +2 -0
- package/dist/actions/event-derivation.d.ts.map +1 -1
- package/dist/actions/event-derivation.js +2 -0
- package/dist/actions/event-derivation.js.map +1 -1
- package/dist/cli/guide-commands.d.ts.map +1 -1
- package/dist/cli/guide-commands.js +61 -0
- package/dist/cli/guide-commands.js.map +1 -1
- package/dist/cli/senpi-commands.d.ts +14 -0
- package/dist/cli/senpi-commands.d.ts.map +1 -1
- package/dist/cli/senpi-commands.js +386 -2
- package/dist/cli/senpi-commands.js.map +1 -1
- package/dist/dsl/events/initial-state.d.ts.map +1 -1
- package/dist/dsl/events/initial-state.js +1 -0
- package/dist/dsl/events/initial-state.js.map +1 -1
- package/dist/dsl/monitor/dsl-failure-events.d.ts +42 -0
- package/dist/dsl/monitor/dsl-failure-events.d.ts.map +1 -0
- package/dist/dsl/monitor/dsl-failure-events.js +73 -0
- package/dist/dsl/monitor/dsl-failure-events.js.map +1 -0
- package/dist/dsl/monitor/process-one-position.d.ts.map +1 -1
- package/dist/dsl/monitor/process-one-position.js +46 -37
- package/dist/dsl/monitor/process-one-position.js.map +1 -1
- package/dist/dsl/plugin/index.d.ts.map +1 -1
- package/dist/dsl/plugin/index.js +6 -0
- package/dist/dsl/plugin/index.js.map +1 -1
- package/dist/health/types.d.ts +14 -0
- package/dist/health/types.d.ts.map +1 -1
- package/dist/health/types.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +220 -7
- package/dist/index.js.map +1 -1
- package/dist/risk/risk-audit-log.d.ts +7 -0
- package/dist/risk/risk-audit-log.d.ts.map +1 -1
- package/dist/risk/risk-audit-log.js +48 -1
- package/dist/risk/risk-audit-log.js.map +1 -1
- package/dist/runtime/lifecycle-events.d.ts +43 -0
- package/dist/runtime/lifecycle-events.d.ts.map +1 -1
- package/dist/runtime/lifecycle-events.js +61 -0
- package/dist/runtime/lifecycle-events.js.map +1 -1
- package/dist/runtime/runtime-schema.d.ts +63 -9
- package/dist/runtime/runtime-schema.d.ts.map +1 -1
- package/dist/runtime/runtime-schema.js +8 -1
- package/dist/runtime/runtime-schema.js.map +1 -1
- package/dist/runtime/runtime.d.ts +8 -0
- package/dist/runtime/runtime.d.ts.map +1 -1
- package/dist/runtime/runtime.js +104 -5
- package/dist/runtime/runtime.js.map +1 -1
- package/dist/runtime/supervisor/long-running-supervisor.d.ts +3 -0
- package/dist/runtime/supervisor/long-running-supervisor.d.ts.map +1 -1
- package/dist/runtime/supervisor/long-running-supervisor.js +27 -53
- package/dist/runtime/supervisor/long-running-supervisor.js.map +1 -1
- package/dist/runtime-api/intake/intake-events.d.ts +44 -0
- package/dist/runtime-api/intake/intake-events.d.ts.map +1 -0
- package/dist/runtime-api/intake/intake-events.js +131 -0
- package/dist/runtime-api/intake/intake-events.js.map +1 -0
- package/dist/runtime-api/intake/scanner-launcher.d.ts +2 -0
- package/dist/runtime-api/intake/scanner-launcher.d.ts.map +1 -1
- package/dist/runtime-api/intake/scanner-launcher.js +58 -15
- package/dist/runtime-api/intake/scanner-launcher.js.map +1 -1
- package/dist/runtime-api/intake/scanner-registry.d.ts +3 -0
- package/dist/runtime-api/intake/scanner-registry.d.ts.map +1 -1
- package/dist/runtime-api/intake/scanner-registry.js +3 -0
- package/dist/runtime-api/intake/scanner-registry.js.map +1 -1
- package/dist/runtime-api/intake/supervisor-events.d.ts +61 -0
- package/dist/runtime-api/intake/supervisor-events.d.ts.map +1 -0
- package/dist/runtime-api/intake/supervisor-events.js +173 -0
- package/dist/runtime-api/intake/supervisor-events.js.map +1 -0
- package/dist/runtime-api/routes/signals.d.ts.map +1 -1
- package/dist/runtime-api/routes/signals.js +119 -27
- package/dist/runtime-api/routes/signals.js.map +1 -1
- package/dist/runtime-api/server.d.ts.map +1 -1
- package/dist/runtime-api/server.js +10 -1
- package/dist/runtime-api/server.js.map +1 -1
- package/dist/scanners/engine/engine.d.ts.map +1 -1
- package/dist/scanners/engine/engine.js +3 -0
- package/dist/scanners/engine/engine.js.map +1 -1
- package/dist/scanners/runtime-module.d.ts.map +1 -1
- package/dist/scanners/runtime-module.js +6 -0
- package/dist/scanners/runtime-module.js.map +1 -1
- package/dist/senpi/client.d.ts.map +1 -1
- package/dist/senpi/client.js +50 -5
- package/dist/senpi/client.js.map +1 -1
- package/dist/state/state-events.d.ts +13 -0
- package/dist/state/state-events.d.ts.map +1 -0
- package/dist/state/state-events.js +22 -0
- package/dist/state/state-events.js.map +1 -0
- package/dist/state/state-manager.d.ts.map +1 -1
- package/dist/state/state-manager.js +14 -1
- package/dist/state/state-manager.js.map +1 -1
- package/dist/telemetry/event-builders.d.ts +0 -2
- package/dist/telemetry/event-builders.d.ts.map +1 -1
- package/dist/telemetry/event-builders.js.map +1 -1
- package/dist/telemetry/event-store.d.ts +66 -0
- package/dist/telemetry/event-store.d.ts.map +1 -0
- package/dist/telemetry/event-store.js +202 -0
- package/dist/telemetry/event-store.js.map +1 -0
- package/dist/telemetry/identity.d.ts +10 -0
- package/dist/telemetry/identity.d.ts.map +1 -1
- package/dist/telemetry/identity.js +1 -0
- package/dist/telemetry/identity.js.map +1 -1
- package/dist/telemetry/metrics.d.ts +137 -15
- package/dist/telemetry/metrics.d.ts.map +1 -1
- package/dist/telemetry/metrics.js +357 -74
- package/dist/telemetry/metrics.js.map +1 -1
- package/dist/telemetry/otel-sdk.d.ts +1 -1
- package/dist/telemetry/otel-sdk.js +1 -1
- package/dist/telemetry/spans/gen-ai.d.ts +4 -0
- package/dist/telemetry/spans/gen-ai.d.ts.map +1 -1
- package/dist/telemetry/spans/gen-ai.js +9 -0
- package/dist/telemetry/spans/gen-ai.js.map +1 -1
- package/dist/telemetry/spans/redact-pii.d.ts.map +1 -1
- package/dist/telemetry/spans/redact-pii.js +4 -1
- package/dist/telemetry/spans/redact-pii.js.map +1 -1
- package/dist/telemetry/spans/span-hooks.d.ts +7 -0
- package/dist/telemetry/spans/span-hooks.d.ts.map +1 -1
- package/dist/telemetry/spans/span-hooks.js +179 -24
- package/dist/telemetry/spans/span-hooks.js.map +1 -1
- package/dist/telemetry/tool-plane.d.ts +31 -0
- package/dist/telemetry/tool-plane.d.ts.map +1 -0
- package/dist/telemetry/tool-plane.js +70 -0
- package/dist/telemetry/tool-plane.js.map +1 -0
- package/dist/telemetry/trace-identity.d.ts.map +1 -1
- package/dist/telemetry/trace-identity.js +3 -0
- package/dist/telemetry/trace-identity.js.map +1 -1
- package/dist/types/dsl/index.d.ts +6 -0
- package/dist/types/dsl/index.d.ts.map +1 -1
- package/dist/utils/event-catalog.d.ts +237 -0
- package/dist/utils/event-catalog.d.ts.map +1 -1
- package/dist/utils/event-catalog.js +51 -0
- package/dist/utils/event-catalog.js.map +1 -1
- package/dist/utils/logger.d.ts +5 -0
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +24 -22
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/wallet-address.d.ts +3 -0
- package/dist/utils/wallet-address.d.ts.map +1 -0
- package/dist/utils/wallet-address.js +17 -0
- package/dist/utils/wallet-address.js.map +1 -0
- package/package.json +1 -1
|
@@ -659,6 +659,236 @@ export function registerSenpiCli(program) {
|
|
|
659
659
|
console.log("");
|
|
660
660
|
}
|
|
661
661
|
});
|
|
662
|
+
senpi
|
|
663
|
+
.command("scanner")
|
|
664
|
+
.description("Per-scanner supervisor health: runs, errors, liveness, barren detection")
|
|
665
|
+
.option("-r, --runtime <id>", "Filter by runtime id")
|
|
666
|
+
.option("--json", "Print raw JSON from the gateway")
|
|
667
|
+
.action(async (opts) => {
|
|
668
|
+
const params = {};
|
|
669
|
+
if (typeof opts.runtime === "string" && opts.runtime.length > 0)
|
|
670
|
+
params.runtimeId = opts.runtime;
|
|
671
|
+
const data = callGatewayJson("senpi.getSystemState", params);
|
|
672
|
+
if (!data) {
|
|
673
|
+
return;
|
|
674
|
+
}
|
|
675
|
+
if (data.ok === false) {
|
|
676
|
+
const err = data.error;
|
|
677
|
+
console.error(err?.message ?? "Request failed.");
|
|
678
|
+
process.exitCode = 1;
|
|
679
|
+
return;
|
|
680
|
+
}
|
|
681
|
+
const payload = (data.payload ?? data);
|
|
682
|
+
const states = selectMany(payload.state, payload.states ?? []);
|
|
683
|
+
if (opts.json) {
|
|
684
|
+
const scanners = states.map((s) => ({
|
|
685
|
+
runtimeId: s.runtimeId ?? s.runtimeName,
|
|
686
|
+
scanners: s.components?.scanners?.state?.scanners ?? [],
|
|
687
|
+
}));
|
|
688
|
+
console.log(JSON.stringify({ ok: true, scanners }, null, 2));
|
|
689
|
+
return;
|
|
690
|
+
}
|
|
691
|
+
if (states.length === 0) {
|
|
692
|
+
console.log("No running runtime scanners.");
|
|
693
|
+
return;
|
|
694
|
+
}
|
|
695
|
+
for (const state of states) {
|
|
696
|
+
printScanners(state);
|
|
697
|
+
if (states.length > 1)
|
|
698
|
+
console.log("");
|
|
699
|
+
}
|
|
700
|
+
});
|
|
701
|
+
const riskCmd = senpi.command("risk");
|
|
702
|
+
riskCmd
|
|
703
|
+
.description("Risk eligibility + per-gate status — am I allowed to trade, and why not")
|
|
704
|
+
.option("-r, --runtime <id>", "Filter by runtime id")
|
|
705
|
+
.option("--json", "Print raw JSON from the gateway")
|
|
706
|
+
.action(async (opts) => {
|
|
707
|
+
const params = {};
|
|
708
|
+
if (typeof opts.runtime === "string" && opts.runtime.length > 0)
|
|
709
|
+
params.runtimeId = opts.runtime;
|
|
710
|
+
const data = callGatewayJson("senpi.getHealthStatus", params);
|
|
711
|
+
if (!data) {
|
|
712
|
+
return;
|
|
713
|
+
}
|
|
714
|
+
if (data.ok === false) {
|
|
715
|
+
const err = data.error;
|
|
716
|
+
console.error(err?.message ?? "Request failed.");
|
|
717
|
+
process.exitCode = 1;
|
|
718
|
+
return;
|
|
719
|
+
}
|
|
720
|
+
const payload = (data.payload ?? data);
|
|
721
|
+
const statuses = selectMany(payload.status, payload.statuses ?? []);
|
|
722
|
+
if (opts.json) {
|
|
723
|
+
const risk = statuses.map((s) => ({
|
|
724
|
+
runtimeId: s.runtimeId ?? s.runtimeName,
|
|
725
|
+
risk: s.components?.risk ?? null,
|
|
726
|
+
}));
|
|
727
|
+
console.log(JSON.stringify({ ok: true, risk }, null, 2));
|
|
728
|
+
return;
|
|
729
|
+
}
|
|
730
|
+
if (statuses.length === 0) {
|
|
731
|
+
console.log("No running runtime risk status.");
|
|
732
|
+
return;
|
|
733
|
+
}
|
|
734
|
+
for (const status of statuses) {
|
|
735
|
+
printRisk(status);
|
|
736
|
+
if (statuses.length > 1)
|
|
737
|
+
console.log("");
|
|
738
|
+
}
|
|
739
|
+
});
|
|
740
|
+
riskCmd
|
|
741
|
+
.command("audit")
|
|
742
|
+
.description("Risk gate-check audit log — per-evaluation pass/CLOSED/COOLDOWN history")
|
|
743
|
+
.option("-r, --runtime <id>", "Runtime id (required)")
|
|
744
|
+
.option("-a, --address <addr>", "Strategy address (defaults to runtime wallet)")
|
|
745
|
+
.option("--since <iso>", "ISO 8601 lower bound on entry time")
|
|
746
|
+
.option("-l, --limit <n>", "Max entries (default 100)")
|
|
747
|
+
.option("--json", "Print raw JSON from the gateway")
|
|
748
|
+
.action(async (opts) => {
|
|
749
|
+
if (!opts.runtime) {
|
|
750
|
+
console.error("--runtime <id> is required.");
|
|
751
|
+
process.exitCode = 1;
|
|
752
|
+
return;
|
|
753
|
+
}
|
|
754
|
+
const params = { runtimeId: opts.runtime };
|
|
755
|
+
if (opts.address)
|
|
756
|
+
params.address = opts.address;
|
|
757
|
+
const limit = opts.limit ? Number.parseInt(opts.limit, 10) : 100;
|
|
758
|
+
if (Number.isFinite(limit))
|
|
759
|
+
params.limit = limit;
|
|
760
|
+
const data = callGatewayJson("senpi.getRiskAudit", params);
|
|
761
|
+
if (!data) {
|
|
762
|
+
return;
|
|
763
|
+
}
|
|
764
|
+
if (data.ok === false) {
|
|
765
|
+
const err = data.error;
|
|
766
|
+
console.error(err?.message ?? "Request failed.");
|
|
767
|
+
process.exitCode = 1;
|
|
768
|
+
return;
|
|
769
|
+
}
|
|
770
|
+
const inner = (data.payload ?? data);
|
|
771
|
+
let entries = inner.entries ?? [];
|
|
772
|
+
// ISO 8601 UTC timestamps sort lexicographically, so a string compare
|
|
773
|
+
// is a correct lower-bound filter.
|
|
774
|
+
if (opts.since)
|
|
775
|
+
entries = entries.filter((e) => e.ts >= opts.since);
|
|
776
|
+
if (opts.json) {
|
|
777
|
+
console.log(JSON.stringify({ ok: true, entries }, null, 2));
|
|
778
|
+
return;
|
|
779
|
+
}
|
|
780
|
+
console.log(renderRiskAuditDigest(entries));
|
|
781
|
+
});
|
|
782
|
+
senpi
|
|
783
|
+
.command("audit")
|
|
784
|
+
.description("Backend trade-audit trail (MCP tool calls + AI reasoning)")
|
|
785
|
+
.option("-r, --runtime <id>", "Runtime id (required)")
|
|
786
|
+
.option("--tool <name>", "Filter by exact tool name")
|
|
787
|
+
.option("--action-type <type>", "Filter by action type (read|create|update|delete)")
|
|
788
|
+
.option("--success <bool>", "Filter by success (true|false)")
|
|
789
|
+
.option("--since <iso>", "ISO 8601 lower bound on request time")
|
|
790
|
+
.option("--until <iso>", "ISO 8601 upper bound on request time")
|
|
791
|
+
.option("-l, --limit <n>", "Max rows (default 50)")
|
|
792
|
+
.option("--json", "Print raw JSON from the gateway")
|
|
793
|
+
.action(async (opts) => {
|
|
794
|
+
if (!opts.runtime) {
|
|
795
|
+
console.error("--runtime <id> is required.");
|
|
796
|
+
process.exitCode = 1;
|
|
797
|
+
return;
|
|
798
|
+
}
|
|
799
|
+
const params = { runtimeId: opts.runtime };
|
|
800
|
+
if (opts.tool)
|
|
801
|
+
params.tool_name = opts.tool;
|
|
802
|
+
if (opts.actionType)
|
|
803
|
+
params.action_type = opts.actionType;
|
|
804
|
+
if (opts.success === "true")
|
|
805
|
+
params.success = true;
|
|
806
|
+
else if (opts.success === "false")
|
|
807
|
+
params.success = false;
|
|
808
|
+
if (opts.since)
|
|
809
|
+
params.start_time = opts.since;
|
|
810
|
+
if (opts.until)
|
|
811
|
+
params.end_time = opts.until;
|
|
812
|
+
const limit = opts.limit ? Number.parseInt(opts.limit, 10) : 50;
|
|
813
|
+
if (Number.isFinite(limit))
|
|
814
|
+
params.limit = limit;
|
|
815
|
+
const data = callGatewayJson("senpi.auditQuery", params);
|
|
816
|
+
if (!data) {
|
|
817
|
+
return;
|
|
818
|
+
}
|
|
819
|
+
if (data.ok === false) {
|
|
820
|
+
const err = data.error;
|
|
821
|
+
console.error(err?.message ?? "Request failed.");
|
|
822
|
+
process.exitCode = 1;
|
|
823
|
+
return;
|
|
824
|
+
}
|
|
825
|
+
const inner = (data.payload ?? data);
|
|
826
|
+
const result = inner.result ?? { entries: [], count: 0, total: 0, has_more: false };
|
|
827
|
+
if (opts.json) {
|
|
828
|
+
console.log(JSON.stringify({ ok: true, result }, null, 2));
|
|
829
|
+
return;
|
|
830
|
+
}
|
|
831
|
+
if (result.entries.length === 0) {
|
|
832
|
+
console.log("No audit entries.");
|
|
833
|
+
return;
|
|
834
|
+
}
|
|
835
|
+
console.log("Time\tTool\tType\tOK\tDur(ms)\tReasoning");
|
|
836
|
+
for (const e of result.entries) {
|
|
837
|
+
// A malformed backend timestamp makes toISOString() throw RangeError; show a placeholder.
|
|
838
|
+
const parsed = new Date(e.timestamp);
|
|
839
|
+
const t = Number.isNaN(parsed.getTime()) ? "—" : parsed.toISOString().slice(11, 19);
|
|
840
|
+
const dur = e.duration_ms == null ? "—" : String(e.duration_ms);
|
|
841
|
+
const reason = (e.ai_reasoning ?? "").replace(/\s+/g, " ").slice(0, 60);
|
|
842
|
+
console.log([t, e.tool, e.action_type, e.success ? "ok" : "FAIL", dur, reason].join("\t"));
|
|
843
|
+
}
|
|
844
|
+
if (result.has_more) {
|
|
845
|
+
console.log(`… more (showing ${result.count} of ~${result.total}; raise -l or use --json)`);
|
|
846
|
+
}
|
|
847
|
+
});
|
|
848
|
+
senpi
|
|
849
|
+
.command("events")
|
|
850
|
+
.description("Local domain-event log — the trade narrative (position/dsl/order/signal events)")
|
|
851
|
+
.option("-r, --runtime <id>", "Runtime id (required)")
|
|
852
|
+
.option("-a, --address <addr>", "Strategy address (defaults to runtime wallet)")
|
|
853
|
+
.option("--name <event>", "Filter by exact event name (e.g. position.opened)")
|
|
854
|
+
.option("--asset <symbol>", "Filter by asset (e.g. BTC)")
|
|
855
|
+
.option("--level <level>", "Filter by severity (debug|info|warn|error)")
|
|
856
|
+
.option("--since <iso>", "ISO 8601 lower bound on event time")
|
|
857
|
+
.option("--until <iso>", "ISO 8601 upper bound on event time")
|
|
858
|
+
.option("-l, --limit <n>", "Max events (default 200)")
|
|
859
|
+
.option("--json", "Print raw JSON from the gateway")
|
|
860
|
+
.action(async (opts) => {
|
|
861
|
+
const entries = await fetchEvents(opts);
|
|
862
|
+
if (entries === null)
|
|
863
|
+
return;
|
|
864
|
+
if (opts.json) {
|
|
865
|
+
console.log(JSON.stringify({ ok: true, entries }, null, 2));
|
|
866
|
+
return;
|
|
867
|
+
}
|
|
868
|
+
console.log(renderEventsDigest(entries));
|
|
869
|
+
});
|
|
870
|
+
senpi
|
|
871
|
+
.command("explain <asset>")
|
|
872
|
+
.description("Stitch one asset's position lifecycle from the local event log (opened → dsl → closed)")
|
|
873
|
+
.option("-r, --runtime <id>", "Runtime id (required)")
|
|
874
|
+
.option("-a, --address <addr>", "Strategy address (defaults to runtime wallet)")
|
|
875
|
+
.option("-l, --limit <n>", "Max events to scan (default 500)")
|
|
876
|
+
.option("--json", "Print raw JSON from the gateway")
|
|
877
|
+
.action(async (asset, opts) => {
|
|
878
|
+
const entries = await fetchEvents({
|
|
879
|
+
runtime: opts.runtime,
|
|
880
|
+
address: opts.address,
|
|
881
|
+
asset,
|
|
882
|
+
limit: opts.limit ?? "500",
|
|
883
|
+
});
|
|
884
|
+
if (entries === null)
|
|
885
|
+
return;
|
|
886
|
+
if (opts.json) {
|
|
887
|
+
console.log(JSON.stringify({ ok: true, asset, entries }, null, 2));
|
|
888
|
+
return;
|
|
889
|
+
}
|
|
890
|
+
console.log(renderExplain(asset, entries));
|
|
891
|
+
});
|
|
662
892
|
// ── Skills subcommand ────────────────────────────────────────────────────────
|
|
663
893
|
const skills = senpi.command("skills");
|
|
664
894
|
skills.description("Manage Senpi skills (status, update)");
|
|
@@ -891,12 +1121,23 @@ function printHealthStatus(status) {
|
|
|
891
1121
|
const scannerSummary = scanners?.summary ?? "Scanner summary unavailable.";
|
|
892
1122
|
const enabled = typeof scanners?.totals?.enabled === "number" ? scanners.totals.enabled : "?";
|
|
893
1123
|
const registered = typeof scanners?.totals?.registered === "number" ? scanners.totals.registered : "?";
|
|
1124
|
+
const degraded = typeof scanners?.totals?.degraded === "number" ? scanners.totals.degraded : 0;
|
|
1125
|
+
const degradedSuffix = degraded > 0 ? `, ${degraded} degraded` : "";
|
|
894
1126
|
console.log(`Runtime: ${status.runtimeId ?? status.runtimeName ?? "unknown"}`);
|
|
895
1127
|
console.log(`Overall Health: ${status.health ?? "unknown"}`);
|
|
896
1128
|
console.log(`Log Level: ${status.logLevel ?? "unknown"}`);
|
|
897
|
-
console.log(`Scanners: ${scannerSummary} (enabled ${enabled}/${registered})`);
|
|
1129
|
+
console.log(`Scanners: ${scannerSummary} (enabled ${enabled}/${registered}${degradedSuffix})`);
|
|
898
1130
|
if (dsl?.summary) {
|
|
899
1131
|
console.log(`DSL: ${dsl.summary}`);
|
|
1132
|
+
const monitor = dsl.monitor;
|
|
1133
|
+
if (monitor) {
|
|
1134
|
+
const flight = monitor.inFlight ? " (tick in flight)" : "";
|
|
1135
|
+
const next = monitor.nextTickAt
|
|
1136
|
+
? `, next tick ${new Date(monitor.nextTickAt).toISOString().slice(11, 19)}`
|
|
1137
|
+
: "";
|
|
1138
|
+
const err = monitor.lastTickError ? ` — last tick error: ${monitor.lastTickError}` : "";
|
|
1139
|
+
console.log(` Monitor: ${monitor.running ? "running" : "stopped"}${flight}${next}${err}`);
|
|
1140
|
+
}
|
|
900
1141
|
}
|
|
901
1142
|
if (actions?.summary) {
|
|
902
1143
|
const ar = typeof actions.totals?.registered === "number" ? actions.totals.registered : "?";
|
|
@@ -904,7 +1145,7 @@ function printHealthStatus(status) {
|
|
|
904
1145
|
console.log(`Actions: ${actions.summary} (registered ${ar}, ${ae} enabled)`);
|
|
905
1146
|
}
|
|
906
1147
|
if (risk && risk.enabled) {
|
|
907
|
-
console.log(`Risk Health: ${risk.health}`);
|
|
1148
|
+
console.log(`Risk Eligibility: ${risk.eligibility} | Health: ${risk.health}`);
|
|
908
1149
|
console.log("Risk Gates:");
|
|
909
1150
|
for (const gate of risk.gates) {
|
|
910
1151
|
const label = gate.gateName.padEnd(22);
|
|
@@ -913,6 +1154,117 @@ function printHealthStatus(status) {
|
|
|
913
1154
|
}
|
|
914
1155
|
}
|
|
915
1156
|
}
|
|
1157
|
+
function printRisk(status) {
|
|
1158
|
+
const risk = status.components?.risk;
|
|
1159
|
+
console.log(`Runtime: ${status.runtimeId ?? status.runtimeName ?? "unknown"}`);
|
|
1160
|
+
if (!risk || !risk.enabled) {
|
|
1161
|
+
console.log("Risk guardrails: disabled.");
|
|
1162
|
+
return;
|
|
1163
|
+
}
|
|
1164
|
+
console.log(`Eligibility: ${risk.eligibility} | Health: ${risk.health}`);
|
|
1165
|
+
const t = risk.totals;
|
|
1166
|
+
console.log(`Gates: ${t.open} open, ${t.cooldown} cooldown, ${t.closed} closed (${t.fallbackCount} fallback, ${t.failedCount} failed)`);
|
|
1167
|
+
for (const g of risk.gates) {
|
|
1168
|
+
const label = g.gateName.padEnd(22);
|
|
1169
|
+
const reason = g.reason && g.status !== "OPEN" ? ` — ${g.reason}` : "";
|
|
1170
|
+
// Surface evaluation faults the status summary hides: a gate that errored
|
|
1171
|
+
// and the fallback it fell back to.
|
|
1172
|
+
const flag = !g.evaluationOk ? ` [${g.failureKind}${g.fallbackApplied ? ",fallback" : ""}]` : "";
|
|
1173
|
+
console.log(` ${label}${g.status}${reason}${flag}`);
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
/**
|
|
1177
|
+
* Render the risk audit log as a tab-separated digest (newest entries last,
|
|
1178
|
+
* as returned). Each row: time, source, then `guardrail=result` per
|
|
1179
|
+
* evaluation (with the reason appended for non-pass results).
|
|
1180
|
+
*/
|
|
1181
|
+
export function renderRiskAuditDigest(entries) {
|
|
1182
|
+
if (entries.length === 0)
|
|
1183
|
+
return "No risk audit.";
|
|
1184
|
+
const lines = ["Time\tSource\tEvaluations"];
|
|
1185
|
+
for (const e of entries) {
|
|
1186
|
+
const t = new Date(e.ts).toISOString().slice(11, 19);
|
|
1187
|
+
const evals = (e.evaluations ?? [])
|
|
1188
|
+
.map((ev) => `${ev.guardrail}=${ev.result}${ev.reason && ev.result !== "pass" ? `(${ev.reason})` : ""}`)
|
|
1189
|
+
.join(" ");
|
|
1190
|
+
lines.push([t, e.source, evals || "—"].join("\t"));
|
|
1191
|
+
}
|
|
1192
|
+
return lines.join("\n");
|
|
1193
|
+
}
|
|
1194
|
+
/**
|
|
1195
|
+
* Shared fetch for `senpi events` / `senpi explain`: validate --runtime, call senpi.getEvents, and
|
|
1196
|
+
* return the entries. Returns `null` (and sets exitCode/prints) on a missing runtime, transport
|
|
1197
|
+
* failure, or a gateway error — the caller stops.
|
|
1198
|
+
*/
|
|
1199
|
+
function fetchEvents(opts) {
|
|
1200
|
+
if (!opts.runtime) {
|
|
1201
|
+
console.error("--runtime <id> is required.");
|
|
1202
|
+
process.exitCode = 1;
|
|
1203
|
+
return null;
|
|
1204
|
+
}
|
|
1205
|
+
const params = { runtimeId: opts.runtime };
|
|
1206
|
+
if (opts.address)
|
|
1207
|
+
params.address = opts.address;
|
|
1208
|
+
if (opts.name)
|
|
1209
|
+
params.name = opts.name;
|
|
1210
|
+
if (opts.asset)
|
|
1211
|
+
params.asset = opts.asset;
|
|
1212
|
+
if (opts.level)
|
|
1213
|
+
params.level = opts.level;
|
|
1214
|
+
if (opts.since)
|
|
1215
|
+
params.since = opts.since;
|
|
1216
|
+
if (opts.until)
|
|
1217
|
+
params.until = opts.until;
|
|
1218
|
+
if (opts.limit) {
|
|
1219
|
+
const limit = Number.parseInt(opts.limit, 10);
|
|
1220
|
+
if (Number.isFinite(limit))
|
|
1221
|
+
params.limit = limit;
|
|
1222
|
+
}
|
|
1223
|
+
const data = callGatewayJson("senpi.getEvents", params);
|
|
1224
|
+
if (!data)
|
|
1225
|
+
return null;
|
|
1226
|
+
if (data.ok === false) {
|
|
1227
|
+
const err = data.error;
|
|
1228
|
+
console.error(err?.message ?? "Request failed.");
|
|
1229
|
+
process.exitCode = 1;
|
|
1230
|
+
return null;
|
|
1231
|
+
}
|
|
1232
|
+
const inner = (data.payload ?? data);
|
|
1233
|
+
return inner.entries ?? [];
|
|
1234
|
+
}
|
|
1235
|
+
/** The asset an event concerns, if any (position/dsl carry `senpi.asset`; signals carry `senpi.signal.asset`). */
|
|
1236
|
+
function eventAsset(e) {
|
|
1237
|
+
const a = e.attrs?.["senpi.asset"] ?? e.attrs?.["senpi.signal.asset"];
|
|
1238
|
+
return typeof a === "string" ? a : undefined;
|
|
1239
|
+
}
|
|
1240
|
+
export function renderEventsDigest(entries) {
|
|
1241
|
+
if (entries.length === 0)
|
|
1242
|
+
return "No events.";
|
|
1243
|
+
const lines = ["Time\tLevel\tEvent\tAsset\tDetail"];
|
|
1244
|
+
for (const e of entries) {
|
|
1245
|
+
const t = new Date(e.ts).toISOString().slice(11, 19);
|
|
1246
|
+
const detail = (e.body ?? "").replace(/\s+/g, " ").slice(0, 60);
|
|
1247
|
+
lines.push([t, e.level, e.name, eventAsset(e) ?? "—", detail].join("\t"));
|
|
1248
|
+
}
|
|
1249
|
+
return lines.join("\n");
|
|
1250
|
+
}
|
|
1251
|
+
/**
|
|
1252
|
+
* Stitch an asset's lifecycle: events oldest-first, each line tagged with its position id where one is
|
|
1253
|
+
* present, so a reader can follow opened → dsl transitions → close across one or more positions.
|
|
1254
|
+
*/
|
|
1255
|
+
export function renderExplain(asset, entries) {
|
|
1256
|
+
if (entries.length === 0)
|
|
1257
|
+
return `No events for ${asset}.`;
|
|
1258
|
+
const lines = [`${asset} — ${entries.length} event(s)`];
|
|
1259
|
+
for (const e of entries) {
|
|
1260
|
+
const t = new Date(e.ts).toISOString().slice(11, 19);
|
|
1261
|
+
const pos = typeof e.attrs?.["senpi.position.id"] === "string" ? e.attrs["senpi.position.id"] : undefined;
|
|
1262
|
+
const tag = pos ? ` [pos ${truncateAddr(String(pos))}]` : "";
|
|
1263
|
+
const detail = (e.body ?? "").replace(/\s+/g, " ");
|
|
1264
|
+
lines.push(` ${t} ${e.name}\t${detail}${tag}`);
|
|
1265
|
+
}
|
|
1266
|
+
return lines.join("\n");
|
|
1267
|
+
}
|
|
916
1268
|
function printSystemState(state) {
|
|
917
1269
|
const scanners = state.components?.scanners?.state;
|
|
918
1270
|
const dsl = state.components?.dsl;
|
|
@@ -983,4 +1335,36 @@ function printSystemState(state) {
|
|
|
983
1335
|
}
|
|
984
1336
|
}
|
|
985
1337
|
}
|
|
1338
|
+
function printScanners(state) {
|
|
1339
|
+
const rows = state.components?.scanners?.state?.scanners ?? [];
|
|
1340
|
+
console.log(`Runtime: ${state.runtimeId ?? state.runtimeName ?? "unknown"}`);
|
|
1341
|
+
console.log(`Scanners: ${rows.length}`);
|
|
1342
|
+
for (const s of rows) {
|
|
1343
|
+
// Barren = wired and running but never emitted a signal — a real "alive but producing nothing" gap.
|
|
1344
|
+
const barren = s.health !== "disabled" && (s.runCount ?? 0) > 0 && (s.signalsProduced ?? 0) === 0;
|
|
1345
|
+
// Liveness clock is external-only: n/a for interval scanners, — for an external one not yet heard from.
|
|
1346
|
+
const alive = s.lastAliveAt != null
|
|
1347
|
+
? String(s.lastAliveAt)
|
|
1348
|
+
: s.scheduleMode === "external"
|
|
1349
|
+
? "—"
|
|
1350
|
+
: "n/a";
|
|
1351
|
+
console.log([
|
|
1352
|
+
" scanner",
|
|
1353
|
+
s.scannerId ?? "—",
|
|
1354
|
+
`mode=${s.scheduleMode ?? "—"}`,
|
|
1355
|
+
`health=${s.health ?? "unknown"}`,
|
|
1356
|
+
`enabled=${String(s.enabled ?? false)}`,
|
|
1357
|
+
`inFlight=${String(s.inFlight ?? false)}`,
|
|
1358
|
+
`runs=${s.runCount ?? 0}`,
|
|
1359
|
+
`errors=${s.errorCount ?? 0}`,
|
|
1360
|
+
`consec_errors=${s.consecutiveErrorCount ?? 0}`,
|
|
1361
|
+
`signals=${s.signalsProduced ?? 0}`,
|
|
1362
|
+
`alive=${alive}`,
|
|
1363
|
+
`next=${s.nextRunAt == null ? "—" : String(s.nextRunAt)}`,
|
|
1364
|
+
`last=${s.lastRunStatus ?? "—"}`,
|
|
1365
|
+
...(s.lastRunReason ? [`reason=${s.lastRunReason}`] : []),
|
|
1366
|
+
...(barren ? ["BARREN"] : []),
|
|
1367
|
+
].join(" "));
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
986
1370
|
//# sourceMappingURL=senpi-commands.js.map
|