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.
- package/dist/api-error-envelope.d.ts +15 -0
- package/dist/api-error-envelope.js +80 -0
- package/dist/server.js +3 -1
- package/package.json +1 -1
|
@@ -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
|
-
|
|
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;
|