aegis-bridge 2.8.0 → 2.8.1

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.
@@ -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/server.js CHANGED
@@ -41,6 +41,7 @@ import { execFileSync } from 'node:child_process';
41
41
  import { negotiate } from './handshake.js';
42
42
  import { diagnosticsBus } from './diagnostics.js';
43
43
  import { setStructuredLogSink } from './logger.js';
44
+ import { normalizeApiErrorPayload } from './api-error-envelope.js';
44
45
  import { authKeySchema, sendMessageSchema, commandSchema, bashSchema, screenshotSchema, permissionHookSchema, stopHookSchema, batchSessionSchema, pipelineSchema, handshakeRequestSchema, parseIntSafe, isValidUUID, compareSemver, extractCCVersion, MIN_CC_VERSION, } from './validation.js';
45
46
  const __filename = fileURLToPath(import.meta.url);
46
47
  const __dirname = path.dirname(__filename);
@@ -129,7 +130,8 @@ app.addHook('onSend', (req, reply, payload, done) => {
129
130
  reply.header('X-Frame-Options', 'DENY');
130
131
  reply.header('Referrer-Policy', 'strict-origin-when-cross-origin');
131
132
  reply.header('Permissions-Policy', 'camera=(), microphone=()');
132
- done();
133
+ const normalizedPayload = normalizeApiErrorPayload({ payload, statusCode: reply.statusCode, requestId: req.id, contentType: typeof contentType === "string" ? contentType : undefined });
134
+ done(null, normalizedPayload);
133
135
  });
134
136
  const ipRateLimits = new Map();
135
137
  const IP_WINDOW_MS = 60_000;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aegis-bridge",
3
- "version": "2.8.0",
3
+ "version": "2.8.1",
4
4
  "type": "module",
5
5
  "description": "Orchestrate Claude Code sessions via API. Create, brief, monitor, refine, ship.",
6
6
  "main": "dist/server.js",