aegis-bridge 0.1.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.
Files changed (137) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +404 -0
  3. package/dashboard/dist/assets/index-BoZwGLAx.css +32 -0
  4. package/dashboard/dist/assets/index-C61BkKH-.js +312 -0
  5. package/dashboard/dist/assets/index-C61BkKH-.js.map +1 -0
  6. package/dashboard/dist/index.html +14 -0
  7. package/dist/api-contracts.d.ts +229 -0
  8. package/dist/api-contracts.js +7 -0
  9. package/dist/api-contracts.typecheck.d.ts +14 -0
  10. package/dist/api-contracts.typecheck.js +1 -0
  11. package/dist/api-error-envelope.d.ts +15 -0
  12. package/dist/api-error-envelope.js +80 -0
  13. package/dist/auth.d.ts +87 -0
  14. package/dist/auth.js +276 -0
  15. package/dist/channels/index.d.ts +8 -0
  16. package/dist/channels/index.js +8 -0
  17. package/dist/channels/manager.d.ts +47 -0
  18. package/dist/channels/manager.js +115 -0
  19. package/dist/channels/telegram-style.d.ts +118 -0
  20. package/dist/channels/telegram-style.js +202 -0
  21. package/dist/channels/telegram.d.ts +91 -0
  22. package/dist/channels/telegram.js +1518 -0
  23. package/dist/channels/types.d.ts +77 -0
  24. package/dist/channels/types.js +8 -0
  25. package/dist/channels/webhook.d.ts +60 -0
  26. package/dist/channels/webhook.js +216 -0
  27. package/dist/cli.d.ts +8 -0
  28. package/dist/cli.js +252 -0
  29. package/dist/config.d.ts +90 -0
  30. package/dist/config.js +214 -0
  31. package/dist/consensus.d.ts +16 -0
  32. package/dist/consensus.js +19 -0
  33. package/dist/continuation-pointer.d.ts +11 -0
  34. package/dist/continuation-pointer.js +65 -0
  35. package/dist/diagnostics.d.ts +27 -0
  36. package/dist/diagnostics.js +95 -0
  37. package/dist/error-categories.d.ts +39 -0
  38. package/dist/error-categories.js +73 -0
  39. package/dist/events.d.ts +133 -0
  40. package/dist/events.js +389 -0
  41. package/dist/fault-injection.d.ts +29 -0
  42. package/dist/fault-injection.js +115 -0
  43. package/dist/file-utils.d.ts +2 -0
  44. package/dist/file-utils.js +37 -0
  45. package/dist/handshake.d.ts +60 -0
  46. package/dist/handshake.js +124 -0
  47. package/dist/hook-settings.d.ts +80 -0
  48. package/dist/hook-settings.js +272 -0
  49. package/dist/hook.d.ts +19 -0
  50. package/dist/hook.js +231 -0
  51. package/dist/hooks.d.ts +32 -0
  52. package/dist/hooks.js +364 -0
  53. package/dist/jsonl-watcher.d.ts +59 -0
  54. package/dist/jsonl-watcher.js +166 -0
  55. package/dist/logger.d.ts +35 -0
  56. package/dist/logger.js +65 -0
  57. package/dist/mcp-server.d.ts +123 -0
  58. package/dist/mcp-server.js +869 -0
  59. package/dist/memory-bridge.d.ts +27 -0
  60. package/dist/memory-bridge.js +137 -0
  61. package/dist/memory-routes.d.ts +3 -0
  62. package/dist/memory-routes.js +100 -0
  63. package/dist/metrics.d.ts +126 -0
  64. package/dist/metrics.js +286 -0
  65. package/dist/model-router.d.ts +53 -0
  66. package/dist/model-router.js +150 -0
  67. package/dist/monitor.d.ts +103 -0
  68. package/dist/monitor.js +820 -0
  69. package/dist/path-utils.d.ts +11 -0
  70. package/dist/path-utils.js +21 -0
  71. package/dist/permission-evaluator.d.ts +10 -0
  72. package/dist/permission-evaluator.js +48 -0
  73. package/dist/permission-guard.d.ts +51 -0
  74. package/dist/permission-guard.js +196 -0
  75. package/dist/permission-request-manager.d.ts +12 -0
  76. package/dist/permission-request-manager.js +36 -0
  77. package/dist/permission-routes.d.ts +7 -0
  78. package/dist/permission-routes.js +28 -0
  79. package/dist/pipeline.d.ts +97 -0
  80. package/dist/pipeline.js +291 -0
  81. package/dist/process-utils.d.ts +4 -0
  82. package/dist/process-utils.js +73 -0
  83. package/dist/question-manager.d.ts +54 -0
  84. package/dist/question-manager.js +80 -0
  85. package/dist/retry.d.ts +11 -0
  86. package/dist/retry.js +34 -0
  87. package/dist/safe-json.d.ts +12 -0
  88. package/dist/safe-json.js +22 -0
  89. package/dist/screenshot.d.ts +28 -0
  90. package/dist/screenshot.js +60 -0
  91. package/dist/server.d.ts +10 -0
  92. package/dist/server.js +1973 -0
  93. package/dist/session-cleanup.d.ts +18 -0
  94. package/dist/session-cleanup.js +11 -0
  95. package/dist/session.d.ts +379 -0
  96. package/dist/session.js +1568 -0
  97. package/dist/shutdown-utils.d.ts +5 -0
  98. package/dist/shutdown-utils.js +24 -0
  99. package/dist/signal-cleanup-helper.d.ts +48 -0
  100. package/dist/signal-cleanup-helper.js +117 -0
  101. package/dist/sse-limiter.d.ts +47 -0
  102. package/dist/sse-limiter.js +61 -0
  103. package/dist/sse-writer.d.ts +31 -0
  104. package/dist/sse-writer.js +94 -0
  105. package/dist/ssrf.d.ts +102 -0
  106. package/dist/ssrf.js +267 -0
  107. package/dist/startup.d.ts +6 -0
  108. package/dist/startup.js +162 -0
  109. package/dist/suppress.d.ts +33 -0
  110. package/dist/suppress.js +79 -0
  111. package/dist/swarm-monitor.d.ts +117 -0
  112. package/dist/swarm-monitor.js +300 -0
  113. package/dist/template-store.d.ts +45 -0
  114. package/dist/template-store.js +142 -0
  115. package/dist/terminal-parser.d.ts +16 -0
  116. package/dist/terminal-parser.js +346 -0
  117. package/dist/tmux-capture-cache.d.ts +18 -0
  118. package/dist/tmux-capture-cache.js +34 -0
  119. package/dist/tmux.d.ts +183 -0
  120. package/dist/tmux.js +906 -0
  121. package/dist/tool-registry.d.ts +40 -0
  122. package/dist/tool-registry.js +83 -0
  123. package/dist/transcript.d.ts +63 -0
  124. package/dist/transcript.js +284 -0
  125. package/dist/utils/circular-buffer.d.ts +11 -0
  126. package/dist/utils/circular-buffer.js +37 -0
  127. package/dist/utils/redact-headers.d.ts +13 -0
  128. package/dist/utils/redact-headers.js +54 -0
  129. package/dist/validation.d.ts +406 -0
  130. package/dist/validation.js +415 -0
  131. package/dist/verification.d.ts +2 -0
  132. package/dist/verification.js +72 -0
  133. package/dist/worktree-lookup.d.ts +24 -0
  134. package/dist/worktree-lookup.js +71 -0
  135. package/dist/ws-terminal.d.ts +32 -0
  136. package/dist/ws-terminal.js +348 -0
  137. package/package.json +83 -0
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en" class="dark">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Aegis Dashboard</title>
7
+ <link rel="icon" type="image/svg+xml" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🛡️</text></svg>" />
8
+ <script type="module" crossorigin src="/dashboard/assets/index-C61BkKH-.js"></script>
9
+ <link rel="stylesheet" crossorigin href="/dashboard/assets/index-BoZwGLAx.css">
10
+ </head>
11
+ <body class="bg-[#0a0a0f] text-gray-200 antialiased">
12
+ <div id="root"></div>
13
+ </body>
14
+ </html>
@@ -0,0 +1,229 @@
1
+ /**
2
+ * api-contracts.ts — Shared API contract types for backend and dashboard.
3
+ *
4
+ * Keep this file runtime-free (types only) so both packages can import it
5
+ * without bundling backend implementation code into the frontend.
6
+ */
7
+ export type UIState = 'idle' | 'working' | 'compacting' | 'context_warning' | 'waiting_for_input' | 'permission_prompt' | 'plan_mode' | 'ask_question' | 'bash_approval' | 'settings' | 'error' | 'unknown';
8
+ export type SessionStatusFilter = 'all' | UIState;
9
+ export interface SessionInfo {
10
+ id: string;
11
+ windowId: string;
12
+ windowName: string;
13
+ workDir: string;
14
+ claudeSessionId?: string;
15
+ jsonlPath?: string;
16
+ byteOffset: number;
17
+ monitorOffset: number;
18
+ status: UIState;
19
+ createdAt: number;
20
+ lastActivity: number;
21
+ stallThresholdMs: number;
22
+ permissionMode: string;
23
+ autoApprove?: boolean;
24
+ settingsPatched?: boolean;
25
+ promptDelivery?: {
26
+ delivered: boolean;
27
+ attempts: number;
28
+ };
29
+ actionHints?: Record<string, {
30
+ method: string;
31
+ url: string;
32
+ description: string;
33
+ }>;
34
+ }
35
+ export interface SessionHealth {
36
+ alive: boolean;
37
+ windowExists: boolean;
38
+ claudeRunning: boolean;
39
+ paneCommand: string | null;
40
+ status: UIState;
41
+ hasTranscript: boolean;
42
+ lastActivity: number;
43
+ lastActivityAgo: number;
44
+ sessionAge: number;
45
+ details: string;
46
+ actionHints?: Record<string, {
47
+ method: string;
48
+ url: string;
49
+ description: string;
50
+ }>;
51
+ }
52
+ export interface HealthResponse {
53
+ status: string;
54
+ version: string;
55
+ platform: NodeJS.Platform;
56
+ uptime: number;
57
+ sessions: {
58
+ active: number;
59
+ total: number;
60
+ };
61
+ timestamp: string;
62
+ }
63
+ export interface ParsedEntry {
64
+ role: 'user' | 'assistant' | 'system';
65
+ contentType: 'text' | 'thinking' | 'tool_use' | 'tool_result' | 'tool_error' | 'permission_request' | 'progress';
66
+ text: string;
67
+ toolName?: string;
68
+ toolUseId?: string;
69
+ timestamp?: string;
70
+ }
71
+ export interface MessagesResponse {
72
+ messages: ParsedEntry[];
73
+ status: UIState;
74
+ statusText: string | null;
75
+ interactiveContent: string | null;
76
+ }
77
+ export interface SessionMetrics {
78
+ durationSec: number;
79
+ messages: number;
80
+ toolCalls: number;
81
+ approvals: number;
82
+ autoApprovals: number;
83
+ statusChanges: string[];
84
+ /** Issue #488: Cumulative token usage and estimated cost. Present once tokens are first observed. */
85
+ tokenUsage?: {
86
+ inputTokens: number;
87
+ outputTokens: number;
88
+ cacheCreationTokens: number;
89
+ cacheReadTokens: number;
90
+ estimatedCostUsd: number;
91
+ };
92
+ }
93
+ export interface LatencySummaryStat {
94
+ min: number | null;
95
+ max: number | null;
96
+ avg: number | null;
97
+ count: number;
98
+ }
99
+ export interface SessionLatency {
100
+ sessionId: string;
101
+ realtime: {
102
+ hook_latency_ms: number | null;
103
+ state_change_detection_ms: number | null;
104
+ permission_response_ms: number | null;
105
+ } | null;
106
+ aggregated: {
107
+ hook_latency_ms: LatencySummaryStat;
108
+ state_change_detection_ms: LatencySummaryStat;
109
+ permission_response_ms: LatencySummaryStat;
110
+ channel_delivery_ms: LatencySummaryStat;
111
+ } | null;
112
+ }
113
+ export interface GlobalMetrics {
114
+ uptime: number;
115
+ sessions: {
116
+ total_created: number;
117
+ currently_active: number;
118
+ completed: number;
119
+ failed: number;
120
+ avg_duration_sec: number;
121
+ avg_messages_per_session: number;
122
+ };
123
+ auto_approvals: number;
124
+ webhooks_sent: number;
125
+ webhooks_failed: number;
126
+ screenshots_taken: number;
127
+ pipelines_created: number;
128
+ batches_created: number;
129
+ prompt_delivery: {
130
+ sent: number;
131
+ delivered: number;
132
+ failed: number;
133
+ success_rate: number | null;
134
+ };
135
+ latency: {
136
+ hook_latency_ms: LatencySummaryStat;
137
+ state_change_detection_ms: LatencySummaryStat;
138
+ permission_response_ms: LatencySummaryStat;
139
+ channel_delivery_ms: LatencySummaryStat;
140
+ };
141
+ }
142
+ export type SSEEventType = 'status' | 'message' | 'approval' | 'ended' | 'heartbeat' | 'stall' | 'dead' | 'system' | 'hook' | 'subagent_start' | 'subagent_stop' | 'verification' | 'permission_denied';
143
+ export interface SessionSSEEvent {
144
+ event: SSEEventType;
145
+ sessionId: string;
146
+ timestamp: string;
147
+ emittedAt?: number;
148
+ id?: number;
149
+ data: Record<string, unknown>;
150
+ }
151
+ export type GlobalSSEEventType = 'session_status_change' | 'session_message' | 'session_approval' | 'session_ended' | 'session_created' | 'session_stall' | 'session_dead' | 'session_subagent_start' | 'session_subagent_stop' | 'session_verification';
152
+ export interface GlobalSSEEvent {
153
+ event: GlobalSSEEventType;
154
+ sessionId: string;
155
+ timestamp: string;
156
+ id?: number;
157
+ data: Record<string, unknown>;
158
+ }
159
+ export interface CreateSessionRequest {
160
+ workDir: string;
161
+ name?: string;
162
+ prompt?: string;
163
+ prd?: string;
164
+ resumeSessionId?: string;
165
+ claudeCommand?: string;
166
+ env?: Record<string, string>;
167
+ stallThresholdMs?: number;
168
+ permissionMode?: string;
169
+ autoApprove?: boolean;
170
+ parentId?: string;
171
+ memoryKeys?: string[];
172
+ }
173
+ export interface PaneResponse {
174
+ pane: string;
175
+ }
176
+ export interface SessionSummary {
177
+ sessionId: string;
178
+ windowName: string;
179
+ status: UIState;
180
+ totalMessages: number;
181
+ messages: Array<{
182
+ role: string;
183
+ contentType: string;
184
+ text: string;
185
+ }>;
186
+ createdAt: number;
187
+ lastActivity: number;
188
+ permissionMode: string;
189
+ prd?: string;
190
+ }
191
+ export interface OkResponse {
192
+ ok: boolean;
193
+ }
194
+ export interface SendResponse extends OkResponse {
195
+ delivered: boolean;
196
+ attempts: number;
197
+ }
198
+ export interface ApiError {
199
+ error: string;
200
+ }
201
+ export interface SessionsListResponse {
202
+ sessions: SessionInfo[];
203
+ pagination: {
204
+ page: number;
205
+ limit: number;
206
+ total: number;
207
+ totalPages: number;
208
+ };
209
+ }
210
+ export type SessionStatusCounts = Record<SessionStatusFilter, number>;
211
+ /** Issue #754: Aggregated session statistics. */
212
+ export interface SessionStats {
213
+ active: number;
214
+ byStatus: Partial<Record<UIState, number>>;
215
+ totalCreated: number;
216
+ totalCompleted: number;
217
+ totalFailed: number;
218
+ }
219
+ /** Issue #754: Bulk-delete request body. */
220
+ export interface BatchDeleteRequest {
221
+ ids?: string[];
222
+ status?: UIState;
223
+ }
224
+ /** Issue #754: Bulk-delete response. */
225
+ export interface BatchDeleteResponse {
226
+ deleted: number;
227
+ notFound: string[];
228
+ errors: string[];
229
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * api-contracts.ts — Shared API contract types for backend and dashboard.
3
+ *
4
+ * Keep this file runtime-free (types only) so both packages can import it
5
+ * without bundling backend implementation code into the frontend.
6
+ */
7
+ export {};
@@ -0,0 +1,14 @@
1
+ import type { SessionInfo as InternalSessionInfo, SessionManager } from './session.js';
2
+ import type { MetricsCollector, SessionLatencySummary } from './metrics.js';
3
+ import type { SessionSSEEvent as InternalSessionSSEEvent, GlobalSSEEvent as InternalGlobalSSEEvent } from './events.js';
4
+ import type { SessionInfo, MessagesResponse, SessionSummary, SessionMetrics, SessionLatency, GlobalMetrics, SessionSSEEvent, GlobalSSEEvent } from './api-contracts.js';
5
+ type Assert<T extends true> = T;
6
+ export type SessionInfoContractCompat = Assert<InternalSessionInfo extends SessionInfo ? true : false>;
7
+ export type SessionReadMessagesContractCompat = Assert<Awaited<ReturnType<SessionManager['readMessages']>> extends MessagesResponse ? true : false>;
8
+ export type SessionSummaryContractCompat = Assert<Awaited<ReturnType<SessionManager['getSummary']>> extends SessionSummary ? true : false>;
9
+ export type SessionMetricsContractCompat = Assert<NonNullable<ReturnType<MetricsCollector['getSessionMetrics']>> extends SessionMetrics ? true : false>;
10
+ export type SessionLatencySummaryContractCompat = Assert<SessionLatencySummary extends NonNullable<SessionLatency['aggregated']> ? true : false>;
11
+ export type GlobalMetricsContractCompat = Assert<ReturnType<MetricsCollector['getGlobalMetrics']> extends GlobalMetrics ? true : false>;
12
+ export type SessionSSEContractCompat = Assert<InternalSessionSSEEvent extends SessionSSEEvent ? true : false>;
13
+ export type GlobalSSEContractCompat = Assert<InternalGlobalSSEEvent extends GlobalSSEEvent ? true : false>;
14
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,15 @@
1
+ export interface ApiErrorEnvelope {
2
+ code: string;
3
+ message: string;
4
+ details?: unknown;
5
+ requestId: string;
6
+ error: string;
7
+ }
8
+ interface NormalizeApiErrorInput {
9
+ payload: unknown;
10
+ statusCode: number;
11
+ requestId: string;
12
+ contentType?: string;
13
+ }
14
+ export declare function normalizeApiErrorPayload(input: NormalizeApiErrorInput): unknown;
15
+ export {};
@@ -0,0 +1,80 @@
1
+ function defaultErrorMessage(statusCode) {
2
+ if (statusCode >= 500)
3
+ return 'Internal server error';
4
+ if (statusCode === 404)
5
+ return 'Not found';
6
+ if (statusCode === 401)
7
+ return 'Unauthorized';
8
+ if (statusCode === 403)
9
+ return 'Forbidden';
10
+ if (statusCode === 429)
11
+ return 'Rate limit exceeded';
12
+ return 'Request failed';
13
+ }
14
+ function mapStatusToCode(statusCode) {
15
+ if (statusCode === 400)
16
+ return 'VALIDATION_ERROR';
17
+ if (statusCode === 401)
18
+ return 'UNAUTHORIZED';
19
+ if (statusCode === 403)
20
+ return 'FORBIDDEN';
21
+ if (statusCode === 404)
22
+ return 'NOT_FOUND';
23
+ if (statusCode === 409)
24
+ return 'CONFLICT';
25
+ if (statusCode === 429)
26
+ return 'RATE_LIMITED';
27
+ if (statusCode === 501)
28
+ return 'NOT_IMPLEMENTED';
29
+ if (statusCode >= 500)
30
+ return 'INTERNAL_ERROR';
31
+ return `HTTP_${statusCode}`;
32
+ }
33
+ function parseJsonObjectString(payload) {
34
+ try {
35
+ const parsed = JSON.parse(payload);
36
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
37
+ return parsed;
38
+ }
39
+ return null;
40
+ }
41
+ catch {
42
+ return null;
43
+ }
44
+ }
45
+ function isJsonContentType(contentType) {
46
+ return typeof contentType === 'string' && contentType.toLowerCase().includes('application/json');
47
+ }
48
+ export function normalizeApiErrorPayload(input) {
49
+ const { payload, statusCode, requestId, contentType } = input;
50
+ if (statusCode < 400)
51
+ return payload;
52
+ if (typeof contentType === 'string' && contentType.includes('text/event-stream'))
53
+ return payload;
54
+ let source = null;
55
+ if (payload && typeof payload === 'object' && !Array.isArray(payload) && !(payload instanceof Buffer)) {
56
+ source = payload;
57
+ }
58
+ else if (typeof payload === 'string' && isJsonContentType(contentType)) {
59
+ source = parseJsonObjectString(payload);
60
+ }
61
+ if (!source)
62
+ return payload;
63
+ const legacyError = typeof source.error === 'string' ? source.error : undefined;
64
+ const sourceMessage = typeof source.message === 'string' ? source.message : undefined;
65
+ const message = sourceMessage ?? legacyError ?? defaultErrorMessage(statusCode);
66
+ const code = typeof source.code === 'string' ? source.code : mapStatusToCode(statusCode);
67
+ const envelope = {
68
+ code,
69
+ message,
70
+ requestId,
71
+ error: legacyError ?? message,
72
+ };
73
+ if (source.details !== undefined) {
74
+ envelope.details = source.details;
75
+ }
76
+ if (typeof payload === 'string') {
77
+ return JSON.stringify(envelope);
78
+ }
79
+ return envelope;
80
+ }
package/dist/auth.d.ts ADDED
@@ -0,0 +1,87 @@
1
+ /**
2
+ * auth.ts — API key management and authentication middleware.
3
+ *
4
+ * Issue #39: Multi-key auth with rate limiting.
5
+ * Keys are hashed with SHA-256 (no bcrypt dependency needed).
6
+ * Backward compatible with single authToken from config.
7
+ */
8
+ export interface ApiKey {
9
+ id: string;
10
+ name: string;
11
+ hash: string;
12
+ createdAt: number;
13
+ lastUsedAt: number;
14
+ rateLimit: number;
15
+ }
16
+ export interface ApiKeyStore {
17
+ keys: ApiKey[];
18
+ }
19
+ /** Route-level auth policy for bearer tokens. */
20
+ export declare function classifyBearerTokenForRoute(token: string, isSSERoute: boolean): 'bearer' | 'sse' | 'reject';
21
+ export declare class AuthManager {
22
+ private keysFile;
23
+ private store;
24
+ private rateLimits;
25
+ private masterToken;
26
+ /** #297: Short-lived SSE tokens. Keyed by token string for O(1) lookup. */
27
+ private sseTokens;
28
+ /** Track how many SSE tokens each bearer key has outstanding. */
29
+ private sseTokenCounts;
30
+ /** #414: Mutex to prevent concurrent SSE token generation from exceeding per-key limits. */
31
+ private sseMutex;
32
+ /** #583: Last batch creation timestamp per key ID. */
33
+ private batchRateLimits;
34
+ constructor(keysFile: string, masterToken?: string);
35
+ /** Load keys from disk. */
36
+ load(): Promise<void>;
37
+ /** Save keys to disk. */
38
+ save(): Promise<void>;
39
+ /** Create a new API key. Returns the plaintext key (only shown once). */
40
+ createKey(name: string, rateLimit?: number): Promise<{
41
+ id: string;
42
+ key: string;
43
+ name: string;
44
+ }>;
45
+ /** List keys (without hashes). */
46
+ listKeys(): Array<Omit<ApiKey, 'hash'>>;
47
+ /** Revoke a key by ID. */
48
+ revokeKey(id: string): Promise<boolean>;
49
+ /**
50
+ * Validate a bearer token.
51
+ * Returns { valid, keyId, rateLimited } or null if no auth configured.
52
+ */
53
+ validate(token: string): {
54
+ valid: boolean;
55
+ keyId: string | null;
56
+ rateLimited: boolean;
57
+ };
58
+ /** Hash a key with SHA-256. */
59
+ static hashKey(key: string): string;
60
+ /** Constant-time equality check for secret strings. */
61
+ private static timingSafeStringEqual;
62
+ /** #583: Check and update batch rate limit for a key. Returns true if rate-limited. */
63
+ checkBatchRateLimit(keyId: string | null): boolean;
64
+ /** #398: Sweep stale rate limit buckets. Prune entries with expired windows. */
65
+ sweepStaleRateLimits(): void;
66
+ /** Check if auth is enabled (master token or any keys). */
67
+ get authEnabled(): boolean;
68
+ /**
69
+ * Generate a short-lived, single-use SSE token.
70
+ * The caller must already be authenticated (validated via bearer token).
71
+ * Returns the token string and its expiry timestamp.
72
+ * #414: Async with mutex to prevent concurrent calls from exceeding per-key limits.
73
+ */
74
+ generateSSEToken(keyId: string): Promise<{
75
+ token: string;
76
+ expiresAt: number;
77
+ }>;
78
+ /**
79
+ * Validate and consume a short-lived SSE token.
80
+ * Returns true if valid (and marks it as used), false otherwise.
81
+ * #826: Async with mutex to prevent concurrent validation/generation from
82
+ * racing on shared state (sseTokens, sseTokenCounts).
83
+ */
84
+ validateSSEToken(token: string): Promise<boolean>;
85
+ /** Remove expired SSE tokens and recount per-key outstanding. */
86
+ private cleanExpiredSSETokens;
87
+ }