@graphorin/server 0.6.0 → 0.7.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/CHANGELOG.md +85 -0
- package/README.md +6 -6
- package/dist/app-audit-binding.d.ts +15 -0
- package/dist/app-audit-binding.d.ts.map +1 -0
- package/dist/app-audit-binding.js +136 -0
- package/dist/app-audit-binding.js.map +1 -0
- package/dist/app-daemons.d.ts +29 -0
- package/dist/app-daemons.d.ts.map +1 -0
- package/dist/app-daemons.js +27 -0
- package/dist/app-daemons.js.map +1 -0
- package/dist/app-lifecycle.js +272 -0
- package/dist/app-lifecycle.js.map +1 -0
- package/dist/app-metrics.js +79 -0
- package/dist/app-metrics.js.map +1 -0
- package/dist/app-middleware.js +92 -0
- package/dist/app-middleware.js.map +1 -0
- package/dist/app-routes.js +131 -0
- package/dist/app-routes.js.map +1 -0
- package/dist/app-ws.js +65 -0
- package/dist/app-ws.js.map +1 -0
- package/dist/app.d.ts +16 -16
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +40 -611
- package/dist/app.js.map +1 -1
- package/dist/commentary/built-in-patterns.d.ts +1 -1
- package/dist/commentary/built-in-patterns.js +1 -1
- package/dist/commentary/built-in-patterns.js.map +1 -1
- package/dist/config.d.ts +100 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +30 -2
- package/dist/config.js.map +1 -1
- package/dist/health/checks.d.ts +15 -1
- package/dist/health/checks.d.ts.map +1 -1
- package/dist/health/checks.js +21 -0
- package/dist/health/checks.js.map +1 -1
- package/dist/health/routes.js +1 -1
- package/dist/index.d.ts +9 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +22 -26
- package/dist/index.js.map +1 -1
- package/dist/internal/context.d.ts +2 -2
- package/dist/internal/wire-error.js +20 -0
- package/dist/internal/wire-error.js.map +1 -0
- package/dist/lifecycle/pre-bind.d.ts +1 -1
- package/dist/metrics/catalog.d.ts.map +1 -1
- package/dist/metrics/catalog.js.map +1 -1
- package/dist/metrics/tools-bridge.d.ts +15 -0
- package/dist/metrics/tools-bridge.d.ts.map +1 -0
- package/dist/metrics/tools-bridge.js +103 -0
- package/dist/metrics/tools-bridge.js.map +1 -0
- package/dist/middleware/audit.d.ts +1 -1
- package/dist/middleware/auth.js +1 -1
- package/dist/middleware/csrf.js +1 -1
- package/dist/middleware/index.js +1 -1
- package/dist/middleware/scope.d.ts.map +1 -1
- package/dist/middleware/scope.js +44 -4
- package/dist/middleware/scope.js.map +1 -1
- package/dist/package.js +6 -0
- package/dist/package.js.map +1 -0
- package/dist/registry/index.d.ts +33 -0
- package/dist/registry/index.d.ts.map +1 -1
- package/dist/registry/index.js.map +1 -1
- package/dist/replay/routes.d.ts +1 -1
- package/dist/replay/routes.js +2 -2
- package/dist/routes/agents.d.ts.map +1 -1
- package/dist/routes/agents.js +64 -18
- package/dist/routes/agents.js.map +1 -1
- package/dist/routes/auth.js +2 -2
- package/dist/routes/auth.js.map +1 -1
- package/dist/routes/sessions.js +5 -5
- package/dist/routes/sessions.js.map +1 -1
- package/dist/routes/tokens.d.ts +1 -1
- package/dist/routes/tokens.d.ts.map +1 -1
- package/dist/routes/tokens.js +21 -1
- package/dist/routes/tokens.js.map +1 -1
- package/dist/routes/workflows.d.ts.map +1 -1
- package/dist/routes/workflows.js +165 -19
- package/dist/routes/workflows.js.map +1 -1
- package/dist/runtime/retention.d.ts +105 -0
- package/dist/runtime/retention.d.ts.map +1 -0
- package/dist/runtime/retention.js +154 -0
- package/dist/runtime/retention.js.map +1 -0
- package/dist/runtime/run-state.d.ts +2 -0
- package/dist/runtime/run-state.d.ts.map +1 -1
- package/dist/runtime/run-state.js +34 -2
- package/dist/runtime/run-state.js.map +1 -1
- package/dist/sse/events.js +3 -4
- package/dist/sse/events.js.map +1 -1
- package/dist/tools-audit-bridge.d.ts +29 -0
- package/dist/tools-audit-bridge.d.ts.map +1 -0
- package/dist/tools-audit-bridge.js +103 -0
- package/dist/tools-audit-bridge.js.map +1 -0
- package/dist/workflows/timer-daemon.d.ts +69 -0
- package/dist/workflows/timer-daemon.d.ts.map +1 -0
- package/dist/workflows/timer-daemon.js +37 -0
- package/dist/workflows/timer-daemon.js.map +1 -0
- package/dist/ws/dispatcher.d.ts +1 -1
- package/dist/ws/dispatcher.d.ts.map +1 -1
- package/dist/ws/dispatcher.js +19 -12
- package/dist/ws/dispatcher.js.map +1 -1
- package/dist/ws/index.d.ts +2 -2
- package/dist/ws/index.js +3 -3
- package/dist/ws/replay-buffer.d.ts +35 -1
- package/dist/ws/replay-buffer.d.ts.map +1 -1
- package/dist/ws/replay-buffer.js +35 -3
- package/dist/ws/replay-buffer.js.map +1 -1
- package/dist/ws/upgrade.d.ts.map +1 -1
- package/dist/ws/upgrade.js +26 -19
- package/dist/ws/upgrade.js.map +1 -1
- package/package.json +31 -30
- package/src/app-audit-binding.ts +227 -0
- package/src/app-daemons.ts +73 -0
- package/src/app-lifecycle.ts +476 -0
- package/src/app-metrics.ts +144 -0
- package/src/app-middleware.ts +149 -0
- package/src/app-routes.ts +305 -0
- package/src/app-ws.ts +111 -0
- package/src/app.ts +317 -0
- package/src/commentary/audit-bridge.ts +135 -0
- package/src/commentary/built-in-patterns.ts +79 -0
- package/src/commentary/index.ts +32 -0
- package/src/commentary/sanitizer.ts +238 -0
- package/src/commentary/types.ts +130 -0
- package/src/config.ts +472 -0
- package/src/consolidator/daemon.ts +141 -0
- package/src/consolidator/index.ts +14 -0
- package/src/errors/index.ts +247 -0
- package/src/health/checks.ts +322 -0
- package/src/health/index.ts +34 -0
- package/src/health/routes.ts +154 -0
- package/src/index.ts +243 -0
- package/src/internal/context.ts +77 -0
- package/src/internal/ids.ts +17 -0
- package/src/internal/json.ts +47 -0
- package/src/internal/wire-error.ts +44 -0
- package/src/lifecycle/hooks.ts +66 -0
- package/src/lifecycle/index.ts +16 -0
- package/src/lifecycle/pre-bind.ts +138 -0
- package/src/metrics/catalog.ts +112 -0
- package/src/metrics/index.ts +12 -0
- package/src/metrics/registry.ts +337 -0
- package/src/metrics/tools-bridge.ts +124 -0
- package/src/middleware/audit.ts +114 -0
- package/src/middleware/auth.ts +195 -0
- package/src/middleware/cors.ts +72 -0
- package/src/middleware/csrf.ts +98 -0
- package/src/middleware/idempotency.ts +336 -0
- package/src/middleware/index.ts +38 -0
- package/src/middleware/rate-limit.ts +71 -0
- package/src/middleware/request-state.ts +79 -0
- package/src/middleware/scope.ts +161 -0
- package/src/registry/index.ts +278 -0
- package/src/replay/index.ts +14 -0
- package/src/replay/routes.ts +255 -0
- package/src/routes/agents.ts +363 -0
- package/src/routes/audit.ts +207 -0
- package/src/routes/auth.ts +80 -0
- package/src/routes/health.ts +42 -0
- package/src/routes/index.ts +16 -0
- package/src/routes/mcp.ts +97 -0
- package/src/routes/memory.ts +152 -0
- package/src/routes/sessions.ts +250 -0
- package/src/routes/skills.ts +91 -0
- package/src/routes/tokens.ts +166 -0
- package/src/routes/workflows.ts +616 -0
- package/src/runtime/retention.ts +284 -0
- package/src/runtime/run-state.ts +422 -0
- package/src/sse/events.ts +303 -0
- package/src/sse/index.ts +7 -0
- package/src/tools-audit-bridge.ts +120 -0
- package/src/triggers/daemon.ts +198 -0
- package/src/triggers/index.ts +16 -0
- package/src/triggers/routes.ts +139 -0
- package/src/workflows/timer-daemon.ts +102 -0
- package/src/ws/dispatcher.ts +537 -0
- package/src/ws/index.ts +45 -0
- package/src/ws/replay-buffer.ts +209 -0
- package/src/ws/subjects.ts +162 -0
- package/src/ws/ticket.ts +174 -0
- package/src/ws/upgrade.ts +507 -0
- package/dist/lifecycle/index.js +0 -3
- package/dist/routes/index.js +0 -12
package/dist/app.js
CHANGED
|
@@ -1,148 +1,21 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import "./
|
|
3
|
-
import {
|
|
4
|
-
import { parseServerConfig } from "./config.js";
|
|
5
|
-
import { createConsolidatorDaemon } from "./consolidator/daemon.js";
|
|
6
|
-
import { createExtendedHealthRoutes, createMetricsRoutes, createSecretsHealthRoutes } from "./health/routes.js";
|
|
7
|
-
import "./health/index.js";
|
|
8
|
-
import { runPreBind } from "./lifecycle/pre-bind.js";
|
|
9
|
-
import "./lifecycle/index.js";
|
|
1
|
+
import { version } from "./package.js";
|
|
2
|
+
import { buildDaemons } from "./app-daemons.js";
|
|
3
|
+
import { ensureStoreAuditBinding } from "./app-audit-binding.js";
|
|
10
4
|
import { SERVER_METRIC_NAMES, createServerMetricRegistry } from "./metrics/catalog.js";
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import { createRateLimitMiddleware } from "./middleware/rate-limit.js";
|
|
17
|
-
import { createRequestStateMiddleware } from "./middleware/request-state.js";
|
|
18
|
-
import "./middleware/index.js";
|
|
5
|
+
import { attachGlobalMiddleware } from "./app-middleware.js";
|
|
6
|
+
import { RunStateTracker } from "./runtime/run-state.js";
|
|
7
|
+
import { createLifecycle } from "./app-lifecycle.js";
|
|
8
|
+
import { buildWsLayer } from "./app-ws.js";
|
|
9
|
+
import { parseServerConfig } from "./config.js";
|
|
19
10
|
import { AgentRegistry, WorkflowRegistry } from "./registry/index.js";
|
|
20
|
-
import {
|
|
21
|
-
import "./replay/index.js";
|
|
22
|
-
import { createAgentRoutes, createRunRoutes } from "./routes/agents.js";
|
|
23
|
-
import { createAuditRoutes } from "./routes/audit.js";
|
|
24
|
-
import { createAuthRoutes } from "./routes/auth.js";
|
|
25
|
-
import { createMcpRoutes } from "./routes/mcp.js";
|
|
26
|
-
import { createMemoryRoutes } from "./routes/memory.js";
|
|
27
|
-
import { createSessionRoutes } from "./routes/sessions.js";
|
|
28
|
-
import { createSkillsRoutes } from "./routes/skills.js";
|
|
29
|
-
import { createTokensRoutes } from "./routes/tokens.js";
|
|
30
|
-
import { createWorkflowRoutes } from "./routes/workflows.js";
|
|
31
|
-
import "./routes/index.js";
|
|
32
|
-
import { RunStateTracker, scheduleRunPruning } from "./runtime/run-state.js";
|
|
33
|
-
import { createSseRoutes } from "./sse/events.js";
|
|
34
|
-
import "./sse/index.js";
|
|
35
|
-
import { createTriggersDaemon } from "./triggers/daemon.js";
|
|
36
|
-
import { createTriggersRoutes } from "./triggers/routes.js";
|
|
37
|
-
import { createWsDispatcher } from "./ws/dispatcher.js";
|
|
38
|
-
import { createWsTicketStore } from "./ws/ticket.js";
|
|
39
|
-
import { createWsUpgradeEvents } from "./ws/upgrade.js";
|
|
40
|
-
import "./ws/index.js";
|
|
41
|
-
import { SUBPROTOCOL_NAME, negotiateSubprotocol } from "@graphorin/protocol";
|
|
42
|
-
import { TokenVerifier, applyProcessHardening, generatePepper } from "@graphorin/security";
|
|
43
|
-
import { openAuditDb, registerAuditDbBinding } from "@graphorin/security/audit";
|
|
44
|
-
import { createSqliteStore, loadCipherDriver, readWalSize } from "@graphorin/store-sqlite";
|
|
45
|
-
import { serve } from "@hono/node-server";
|
|
46
|
-
import { createNodeWebSocket } from "@hono/node-ws";
|
|
11
|
+
import { createSqliteStore } from "@graphorin/store-sqlite";
|
|
47
12
|
import { Hono } from "hono";
|
|
48
13
|
|
|
49
14
|
//#region src/app.ts
|
|
50
15
|
/**
|
|
51
|
-
* Pre-built audit-db binding shipped from `@graphorin/store-sqlite`.
|
|
52
|
-
* Registered exactly once per process so {@link openAuditDb} can find
|
|
53
|
-
* a default binding without forcing operators to wire it manually.
|
|
54
|
-
*
|
|
55
|
-
* Exported as {@link ensureStoreAuditBinding} so the CLI (Phase 15
|
|
56
|
-
* `graphorin audit verify | prune | export`) can reach into the same
|
|
57
|
-
* binding without booting the HTTP listener.
|
|
58
|
-
*
|
|
59
|
-
* @stable
|
|
60
|
-
*/
|
|
61
|
-
let storeAuditBindingRegistered = false;
|
|
62
|
-
function ensureStoreAuditBinding() {
|
|
63
|
-
if (storeAuditBindingRegistered) return;
|
|
64
|
-
registerAuditDbBinding({
|
|
65
|
-
id: "better-sqlite3-multiple-ciphers",
|
|
66
|
-
description: "Default audit-db binding shipped by @graphorin/store-sqlite.",
|
|
67
|
-
open: async (opts) => {
|
|
68
|
-
const driver = await loadCipherDriver();
|
|
69
|
-
const passphrase = await opts.passphrase.use((value) => value);
|
|
70
|
-
const db = new driver(opts.path);
|
|
71
|
-
db.pragma(`key = '${passphrase.replace(/'/g, "''")}'`);
|
|
72
|
-
db.pragma("journal_mode = WAL");
|
|
73
|
-
db.pragma("synchronous = NORMAL");
|
|
74
|
-
db.pragma("busy_timeout = 5000");
|
|
75
|
-
db.pragma("foreign_keys = ON");
|
|
76
|
-
db.exec(`CREATE TABLE IF NOT EXISTS audit_log (
|
|
77
|
-
seq INTEGER PRIMARY KEY,
|
|
78
|
-
ts INTEGER NOT NULL,
|
|
79
|
-
actor_json TEXT NOT NULL,
|
|
80
|
-
action TEXT NOT NULL,
|
|
81
|
-
target TEXT NOT NULL,
|
|
82
|
-
decision TEXT NOT NULL,
|
|
83
|
-
context_json TEXT,
|
|
84
|
-
metadata_json TEXT,
|
|
85
|
-
prev_hash TEXT NOT NULL,
|
|
86
|
-
hash TEXT NOT NULL UNIQUE
|
|
87
|
-
) WITHOUT ROWID;`);
|
|
88
|
-
return {
|
|
89
|
-
binding: "better-sqlite3-multiple-ciphers",
|
|
90
|
-
path: opts.path,
|
|
91
|
-
async insert(entry) {
|
|
92
|
-
db.prepare(`INSERT INTO audit_log (
|
|
93
|
-
seq, ts, actor_json, action, target, decision,
|
|
94
|
-
context_json, metadata_json, prev_hash, hash
|
|
95
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(entry.seq, entry.ts, JSON.stringify(entry.actor), entry.action, entry.target, entry.decision, entry.context !== void 0 ? JSON.stringify(entry.context) : null, entry.metadata !== void 0 ? JSON.stringify(entry.metadata) : null, entry.prevHash, entry.hash);
|
|
96
|
-
return entry;
|
|
97
|
-
},
|
|
98
|
-
async latest() {
|
|
99
|
-
const row = db.prepare("SELECT * FROM audit_log ORDER BY seq DESC LIMIT 1").get();
|
|
100
|
-
if (row === void 0) return void 0;
|
|
101
|
-
return rowToEntry(row);
|
|
102
|
-
},
|
|
103
|
-
async *iterate(bounds) {
|
|
104
|
-
const lo = bounds?.fromSeq ?? 1;
|
|
105
|
-
const hi = bounds?.toSeq ?? Number.MAX_SAFE_INTEGER;
|
|
106
|
-
const iter = db.prepare("SELECT * FROM audit_log WHERE seq BETWEEN ? AND ? ORDER BY seq ASC").iterate(lo, hi);
|
|
107
|
-
for (const row of iter) yield rowToEntry(row);
|
|
108
|
-
},
|
|
109
|
-
async count() {
|
|
110
|
-
return db.prepare("SELECT COUNT(*) AS n FROM audit_log").get().n;
|
|
111
|
-
},
|
|
112
|
-
async deleteUpTo(threshold) {
|
|
113
|
-
const before = db.prepare("SELECT COUNT(*) AS n FROM audit_log WHERE seq <= ?").get(threshold);
|
|
114
|
-
db.prepare("DELETE FROM audit_log WHERE seq <= ?").run(threshold);
|
|
115
|
-
return before.n;
|
|
116
|
-
},
|
|
117
|
-
async replaceEntry(entry) {
|
|
118
|
-
db.prepare(`UPDATE audit_log SET prev_hash = ?, hash = ? WHERE seq = ?`).run(entry.prevHash, entry.hash, entry.seq);
|
|
119
|
-
},
|
|
120
|
-
async close() {
|
|
121
|
-
if (db.open) db.close();
|
|
122
|
-
}
|
|
123
|
-
};
|
|
124
|
-
}
|
|
125
|
-
}, { setAsDefault: true });
|
|
126
|
-
storeAuditBindingRegistered = true;
|
|
127
|
-
}
|
|
128
|
-
function rowToEntry(row) {
|
|
129
|
-
return {
|
|
130
|
-
seq: row.seq,
|
|
131
|
-
ts: row.ts,
|
|
132
|
-
actor: JSON.parse(row.actor_json),
|
|
133
|
-
action: row.action,
|
|
134
|
-
target: row.target,
|
|
135
|
-
decision: row.decision,
|
|
136
|
-
...row.context_json !== null ? { context: JSON.parse(row.context_json) } : {},
|
|
137
|
-
...row.metadata_json !== null ? { metadata: JSON.parse(row.metadata_json) } : {},
|
|
138
|
-
prevHash: row.prev_hash,
|
|
139
|
-
hash: row.hash
|
|
140
|
-
};
|
|
141
|
-
}
|
|
142
|
-
/**
|
|
143
16
|
* @stable
|
|
144
17
|
*/
|
|
145
|
-
const VERSION =
|
|
18
|
+
const VERSION = version;
|
|
146
19
|
/**
|
|
147
20
|
* Build a fully-wired Graphorin server. The returned handle is
|
|
148
21
|
* inert until `start()` is awaited.
|
|
@@ -153,7 +26,7 @@ async function createServer(options = {}) {
|
|
|
153
26
|
const config = options.validatedConfig ?? parseServerConfig(options.config ?? {});
|
|
154
27
|
const now = options.now ?? Date.now;
|
|
155
28
|
const startedAt = now();
|
|
156
|
-
const version = options.version ?? VERSION;
|
|
29
|
+
const version$1 = options.version ?? VERSION;
|
|
157
30
|
let storeEncryption;
|
|
158
31
|
if (options.store === void 0 && config.storage.encryption.enabled) {
|
|
159
32
|
if (config.storage.encryption.passphraseRef === void 0) throw new Error("[graphorin/server] storage.encryption.enabled is true but no passphraseRef is configured.");
|
|
@@ -174,7 +47,7 @@ async function createServer(options = {}) {
|
|
|
174
47
|
} } : {}
|
|
175
48
|
});
|
|
176
49
|
const metricRegistry = options.metricRegistry ?? createServerMetricRegistry();
|
|
177
|
-
metricRegistry.set(SERVER_METRIC_NAMES.buildInfo, 1, { version });
|
|
50
|
+
metricRegistry.set(SERVER_METRIC_NAMES.buildInfo, 1, { version: version$1 });
|
|
178
51
|
const runs = options.runs ?? new RunStateTracker({
|
|
179
52
|
now,
|
|
180
53
|
onTerminal: (info) => {
|
|
@@ -185,61 +58,27 @@ async function createServer(options = {}) {
|
|
|
185
58
|
const agents = options.agents ?? new AgentRegistry();
|
|
186
59
|
const workflows = options.workflows ?? new WorkflowRegistry();
|
|
187
60
|
const app = new Hono();
|
|
188
|
-
app
|
|
61
|
+
attachGlobalMiddleware(app, config, now);
|
|
62
|
+
const { triggersDaemon, consolidatorDaemon, workflowTimerDaemon } = buildDaemons(options);
|
|
63
|
+
const lifecycle = createLifecycle({
|
|
64
|
+
options,
|
|
65
|
+
config,
|
|
66
|
+
store,
|
|
67
|
+
app,
|
|
68
|
+
metricRegistry,
|
|
69
|
+
runs,
|
|
70
|
+
agents,
|
|
71
|
+
workflows,
|
|
72
|
+
version: version$1,
|
|
73
|
+
startedAt,
|
|
189
74
|
now,
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
let serverInstance;
|
|
196
|
-
let listening;
|
|
197
|
-
let started = false;
|
|
198
|
-
let stopped = false;
|
|
199
|
-
let stopRunPruning;
|
|
200
|
-
let auditDb;
|
|
201
|
-
let preBind;
|
|
202
|
-
let verifier;
|
|
203
|
-
let pepperHandle;
|
|
204
|
-
let triggersDaemon;
|
|
205
|
-
if (options.triggers !== void 0) if ("daemon" in options.triggers) triggersDaemon = options.triggers.daemon;
|
|
206
|
-
else triggersDaemon = createTriggersDaemon({ scheduler: options.triggers.scheduler });
|
|
207
|
-
let consolidatorDaemon;
|
|
208
|
-
if (options.consolidator !== void 0) consolidatorDaemon = createConsolidatorDaemon({ consolidator: options.consolidator });
|
|
209
|
-
let dispatcher;
|
|
210
|
-
let tickets;
|
|
211
|
-
let wsAdapter;
|
|
212
|
-
let commentaryAuditSink;
|
|
213
|
-
if (config.server.ws.enabled) {
|
|
214
|
-
commentaryAuditSink = createLateBoundCommentarySink();
|
|
215
|
-
dispatcher = createWsDispatcher({
|
|
216
|
-
commentary: {
|
|
217
|
-
policy: config.server.ws.commentarySanitization.policy,
|
|
218
|
-
applyToEvents: config.server.ws.commentarySanitization.applyToEvents,
|
|
219
|
-
sink: commentaryAuditSink
|
|
220
|
-
},
|
|
221
|
-
replayBuffer: {
|
|
222
|
-
maxEvents: config.server.stream.replayBuffer.maxEvents,
|
|
223
|
-
ttlMs: config.server.stream.replayBuffer.ttlSeconds * 1e3
|
|
224
|
-
},
|
|
225
|
-
perConnectionQueueLimit: config.server.stream.perConnectionQueueLimit,
|
|
226
|
-
now
|
|
227
|
-
});
|
|
228
|
-
tickets = createWsTicketStore({
|
|
229
|
-
ttlMs: config.server.ws.ticketTtlMs,
|
|
230
|
-
now
|
|
231
|
-
});
|
|
232
|
-
wsAdapter = createNodeWebSocket({ app });
|
|
233
|
-
const wssOptions = wsAdapter.wss.options;
|
|
234
|
-
wssOptions.handleProtocols = (protocols) => {
|
|
235
|
-
const negotiated = negotiateSubprotocol(Array.from(protocols));
|
|
236
|
-
if (negotiated !== null) return negotiated;
|
|
237
|
-
for (const candidate of protocols) if (candidate === SUBPROTOCOL_NAME) return SUBPROTOCOL_NAME;
|
|
238
|
-
return false;
|
|
239
|
-
};
|
|
240
|
-
}
|
|
75
|
+
ws: buildWsLayer(app, config, now),
|
|
76
|
+
triggersDaemon,
|
|
77
|
+
consolidatorDaemon,
|
|
78
|
+
workflowTimerDaemon
|
|
79
|
+
});
|
|
241
80
|
return {
|
|
242
|
-
version,
|
|
81
|
+
version: version$1,
|
|
243
82
|
config,
|
|
244
83
|
app,
|
|
245
84
|
agents,
|
|
@@ -247,10 +86,10 @@ async function createServer(options = {}) {
|
|
|
247
86
|
runs,
|
|
248
87
|
metrics: metricRegistry,
|
|
249
88
|
get wsDispatcher() {
|
|
250
|
-
return
|
|
89
|
+
return lifecycle.wsDispatcher;
|
|
251
90
|
},
|
|
252
91
|
get wsTickets() {
|
|
253
|
-
return
|
|
92
|
+
return lifecycle.wsTickets;
|
|
254
93
|
},
|
|
255
94
|
get triggers() {
|
|
256
95
|
return triggersDaemon;
|
|
@@ -258,427 +97,17 @@ async function createServer(options = {}) {
|
|
|
258
97
|
get consolidator() {
|
|
259
98
|
return consolidatorDaemon;
|
|
260
99
|
},
|
|
261
|
-
get
|
|
262
|
-
return
|
|
100
|
+
get workflowTimers() {
|
|
101
|
+
return workflowTimerDaemon;
|
|
263
102
|
},
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
started = true;
|
|
267
|
-
try {
|
|
268
|
-
if (options.hooks?.beforeStart !== void 0) await options.hooks.beforeStart({ config });
|
|
269
|
-
if (options.skipHardening !== true && config.hardening.applyOnStart) applyProcessHardening({
|
|
270
|
-
refuseRoot: config.hardening.refuseRoot,
|
|
271
|
-
umask: config.hardening.umask
|
|
272
|
-
});
|
|
273
|
-
preBind = await runPreBind({
|
|
274
|
-
config,
|
|
275
|
-
store,
|
|
276
|
-
...options.probeCipherPeer !== void 0 ? { probeCipherPeer: options.probeCipherPeer } : {}
|
|
277
|
-
});
|
|
278
|
-
if (config.audit.enabled && preBind.auditPassphrase !== void 0 && preBind.auditPath !== void 0) {
|
|
279
|
-
ensureStoreAuditBinding();
|
|
280
|
-
auditDb = await openAuditDb({
|
|
281
|
-
path: preBind.auditPath,
|
|
282
|
-
passphrase: preBind.auditPassphrase,
|
|
283
|
-
...config.audit.cipher !== void 0 ? { cipher: config.audit.cipher } : {}
|
|
284
|
-
});
|
|
285
|
-
commentaryAuditSink?.bind(bridgeCommentaryToAudit(auditDb));
|
|
286
|
-
}
|
|
287
|
-
if (config.auth.kind === "token") {
|
|
288
|
-
const pepper = preBind.pepper ?? await fallbackPepper(options.skipHardening === true);
|
|
289
|
-
pepperHandle = pepper;
|
|
290
|
-
verifier = new TokenVerifier({
|
|
291
|
-
tokenStore: store.authTokens,
|
|
292
|
-
pepper,
|
|
293
|
-
acceptPrefix: config.auth.tokenPrefix,
|
|
294
|
-
acceptEnvironments: config.auth.tokenEnvironments,
|
|
295
|
-
...config.auth.perIpFailureThreshold !== void 0 ? { perIpFailureThreshold: config.auth.perIpFailureThreshold } : {},
|
|
296
|
-
...config.auth.perIpLockoutMs !== void 0 ? { perIpLockoutMs: config.auth.perIpLockoutMs } : {},
|
|
297
|
-
now
|
|
298
|
-
});
|
|
299
|
-
}
|
|
300
|
-
mountRoutes(app, config, {
|
|
301
|
-
version,
|
|
302
|
-
startedAt,
|
|
303
|
-
now,
|
|
304
|
-
agents,
|
|
305
|
-
workflows,
|
|
306
|
-
runs,
|
|
307
|
-
store,
|
|
308
|
-
metricRegistry,
|
|
309
|
-
...options.sessions !== void 0 ? { sessions: options.sessions } : {},
|
|
310
|
-
...options.memory !== void 0 ? { memory: options.memory } : {},
|
|
311
|
-
...options.skills !== void 0 ? { skills: options.skills } : {},
|
|
312
|
-
...options.mcp !== void 0 ? { mcp: options.mcp } : {},
|
|
313
|
-
...options.audit !== void 0 ? { audit: options.audit } : {},
|
|
314
|
-
...options.replay !== void 0 ? { replay: options.replay } : {},
|
|
315
|
-
...options.healthProbes !== void 0 ? { healthProbes: options.healthProbes } : {},
|
|
316
|
-
...verifier !== void 0 ? { verifier } : {},
|
|
317
|
-
...auditDb !== void 0 ? { auditDb } : {},
|
|
318
|
-
...pepperHandle !== void 0 ? { pepper: pepperHandle } : {},
|
|
319
|
-
...dispatcher !== void 0 ? { wsDispatcher: dispatcher } : {},
|
|
320
|
-
...tickets !== void 0 ? { wsTickets: tickets } : {},
|
|
321
|
-
...wsAdapter !== void 0 ? { wsAdapter } : {},
|
|
322
|
-
...triggersDaemon !== void 0 ? { triggersDaemon } : {},
|
|
323
|
-
...consolidatorDaemon !== void 0 ? { consolidatorDaemon } : {}
|
|
324
|
-
});
|
|
325
|
-
if (consolidatorDaemon !== void 0) {
|
|
326
|
-
await consolidatorDaemon.start();
|
|
327
|
-
if (triggersDaemon !== void 0 && consolidatorDaemon.consolidator.registerWithScheduler !== void 0) await consolidatorDaemon.consolidator.registerWithScheduler(triggersDaemon.scheduler);
|
|
328
|
-
}
|
|
329
|
-
if (triggersDaemon !== void 0) await triggersDaemon.start();
|
|
330
|
-
try {
|
|
331
|
-
const wal = readWalSize(store.connection);
|
|
332
|
-
metricRegistry.set(SERVER_METRIC_NAMES.storageWalSize, wal);
|
|
333
|
-
} catch {}
|
|
334
|
-
metricRegistry.set(SERVER_METRIC_NAMES.serverUptime, Math.max(0, Math.floor((now() - startedAt) / 1e3)));
|
|
335
|
-
metricRegistry.set(SERVER_METRIC_NAMES.inflightRuns, runs.runningCount());
|
|
336
|
-
metricRegistry.set(SERVER_METRIC_NAMES.replayBufferEvents, 0);
|
|
337
|
-
stopRunPruning = scheduleRunPruning(runs, now);
|
|
338
|
-
if (options.skipListen !== true) {
|
|
339
|
-
serverInstance = serve({
|
|
340
|
-
fetch: app.fetch.bind(app),
|
|
341
|
-
hostname: config.server.host,
|
|
342
|
-
port: config.server.port
|
|
343
|
-
});
|
|
344
|
-
if (wsAdapter !== void 0 && serverInstance !== void 0) wsAdapter.injectWebSocket(serverInstance);
|
|
345
|
-
await new Promise((resolve) => {
|
|
346
|
-
const server = serverInstance;
|
|
347
|
-
if ("once" in server && typeof server.once === "function") server.once("listening", () => resolve());
|
|
348
|
-
else setImmediate(() => resolve());
|
|
349
|
-
});
|
|
350
|
-
const address = serverInstance.address();
|
|
351
|
-
if (address !== null && typeof address === "object") listening = {
|
|
352
|
-
host: address.address,
|
|
353
|
-
port: address.port
|
|
354
|
-
};
|
|
355
|
-
else listening = {
|
|
356
|
-
host: config.server.host,
|
|
357
|
-
port: config.server.port
|
|
358
|
-
};
|
|
359
|
-
} else listening = {
|
|
360
|
-
host: config.server.host,
|
|
361
|
-
port: config.server.port
|
|
362
|
-
};
|
|
363
|
-
if (options.hooks?.onReady !== void 0) await options.hooks.onReady({
|
|
364
|
-
config,
|
|
365
|
-
listeningOn: listening
|
|
366
|
-
});
|
|
367
|
-
return listening;
|
|
368
|
-
} catch (err) {
|
|
369
|
-
started = false;
|
|
370
|
-
await emitError(options.hooks, {
|
|
371
|
-
error: err,
|
|
372
|
-
phase: "beforeStart"
|
|
373
|
-
});
|
|
374
|
-
throw err;
|
|
375
|
-
}
|
|
103
|
+
get listeningOn() {
|
|
104
|
+
return lifecycle.listeningOn;
|
|
376
105
|
},
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
if (stopped) return;
|
|
380
|
-
stopped = true;
|
|
381
|
-
stopRunPruning?.();
|
|
382
|
-
stopRunPruning = void 0;
|
|
383
|
-
const drainTimeoutMs = force === true ? 0 : config.server.shutdown.drainTimeoutMs;
|
|
384
|
-
try {
|
|
385
|
-
if (options.hooks?.beforeShutdown !== void 0) await options.hooks.beforeShutdown({
|
|
386
|
-
config,
|
|
387
|
-
inflight: runs.inflightCount(),
|
|
388
|
-
drainTimeoutMs
|
|
389
|
-
});
|
|
390
|
-
} catch (err) {
|
|
391
|
-
await emitError(options.hooks, {
|
|
392
|
-
error: err,
|
|
393
|
-
phase: "beforeShutdown"
|
|
394
|
-
});
|
|
395
|
-
}
|
|
396
|
-
if (triggersDaemon !== void 0) try {
|
|
397
|
-
await triggersDaemon.stop();
|
|
398
|
-
} catch (err) {
|
|
399
|
-
await emitError(options.hooks, {
|
|
400
|
-
error: err,
|
|
401
|
-
phase: "beforeShutdown"
|
|
402
|
-
});
|
|
403
|
-
}
|
|
404
|
-
if (consolidatorDaemon !== void 0) try {
|
|
405
|
-
await consolidatorDaemon.stop();
|
|
406
|
-
} catch (err) {
|
|
407
|
-
await emitError(options.hooks, {
|
|
408
|
-
error: err,
|
|
409
|
-
phase: "beforeShutdown"
|
|
410
|
-
});
|
|
411
|
-
}
|
|
412
|
-
runs.abortPending("server-shutdown");
|
|
413
|
-
if (!await drainInFlight(runs, drainTimeoutMs, now) && drainTimeoutMs > 0) runs.abortAll(new ShutdownTimeoutError(drainTimeoutMs, runs.runningCount()));
|
|
414
|
-
if (dispatcher !== void 0) try {
|
|
415
|
-
dispatcher.shutdown();
|
|
416
|
-
} catch {}
|
|
417
|
-
if (serverInstance !== void 0) {
|
|
418
|
-
await new Promise((resolve) => {
|
|
419
|
-
serverInstance.close(() => resolve());
|
|
420
|
-
});
|
|
421
|
-
serverInstance = void 0;
|
|
422
|
-
}
|
|
423
|
-
if (auditDb !== void 0) {
|
|
424
|
-
await auditDb.close();
|
|
425
|
-
auditDb = void 0;
|
|
426
|
-
}
|
|
427
|
-
await store.close();
|
|
428
|
-
listening = void 0;
|
|
429
|
-
verifier = void 0;
|
|
430
|
-
started = false;
|
|
431
|
-
preBind = void 0;
|
|
432
|
-
dispatcher = void 0;
|
|
433
|
-
tickets = void 0;
|
|
434
|
-
wsAdapter = void 0;
|
|
435
|
-
}
|
|
106
|
+
start: lifecycle.start,
|
|
107
|
+
stop: lifecycle.stop
|
|
436
108
|
};
|
|
437
109
|
}
|
|
438
|
-
/** IP-23: is `host` a loopback interface (so an open /metrics is not exposed)? */
|
|
439
|
-
function isLoopbackHost(host) {
|
|
440
|
-
const h = host.trim().toLowerCase();
|
|
441
|
-
return h === "127.0.0.1" || h === "::1" || h === "[::1]" || h === "localhost";
|
|
442
|
-
}
|
|
443
|
-
function mountRoutes(app, config, ctx) {
|
|
444
|
-
const base = config.server.basePath;
|
|
445
|
-
const probes = ctx.healthProbes ?? (() => buildDefaultHealthProbes(ctx.store, ctx.triggersDaemon, ctx.consolidatorDaemon, ctx.wsDispatcher, config));
|
|
446
|
-
const health = createExtendedHealthRoutes({
|
|
447
|
-
version: ctx.version,
|
|
448
|
-
startedAt: ctx.startedAt,
|
|
449
|
-
now: ctx.now,
|
|
450
|
-
probes
|
|
451
|
-
});
|
|
452
|
-
app.route(`${base}/health`, health);
|
|
453
|
-
if (config.auth.kind === "none" && !isLoopbackHost(config.server.host)) console.warn(`[graphorin/server] WARN: auth.kind='none' disables authentication on every endpoint, but the server binds the non-loopback host '${config.server.host}'. Anyone who can reach it has full admin access - use auth.kind='token' for non-loopback deployments or bind a loopback host.`);
|
|
454
|
-
if (config.metrics.enabled) {
|
|
455
|
-
if (!config.metrics.requireAuth && !isLoopbackHost(config.server.host)) console.warn(`[graphorin/server] WARN: /metrics is unauthenticated (metrics.requireAuth=false) and the server binds the non-loopback host '${config.server.host}'. The exposition leaks operational detail to anyone who can reach it - set metrics.requireAuth=true or bind a loopback host.`);
|
|
456
|
-
const metricsRoute = createMetricsRoutes({
|
|
457
|
-
registry: ctx.metricRegistry,
|
|
458
|
-
requireAuth: config.metrics.requireAuth,
|
|
459
|
-
refresh: () => refreshLiveMetrics({
|
|
460
|
-
registry: ctx.metricRegistry,
|
|
461
|
-
store: ctx.store,
|
|
462
|
-
runs: ctx.runs,
|
|
463
|
-
startedAt: ctx.startedAt,
|
|
464
|
-
now: ctx.now,
|
|
465
|
-
...ctx.triggersDaemon !== void 0 ? { triggersDaemon: ctx.triggersDaemon } : {},
|
|
466
|
-
...ctx.consolidatorDaemon !== void 0 ? { consolidatorDaemon: ctx.consolidatorDaemon } : {},
|
|
467
|
-
...ctx.wsDispatcher !== void 0 ? { wsDispatcher: ctx.wsDispatcher } : {}
|
|
468
|
-
})
|
|
469
|
-
});
|
|
470
|
-
app.route(`${base}${config.metrics.path}`, metricsRoute);
|
|
471
|
-
}
|
|
472
|
-
const wsUpgradePath = config.server.ws.enabled ? `${base}${config.server.ws.path}` : void 0;
|
|
473
|
-
const metricsPath = config.metrics.enabled && !config.metrics.requireAuth ? `${base}${config.metrics.path}` : void 0;
|
|
474
|
-
function shouldSkipAuth(path) {
|
|
475
|
-
if (path === `${base}/health` || path === `${base}/health/`) return true;
|
|
476
|
-
if (wsUpgradePath !== void 0) {
|
|
477
|
-
if (path === wsUpgradePath || path === `${wsUpgradePath}/`) return true;
|
|
478
|
-
}
|
|
479
|
-
if (metricsPath !== void 0) {
|
|
480
|
-
if (path === metricsPath || path === `${metricsPath}/`) return true;
|
|
481
|
-
}
|
|
482
|
-
return false;
|
|
483
|
-
}
|
|
484
|
-
const anonymousAuth = ctx.verifier === void 0 && config.auth.kind === "none";
|
|
485
|
-
if (ctx.verifier !== void 0 || anonymousAuth) {
|
|
486
|
-
const authMw = ctx.verifier !== void 0 ? createAuthMiddleware({ verifier: ctx.verifier }) : createAnonymousAuthMiddleware();
|
|
487
|
-
app.use(`${base}/*`, async (c, next) => {
|
|
488
|
-
if (shouldSkipAuth(c.req.path)) {
|
|
489
|
-
await next();
|
|
490
|
-
return;
|
|
491
|
-
}
|
|
492
|
-
return authMw(c, next);
|
|
493
|
-
});
|
|
494
|
-
if (ctx.auditDb !== void 0) {
|
|
495
|
-
const auditMw = createAuditMiddleware({
|
|
496
|
-
auditDb: ctx.auditDb,
|
|
497
|
-
now: ctx.now
|
|
498
|
-
});
|
|
499
|
-
app.use(`${base}/*`, async (c, next) => {
|
|
500
|
-
if (shouldSkipAuth(c.req.path)) {
|
|
501
|
-
await next();
|
|
502
|
-
return;
|
|
503
|
-
}
|
|
504
|
-
return auditMw(c, next);
|
|
505
|
-
});
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
if (config.server.idempotency.enabled) {
|
|
509
|
-
const idempotencyMw = createIdempotencyMiddleware({
|
|
510
|
-
store: ctx.store.idempotency,
|
|
511
|
-
config: config.server.idempotency,
|
|
512
|
-
now: ctx.now,
|
|
513
|
-
excludeResponseCachePaths: [`${base}/tokens`],
|
|
514
|
-
metricRegistry: ctx.metricRegistry
|
|
515
|
-
});
|
|
516
|
-
app.use(`${base}/*`, async (c, next) => {
|
|
517
|
-
if (shouldSkipAuth(c.req.path)) {
|
|
518
|
-
await next();
|
|
519
|
-
return;
|
|
520
|
-
}
|
|
521
|
-
return idempotencyMw(c, next);
|
|
522
|
-
});
|
|
523
|
-
}
|
|
524
|
-
app.route(`${base}/health/secrets`, createSecretsHealthRoutes());
|
|
525
|
-
app.route(`${base}/agents`, createAgentRoutes({
|
|
526
|
-
agents: ctx.agents,
|
|
527
|
-
runs: ctx.runs,
|
|
528
|
-
...ctx.wsDispatcher !== void 0 ? { dispatcher: ctx.wsDispatcher } : {}
|
|
529
|
-
}));
|
|
530
|
-
app.route(`${base}/runs`, createRunRoutes({
|
|
531
|
-
agents: ctx.agents,
|
|
532
|
-
runs: ctx.runs
|
|
533
|
-
}));
|
|
534
|
-
app.route(`${base}/workflows`, createWorkflowRoutes({
|
|
535
|
-
workflows: ctx.workflows,
|
|
536
|
-
runs: ctx.runs,
|
|
537
|
-
...ctx.wsDispatcher !== void 0 ? { dispatcher: ctx.wsDispatcher } : {}
|
|
538
|
-
}));
|
|
539
|
-
if (ctx.sessions !== void 0) app.route(`${base}/sessions`, createSessionRoutes({ sessions: ctx.sessions }));
|
|
540
|
-
if (ctx.memory !== void 0) app.route(`${base}/memory`, createMemoryRoutes({ memory: ctx.memory }));
|
|
541
|
-
if (ctx.skills !== void 0) app.route(`${base}/skills`, createSkillsRoutes({ skills: ctx.skills }));
|
|
542
|
-
if (ctx.mcp !== void 0) app.route(`${base}/mcp`, createMcpRoutes({ mcp: ctx.mcp }));
|
|
543
|
-
if (ctx.audit !== void 0) app.route(`${base}/audit`, createAuditRoutes({ audit: ctx.audit }));
|
|
544
|
-
if (ctx.triggersDaemon !== void 0) app.route(`${base}/triggers`, createTriggersRoutes({ daemon: ctx.triggersDaemon }));
|
|
545
|
-
if (ctx.replay !== void 0) app.route(`${base}`, createReplayRoutes({
|
|
546
|
-
replay: ctx.replay,
|
|
547
|
-
...ctx.auditDb !== void 0 ? { auditDb: ctx.auditDb } : {},
|
|
548
|
-
now: ctx.now
|
|
549
|
-
}));
|
|
550
|
-
if (config.auth.kind === "token" && ctx.pepper !== void 0) app.route(`${base}/tokens`, createTokensRoutes({
|
|
551
|
-
tokenStore: ctx.store.authTokens,
|
|
552
|
-
pepper: ctx.pepper,
|
|
553
|
-
defaultEnv: "live",
|
|
554
|
-
allowedEnvs: config.auth.tokenEnvironments
|
|
555
|
-
}));
|
|
556
|
-
if (ctx.wsTickets !== void 0) app.route(`${base}`, createAuthRoutes({ tickets: ctx.wsTickets }));
|
|
557
|
-
if (ctx.wsDispatcher !== void 0 && ctx.wsTickets !== void 0 && ctx.wsAdapter !== void 0 && (ctx.verifier !== void 0 || anonymousAuth) && config.server.ws.enabled) {
|
|
558
|
-
const dispatcher = ctx.wsDispatcher;
|
|
559
|
-
const tickets = ctx.wsTickets;
|
|
560
|
-
const verifier = ctx.verifier;
|
|
561
|
-
const runs = ctx.runs;
|
|
562
|
-
app.get(`${base}${config.server.ws.path}`, ctx.wsAdapter.upgradeWebSocket((c) => createWsUpgradeEvents(c, {
|
|
563
|
-
dispatcher,
|
|
564
|
-
tickets,
|
|
565
|
-
...verifier !== void 0 ? { verifier } : {},
|
|
566
|
-
anonymous: anonymousAuth,
|
|
567
|
-
runs,
|
|
568
|
-
now: ctx.now
|
|
569
|
-
})));
|
|
570
|
-
}
|
|
571
|
-
if (ctx.wsDispatcher !== void 0 && config.server.sse.enabled) app.route(`${base}${config.server.sse.path}`, createSseRoutes({
|
|
572
|
-
dispatcher: ctx.wsDispatcher,
|
|
573
|
-
keepAliveMs: config.server.sse.keepAliveMs,
|
|
574
|
-
perConnectionQueueLimit: config.server.stream.perConnectionQueueLimit,
|
|
575
|
-
now: ctx.now
|
|
576
|
-
}));
|
|
577
|
-
}
|
|
578
|
-
async function drainInFlight(runs, drainTimeoutMs, now) {
|
|
579
|
-
if (runs.runningCount() === 0) return true;
|
|
580
|
-
if (drainTimeoutMs <= 0) return runs.runningCount() === 0;
|
|
581
|
-
const deadline = now() + drainTimeoutMs;
|
|
582
|
-
while (now() < deadline) {
|
|
583
|
-
if (runs.runningCount() === 0) return true;
|
|
584
|
-
await new Promise((resolve) => setTimeout(resolve, Math.min(50, deadline - now())));
|
|
585
|
-
}
|
|
586
|
-
return runs.runningCount() === 0;
|
|
587
|
-
}
|
|
588
|
-
async function emitError(hooks, ctx) {
|
|
589
|
-
if (hooks?.onError === void 0) return;
|
|
590
|
-
try {
|
|
591
|
-
await hooks.onError(ctx);
|
|
592
|
-
} catch {}
|
|
593
|
-
}
|
|
594
|
-
async function refreshLiveMetrics(options) {
|
|
595
|
-
const { registry, store, runs, startedAt, now } = options;
|
|
596
|
-
try {
|
|
597
|
-
const wal = readWalSize(store.connection);
|
|
598
|
-
registry.set(SERVER_METRIC_NAMES.storageWalSize, wal);
|
|
599
|
-
} catch {}
|
|
600
|
-
registry.set(SERVER_METRIC_NAMES.serverUptime, Math.max(0, Math.floor((now() - startedAt) / 1e3)));
|
|
601
|
-
registry.set(SERVER_METRIC_NAMES.inflightRuns, runs.runningCount());
|
|
602
|
-
if (options.wsDispatcher !== void 0) {
|
|
603
|
-
const sizes = options.wsDispatcher.size();
|
|
604
|
-
registry.set(SERVER_METRIC_NAMES.replayBufferEvents, sizes.subscriptions);
|
|
605
|
-
}
|
|
606
|
-
if (options.triggersDaemon !== void 0) {
|
|
607
|
-
const metrics = options.triggersDaemon.metrics();
|
|
608
|
-
for (const [triggerId, counts] of metrics.fires) {
|
|
609
|
-
const sanitized = sanitizeMetricLabelValue(triggerId);
|
|
610
|
-
const successCurrent = readCounter(registry, SERVER_METRIC_NAMES.triggersFiresTotal, {
|
|
611
|
-
trigger_id: sanitized,
|
|
612
|
-
status: "success"
|
|
613
|
-
});
|
|
614
|
-
const errorCurrent = readCounter(registry, SERVER_METRIC_NAMES.triggersFiresTotal, {
|
|
615
|
-
trigger_id: sanitized,
|
|
616
|
-
status: "error"
|
|
617
|
-
});
|
|
618
|
-
const successDelta = counts.success - successCurrent;
|
|
619
|
-
const errorDelta = counts.error - errorCurrent;
|
|
620
|
-
if (successDelta > 0) registry.inc(SERVER_METRIC_NAMES.triggersFiresTotal, {
|
|
621
|
-
trigger_id: sanitized,
|
|
622
|
-
status: "success"
|
|
623
|
-
}, successDelta);
|
|
624
|
-
if (errorDelta > 0) registry.inc(SERVER_METRIC_NAMES.triggersFiresTotal, {
|
|
625
|
-
trigger_id: sanitized,
|
|
626
|
-
status: "error"
|
|
627
|
-
}, errorDelta);
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
|
-
if (options.consolidatorDaemon !== void 0) try {
|
|
631
|
-
const status = await options.consolidatorDaemon.status();
|
|
632
|
-
registry.set(SERVER_METRIC_NAMES.consolidatorQueueDepth, status.queueDepth, { phase: "aggregate" });
|
|
633
|
-
registry.set(SERVER_METRIC_NAMES.consolidatorDlqSize, status.dlqSize);
|
|
634
|
-
registry.set(SERVER_METRIC_NAMES.consolidatorBudgetRemainingUsd, status.budget.costRemaining);
|
|
635
|
-
registry.set(SERVER_METRIC_NAMES.consolidatorBudgetRemainingTokens, status.budget.tokensRemaining);
|
|
636
|
-
} catch {}
|
|
637
|
-
}
|
|
638
|
-
/**
|
|
639
|
-
* Convert an arbitrary user-supplied identifier (trigger id) into a
|
|
640
|
-
* Prometheus-safe label value. Replaces every character outside the
|
|
641
|
-
* `[A-Za-z0-9_:]` range with `_`. This guarantees the cardinality
|
|
642
|
-
* never explodes on UTF-8 sequences while keeping the value
|
|
643
|
-
* recognizable.
|
|
644
|
-
*/
|
|
645
|
-
function sanitizeMetricLabelValue(value) {
|
|
646
|
-
return value.replace(/[^A-Za-z0-9_:.-]/g, "_").slice(0, 200);
|
|
647
|
-
}
|
|
648
|
-
function readCounter(registry, name, labels) {
|
|
649
|
-
const snap = registry.snapshot().counters[name] ?? [];
|
|
650
|
-
for (const entry of snap) if (matchesLabels(entry.labels, labels)) return entry.value;
|
|
651
|
-
return 0;
|
|
652
|
-
}
|
|
653
|
-
function matchesLabels(a, b) {
|
|
654
|
-
for (const k of Object.keys(b)) if (String(a[k]) !== b[k]) return false;
|
|
655
|
-
for (const k of Object.keys(a)) if (!(k in b)) return false;
|
|
656
|
-
return true;
|
|
657
|
-
}
|
|
658
|
-
function buildDefaultHealthProbes(store, triggersDaemon, consolidatorDaemon, dispatcher, config) {
|
|
659
|
-
const out = {
|
|
660
|
-
store,
|
|
661
|
-
walWarnThresholdBytes: config.health.walWarnThresholdBytes,
|
|
662
|
-
encryptionEnabled: config.storage.encryption.enabled,
|
|
663
|
-
...config.storage.encryption.enabled ? { cipherPeerInstalled: true } : {}
|
|
664
|
-
};
|
|
665
|
-
if (triggersDaemon !== void 0) out.triggers = triggersDaemon;
|
|
666
|
-
if (consolidatorDaemon !== void 0) out.consolidator = consolidatorDaemon;
|
|
667
|
-
if (dispatcher !== void 0) {
|
|
668
|
-
const sizes = dispatcher.size();
|
|
669
|
-
out.replayBuffer = {
|
|
670
|
-
eventsBuffered: sizes.subscriptions,
|
|
671
|
-
subscribers: sizes.subscribers,
|
|
672
|
-
subscriptions: sizes.subscriptions
|
|
673
|
-
};
|
|
674
|
-
}
|
|
675
|
-
return Object.freeze(out);
|
|
676
|
-
}
|
|
677
|
-
async function fallbackPepper(skipHardening) {
|
|
678
|
-
if (!skipHardening) throw new Error("[graphorin/server] missing resolved pepper after pre-bind; auth.pepperRef is required when auth.kind = token.");
|
|
679
|
-
return generatePepper();
|
|
680
|
-
}
|
|
681
110
|
|
|
682
111
|
//#endregion
|
|
683
|
-
export { createServer
|
|
112
|
+
export { createServer };
|
|
684
113
|
//# sourceMappingURL=app.js.map
|