@omnitron-dev/omnitron 0.1.3 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/omnitron.js +32 -1
- package/dist/cli/omnitron.js.map +1 -1
- package/dist/cluster/leader-election.d.ts.map +1 -1
- package/dist/cluster/leader-election.js +0 -2
- package/dist/cluster/leader-election.js.map +1 -1
- package/dist/commands/env.d.ts.map +1 -1
- package/dist/commands/env.js +10 -2
- package/dist/commands/env.js.map +1 -1
- package/dist/commands/health.d.ts.map +1 -1
- package/dist/commands/health.js +10 -2
- package/dist/commands/health.js.map +1 -1
- package/dist/commands/inspect-graph.d.ts +20 -0
- package/dist/commands/inspect-graph.d.ts.map +1 -0
- package/dist/commands/inspect-graph.js +78 -0
- package/dist/commands/inspect-graph.js.map +1 -0
- package/dist/commands/inspect.d.ts.map +1 -1
- package/dist/commands/inspect.js +10 -2
- package/dist/commands/inspect.js.map +1 -1
- package/dist/commands/list.d.ts.map +1 -1
- package/dist/commands/list.js +11 -1
- package/dist/commands/list.js.map +1 -1
- package/dist/commands/logs.d.ts.map +1 -1
- package/dist/commands/logs.js +17 -3
- package/dist/commands/logs.js.map +1 -1
- package/dist/commands/metrics.d.ts.map +1 -1
- package/dist/commands/metrics.js +10 -2
- package/dist/commands/metrics.js.map +1 -1
- package/dist/commands/monit.d.ts.map +1 -1
- package/dist/commands/monit.js +9 -0
- package/dist/commands/monit.js.map +1 -1
- package/dist/commands/output.d.ts +35 -0
- package/dist/commands/output.d.ts.map +1 -0
- package/dist/commands/output.js +63 -0
- package/dist/commands/output.js.map +1 -0
- package/dist/commands/stack.d.ts.map +1 -1
- package/dist/commands/stack.js +22 -7
- package/dist/commands/stack.js.map +1 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +17 -0
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/tor.d.ts +13 -0
- package/dist/commands/tor.d.ts.map +1 -0
- package/dist/commands/tor.js +100 -0
- package/dist/commands/tor.js.map +1 -0
- package/dist/commands/up.js +1 -1
- package/dist/commands/up.js.map +1 -1
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/loader.js +29 -5
- package/dist/config/loader.js.map +1 -1
- package/dist/config/types.d.ts +14 -0
- package/dist/config/types.d.ts.map +1 -1
- package/dist/daemon/daemon-client.d.ts +14 -0
- package/dist/daemon/daemon-client.d.ts.map +1 -1
- package/dist/daemon/daemon-client.js +4 -0
- package/dist/daemon/daemon-client.js.map +1 -1
- package/dist/daemon/daemon-entry.js +17 -23
- package/dist/daemon/daemon-entry.js.map +1 -1
- package/dist/daemon/daemon.d.ts +2 -0
- package/dist/daemon/daemon.d.ts.map +1 -1
- package/dist/daemon/daemon.js +118 -7
- package/dist/daemon/daemon.js.map +1 -1
- package/dist/daemon/daemon.module.d.ts.map +1 -1
- package/dist/daemon/daemon.module.js +41 -14
- package/dist/daemon/daemon.module.js.map +1 -1
- package/dist/daemon/daemon.rpc-service.d.ts +19 -0
- package/dist/daemon/daemon.rpc-service.d.ts.map +1 -1
- package/dist/daemon/daemon.rpc-service.js +14 -0
- package/dist/daemon/daemon.rpc-service.js.map +1 -1
- package/dist/daemon/pid-manager.d.ts +55 -4
- package/dist/daemon/pid-manager.d.ts.map +1 -1
- package/dist/daemon/pid-manager.js +123 -16
- package/dist/daemon/pid-manager.js.map +1 -1
- package/dist/daemon/state-store.d.ts.map +1 -1
- package/dist/daemon/state-store.js +43 -1
- package/dist/daemon/state-store.js.map +1 -1
- package/dist/infrastructure/container-runtime.d.ts +10 -0
- package/dist/infrastructure/container-runtime.d.ts.map +1 -1
- package/dist/infrastructure/container-runtime.js +111 -36
- package/dist/infrastructure/container-runtime.js.map +1 -1
- package/dist/infrastructure/infrastructure.service.d.ts +41 -0
- package/dist/infrastructure/infrastructure.service.d.ts.map +1 -1
- package/dist/infrastructure/infrastructure.service.js +157 -8
- package/dist/infrastructure/infrastructure.service.js.map +1 -1
- package/dist/infrastructure/phantom-endpoint-janitor.d.ts +72 -0
- package/dist/infrastructure/phantom-endpoint-janitor.d.ts.map +1 -0
- package/dist/infrastructure/phantom-endpoint-janitor.js +149 -0
- package/dist/infrastructure/phantom-endpoint-janitor.js.map +1 -0
- package/dist/infrastructure/presets/index.d.ts.map +1 -1
- package/dist/infrastructure/presets/index.js +2 -0
- package/dist/infrastructure/presets/index.js.map +1 -1
- package/dist/infrastructure/presets/postgres.d.ts.map +1 -1
- package/dist/infrastructure/presets/postgres.js +9 -3
- package/dist/infrastructure/presets/postgres.js.map +1 -1
- package/dist/infrastructure/presets/redis.d.ts.map +1 -1
- package/dist/infrastructure/presets/redis.js +10 -4
- package/dist/infrastructure/presets/redis.js.map +1 -1
- package/dist/infrastructure/presets/registry.d.ts.map +1 -1
- package/dist/infrastructure/presets/registry.js +12 -0
- package/dist/infrastructure/presets/registry.js.map +1 -1
- package/dist/infrastructure/presets/tor.d.ts +74 -0
- package/dist/infrastructure/presets/tor.d.ts.map +1 -0
- package/dist/infrastructure/presets/tor.js +176 -0
- package/dist/infrastructure/presets/tor.js.map +1 -0
- package/dist/infrastructure/service-resolver.d.ts +31 -0
- package/dist/infrastructure/service-resolver.d.ts.map +1 -1
- package/dist/infrastructure/service-resolver.js +60 -9
- package/dist/infrastructure/service-resolver.js.map +1 -1
- package/dist/infrastructure/stack-infra-manager.d.ts +1 -1
- package/dist/infrastructure/stack-infra-manager.js +1 -1
- package/dist/infrastructure/types.d.ts +17 -0
- package/dist/infrastructure/types.d.ts.map +1 -1
- package/dist/observability/index.d.ts +4 -0
- package/dist/observability/index.d.ts.map +1 -0
- package/dist/observability/index.js +3 -0
- package/dist/observability/index.js.map +1 -0
- package/dist/observability/metrics-bridge.d.ts +87 -0
- package/dist/observability/metrics-bridge.d.ts.map +1 -0
- package/dist/observability/metrics-bridge.js +295 -0
- package/dist/observability/metrics-bridge.js.map +1 -0
- package/dist/observability/metrics-server.d.ts +68 -0
- package/dist/observability/metrics-server.d.ts.map +1 -0
- package/dist/observability/metrics-server.js +146 -0
- package/dist/observability/metrics-server.js.map +1 -0
- package/dist/orchestrator/app-handle.d.ts +13 -1
- package/dist/orchestrator/app-handle.d.ts.map +1 -1
- package/dist/orchestrator/app-handle.js +12 -0
- package/dist/orchestrator/app-handle.js.map +1 -1
- package/dist/orchestrator/bootstrap-process.d.ts +49 -0
- package/dist/orchestrator/bootstrap-process.d.ts.map +1 -1
- package/dist/orchestrator/bootstrap-process.js +167 -0
- package/dist/orchestrator/bootstrap-process.js.map +1 -1
- package/dist/orchestrator/build-service.d.ts +50 -3
- package/dist/orchestrator/build-service.d.ts.map +1 -1
- package/dist/orchestrator/build-service.js +357 -85
- package/dist/orchestrator/build-service.js.map +1 -1
- package/dist/orchestrator/file-watcher.d.ts.map +1 -1
- package/dist/orchestrator/file-watcher.js +60 -38
- package/dist/orchestrator/file-watcher.js.map +1 -1
- package/dist/orchestrator/orchestrator.service.d.ts +176 -3
- package/dist/orchestrator/orchestrator.service.d.ts.map +1 -1
- package/dist/orchestrator/orchestrator.service.js +587 -56
- package/dist/orchestrator/orchestrator.service.js.map +1 -1
- package/dist/orchestrator/process-janitor.d.ts +153 -0
- package/dist/orchestrator/process-janitor.d.ts.map +1 -0
- package/dist/orchestrator/process-janitor.js +279 -0
- package/dist/orchestrator/process-janitor.js.map +1 -0
- package/dist/project/config-resolver.d.ts.map +1 -1
- package/dist/project/config-resolver.js +38 -12
- package/dist/project/config-resolver.js.map +1 -1
- package/dist/project/registry.d.ts +1 -0
- package/dist/project/registry.d.ts.map +1 -1
- package/dist/project/registry.js +30 -0
- package/dist/project/registry.js.map +1 -1
- package/dist/services/auth.service.d.ts.map +1 -1
- package/dist/services/auth.service.js +9 -2
- package/dist/services/auth.service.js.map +1 -1
- package/dist/services/netron-telemetry-transport.d.ts.map +1 -1
- package/dist/services/netron-telemetry-transport.js +0 -4
- package/dist/services/netron-telemetry-transport.js.map +1 -1
- package/dist/services/node-manager.service.js +1 -1
- package/dist/services/node-manager.service.js.map +1 -1
- package/dist/services/pipeline.service.d.ts +15 -0
- package/dist/services/pipeline.service.d.ts.map +1 -1
- package/dist/services/pipeline.service.js +53 -2
- package/dist/services/pipeline.service.js.map +1 -1
- package/dist/services/project.rpc-service.d.ts +4 -0
- package/dist/services/project.rpc-service.d.ts.map +1 -1
- package/dist/services/project.rpc-service.js +9 -0
- package/dist/services/project.rpc-service.js.map +1 -1
- package/dist/services/project.service.d.ts +9 -0
- package/dist/services/project.service.d.ts.map +1 -1
- package/dist/services/project.service.js +148 -21
- package/dist/services/project.service.js.map +1 -1
- package/dist/shared/dto/project.d.ts +1 -1
- package/dist/shared/dto/project.d.ts.map +1 -1
- package/dist/shared/dto/services.d.ts +24 -0
- package/dist/shared/dto/services.d.ts.map +1 -1
- package/dist/workers/health-monitor.service.d.ts.map +1 -1
- package/dist/workers/health-monitor.service.js +4 -1
- package/dist/workers/health-monitor.service.js.map +1 -1
- package/dist/workers/types.d.ts +3 -0
- package/dist/workers/types.d.ts.map +1 -1
- package/package.json +19 -18
- package/webapp/dist/assets/Badge-D2ZBP5iK.js +1 -0
- package/webapp/dist/assets/Card-vO88g06B.js +1 -0
- package/webapp/dist/assets/CardActions-C9h7Q2wW.js +1 -0
- package/webapp/dist/assets/CardContent-BA_Xmmhk.js +1 -0
- package/webapp/dist/assets/CardHeader-ceIs__6x.js +1 -0
- package/webapp/dist/assets/Chip-Ckk0xUhK.js +1 -0
- package/webapp/dist/assets/DialogActions-BhHM7hov.js +1 -0
- package/webapp/dist/assets/DialogContentText-BT2JQRmK.js +1 -0
- package/webapp/dist/assets/FormControlLabel-CcQByVS8.js +1 -0
- package/webapp/dist/assets/Grid-14PETRQW.js +1 -0
- package/webapp/dist/assets/InputAdornment-B8aHIsQ7.js +1 -0
- package/webapp/dist/assets/Skeleton-B74-Ds6O.js +37 -0
- package/webapp/dist/assets/Switch-DvWD2wh4.js +1 -0
- package/webapp/dist/assets/SwitchBase-CDU4nY8O.js +1 -0
- package/webapp/dist/assets/Tab-DcCOYYp5.js +1 -0
- package/webapp/dist/assets/TableContainer-CzTh4mu-.js +1 -0
- package/webapp/dist/assets/TableRow-BySEreZR.js +2 -0
- package/webapp/dist/assets/TextField-BLf-BHz2.js +1 -0
- package/webapp/dist/assets/alerts-DnRbT41C.js +1 -0
- package/webapp/dist/assets/apps-CdR0VH2d.js +1 -0
- package/webapp/dist/assets/chunk-DtcAfS_W.js +1 -0
- package/webapp/dist/assets/client-DU8I_lDc.js +2 -0
- package/webapp/dist/assets/containers-Dw-rRXV1.js +1 -0
- package/webapp/dist/assets/dashboard-CzgB0uZz.js +1 -0
- package/webapp/dist/assets/dashboard-builder-DsA0yg33.js +1 -0
- package/webapp/dist/assets/deployments-DtQAoIcQ.js +1 -0
- package/webapp/dist/assets/detail-BJR-G7JG.js +1 -0
- package/webapp/dist/assets/detail-nlCeSltl.js +8 -0
- package/webapp/dist/assets/index-ByQDwp2r.js +566 -0
- package/webapp/dist/assets/isMuiElement-DwF2naaF.js +1 -0
- package/webapp/dist/assets/logs-Kn8o4H7k.js +11 -0
- package/webapp/dist/assets/metrics-DN8Zu5wT.js +2 -0
- package/webapp/dist/assets/nodes-Bgch7AtN.js +7 -0
- package/webapp/dist/assets/pipelines-Cry3qm-S.js +4 -0
- package/webapp/dist/assets/popper-BJFSj6rc.js +1 -0
- package/webapp/dist/assets/preload-helper-DSXbuxSR.js +1 -0
- package/webapp/dist/assets/projects-Dym2GI1U.js +1 -0
- package/webapp/dist/assets/react-apexcharts.esm-CSGK5Cxn.js +958 -0
- package/webapp/dist/assets/react-apexcharts.esm-DEAWOwx5.js +1003 -0
- package/webapp/dist/assets/settings-va3zTcow.js +1 -0
- package/webapp/dist/assets/sign-in-ik35umbp.js +1 -0
- package/webapp/dist/assets/stacks-CVFOnxbF.js +1 -0
- package/webapp/dist/assets/system-info-npIXAjX2.js +1 -0
- package/webapp/dist/assets/topology-bDe70vLs.js +12 -0
- package/webapp/dist/assets/traces-DIIcT8DF.js +1 -0
- package/webapp/dist/assets/use-stack-context-DHLwZv1r.js +1 -0
- package/webapp/dist/index.html +5 -6
- package/webapp/dist/assets/Chip-BySxTjyN.js +0 -1
- package/webapp/dist/assets/DialogContentText-YQWiAVrU.js +0 -1
- package/webapp/dist/assets/DialogTitle-CDkgUIwp.js +0 -1
- package/webapp/dist/assets/FormControlLabel-Bu3CCsBM.js +0 -1
- package/webapp/dist/assets/Grid-BFY8hAzL.js +0 -1
- package/webapp/dist/assets/InputAdornment-_Dayk-3y.js +0 -3
- package/webapp/dist/assets/Skeleton-BGj6Sw8m.js +0 -37
- package/webapp/dist/assets/Switch-CA6682vv.js +0 -1
- package/webapp/dist/assets/TableContainer-4epq10KJ.js +0 -1
- package/webapp/dist/assets/TextField-C73KHMVN.js +0 -1
- package/webapp/dist/assets/alerts-JEQALS7Y.js +0 -1
- package/webapp/dist/assets/apps-DMPysEKB.js +0 -1
- package/webapp/dist/assets/client-DlGPter5.js +0 -110
- package/webapp/dist/assets/containers-CvkC7do8.js +0 -1
- package/webapp/dist/assets/dashboard-C0Fre9pC.js +0 -1
- package/webapp/dist/assets/dashboard-builder-DAghqdpE.js +0 -1
- package/webapp/dist/assets/deployments-Cg1lI1uT.js +0 -1
- package/webapp/dist/assets/detail-CyFtEson.js +0 -1
- package/webapp/dist/assets/detail-DqoSGKT1.js +0 -8
- package/webapp/dist/assets/icons-ynIWYfBP.js +0 -3
- package/webapp/dist/assets/index-vKrDmEA_.js +0 -259
- package/webapp/dist/assets/isMuiElement-BRL3YuCT.js +0 -1
- package/webapp/dist/assets/logs-IovIij1C.js +0 -11
- package/webapp/dist/assets/metrics-Dufwtwjq.js +0 -1
- package/webapp/dist/assets/nodes-Brvu8W3j.js +0 -7
- package/webapp/dist/assets/pipelines-t40X4MuC.js +0 -4
- package/webapp/dist/assets/prop-types-CvVtHxp4.js +0 -1
- package/webapp/dist/assets/react-apexcharts.esm-CLO6gala.js +0 -954
- package/webapp/dist/assets/settings-DTAIST3v.js +0 -1
- package/webapp/dist/assets/sign-in-DQEAkJum.js +0 -1
- package/webapp/dist/assets/stacks-BjM1f45N.js +0 -1
- package/webapp/dist/assets/system-info-BQSbSEK-.js +0 -1
- package/webapp/dist/assets/topology-6N8-Az_Z.js +0 -12
- package/webapp/dist/assets/traces-DVXyb-4m.js +0 -1
- package/webapp/dist/assets/use-stack-context-CbE8fxBA.js +0 -1
- /package/webapp/dist/assets/{constants-DqFQJ27U.js → constants-BgnT-Xdy.js} +0 -0
- /package/webapp/dist/assets/{formatters-J3Xqn6BQ.js → formatters-Do41SRmE.js} +0 -0
- /package/webapp/dist/assets/{fs-uP8eiZAR.js → fs-CgZLtHiQ.js} +0 -0
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Omnitron metrics bridge — single point of contact between the daemon's
|
|
3
|
+
* runtime hooks (orchestrator events, ProcessJanitor sweeps,
|
|
4
|
+
* ResilientHandle resets, esbuild rebuilds) and the underlying
|
|
5
|
+
* `IMetricsService` registry from `@omnitron-dev/titan-metrics`.
|
|
6
|
+
*
|
|
7
|
+
* Every issue we've debugged in production was diagnosed REACTIVELY
|
|
8
|
+
* from log greps: postgres connection exhaustion (we found 207 leaked
|
|
9
|
+
* fork-workers only after pg_stat_activity told us to look), esbuild
|
|
10
|
+
* Go sidecar deaths (the build self-heal was silent until we wired up
|
|
11
|
+
* a dedicated log line), RPC name mismatches (only surfaced when an app
|
|
12
|
+
* couldn't talk to its dependency at runtime). The metrics this bridge
|
|
13
|
+
* exposes are what we wished we'd had during each of those incidents.
|
|
14
|
+
*
|
|
15
|
+
* Design notes:
|
|
16
|
+
*
|
|
17
|
+
* - Metrics live in the titan-metrics registry; this bridge is the
|
|
18
|
+
* DAO that types the writes. Callers don't touch the registry
|
|
19
|
+
* directly — they call `metrics.recordEsbuildBuild(...)` etc.
|
|
20
|
+
*
|
|
21
|
+
* - Per-app gauges (`status`, `restart_total`, `uptime_seconds`) are
|
|
22
|
+
* refreshed on every Prometheus scrape via `refreshAppGauges()`.
|
|
23
|
+
* The HTTP server calls it before formatting text. This avoids
|
|
24
|
+
* wiring callbacks into every state mutation in the orchestrator.
|
|
25
|
+
*
|
|
26
|
+
* - Event-driven counters/histograms (orphan kills, esbuild
|
|
27
|
+
* rebuilds, lifecycle phases) are recorded eagerly via the
|
|
28
|
+
* record* helpers, since events are sparse and we don't want to
|
|
29
|
+
* drop them between scrapes.
|
|
30
|
+
*/
|
|
31
|
+
// All known statuses; we emit one row per (app, status) so Grafana can
|
|
32
|
+
// pivot. Keeping the list explicit means a typo in OrchestratorService
|
|
33
|
+
// produces a dropped metric instead of a silent new label value.
|
|
34
|
+
const APP_STATUSES = [
|
|
35
|
+
'stopped',
|
|
36
|
+
'starting',
|
|
37
|
+
'online',
|
|
38
|
+
'stopping',
|
|
39
|
+
'errored',
|
|
40
|
+
'crashed',
|
|
41
|
+
];
|
|
42
|
+
export class MetricsBridge {
|
|
43
|
+
metrics;
|
|
44
|
+
logger;
|
|
45
|
+
orchestrator = null;
|
|
46
|
+
definitionsRegistered = false;
|
|
47
|
+
constructor(metrics, logger) {
|
|
48
|
+
this.metrics = metrics;
|
|
49
|
+
this.logger = logger;
|
|
50
|
+
this.registerDefinitions();
|
|
51
|
+
}
|
|
52
|
+
/** Wire the orchestrator so per-app gauges can be refreshed at scrape time. */
|
|
53
|
+
attachOrchestrator(orchestrator) {
|
|
54
|
+
this.orchestrator = orchestrator;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Refresh per-app gauges. Called by the metrics HTTP server before it
|
|
58
|
+
* formats text — keeps the gauges current without instrumenting every
|
|
59
|
+
* status mutation in the orchestrator.
|
|
60
|
+
*/
|
|
61
|
+
refreshAppGauges() {
|
|
62
|
+
if (!this.orchestrator)
|
|
63
|
+
return;
|
|
64
|
+
const apps = this.orchestrator.list();
|
|
65
|
+
for (const app of apps) {
|
|
66
|
+
// One gauge series per (app, status) pair — value 1 if the app
|
|
67
|
+
// is currently in that status, else 0. Lets Grafana plot
|
|
68
|
+
// `omnitron_app_status{status="online"}` as a stacked bar.
|
|
69
|
+
for (const status of APP_STATUSES) {
|
|
70
|
+
this.gauge('omnitron_app_status', { app: app.name, status }, app.status === status ? 1 : 0);
|
|
71
|
+
}
|
|
72
|
+
this.gauge('omnitron_app_restarts', { app: app.name }, app.restarts ?? 0);
|
|
73
|
+
this.gauge('omnitron_app_uptime_seconds', { app: app.name }, app.uptime ?? 0);
|
|
74
|
+
if (typeof app.cpu === 'number') {
|
|
75
|
+
this.gauge('omnitron_app_cpu_percent', { app: app.name }, app.cpu);
|
|
76
|
+
}
|
|
77
|
+
if (typeof app.memory === 'number') {
|
|
78
|
+
this.gauge('omnitron_app_memory_bytes', { app: app.name }, app.memory);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// -------------------------------------------------------------------------
|
|
83
|
+
// Event-driven recorders
|
|
84
|
+
// -------------------------------------------------------------------------
|
|
85
|
+
/** Record a janitor sweep — gauges current orphan/owned counts + bumps total counter. */
|
|
86
|
+
recordJanitorSweep(m) {
|
|
87
|
+
this.gauge('omnitron_workers_alive', {}, m.forkWorkersAlive);
|
|
88
|
+
this.gauge('omnitron_workers_owned', {}, m.ownedPids);
|
|
89
|
+
this.gauge('omnitron_workers_orphans_pending', {}, m.orphansFound - m.orphansKilled);
|
|
90
|
+
if (m.orphansKilled > 0) {
|
|
91
|
+
this.counter('omnitron_workers_orphans_killed_total', {}, m.orphansKilled);
|
|
92
|
+
}
|
|
93
|
+
if (m.killErrors > 0) {
|
|
94
|
+
this.counter('omnitron_workers_orphan_kill_errors_total', {}, m.killErrors);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/** Record a ResilientHandle reset — fires once per fatal-error reset across the daemon. */
|
|
98
|
+
recordResilientHandleReset(info) {
|
|
99
|
+
this.counter('resilient_handle_resets_total', { name: info.name }, 1);
|
|
100
|
+
}
|
|
101
|
+
/** Record an esbuild build (initial or rebuild). */
|
|
102
|
+
recordEsbuildBuild(app, kind, durationMs, ok) {
|
|
103
|
+
this.histogram('omnitron_esbuild_duration_seconds', { app, kind }, durationMs / 1000);
|
|
104
|
+
this.counter('omnitron_esbuild_builds_total', { app, kind, outcome: ok ? 'success' : 'failure' }, 1);
|
|
105
|
+
}
|
|
106
|
+
/** Record an app restart event. Called from orchestrator's `app:restart` listener. */
|
|
107
|
+
recordAppRestart(app, attempt, reason) {
|
|
108
|
+
this.counter('omnitron_app_restarts_total', { app, reason }, 1);
|
|
109
|
+
this.gauge('omnitron_app_restart_attempt', { app }, attempt);
|
|
110
|
+
}
|
|
111
|
+
/** Record an app crash. */
|
|
112
|
+
recordAppCrash(app) {
|
|
113
|
+
this.counter('omnitron_app_crashes_total', { app }, 1);
|
|
114
|
+
}
|
|
115
|
+
/** Record an app escalation (max restarts exceeded). */
|
|
116
|
+
recordAppEscalated(app) {
|
|
117
|
+
this.counter('omnitron_app_escalations_total', { app }, 1);
|
|
118
|
+
}
|
|
119
|
+
/** Record a lifecycle controller phase event. */
|
|
120
|
+
recordLifecyclePhase(phase, durationMs, kind) {
|
|
121
|
+
this.histogram('lifecycle_shutdown_phase_duration_seconds', { phase }, durationMs / 1000);
|
|
122
|
+
if (kind === 'timeout') {
|
|
123
|
+
this.counter('lifecycle_shutdown_phase_timeouts_total', { phase }, 1);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Permanently drop every sample for a named app from both the in-memory
|
|
128
|
+
* registry and the storage backend. Use when an app is definitively
|
|
129
|
+
* removed (handle replaced under a different canonical name, app
|
|
130
|
+
* un-registered) so it cannot linger as a "ghost offline" in subsequent
|
|
131
|
+
* snapshots.
|
|
132
|
+
*
|
|
133
|
+
* Best-effort: swallowed errors so an eviction failure can never block
|
|
134
|
+
* the orchestrator's primary lifecycle path.
|
|
135
|
+
*/
|
|
136
|
+
evictApp(app) {
|
|
137
|
+
void this.metrics.evictApp(app).catch((err) => {
|
|
138
|
+
this.logger?.warn?.({ app, error: err.message }, 'Failed to evict metrics for app');
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
// -------------------------------------------------------------------------
|
|
142
|
+
// Internals
|
|
143
|
+
// -------------------------------------------------------------------------
|
|
144
|
+
/**
|
|
145
|
+
* Pre-register every metric this bridge writes. The titan-metrics
|
|
146
|
+
* registry would auto-register them with default help text on first
|
|
147
|
+
* write, but we want stable, descriptive HELP lines and explicit
|
|
148
|
+
* label sets in the Prometheus output.
|
|
149
|
+
*/
|
|
150
|
+
registerDefinitions() {
|
|
151
|
+
if (this.definitionsRegistered)
|
|
152
|
+
return;
|
|
153
|
+
this.definitionsRegistered = true;
|
|
154
|
+
const registry = this.metrics.getRegistry();
|
|
155
|
+
registry.define({
|
|
156
|
+
name: 'omnitron_app_status',
|
|
157
|
+
type: 'gauge',
|
|
158
|
+
help: '1 if the app is currently in the labelled status, else 0',
|
|
159
|
+
labelNames: ['app', 'status'],
|
|
160
|
+
});
|
|
161
|
+
registry.define({
|
|
162
|
+
name: 'omnitron_app_restarts',
|
|
163
|
+
type: 'gauge',
|
|
164
|
+
help: 'Current restart counter for the app (resets on full stop)',
|
|
165
|
+
labelNames: ['app'],
|
|
166
|
+
});
|
|
167
|
+
registry.define({
|
|
168
|
+
name: 'omnitron_app_uptime_seconds',
|
|
169
|
+
type: 'gauge',
|
|
170
|
+
help: 'Seconds since the app last entered the online state',
|
|
171
|
+
labelNames: ['app'],
|
|
172
|
+
});
|
|
173
|
+
registry.define({
|
|
174
|
+
name: 'omnitron_app_cpu_percent',
|
|
175
|
+
type: 'gauge',
|
|
176
|
+
help: 'Last sampled CPU percent for the app',
|
|
177
|
+
labelNames: ['app'],
|
|
178
|
+
});
|
|
179
|
+
registry.define({
|
|
180
|
+
name: 'omnitron_app_memory_bytes',
|
|
181
|
+
type: 'gauge',
|
|
182
|
+
help: 'Last sampled RSS memory for the app',
|
|
183
|
+
labelNames: ['app'],
|
|
184
|
+
});
|
|
185
|
+
registry.define({
|
|
186
|
+
name: 'omnitron_workers_alive',
|
|
187
|
+
type: 'gauge',
|
|
188
|
+
help: 'Total fork-worker processes the daemon can see in `ps`',
|
|
189
|
+
});
|
|
190
|
+
registry.define({
|
|
191
|
+
name: 'omnitron_workers_owned',
|
|
192
|
+
type: 'gauge',
|
|
193
|
+
help: 'Fork-worker pids the daemon has registered as owned',
|
|
194
|
+
});
|
|
195
|
+
registry.define({
|
|
196
|
+
name: 'omnitron_workers_orphans_pending',
|
|
197
|
+
type: 'gauge',
|
|
198
|
+
help: 'Orphan fork-worker pids found in the last sweep but not yet killed',
|
|
199
|
+
});
|
|
200
|
+
registry.define({
|
|
201
|
+
name: 'omnitron_workers_orphans_killed_total',
|
|
202
|
+
type: 'counter',
|
|
203
|
+
help: 'Total fork-worker orphans the janitor has killed',
|
|
204
|
+
});
|
|
205
|
+
registry.define({
|
|
206
|
+
name: 'omnitron_workers_orphan_kill_errors_total',
|
|
207
|
+
type: 'counter',
|
|
208
|
+
help: 'Total errors when the janitor tried to kill an orphan',
|
|
209
|
+
});
|
|
210
|
+
registry.define({
|
|
211
|
+
name: 'resilient_handle_resets_total',
|
|
212
|
+
type: 'counter',
|
|
213
|
+
help: 'ResilientHandle reset count (cached external resource went unhealthy and was re-acquired)',
|
|
214
|
+
labelNames: ['name'],
|
|
215
|
+
});
|
|
216
|
+
registry.define({
|
|
217
|
+
name: 'omnitron_esbuild_duration_seconds',
|
|
218
|
+
type: 'histogram',
|
|
219
|
+
help: 'esbuild build/rebuild duration in seconds',
|
|
220
|
+
labelNames: ['app', 'kind'],
|
|
221
|
+
});
|
|
222
|
+
registry.define({
|
|
223
|
+
name: 'omnitron_esbuild_builds_total',
|
|
224
|
+
type: 'counter',
|
|
225
|
+
help: 'Total esbuild builds and rebuilds, partitioned by outcome',
|
|
226
|
+
labelNames: ['app', 'kind', 'outcome'],
|
|
227
|
+
});
|
|
228
|
+
registry.define({
|
|
229
|
+
name: 'omnitron_app_restarts_total',
|
|
230
|
+
type: 'counter',
|
|
231
|
+
help: 'Total app restarts, partitioned by reason',
|
|
232
|
+
labelNames: ['app', 'reason'],
|
|
233
|
+
});
|
|
234
|
+
registry.define({
|
|
235
|
+
name: 'omnitron_app_restart_attempt',
|
|
236
|
+
type: 'gauge',
|
|
237
|
+
help: 'Most recent restart attempt number for the app',
|
|
238
|
+
labelNames: ['app'],
|
|
239
|
+
});
|
|
240
|
+
registry.define({
|
|
241
|
+
name: 'omnitron_app_crashes_total',
|
|
242
|
+
type: 'counter',
|
|
243
|
+
help: 'Total times the app crashed (process exited with non-zero before going online)',
|
|
244
|
+
labelNames: ['app'],
|
|
245
|
+
});
|
|
246
|
+
registry.define({
|
|
247
|
+
name: 'omnitron_app_escalations_total',
|
|
248
|
+
type: 'counter',
|
|
249
|
+
help: 'Total times the supervisor escalated past max restarts',
|
|
250
|
+
labelNames: ['app'],
|
|
251
|
+
});
|
|
252
|
+
registry.define({
|
|
253
|
+
name: 'lifecycle_shutdown_phase_duration_seconds',
|
|
254
|
+
type: 'histogram',
|
|
255
|
+
help: 'LifecycleController shutdown phase duration in seconds',
|
|
256
|
+
labelNames: ['phase'],
|
|
257
|
+
});
|
|
258
|
+
registry.define({
|
|
259
|
+
name: 'lifecycle_shutdown_phase_timeouts_total',
|
|
260
|
+
type: 'counter',
|
|
261
|
+
help: 'Total LifecycleController phases that hit their timeout',
|
|
262
|
+
labelNames: ['phase'],
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
// Thin record helpers — wrap the unified `recordTyped` entry
|
|
266
|
+
// point so every metric the bridge emits appears in BOTH the
|
|
267
|
+
// Prometheus exposition (`/metrics`) AND the storage-backed
|
|
268
|
+
// `querySeries()` API (T#74). Errors are swallowed so a metrics-
|
|
269
|
+
// pipeline hiccup never crashes the broader daemon.
|
|
270
|
+
gauge(name, labels, value) {
|
|
271
|
+
try {
|
|
272
|
+
this.metrics.recordTyped('gauge', name, labels, value);
|
|
273
|
+
}
|
|
274
|
+
catch (err) {
|
|
275
|
+
this.logger?.warn?.({ err, name }, 'metrics-bridge: gauge write failed');
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
counter(name, labels, delta) {
|
|
279
|
+
try {
|
|
280
|
+
this.metrics.recordTyped('counter', name, labels, delta);
|
|
281
|
+
}
|
|
282
|
+
catch (err) {
|
|
283
|
+
this.logger?.warn?.({ err, name }, 'metrics-bridge: counter write failed');
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
histogram(name, labels, value) {
|
|
287
|
+
try {
|
|
288
|
+
this.metrics.recordTyped('histogram', name, labels, value);
|
|
289
|
+
}
|
|
290
|
+
catch (err) {
|
|
291
|
+
this.logger?.warn?.({ err, name }, 'metrics-bridge: histogram write failed');
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
//# sourceMappingURL=metrics-bridge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metrics-bridge.js","sourceRoot":"","sources":["../../src/observability/metrics-bridge.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standalone HTTP server for Prometheus scraping.
|
|
3
|
+
*
|
|
4
|
+
* Bound to a dedicated port (default `httpPort + 3` ≈ 9803) so that
|
|
5
|
+
* scraping is isolated from the main RPC port: a stuck Netron handler
|
|
6
|
+
* can't starve `/metrics`, and scraping can't accidentally generate
|
|
7
|
+
* RPC load. Two routes only:
|
|
8
|
+
*
|
|
9
|
+
* GET /metrics → text/plain Prometheus exposition (refreshes per-app
|
|
10
|
+
* gauges via MetricsBridge.refreshAppGauges() before
|
|
11
|
+
* formatting, so the snapshot is always current).
|
|
12
|
+
* GET /healthz → 200 OK / "ok\n" — used by liveness checks.
|
|
13
|
+
*
|
|
14
|
+
* Anything else returns 404. We deliberately don't expose more — if
|
|
15
|
+
* we ever need richer introspection, it goes through the daemon's RPC
|
|
16
|
+
* surface, which has authentication.
|
|
17
|
+
*/
|
|
18
|
+
import type { IMetricsService } from '@omnitron-dev/titan-metrics';
|
|
19
|
+
import type { ILogger } from '@omnitron-dev/titan/module/logger';
|
|
20
|
+
import type { MetricsBridge } from './metrics-bridge.js';
|
|
21
|
+
export interface MetricsServerOptions {
|
|
22
|
+
/** Listen port. */
|
|
23
|
+
port: number;
|
|
24
|
+
/**
|
|
25
|
+
* Bind host. **Default `127.0.0.1` (loopback)** as of T#65. The
|
|
26
|
+
* historical default of `0.0.0.0` exposed `/metrics` on every
|
|
27
|
+
* network interface — internal aggregated traffic counters,
|
|
28
|
+
* per-app health, infrastructure topology — to anyone on the same
|
|
29
|
+
* network. Production scraping by a co-located Prometheus on
|
|
30
|
+
* loopback is unaffected. Remote scraping must opt in by
|
|
31
|
+
* setting host = '0.0.0.0' AND configuring a bearer token via
|
|
32
|
+
* `authToken`.
|
|
33
|
+
*/
|
|
34
|
+
host?: string;
|
|
35
|
+
/**
|
|
36
|
+
* Optional bearer token required on `Authorization` header
|
|
37
|
+
* (T#65). When set, requests without `Authorization: Bearer
|
|
38
|
+
* <token>` get 401. `/healthz` is always public so external
|
|
39
|
+
* health probes work; only `/metrics` is gated. Without an
|
|
40
|
+
* `authToken` set, the endpoint is unauthenticated — which is
|
|
41
|
+
* safe ONLY while bound to loopback. The server logs a warning
|
|
42
|
+
* at start when host is non-loopback AND no token is set.
|
|
43
|
+
*/
|
|
44
|
+
authToken?: string;
|
|
45
|
+
/** Metric service backing `/metrics`. */
|
|
46
|
+
metrics: IMetricsService;
|
|
47
|
+
/** Bridge for refreshing per-app gauges before formatting. */
|
|
48
|
+
bridge: MetricsBridge;
|
|
49
|
+
/** Logger. */
|
|
50
|
+
logger: ILogger;
|
|
51
|
+
}
|
|
52
|
+
export declare class MetricsServer {
|
|
53
|
+
private server;
|
|
54
|
+
private readonly opts;
|
|
55
|
+
constructor(opts: MetricsServerOptions);
|
|
56
|
+
start(): Promise<{
|
|
57
|
+
port: number;
|
|
58
|
+
}>;
|
|
59
|
+
stop(): Promise<void>;
|
|
60
|
+
private handle;
|
|
61
|
+
/**
|
|
62
|
+
* T#65: constant-time compare against the configured bearer
|
|
63
|
+
* token. Accepts `Authorization: Bearer <token>` only. Returns
|
|
64
|
+
* `true` if the request carries a matching token.
|
|
65
|
+
*/
|
|
66
|
+
private authorized;
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=metrics-server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metrics-server.d.ts","sourceRoot":"","sources":["../../src/observability/metrics-server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAIH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mCAAmC,CAAC;AACjE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,MAAM,WAAW,oBAAoB;IACnC,mBAAmB;IACnB,IAAI,EAAE,MAAM,CAAC;IACb;;;;;;;;;OASG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;;;;OAQG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yCAAyC;IACzC,OAAO,EAAE,eAAe,CAAC;IACzB,8DAA8D;IAC9D,MAAM,EAAE,aAAa,CAAC;IACtB,cAAc;IACd,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAuB;gBAEhC,IAAI,EAAE,oBAAoB;IAIhC,KAAK,IAAI,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAkDlC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;YAcb,MAAM;IA6CpB;;;;OAIG;IACH,OAAO,CAAC,UAAU;CASnB"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standalone HTTP server for Prometheus scraping.
|
|
3
|
+
*
|
|
4
|
+
* Bound to a dedicated port (default `httpPort + 3` ≈ 9803) so that
|
|
5
|
+
* scraping is isolated from the main RPC port: a stuck Netron handler
|
|
6
|
+
* can't starve `/metrics`, and scraping can't accidentally generate
|
|
7
|
+
* RPC load. Two routes only:
|
|
8
|
+
*
|
|
9
|
+
* GET /metrics → text/plain Prometheus exposition (refreshes per-app
|
|
10
|
+
* gauges via MetricsBridge.refreshAppGauges() before
|
|
11
|
+
* formatting, so the snapshot is always current).
|
|
12
|
+
* GET /healthz → 200 OK / "ok\n" — used by liveness checks.
|
|
13
|
+
*
|
|
14
|
+
* Anything else returns 404. We deliberately don't expose more — if
|
|
15
|
+
* we ever need richer introspection, it goes through the daemon's RPC
|
|
16
|
+
* surface, which has authentication.
|
|
17
|
+
*/
|
|
18
|
+
import http from 'node:http';
|
|
19
|
+
export class MetricsServer {
|
|
20
|
+
server = null;
|
|
21
|
+
opts;
|
|
22
|
+
constructor(opts) {
|
|
23
|
+
this.opts = opts;
|
|
24
|
+
}
|
|
25
|
+
async start() {
|
|
26
|
+
if (this.server) {
|
|
27
|
+
return { port: this.server.address().port };
|
|
28
|
+
}
|
|
29
|
+
const server = http.createServer((req, res) => {
|
|
30
|
+
void this.handle(req, res).catch((err) => {
|
|
31
|
+
this.opts.logger.error({ err, url: req.url }, 'metrics server: handler crash');
|
|
32
|
+
if (!res.headersSent) {
|
|
33
|
+
res.statusCode = 500;
|
|
34
|
+
res.end('internal error\n');
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
server.keepAliveTimeout = 5_000;
|
|
39
|
+
server.headersTimeout = 6_000;
|
|
40
|
+
// T#65: default bind to loopback so the metrics endpoint isn't
|
|
41
|
+
// reachable from outside the host without an explicit opt-in.
|
|
42
|
+
const host = this.opts.host ?? '127.0.0.1';
|
|
43
|
+
await new Promise((resolve, reject) => {
|
|
44
|
+
server.once('error', reject);
|
|
45
|
+
server.listen(this.opts.port, host, () => {
|
|
46
|
+
server.off('error', reject);
|
|
47
|
+
resolve();
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
this.server = server;
|
|
51
|
+
const address = server.address();
|
|
52
|
+
this.opts.logger.info({ port: address.port, host: address.address, authenticated: Boolean(this.opts.authToken) }, 'metrics server: listening on /metrics');
|
|
53
|
+
// T#65: warn loudly if the operator bound to a non-loopback
|
|
54
|
+
// address WITHOUT setting an auth token. That combination
|
|
55
|
+
// hands `/metrics` to anyone on the network — usually not
|
|
56
|
+
// intended, especially in cloud deploys where the daemon's
|
|
57
|
+
// host is reachable from peer workloads.
|
|
58
|
+
const isLoopback = host === '127.0.0.1' || host === '::1' || host === 'localhost' || host === '127.1';
|
|
59
|
+
if (!isLoopback && !this.opts.authToken) {
|
|
60
|
+
this.opts.logger.warn({ host, port: address.port }, 'metrics server: bound to non-loopback WITHOUT authToken — `/metrics` is publicly readable (T#65)');
|
|
61
|
+
}
|
|
62
|
+
return { port: address.port };
|
|
63
|
+
}
|
|
64
|
+
async stop() {
|
|
65
|
+
if (!this.server)
|
|
66
|
+
return;
|
|
67
|
+
const server = this.server;
|
|
68
|
+
this.server = null;
|
|
69
|
+
await new Promise((resolve) => {
|
|
70
|
+
server.close(() => resolve());
|
|
71
|
+
// close() rejects new connections but waits for in-flight ones to
|
|
72
|
+
// finish naturally. Force close keep-alive connections so we
|
|
73
|
+
// don't drag shutdown out — they hold sockets idle for keepAliveTimeout.
|
|
74
|
+
server.closeIdleConnections?.();
|
|
75
|
+
});
|
|
76
|
+
this.opts.logger.info({}, 'metrics server: stopped');
|
|
77
|
+
}
|
|
78
|
+
async handle(req, res) {
|
|
79
|
+
const url = req.url ?? '/';
|
|
80
|
+
if (req.method !== 'GET') {
|
|
81
|
+
res.statusCode = 405;
|
|
82
|
+
res.setHeader('Allow', 'GET');
|
|
83
|
+
res.end('method not allowed\n');
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (url === '/healthz' || url === '/health') {
|
|
87
|
+
res.statusCode = 200;
|
|
88
|
+
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
|
|
89
|
+
res.end('ok\n');
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (url === '/metrics' || url.startsWith('/metrics?')) {
|
|
93
|
+
// T#65: gate on bearer token when configured. `/healthz`
|
|
94
|
+
// remains public so external health probes still work; only
|
|
95
|
+
// `/metrics` carries the sensitive aggregate data.
|
|
96
|
+
if (this.opts.authToken && !this.authorized(req)) {
|
|
97
|
+
res.statusCode = 401;
|
|
98
|
+
res.setHeader('WWW-Authenticate', 'Bearer realm="omnitron-metrics"');
|
|
99
|
+
res.end('unauthorized\n');
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
try {
|
|
103
|
+
this.opts.bridge.refreshAppGauges();
|
|
104
|
+
}
|
|
105
|
+
catch (err) {
|
|
106
|
+
this.opts.logger.warn({ err }, 'metrics server: refreshAppGauges threw — emitting last-known values');
|
|
107
|
+
}
|
|
108
|
+
const text = await this.opts.metrics.getPrometheusText();
|
|
109
|
+
res.statusCode = 200;
|
|
110
|
+
res.setHeader('Content-Type', 'text/plain; version=0.0.4; charset=utf-8');
|
|
111
|
+
res.end(text);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
res.statusCode = 404;
|
|
115
|
+
res.end('not found\n');
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* T#65: constant-time compare against the configured bearer
|
|
119
|
+
* token. Accepts `Authorization: Bearer <token>` only. Returns
|
|
120
|
+
* `true` if the request carries a matching token.
|
|
121
|
+
*/
|
|
122
|
+
authorized(req) {
|
|
123
|
+
const expected = this.opts.authToken;
|
|
124
|
+
if (!expected)
|
|
125
|
+
return true; // no auth configured → public
|
|
126
|
+
const header = req.headers['authorization'];
|
|
127
|
+
if (typeof header !== 'string')
|
|
128
|
+
return false;
|
|
129
|
+
const m = header.match(/^Bearer\s+(.+)$/);
|
|
130
|
+
if (!m)
|
|
131
|
+
return false;
|
|
132
|
+
return timingSafeEqual(m[1], expected);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
function timingSafeEqual(a, b) {
|
|
136
|
+
// Constant-time string compare. Returns false fast on differing
|
|
137
|
+
// length (the length itself isn't a secret in this protocol), and
|
|
138
|
+
// OR-accumulates byte differences across the shorter length.
|
|
139
|
+
if (a.length !== b.length)
|
|
140
|
+
return false;
|
|
141
|
+
let diff = 0;
|
|
142
|
+
for (let i = 0; i < a.length; i++)
|
|
143
|
+
diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
144
|
+
return diff === 0;
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=metrics-server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metrics-server.js","sourceRoot":"","sources":["../../src/observability/metrics-server.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* along with app-level metadata.
|
|
6
6
|
*/
|
|
7
7
|
import type { ChildProcess } from 'node:child_process';
|
|
8
|
-
import type { IProcessMetrics, IHealthStatus } from '@omnitron-dev/titan-pm';
|
|
8
|
+
import type { IProcessMetrics, IHealthStatus, IProcessPool } from '@omnitron-dev/titan-pm';
|
|
9
9
|
import type { ProcessSupervisor } from '@omnitron-dev/titan-pm';
|
|
10
10
|
import type { AppStatus, IEcosystemAppEntry, IProcessEntry } from '../config/types.js';
|
|
11
11
|
import type { ServiceRouter } from './service-router.js';
|
|
@@ -29,6 +29,18 @@ export declare class AppHandle {
|
|
|
29
29
|
serviceRouter: ServiceRouter | null;
|
|
30
30
|
/** Process topology entries from defineSystem() */
|
|
31
31
|
topologyProcesses: IProcessEntry[] | null;
|
|
32
|
+
/**
|
|
33
|
+
* Pool refs keyed by topology entry `name` (only for entries with
|
|
34
|
+
* `instances > 1` — single-instance topology runs as a supervisor
|
|
35
|
+
* child instead). Populated when the pool is created in
|
|
36
|
+
* `OrchestratorService.startBootstrapApp`; consumed by `getInfo()`
|
|
37
|
+
* so per-pool status reflects actual worker count instead of
|
|
38
|
+
* defaulting to 'stopped' (supervisor knows nothing about pool
|
|
39
|
+
* workers, so without this map the orchestrator would always
|
|
40
|
+
* report pool-managed topology entries as stopped even when they
|
|
41
|
+
* have N live workers).
|
|
42
|
+
*/
|
|
43
|
+
topologyPools: Map<string, IProcessPool<unknown>>;
|
|
32
44
|
/** Timer for classic-mode crash restart backoff (cleared on explicit stop) */
|
|
33
45
|
crashRestartTimer: ReturnType<typeof setTimeout> | null;
|
|
34
46
|
/** Stdout/stderr log buffer (ring buffer) */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-handle.d.ts","sourceRoot":"","sources":["../../src/orchestrator/app-handle.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"app-handle.d.ts","sourceRoot":"","sources":["../../src/orchestrator/app-handle.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3F,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,KAAK,EAAE,SAAS,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACvF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,qBAAa,SAAS;aA6CF,KAAK,EAAE,kBAAkB;aACzB,IAAI,EAAE,SAAS,GAAG,WAAW;IA7CxC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAQ;IAC1B,MAAM,EAAE,SAAS,CAAa;IAC9B,SAAS,EAAE,MAAM,CAAK;IACtB,QAAQ,EAAE,MAAM,CAAK;IACrB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAQ;IAC3B,WAAW,EAAE,eAAe,GAAG,IAAI,CAAQ;IAC3C,UAAU,EAAE,aAAa,GAAG,IAAI,CAAQ;IAE/C,mEAAmE;IAC5D,UAAU,EAAE,iBAAiB,GAAG,IAAI,CAAQ;IAEnD,6DAA6D;IACtD,YAAY,EAAE,YAAY,GAAG,IAAI,CAAQ;IAEhD,iCAAiC;IAC1B,aAAa,EAAE,MAAM,CAAK;IAEjC,gDAAgD;IACzC,aAAa,EAAE,aAAa,GAAG,IAAI,CAAQ;IAElD,mDAAmD;IAC5C,iBAAiB,EAAE,aAAa,EAAE,GAAG,IAAI,CAAQ;IAExD;;;;;;;;;;OAUG;IACI,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC,CAAa;IAErE,8EAA8E;IACvE,iBAAiB,EAAE,UAAU,CAAC,OAAO,UAAU,CAAC,GAAG,IAAI,CAAQ;IAEtE,6CAA6C;IAC7C,OAAO,CAAC,SAAS,CAAgB;IACjC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAQ;gBAGlB,KAAK,EAAE,kBAAkB,EACzB,IAAI,EAAE,SAAS,GAAG,WAAW;IAG/C,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,IAAI,MAAM,IAAI,MAAM,CAGnB;IAED,IAAI,GAAG,IAAI,MAAM,CAEhB;IAED,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAO7B,OAAO,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE;IAKjC,YAAY,IAAI,IAAI;IAKpB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAK7B,YAAY,IAAI,IAAI;IAIpB,WAAW,IAAI,IAAI;IAWnB,WAAW,IAAI,IAAI;IAInB,WAAW,IAAI,IAAI;CAIpB"}
|
|
@@ -24,6 +24,18 @@ export class AppHandle {
|
|
|
24
24
|
serviceRouter = null;
|
|
25
25
|
/** Process topology entries from defineSystem() */
|
|
26
26
|
topologyProcesses = null;
|
|
27
|
+
/**
|
|
28
|
+
* Pool refs keyed by topology entry `name` (only for entries with
|
|
29
|
+
* `instances > 1` — single-instance topology runs as a supervisor
|
|
30
|
+
* child instead). Populated when the pool is created in
|
|
31
|
+
* `OrchestratorService.startBootstrapApp`; consumed by `getInfo()`
|
|
32
|
+
* so per-pool status reflects actual worker count instead of
|
|
33
|
+
* defaulting to 'stopped' (supervisor knows nothing about pool
|
|
34
|
+
* workers, so without this map the orchestrator would always
|
|
35
|
+
* report pool-managed topology entries as stopped even when they
|
|
36
|
+
* have N live workers).
|
|
37
|
+
*/
|
|
38
|
+
topologyPools = new Map();
|
|
27
39
|
/** Timer for classic-mode crash restart backoff (cleared on explicit stop) */
|
|
28
40
|
crashRestartTimer = null;
|
|
29
41
|
/** Stdout/stderr log buffer (ring buffer) */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-handle.js","sourceRoot":"","sources":["../../src/orchestrator/app-handle.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAQH,MAAM,OAAO,SAAS;
|
|
1
|
+
{"version":3,"file":"app-handle.js","sourceRoot":"","sources":["../../src/orchestrator/app-handle.ts"],"names":[],"mappings":"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"}
|