@graphorin/server 0.6.1 → 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 +71 -0
- package/README.md +3 -3
- 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 +35 -607
- 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 +8 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -24
- 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 +1 -1
- package/dist/package.js.map +1 -1
- 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.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 +24 -18
- package/dist/ws/upgrade.js.map +1 -1
- package/package.json +30 -29
- 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
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { refreshLiveMetrics } from "./app-metrics.js";
|
|
2
|
+
import { attachProtectedMiddleware } from "./app-middleware.js";
|
|
3
|
+
import { createExtendedHealthRoutes, createMetricsRoutes, createSecretsHealthRoutes } from "./health/routes.js";
|
|
4
|
+
import { createReplayRoutes } from "./replay/routes.js";
|
|
5
|
+
import { createAgentRoutes, createRunRoutes } from "./routes/agents.js";
|
|
6
|
+
import { createAuditRoutes } from "./routes/audit.js";
|
|
7
|
+
import { createAuthRoutes } from "./routes/auth.js";
|
|
8
|
+
import { createMcpRoutes } from "./routes/mcp.js";
|
|
9
|
+
import { createMemoryRoutes } from "./routes/memory.js";
|
|
10
|
+
import { createSessionRoutes } from "./routes/sessions.js";
|
|
11
|
+
import { createSkillsRoutes } from "./routes/skills.js";
|
|
12
|
+
import { createTokensRoutes } from "./routes/tokens.js";
|
|
13
|
+
import { createWorkflowRoutes } from "./routes/workflows.js";
|
|
14
|
+
import { createSseRoutes } from "./sse/events.js";
|
|
15
|
+
import { createTriggersRoutes } from "./triggers/routes.js";
|
|
16
|
+
import { createWsUpgradeEvents } from "./ws/upgrade.js";
|
|
17
|
+
|
|
18
|
+
//#region src/app-routes.ts
|
|
19
|
+
/** IP-23: is `host` a loopback interface (so an open /metrics is not exposed)? */
|
|
20
|
+
function isLoopbackHost(host) {
|
|
21
|
+
const h = host.trim().toLowerCase();
|
|
22
|
+
return h === "127.0.0.1" || h === "::1" || h === "[::1]" || h === "localhost";
|
|
23
|
+
}
|
|
24
|
+
function mountRoutes(app, config, ctx) {
|
|
25
|
+
const base = config.server.basePath;
|
|
26
|
+
const probes = ctx.healthProbes ?? (() => buildDefaultHealthProbes(ctx.store, ctx.triggersDaemon, ctx.consolidatorDaemon, ctx.wsDispatcher, config, ctx.workflowTimerDaemon));
|
|
27
|
+
const health = createExtendedHealthRoutes({
|
|
28
|
+
version: ctx.version,
|
|
29
|
+
startedAt: ctx.startedAt,
|
|
30
|
+
now: ctx.now,
|
|
31
|
+
probes
|
|
32
|
+
});
|
|
33
|
+
app.route(`${base}/health`, health);
|
|
34
|
+
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.`);
|
|
35
|
+
if (config.metrics.enabled) {
|
|
36
|
+
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.`);
|
|
37
|
+
const metricsRoute = createMetricsRoutes({
|
|
38
|
+
registry: ctx.metricRegistry,
|
|
39
|
+
requireAuth: config.metrics.requireAuth,
|
|
40
|
+
refresh: () => refreshLiveMetrics({
|
|
41
|
+
registry: ctx.metricRegistry,
|
|
42
|
+
store: ctx.store,
|
|
43
|
+
runs: ctx.runs,
|
|
44
|
+
startedAt: ctx.startedAt,
|
|
45
|
+
now: ctx.now,
|
|
46
|
+
...ctx.triggersDaemon !== void 0 ? { triggersDaemon: ctx.triggersDaemon } : {},
|
|
47
|
+
...ctx.consolidatorDaemon !== void 0 ? { consolidatorDaemon: ctx.consolidatorDaemon } : {},
|
|
48
|
+
...ctx.wsDispatcher !== void 0 ? { wsDispatcher: ctx.wsDispatcher } : {}
|
|
49
|
+
})
|
|
50
|
+
});
|
|
51
|
+
app.route(`${base}${config.metrics.path}`, metricsRoute);
|
|
52
|
+
}
|
|
53
|
+
const { anonymousAuth } = attachProtectedMiddleware(app, config, ctx);
|
|
54
|
+
app.route(`${base}/health/secrets`, createSecretsHealthRoutes());
|
|
55
|
+
app.route(`${base}/agents`, createAgentRoutes({
|
|
56
|
+
agents: ctx.agents,
|
|
57
|
+
runs: ctx.runs,
|
|
58
|
+
...ctx.wsDispatcher !== void 0 ? { dispatcher: ctx.wsDispatcher } : {}
|
|
59
|
+
}));
|
|
60
|
+
app.route(`${base}/runs`, createRunRoutes({
|
|
61
|
+
agents: ctx.agents,
|
|
62
|
+
runs: ctx.runs
|
|
63
|
+
}));
|
|
64
|
+
app.route(`${base}/workflows`, createWorkflowRoutes({
|
|
65
|
+
workflows: ctx.workflows,
|
|
66
|
+
runs: ctx.runs,
|
|
67
|
+
...ctx.wsDispatcher !== void 0 ? { dispatcher: ctx.wsDispatcher } : {}
|
|
68
|
+
}));
|
|
69
|
+
if (ctx.sessions !== void 0) app.route(`${base}/sessions`, createSessionRoutes({ sessions: ctx.sessions }));
|
|
70
|
+
if (ctx.memory !== void 0) app.route(`${base}/memory`, createMemoryRoutes({ memory: ctx.memory }));
|
|
71
|
+
if (ctx.skills !== void 0) app.route(`${base}/skills`, createSkillsRoutes({ skills: ctx.skills }));
|
|
72
|
+
if (ctx.mcp !== void 0) app.route(`${base}/mcp`, createMcpRoutes({ mcp: ctx.mcp }));
|
|
73
|
+
if (ctx.audit !== void 0) app.route(`${base}/audit`, createAuditRoutes({ audit: ctx.audit }));
|
|
74
|
+
if (ctx.triggersDaemon !== void 0) app.route(`${base}/triggers`, createTriggersRoutes({ daemon: ctx.triggersDaemon }));
|
|
75
|
+
if (ctx.replay !== void 0) app.route(`${base}`, createReplayRoutes({
|
|
76
|
+
replay: ctx.replay,
|
|
77
|
+
...ctx.auditDb !== void 0 ? { auditDb: ctx.auditDb } : {},
|
|
78
|
+
now: ctx.now
|
|
79
|
+
}));
|
|
80
|
+
if (config.auth.kind === "token" && ctx.pepper !== void 0) app.route(`${base}/tokens`, createTokensRoutes({
|
|
81
|
+
tokenStore: ctx.store.authTokens,
|
|
82
|
+
pepper: ctx.pepper,
|
|
83
|
+
defaultEnv: "live",
|
|
84
|
+
allowedEnvs: config.auth.tokenEnvironments
|
|
85
|
+
}));
|
|
86
|
+
if (ctx.wsTickets !== void 0) app.route(`${base}`, createAuthRoutes({ tickets: ctx.wsTickets }));
|
|
87
|
+
if (ctx.wsDispatcher !== void 0 && ctx.wsTickets !== void 0 && ctx.wsAdapter !== void 0 && (ctx.verifier !== void 0 || anonymousAuth) && config.server.ws.enabled) {
|
|
88
|
+
const dispatcher = ctx.wsDispatcher;
|
|
89
|
+
const tickets = ctx.wsTickets;
|
|
90
|
+
const verifier = ctx.verifier;
|
|
91
|
+
const runs = ctx.runs;
|
|
92
|
+
app.get(`${base}${config.server.ws.path}`, ctx.wsAdapter.upgradeWebSocket((c) => createWsUpgradeEvents(c, {
|
|
93
|
+
dispatcher,
|
|
94
|
+
tickets,
|
|
95
|
+
...verifier !== void 0 ? { verifier } : {},
|
|
96
|
+
anonymous: anonymousAuth,
|
|
97
|
+
runs,
|
|
98
|
+
now: ctx.now
|
|
99
|
+
})));
|
|
100
|
+
}
|
|
101
|
+
if (ctx.wsDispatcher !== void 0 && config.server.sse.enabled) app.route(`${base}${config.server.sse.path}`, createSseRoutes({
|
|
102
|
+
dispatcher: ctx.wsDispatcher,
|
|
103
|
+
keepAliveMs: config.server.sse.keepAliveMs,
|
|
104
|
+
perConnectionQueueLimit: config.server.stream.perConnectionQueueLimit,
|
|
105
|
+
now: ctx.now
|
|
106
|
+
}));
|
|
107
|
+
}
|
|
108
|
+
function buildDefaultHealthProbes(store, triggersDaemon, consolidatorDaemon, dispatcher, config, workflowTimerDaemon) {
|
|
109
|
+
const out = {
|
|
110
|
+
store,
|
|
111
|
+
walWarnThresholdBytes: config.health.walWarnThresholdBytes,
|
|
112
|
+
encryptionEnabled: config.storage.encryption.enabled,
|
|
113
|
+
...config.storage.encryption.enabled ? { cipherPeerInstalled: true } : {}
|
|
114
|
+
};
|
|
115
|
+
if (triggersDaemon !== void 0) out.triggers = triggersDaemon;
|
|
116
|
+
if (consolidatorDaemon !== void 0) out.consolidator = consolidatorDaemon;
|
|
117
|
+
if (workflowTimerDaemon !== void 0) out.workflowTimers = workflowTimerDaemon;
|
|
118
|
+
if (dispatcher !== void 0) {
|
|
119
|
+
const sizes = dispatcher.size();
|
|
120
|
+
out.replayBuffer = {
|
|
121
|
+
eventsBuffered: sizes.subscriptions,
|
|
122
|
+
subscribers: sizes.subscribers,
|
|
123
|
+
subscriptions: sizes.subscriptions
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
return Object.freeze(out);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
//#endregion
|
|
130
|
+
export { mountRoutes };
|
|
131
|
+
//# sourceMappingURL=app-routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-routes.js","names":["out: {\n -readonly [K in keyof HealthCheckOptions]?: HealthCheckOptions[K];\n }"],"sources":["../src/app-routes.ts"],"sourcesContent":["/**\n * HTTP route mounting for `createServer({...})` - health, metrics,\n * the authenticated middleware boundary, every REST domain surface,\n * the WebSocket upgrade, and SSE. Mount order is part of the\n * documented contract (unauthenticated health/metrics first, then the\n * auth boundary, then the domain routes) - do not reorder.\n *\n * @packageDocumentation\n */\n\nimport type { TokenVerifier } from '@graphorin/security';\nimport type { AuditDb } from '@graphorin/security/audit';\nimport type { GraphorinSqliteStore } from '@graphorin/store-sqlite';\nimport type { createNodeWebSocket } from '@hono/node-ws';\nimport type { Hono } from 'hono';\nimport { refreshLiveMetrics } from './app-metrics.js';\nimport { attachProtectedMiddleware } from './app-middleware.js';\nimport type { ServerConfigSpec } from './config.js';\nimport type { ConsolidatorDaemon } from './consolidator/daemon.js';\nimport {\n createExtendedHealthRoutes,\n createMetricsRoutes,\n createSecretsHealthRoutes,\n type HealthCheckOptions,\n} from './health/index.js';\nimport type { ServerVariables } from './internal/context.js';\nimport type { MetricRegistry } from './metrics/registry.js';\nimport type { AgentRegistry, WorkflowRegistry } from './registry/index.js';\nimport { createReplayRoutes, type ReplayApi } from './replay/index.js';\nimport {\n type AuditApi,\n createAgentRoutes,\n createAuditRoutes,\n createAuthRoutes,\n createMcpRoutes,\n createMemoryRoutes,\n createRunRoutes,\n createSessionRoutes,\n createSkillsRoutes,\n createTokensRoutes,\n createWorkflowRoutes,\n type McpApi,\n type MemoryApi,\n type SessionApi,\n type SkillsApi,\n} from './routes/index.js';\nimport type { RunStateTracker } from './runtime/run-state.js';\nimport { createSseRoutes } from './sse/index.js';\nimport type { TriggersDaemon } from './triggers/daemon.js';\nimport { createTriggersRoutes } from './triggers/routes.js';\nimport type { WorkflowTimerDaemon } from './workflows/timer-daemon.js';\nimport type { WsDispatcher, WsTicketStore } from './ws/index.js';\nimport { createWsUpgradeEvents } from './ws/upgrade.js';\n\nexport interface MountRoutesContext {\n readonly version: string;\n readonly startedAt: number;\n readonly now: () => number;\n readonly agents: AgentRegistry;\n readonly workflows: WorkflowRegistry;\n readonly runs: RunStateTracker;\n readonly store: GraphorinSqliteStore;\n readonly metricRegistry: MetricRegistry;\n readonly sessions?: SessionApi;\n readonly memory?: MemoryApi;\n readonly skills?: SkillsApi;\n readonly mcp?: McpApi;\n readonly audit?: AuditApi;\n readonly replay?: ReplayApi;\n readonly healthProbes?: () => HealthCheckOptions | Promise<HealthCheckOptions>;\n readonly verifier?: TokenVerifier;\n readonly auditDb?: AuditDb;\n readonly pepper?: import('@graphorin/security').SecretValue;\n readonly wsDispatcher?: WsDispatcher;\n readonly wsTickets?: WsTicketStore;\n readonly wsAdapter?: ReturnType<typeof createNodeWebSocket>;\n readonly triggersDaemon?: TriggersDaemon;\n readonly consolidatorDaemon?: ConsolidatorDaemon;\n readonly workflowTimerDaemon?: WorkflowTimerDaemon;\n}\n\n/** IP-23: is `host` a loopback interface (so an open /metrics is not exposed)? */\nfunction isLoopbackHost(host: string): boolean {\n const h = host.trim().toLowerCase();\n return h === '127.0.0.1' || h === '::1' || h === '[::1]' || h === 'localhost';\n}\n\nexport function mountRoutes(\n app: Hono<{ Variables: ServerVariables }>,\n config: ServerConfigSpec,\n ctx: MountRoutesContext,\n): void {\n const base = config.server.basePath;\n const probes =\n ctx.healthProbes ??\n (() =>\n buildDefaultHealthProbes(\n ctx.store,\n ctx.triggersDaemon,\n ctx.consolidatorDaemon,\n ctx.wsDispatcher,\n config,\n ctx.workflowTimerDaemon,\n ));\n const health = createExtendedHealthRoutes({\n version: ctx.version,\n startedAt: ctx.startedAt,\n now: ctx.now,\n probes,\n });\n app.route(`${base}/health`, health);\n\n // IP-13: `auth.kind='none'` disables authentication on every route. It is the\n // documented trusted-loopback / single-operator mode, but binding a\n // non-loopback host with auth off exposes full admin access (including the WS\n // stream) to anyone who can reach it - warn loudly rather than silently.\n if (config.auth.kind === 'none' && !isLoopbackHost(config.server.host)) {\n console.warn(\n `[graphorin/server] WARN: auth.kind='none' disables authentication on every endpoint, ` +\n `but the server binds the non-loopback host '${config.server.host}'. Anyone who can reach ` +\n `it has full admin access - use auth.kind='token' for non-loopback deployments or bind a ` +\n `loopback host.`,\n );\n }\n\n if (config.metrics.enabled) {\n // IP-23: an unauthenticated /metrics endpoint leaks operational intel\n // (trigger ids in labels, consolidator budgets). It is fine on a loopback\n // host, but binding a non-loopback host with auth off silently exposes it.\n if (!config.metrics.requireAuth && !isLoopbackHost(config.server.host)) {\n console.warn(\n `[graphorin/server] WARN: /metrics is unauthenticated (metrics.requireAuth=false) and ` +\n `the server binds the non-loopback host '${config.server.host}'. The exposition leaks ` +\n `operational detail to anyone who can reach it - set metrics.requireAuth=true or bind a ` +\n `loopback host.`,\n );\n }\n const metricsRoute = createMetricsRoutes({\n registry: ctx.metricRegistry,\n requireAuth: config.metrics.requireAuth,\n refresh: () =>\n refreshLiveMetrics({\n registry: ctx.metricRegistry,\n store: ctx.store,\n runs: ctx.runs,\n startedAt: ctx.startedAt,\n now: ctx.now,\n ...(ctx.triggersDaemon !== undefined ? { triggersDaemon: ctx.triggersDaemon } : {}),\n ...(ctx.consolidatorDaemon !== undefined\n ? { consolidatorDaemon: ctx.consolidatorDaemon }\n : {}),\n ...(ctx.wsDispatcher !== undefined ? { wsDispatcher: ctx.wsDispatcher } : {}),\n }),\n });\n app.route(`${base}${config.metrics.path}`, metricsRoute);\n }\n\n // Auth + audit + idempotency for everything below this line - see\n // `app-middleware.ts` for the boundary semantics and skip list.\n const { anonymousAuth } = attachProtectedMiddleware(app, config, ctx);\n\n // Mounted AFTER the auth middleware so the scope check has a\n // verified token to inspect; the unauthenticated `/v1/health` GET\n // continues to serve the rollup.\n app.route(`${base}/health/secrets`, createSecretsHealthRoutes());\n\n app.route(\n `${base}/agents`,\n createAgentRoutes({\n agents: ctx.agents,\n runs: ctx.runs,\n // IP-2: the streaming dispatcher reaches the route layer.\n ...(ctx.wsDispatcher !== undefined ? { dispatcher: ctx.wsDispatcher } : {}),\n }),\n );\n app.route(`${base}/runs`, createRunRoutes({ agents: ctx.agents, runs: ctx.runs }));\n app.route(\n `${base}/workflows`,\n createWorkflowRoutes({\n workflows: ctx.workflows,\n runs: ctx.runs,\n ...(ctx.wsDispatcher !== undefined ? { dispatcher: ctx.wsDispatcher } : {}),\n }),\n );\n if (ctx.sessions !== undefined) {\n app.route(`${base}/sessions`, createSessionRoutes({ sessions: ctx.sessions }));\n }\n if (ctx.memory !== undefined) {\n app.route(`${base}/memory`, createMemoryRoutes({ memory: ctx.memory }));\n }\n if (ctx.skills !== undefined) {\n app.route(`${base}/skills`, createSkillsRoutes({ skills: ctx.skills }));\n }\n if (ctx.mcp !== undefined) {\n app.route(`${base}/mcp`, createMcpRoutes({ mcp: ctx.mcp }));\n }\n if (ctx.audit !== undefined) {\n app.route(`${base}/audit`, createAuditRoutes({ audit: ctx.audit }));\n }\n if (ctx.triggersDaemon !== undefined) {\n app.route(`${base}/triggers`, createTriggersRoutes({ daemon: ctx.triggersDaemon }));\n }\n if (ctx.replay !== undefined) {\n app.route(\n `${base}`,\n createReplayRoutes({\n replay: ctx.replay,\n ...(ctx.auditDb !== undefined ? { auditDb: ctx.auditDb } : {}),\n now: ctx.now,\n }),\n );\n }\n if (config.auth.kind === 'token' && ctx.pepper !== undefined) {\n app.route(\n `${base}/tokens`,\n createTokensRoutes({\n tokenStore: ctx.store.authTokens,\n pepper: ctx.pepper,\n defaultEnv: 'live',\n allowedEnvs: config.auth.tokenEnvironments,\n }),\n );\n }\n if (ctx.wsTickets !== undefined) {\n app.route(`${base}`, createAuthRoutes({ tickets: ctx.wsTickets }));\n }\n // IP-13: mount the WS upgrade when a verifier is wired OR auth is disabled\n // (`auth.kind='none'`). The old condition required a verifier, so\n // `ws.enabled: true` under no-auth was silently ignored - the route never\n // mounted and clients got a bare 404 with no explanation.\n if (\n ctx.wsDispatcher !== undefined &&\n ctx.wsTickets !== undefined &&\n ctx.wsAdapter !== undefined &&\n (ctx.verifier !== undefined || anonymousAuth) &&\n config.server.ws.enabled\n ) {\n const dispatcher = ctx.wsDispatcher;\n const tickets = ctx.wsTickets;\n const verifier = ctx.verifier;\n const runs = ctx.runs;\n app.get(\n `${base}${config.server.ws.path}`,\n ctx.wsAdapter.upgradeWebSocket((c) =>\n createWsUpgradeEvents(c, {\n dispatcher,\n tickets,\n ...(verifier !== undefined ? { verifier } : {}),\n anonymous: anonymousAuth,\n runs,\n now: ctx.now,\n }),\n ),\n );\n }\n if (ctx.wsDispatcher !== undefined && config.server.sse.enabled) {\n app.route(\n `${base}${config.server.sse.path}`,\n createSseRoutes({\n dispatcher: ctx.wsDispatcher,\n keepAliveMs: config.server.sse.keepAliveMs,\n // IP-9: bound the per-connection delivery queue.\n perConnectionQueueLimit: config.server.stream.perConnectionQueueLimit,\n now: ctx.now,\n }),\n );\n }\n}\n\nfunction buildDefaultHealthProbes(\n store: GraphorinSqliteStore,\n triggersDaemon: TriggersDaemon | undefined,\n consolidatorDaemon: ConsolidatorDaemon | undefined,\n dispatcher: WsDispatcher | undefined,\n config: ServerConfigSpec,\n workflowTimerDaemon?: WorkflowTimerDaemon,\n): HealthCheckOptions {\n const out: {\n -readonly [K in keyof HealthCheckOptions]?: HealthCheckOptions[K];\n } = {\n store,\n walWarnThresholdBytes: config.health.walWarnThresholdBytes,\n encryptionEnabled: config.storage.encryption.enabled,\n // IP-1: when this process built the encrypted store itself, the\n // keyed open at boot proved the cipher peer - report the fact.\n ...(config.storage.encryption.enabled ? { cipherPeerInstalled: true } : {}),\n };\n if (triggersDaemon !== undefined) out.triggers = triggersDaemon;\n if (consolidatorDaemon !== undefined) out.consolidator = consolidatorDaemon;\n if (workflowTimerDaemon !== undefined) out.workflowTimers = workflowTimerDaemon;\n if (dispatcher !== undefined) {\n const sizes = dispatcher.size();\n // The dispatcher only exposes (subscribers, subscriptions); the\n // per-subject replay-buffer occupancy is owned by each\n // subscription, not the dispatcher itself. Surface the active\n // subscription count as a usable proxy + the raw subscriber count\n // so operators can correlate.\n out.replayBuffer = {\n eventsBuffered: sizes.subscriptions,\n subscribers: sizes.subscribers,\n subscriptions: sizes.subscriptions,\n };\n }\n return Object.freeze(out as HealthCheckOptions);\n}\n"],"mappings":"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"}
|
package/dist/app-ws.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { createLateBoundCommentarySink } from "./commentary/audit-bridge.js";
|
|
2
|
+
import { createWsDispatcher } from "./ws/dispatcher.js";
|
|
3
|
+
import { createWsTicketStore } from "./ws/ticket.js";
|
|
4
|
+
import { SUBPROTOCOL_NAME, negotiateSubprotocol } from "@graphorin/protocol";
|
|
5
|
+
import { createNodeWebSocket } from "@hono/node-ws";
|
|
6
|
+
|
|
7
|
+
//#region src/app-ws.ts
|
|
8
|
+
/**
|
|
9
|
+
* WebSocket layer construction for `createServer({...})` - dispatcher,
|
|
10
|
+
* single-use ticket store, the `@hono/node-ws` adapter, and the
|
|
11
|
+
* Graphorin-aware subprotocol negotiation policy. Built before the
|
|
12
|
+
* listener starts; the upgrade route itself is mounted in
|
|
13
|
+
* `app-routes.ts` and the adapter is injected into the Node server in
|
|
14
|
+
* `app-lifecycle.ts`.
|
|
15
|
+
*
|
|
16
|
+
* @packageDocumentation
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Build the WS dispatcher + ticket store + node-ws adapter when
|
|
20
|
+
* `server.ws.enabled` is true.
|
|
21
|
+
*/
|
|
22
|
+
function buildWsLayer(app, config, now) {
|
|
23
|
+
let dispatcher;
|
|
24
|
+
let tickets;
|
|
25
|
+
let wsAdapter;
|
|
26
|
+
let commentaryAuditSink;
|
|
27
|
+
if (config.server.ws.enabled) {
|
|
28
|
+
commentaryAuditSink = createLateBoundCommentarySink();
|
|
29
|
+
dispatcher = createWsDispatcher({
|
|
30
|
+
commentary: {
|
|
31
|
+
policy: config.server.ws.commentarySanitization.policy,
|
|
32
|
+
applyToEvents: config.server.ws.commentarySanitization.applyToEvents,
|
|
33
|
+
sink: commentaryAuditSink
|
|
34
|
+
},
|
|
35
|
+
replayBuffer: {
|
|
36
|
+
maxEvents: config.server.stream.replayBuffer.maxEvents,
|
|
37
|
+
ttlMs: config.server.stream.replayBuffer.ttlSeconds * 1e3
|
|
38
|
+
},
|
|
39
|
+
perConnectionQueueLimit: config.server.stream.perConnectionQueueLimit,
|
|
40
|
+
now
|
|
41
|
+
});
|
|
42
|
+
tickets = createWsTicketStore({
|
|
43
|
+
ttlMs: config.server.ws.ticketTtlMs,
|
|
44
|
+
now
|
|
45
|
+
});
|
|
46
|
+
wsAdapter = createNodeWebSocket({ app });
|
|
47
|
+
const wssOptions = wsAdapter.wss.options;
|
|
48
|
+
wssOptions.handleProtocols = (protocols) => {
|
|
49
|
+
const negotiated = negotiateSubprotocol(Array.from(protocols));
|
|
50
|
+
if (negotiated !== null) return negotiated;
|
|
51
|
+
for (const candidate of protocols) if (candidate === SUBPROTOCOL_NAME) return SUBPROTOCOL_NAME;
|
|
52
|
+
return false;
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
dispatcher,
|
|
57
|
+
tickets,
|
|
58
|
+
wsAdapter,
|
|
59
|
+
commentaryAuditSink
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
//#endregion
|
|
64
|
+
export { buildWsLayer };
|
|
65
|
+
//# sourceMappingURL=app-ws.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-ws.js","names":["dispatcher: WsDispatcher | undefined","tickets: WsTicketStore | undefined","wsAdapter: ReturnType<typeof createNodeWebSocket> | undefined","commentaryAuditSink: LateBoundCommentarySink | undefined"],"sources":["../src/app-ws.ts"],"sourcesContent":["/**\n * WebSocket layer construction for `createServer({...})` - dispatcher,\n * single-use ticket store, the `@hono/node-ws` adapter, and the\n * Graphorin-aware subprotocol negotiation policy. Built before the\n * listener starts; the upgrade route itself is mounted in\n * `app-routes.ts` and the adapter is injected into the Node server in\n * `app-lifecycle.ts`.\n *\n * @packageDocumentation\n */\n\nimport { negotiateSubprotocol, SUBPROTOCOL_NAME } from '@graphorin/protocol';\nimport { createNodeWebSocket } from '@hono/node-ws';\nimport type { Hono } from 'hono';\nimport { createLateBoundCommentarySink, type LateBoundCommentarySink } from './commentary/index.js';\nimport type { ServerConfigSpec } from './config.js';\nimport type { ServerVariables } from './internal/context.js';\nimport {\n createWsDispatcher,\n createWsTicketStore,\n type WsDispatcher,\n type WsTicketStore,\n} from './ws/index.js';\n\n/**\n * Everything the WebSocket layer contributes to the composed server.\n * Every member is `undefined` when `server.ws.enabled` is false.\n */\nexport interface WsLayer {\n readonly dispatcher: WsDispatcher | undefined;\n readonly tickets: WsTicketStore | undefined;\n readonly wsAdapter: ReturnType<typeof createNodeWebSocket> | undefined;\n readonly commentaryAuditSink: LateBoundCommentarySink | undefined;\n}\n\n/**\n * Build the WS dispatcher + ticket store + node-ws adapter when\n * `server.ws.enabled` is true.\n */\nexport function buildWsLayer(\n app: Hono<{ Variables: ServerVariables }>,\n config: ServerConfigSpec,\n now: () => number,\n): WsLayer {\n let dispatcher: WsDispatcher | undefined;\n let tickets: WsTicketStore | undefined;\n let wsAdapter: ReturnType<typeof createNodeWebSocket> | undefined;\n // IP-21: the dispatcher is built here, before the audit DB opens in start().\n // Hand it a late-bound commentary sink now and install the audit-writing\n // target once the DB exists - otherwise the sanitizer's documented decisions\n // (wrapped/stripped frames with before/after digests) are dropped.\n let commentaryAuditSink: LateBoundCommentarySink | undefined;\n if (config.server.ws.enabled) {\n commentaryAuditSink = createLateBoundCommentarySink();\n dispatcher = createWsDispatcher({\n commentary: {\n policy: config.server.ws.commentarySanitization.policy,\n applyToEvents: config.server.ws.commentarySanitization.applyToEvents,\n sink: commentaryAuditSink,\n },\n replayBuffer: {\n maxEvents: config.server.stream.replayBuffer.maxEvents,\n ttlMs: config.server.stream.replayBuffer.ttlSeconds * 1000,\n },\n perConnectionQueueLimit: config.server.stream.perConnectionQueueLimit,\n now,\n });\n tickets = createWsTicketStore({\n ttlMs: config.server.ws.ticketTtlMs,\n now,\n });\n wsAdapter = createNodeWebSocket({ app });\n // The WS server inside @hono/node-ws is created without any\n // subprotocol-negotiation policy; without one, the `ws` library\n // never echoes back `Sec-WebSocket-Protocol` and clients that\n // advertised a subprotocol close the connection immediately.\n // Mutate the options to install a Graphorin-aware policy that\n // selects `graphorin.protocol.v1` when the client offered it.\n const wssOptions = (\n wsAdapter.wss as unknown as {\n options: {\n handleProtocols?: (\n protocols: Set<string>,\n request: import('node:http').IncomingMessage,\n ) => string | false;\n };\n }\n ).options;\n wssOptions.handleProtocols = (protocols) => {\n const negotiated = negotiateSubprotocol(Array.from(protocols));\n if (negotiated !== null) return negotiated;\n // Browser ticket flow: the `WebSocket` constructor cannot set an\n // `Authorization` header, so the browser client offers two\n // subprotocol tokens - the canonical `graphorin.protocol.v1`\n // name plus a `ticket.<value>` token (see the wire contract in\n // `@graphorin/protocol`'s `subprotocol.ts`:\n // `SUBPROTOCOL_NAME` / `TICKET_SUBPROTOCOL_PREFIX` /\n // `parseTicketSubprotocol`). The server MUST echo back exactly\n // the canonical name (never the `ticket.*` token) so the\n // handshake's `Sec-WebSocket-Protocol` response stays valid; the\n // ticket value is consumed separately by `createWsUpgradeEvents`\n // via `parseTicketSubprotocol` and exchanged through the\n // single-use `WsTicketStore`.\n for (const candidate of protocols) {\n if (candidate === SUBPROTOCOL_NAME) return SUBPROTOCOL_NAME;\n }\n return false;\n };\n }\n return { dispatcher, tickets, wsAdapter, commentaryAuditSink };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAuCA,SAAgB,aACd,KACA,QACA,KACS;CACT,IAAIA;CACJ,IAAIC;CACJ,IAAIC;CAKJ,IAAIC;AACJ,KAAI,OAAO,OAAO,GAAG,SAAS;AAC5B,wBAAsB,+BAA+B;AACrD,eAAa,mBAAmB;GAC9B,YAAY;IACV,QAAQ,OAAO,OAAO,GAAG,uBAAuB;IAChD,eAAe,OAAO,OAAO,GAAG,uBAAuB;IACvD,MAAM;IACP;GACD,cAAc;IACZ,WAAW,OAAO,OAAO,OAAO,aAAa;IAC7C,OAAO,OAAO,OAAO,OAAO,aAAa,aAAa;IACvD;GACD,yBAAyB,OAAO,OAAO,OAAO;GAC9C;GACD,CAAC;AACF,YAAU,oBAAoB;GAC5B,OAAO,OAAO,OAAO,GAAG;GACxB;GACD,CAAC;AACF,cAAY,oBAAoB,EAAE,KAAK,CAAC;EAOxC,MAAM,aACJ,UAAU,IAQV;AACF,aAAW,mBAAmB,cAAc;GAC1C,MAAM,aAAa,qBAAqB,MAAM,KAAK,UAAU,CAAC;AAC9D,OAAI,eAAe,KAAM,QAAO;AAahC,QAAK,MAAM,aAAa,UACtB,KAAI,cAAc,iBAAkB,QAAO;AAE7C,UAAO;;;AAGX,QAAO;EAAE;EAAY;EAAS;EAAW;EAAqB"}
|
package/dist/app.d.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { ServerConfigInput, ServerConfigSpec } from "./config.js";
|
|
2
2
|
import { ConsolidatorDaemon, ConsolidatorLike } from "./consolidator/daemon.js";
|
|
3
3
|
import { TriggersDaemon } from "./triggers/daemon.js";
|
|
4
|
+
import { WorkflowTimerDaemon } from "./workflows/timer-daemon.js";
|
|
4
5
|
import { HealthCheckOptions } from "./health/checks.js";
|
|
5
6
|
import { ServerVariables } from "./internal/context.js";
|
|
6
7
|
import { MetricRegistry } from "./metrics/registry.js";
|
|
7
8
|
import "./health/index.js";
|
|
9
|
+
import { TriggersDaemonInput, WorkflowTimersInput } from "./app-daemons.js";
|
|
8
10
|
import { LifecycleHooks } from "./lifecycle/hooks.js";
|
|
9
11
|
import "./lifecycle/index.js";
|
|
10
12
|
import { AgentRegistry, WorkflowRegistry } from "./registry/index.js";
|
|
@@ -20,13 +22,12 @@ import { SessionApi } from "./routes/sessions.js";
|
|
|
20
22
|
import { SkillsApi } from "./routes/skills.js";
|
|
21
23
|
import "./routes/index.js";
|
|
22
24
|
import "./ws/index.js";
|
|
25
|
+
import { ensureStoreAuditBinding } from "./app-audit-binding.js";
|
|
23
26
|
import { GraphorinSqliteStore } from "@graphorin/store-sqlite";
|
|
24
27
|
import { Hono } from "hono";
|
|
25
|
-
import * as _graphorin_triggers0 from "@graphorin/triggers";
|
|
26
28
|
|
|
27
29
|
//#region src/app.d.ts
|
|
28
30
|
|
|
29
|
-
declare function ensureStoreAuditBinding(): void;
|
|
30
31
|
/**
|
|
31
32
|
* Public surface returned by {@link createServer}.
|
|
32
33
|
*
|
|
@@ -70,6 +71,11 @@ interface GraphorinServer {
|
|
|
70
71
|
* consolidator })`. Phase 14c integration.
|
|
71
72
|
*/
|
|
72
73
|
readonly consolidator: ConsolidatorDaemon | undefined;
|
|
74
|
+
/**
|
|
75
|
+
* W-032: optional workflow durable-timer daemon - populated when the
|
|
76
|
+
* operator wired a `createTimerDriver(...)` at construction time.
|
|
77
|
+
*/
|
|
78
|
+
readonly workflowTimers: WorkflowTimerDaemon | undefined;
|
|
73
79
|
/**
|
|
74
80
|
* Phase 14c Prometheus registry. Always present; sample updates
|
|
75
81
|
* are observable via `metrics.snapshot()`.
|
|
@@ -83,19 +89,6 @@ interface GraphorinServer {
|
|
|
83
89
|
readonly force?: boolean;
|
|
84
90
|
}): Promise<void>;
|
|
85
91
|
}
|
|
86
|
-
/**
|
|
87
|
-
* Discriminated union accepted by `CreateServerOptions.triggers`. A
|
|
88
|
-
* caller may either supply a fully-built daemon (e.g. constructed
|
|
89
|
-
* around a custom `Scheduler`) or just the underlying scheduler - the
|
|
90
|
-
* server wraps it with {@link createTriggersDaemon} automatically.
|
|
91
|
-
*
|
|
92
|
-
* @stable
|
|
93
|
-
*/
|
|
94
|
-
type TriggersDaemonInput = {
|
|
95
|
-
readonly daemon: TriggersDaemon;
|
|
96
|
-
} | {
|
|
97
|
-
readonly scheduler: _graphorin_triggers0.Scheduler;
|
|
98
|
-
};
|
|
99
92
|
/**
|
|
100
93
|
* @stable
|
|
101
94
|
*/
|
|
@@ -133,6 +126,13 @@ interface CreateServerOptions {
|
|
|
133
126
|
* should wrap with the daemon adapter.
|
|
134
127
|
*/
|
|
135
128
|
readonly triggers?: TriggersDaemonInput;
|
|
129
|
+
/**
|
|
130
|
+
* W-032: optional workflow durable-timer surface - pass a
|
|
131
|
+
* `createTimerDriver(...)` built over your workflows + checkpoint
|
|
132
|
+
* stores (`{ driver }`), or a pre-built daemon. The server starts
|
|
133
|
+
* and stops it with the lifecycle and reports it on `/v1/health`.
|
|
134
|
+
*/
|
|
135
|
+
readonly workflowTimers?: WorkflowTimersInput;
|
|
136
136
|
/**
|
|
137
137
|
* Optional replay API consumed by the scope-enforced replay
|
|
138
138
|
* endpoints. Phase 14c.
|
|
@@ -170,5 +170,5 @@ interface CreateServerOptions {
|
|
|
170
170
|
*/
|
|
171
171
|
declare function createServer(options?: CreateServerOptions): Promise<GraphorinServer>;
|
|
172
172
|
//#endregion
|
|
173
|
-
export { CreateServerOptions, GraphorinServer,
|
|
173
|
+
export { CreateServerOptions, GraphorinServer, createServer };
|
|
174
174
|
//# sourceMappingURL=app.d.ts.map
|
package/dist/app.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app.d.ts","names":[],"sources":["../src/app.ts"],"sourcesContent":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"app.d.ts","names":[],"sources":["../src/app.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0GiB,UAlDA,eAAA,CAkDmB;EAEhB,SAAA,OAAA,EAAA,MAAA;EAMS,SAAA,MAAA,EAxDV,gBAwDU;EAEV,SAAA,GAAA,EAzDH,IAyDG,CAAA;IAED,SAAA,EA3DgB,eA2DhB;EAEE,CAAA,CAAA;EACG,SAAA,MAAA,EA7DJ,aA6DI;EAED,SAAA,SAAA,EA9DA,gBA8DA;EACF,SAAA,IAAA,EA9DH,eA8DG;EACA,SAAA,WAAA,EAAA;IACH,SAAA,IAAA,EAAA,MAAA;IACE,SAAA,IAAA,EAAA,MAAA;EAMO,CAAA,GAAA,SAAA;EAMJ;;;;;;EAuBM,SAAA,YAAA,EA5FH,YA4FG,GAAA,SAAA;EAET;;;AA0BnB;;EAA+E,SAAA,SAAA,EAlHzD,aAkHyD,GAAA,SAAA;EAAR;;;;;qBA5GlD;;;;;;yBAMI;;;;;2BAKE;;;;;oBAKP;WACT;;;;;;MACqC;;;;;UAM/B,mBAAA;;oBAEG;;;;;;6BAMS;;mBAEV;;kBAED;;oBAEE;uBACG;;sBAED;oBACF;oBACA;iBACH;mBACE;;;;;;0BAMO;;;;;;sBAMJ;;;;;;;4BAOM;;;;;oBAKR;;;;;gCAKY,qBAAqB,QAAQ;;;;;;4BAMjC;;mBAET;;;;mCAIgB;;;;;;;;;;;;;;iBAsBb,YAAA,WAAsB,sBAA2B,QAAQ"}
|