@aictrl/hush 0.1.0 → 0.1.6
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/.github/workflows/ci.yml +46 -0
- package/.github/workflows/e2e-opencode.yml +126 -0
- package/.github/workflows/opencode-review.yml +101 -0
- package/.github/workflows/publish.yml +44 -0
- package/CLAUDE.md +6 -0
- package/CONTRIBUTING.md +29 -0
- package/Dockerfile +25 -0
- package/GEMINI.md +6 -0
- package/README.md +86 -71
- package/dist/cli.js +11 -2
- package/dist/cli.js.map +1 -1
- package/dist/index.js +54 -36
- package/dist/index.js.map +1 -1
- package/dist/middleware/redactor.d.ts.map +1 -1
- package/dist/middleware/redactor.js +12 -7
- package/dist/middleware/redactor.js.map +1 -1
- package/dist/vault/token-vault.d.ts.map +1 -1
- package/dist/vault/token-vault.js +103 -16
- package/dist/vault/token-vault.js.map +1 -1
- package/install.sh +37 -0
- package/logo.svg +31 -0
- package/package.json +5 -4
- package/scripts/e2e-gateway-harness.ts +62 -0
- package/scripts/e2e-mock-upstream.mjs +55 -0
- package/scripts/e2e-opencode.sh +217 -0
- package/src/cli.ts +20 -0
- package/src/index.ts +261 -0
- package/src/lib/dashboard.ts +180 -0
- package/src/lib/logger.ts +72 -0
- package/src/middleware/redactor.ts +155 -0
- package/src/vault/token-vault.ts +249 -0
- package/tests/proxy.test.ts +258 -0
- package/tests/redaction.test.ts +102 -0
- package/tests/universal-proxy.test.ts +160 -0
- package/tests/vault.test.ts +73 -0
- package/tsconfig.json +25 -0
- package/vitest.config.ts +12 -0
package/dist/index.js
CHANGED
|
@@ -48,8 +48,17 @@ app.use((req, res, next) => {
|
|
|
48
48
|
async function proxyRequest(req, res, targetUrl, headers) {
|
|
49
49
|
const startTime = performance.now();
|
|
50
50
|
const dashboard = getDashboard();
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
const hasBody = ['POST', 'PUT', 'PATCH'].includes(req.method);
|
|
52
|
+
// 1. Redact Request Body (Prompts, Tool Results) — only for methods with a body
|
|
53
|
+
let redactedBody;
|
|
54
|
+
let tokens = new Map();
|
|
55
|
+
let hasRedacted = false;
|
|
56
|
+
if (hasBody) {
|
|
57
|
+
const result = redactor.redact(req.body);
|
|
58
|
+
redactedBody = result.content;
|
|
59
|
+
tokens = result.tokens;
|
|
60
|
+
hasRedacted = result.hasRedacted;
|
|
61
|
+
}
|
|
53
62
|
const redactionDuration = Math.round(performance.now() - startTime);
|
|
54
63
|
// Log all requests to dashboard
|
|
55
64
|
if (dashboard) {
|
|
@@ -67,13 +76,13 @@ async function proxyRequest(req, res, targetUrl, headers) {
|
|
|
67
76
|
}
|
|
68
77
|
}
|
|
69
78
|
try {
|
|
79
|
+
const fetchHeaders = { ...headers };
|
|
80
|
+
if (hasBody)
|
|
81
|
+
fetchHeaders['Content-Type'] = 'application/json';
|
|
70
82
|
const response = await fetch(targetUrl, {
|
|
71
|
-
method:
|
|
72
|
-
headers:
|
|
73
|
-
|
|
74
|
-
...headers,
|
|
75
|
-
},
|
|
76
|
-
body: JSON.stringify(redactedBody),
|
|
83
|
+
method: req.method,
|
|
84
|
+
headers: fetchHeaders,
|
|
85
|
+
body: hasBody ? JSON.stringify(redactedBody) : undefined,
|
|
77
86
|
signal: AbortSignal.timeout(30000), // 30s timeout
|
|
78
87
|
});
|
|
79
88
|
// Handle Upstream Errors (4xx, 5xx)
|
|
@@ -83,7 +92,8 @@ async function proxyRequest(req, res, targetUrl, headers) {
|
|
|
83
92
|
return res.status(response.status).send(errorData);
|
|
84
93
|
}
|
|
85
94
|
// Case A: Streaming
|
|
86
|
-
|
|
95
|
+
const isStreaming = req.body?.stream === true || req.body?.stream === 'true';
|
|
96
|
+
if (isStreaming && response.body) {
|
|
87
97
|
log.info({ path: req.path }, 'Starting stream proxy');
|
|
88
98
|
res.setHeader('Content-Type', 'text/event-stream');
|
|
89
99
|
res.setHeader('Cache-Control', 'no-cache');
|
|
@@ -129,12 +139,19 @@ async function proxyRequest(req, res, targetUrl, headers) {
|
|
|
129
139
|
*/
|
|
130
140
|
app.post('/v1/messages', async (req, res) => {
|
|
131
141
|
const apiKey = req.headers['x-api-key'];
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
142
|
+
const auth = req.headers['authorization'];
|
|
143
|
+
if (!apiKey && !auth)
|
|
144
|
+
return res.status(401).json({ error: 'Missing Anthropic API Key or Authorization header' });
|
|
145
|
+
const headers = {
|
|
136
146
|
'anthropic-version': req.headers['anthropic-version'] || '2023-06-01',
|
|
137
|
-
}
|
|
147
|
+
};
|
|
148
|
+
if (req.headers['anthropic-beta'])
|
|
149
|
+
headers['anthropic-beta'] = req.headers['anthropic-beta'];
|
|
150
|
+
if (apiKey)
|
|
151
|
+
headers['x-api-key'] = apiKey;
|
|
152
|
+
if (auth)
|
|
153
|
+
headers['Authorization'] = auth;
|
|
154
|
+
await proxyRequest(req, res, 'https://api.anthropic.com/v1/messages', headers);
|
|
138
155
|
});
|
|
139
156
|
/**
|
|
140
157
|
* Handle OpenAI /chat/completions proxy
|
|
@@ -149,8 +166,9 @@ app.post('/v1/chat/completions', async (req, res) => {
|
|
|
149
166
|
});
|
|
150
167
|
/**
|
|
151
168
|
* Handle ZhipuAI GLM API proxy (OpenCode + GLM-5)
|
|
152
|
-
* Supports:
|
|
153
|
-
*
|
|
169
|
+
* Supports both regular and coding plan endpoints:
|
|
170
|
+
* /api/paas/v4/chat/completions → https://api.z.ai/api/paas/v4/chat/completions
|
|
171
|
+
* /api/coding/paas/v4/chat/completions → https://api.z.ai/api/coding/paas/v4/chat/completions
|
|
154
172
|
*/
|
|
155
173
|
app.post('/api/paas/v4/chat/completions', async (req, res) => {
|
|
156
174
|
const auth = req.headers['authorization'];
|
|
@@ -160,6 +178,14 @@ app.post('/api/paas/v4/chat/completions', async (req, res) => {
|
|
|
160
178
|
'Authorization': auth,
|
|
161
179
|
});
|
|
162
180
|
});
|
|
181
|
+
app.post('/api/coding/paas/v4/chat/completions', async (req, res) => {
|
|
182
|
+
const auth = req.headers['authorization'];
|
|
183
|
+
if (!auth)
|
|
184
|
+
return res.status(401).json({ error: 'Missing ZhipuAI Authorization' });
|
|
185
|
+
await proxyRequest(req, res, 'https://api.z.ai/api/coding/paas/v4/chat/completions', {
|
|
186
|
+
'Authorization': auth,
|
|
187
|
+
});
|
|
188
|
+
});
|
|
163
189
|
/**
|
|
164
190
|
* Handle Google Gemini API proxy
|
|
165
191
|
* Supports: /v1beta/models/{model}:generateContent
|
|
@@ -182,29 +208,21 @@ app.get('/health', (req, res) => {
|
|
|
182
208
|
res.json(response);
|
|
183
209
|
});
|
|
184
210
|
/**
|
|
185
|
-
* Catch-all Handler: Forward
|
|
186
|
-
*
|
|
211
|
+
* Catch-all Handler: Forward unmatched requests with redaction/rehydration.
|
|
212
|
+
* Uses HUSH_UPSTREAM if set, otherwise falls back to Google.
|
|
187
213
|
*/
|
|
188
214
|
app.all('/*path', async (req, res) => {
|
|
189
215
|
const targetBase = 'https://generativelanguage.googleapis.com';
|
|
190
216
|
const targetUrl = `${targetBase}${req.url}`;
|
|
191
|
-
log.info({ path: req.path, method: req.method }, 'Forwarding
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
});
|
|
202
|
-
const data = await response.text();
|
|
203
|
-
res.status(response.status).send(data);
|
|
204
|
-
}
|
|
205
|
-
catch (error) {
|
|
206
|
-
log.error({ err: error, path: req.path }, 'Catch-all forwarding failed');
|
|
207
|
-
res.status(500).json({ error: 'Gateway forwarding failed' });
|
|
208
|
-
}
|
|
217
|
+
log.info({ path: req.path, method: req.method, upstream: targetBase }, 'Forwarding to upstream');
|
|
218
|
+
// Collect auth headers to pass through
|
|
219
|
+
const headers = {};
|
|
220
|
+
if (req.headers['authorization'])
|
|
221
|
+
headers['Authorization'] = req.headers['authorization'];
|
|
222
|
+
if (req.headers['x-api-key'])
|
|
223
|
+
headers['x-api-key'] = req.headers['x-api-key'];
|
|
224
|
+
if (req.headers['x-goog-api-key'])
|
|
225
|
+
headers['x-goog-api-key'] = req.headers['x-goog-api-key'];
|
|
226
|
+
await proxyRequest(req, res, targetUrl, headers);
|
|
209
227
|
});
|
|
210
228
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAE/C,MAAM,GAAG,GAAG,YAAY,CAAC,YAAY,CAAC,CAAC;AACvC,MAAM,QAAQ,GAAG,IAAI,QAAQ,EAAE,CAAC;AAChC,MAAM,KAAK,GAAG,IAAI,UAAU,EAAE,CAAC;AAE/B,0EAA0E;AAC1E,IAAI,UAAU,GAAqB,IAAI,CAAC;AACxC,SAAS,YAAY;IACnB,IAAI,UAAU;QAAE,OAAO,UAAU,CAAC;IAClC,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc,KAAK,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC;QAClF,UAAU,GAAG,IAAI,SAAS,EAAE,CAAC;IAC/B,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,8DAA8D;AAC9D,YAAY,EAAE,CAAC;AAEf,MAAM,CAAC,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;AAE7B,0EAA0E;AAC1E,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,WAAW,CAAC;AAE1D,uDAAuD;AACvD,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;AAE/C,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,kBAAkB,EAAE,CAAC,CAAC,CAAC,CAAC,gBAAgB;AAC/D,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;AAEzC;;GAEG;AACH,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;IACzB,IAAI,GAAG,CAAC,IAAI,KAAK,SAAS;QAAE,OAAO,IAAI,EAAE,CAAC;IAE1C,IAAI,UAAU,EAAE,CAAC;QACf,MAAM,UAAU,GAAG,GAAG,CAAC,OAAO,CAAC,cAAc,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;QAC/E,MAAM,aAAa,GAAG,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC;QAE7E,IAAI,CAAC,aAAa,IAAI,CAAC,aAAa,KAAK,UAAU,IAAI,aAAa,KAAK,UAAU,UAAU,EAAE,CAAC,EAAE,CAAC;YACjG,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,EAAE,6BAA6B,CAAC,CAAC;YACxD,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,uCAAuC,EAAE,CAAC,CAAC;QAClF,CAAC;IACH,CAAC;IACD,IAAI,EAAE,CAAC;AACT,CAAC,CAAC,CAAC;AAEH;;GAEG;AACH,KAAK,UAAU,YAAY,CACzB,GAAoB,EACpB,GAAqB,EACrB,SAAiB,EACjB,OAA+B;IAE/B,MAAM,SAAS,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;IACpC,MAAM,SAAS,GAAG,YAAY,EAAE,CAAC;IAEjC,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"redactor.d.ts","sourceRoot":"","sources":["../../src/middleware/redactor.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"redactor.d.ts","sourceRoot":"","sources":["../../src/middleware/redactor.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AASH;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,8CAA8C;IAC9C,OAAO,EAAE,GAAG,CAAC;IACb,uCAAuC;IACvC,WAAW,EAAE,OAAO,CAAC;IACrB,6CAA6C;IAC7C,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC7B;AAED;;GAEG;AACH,qBAAa,QAAQ;IACnB;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAa9B;IAEF;;;;;OAKG;IACI,MAAM,CAAC,KAAK,EAAE,GAAG,GAAG,eAAe;CAoG3C"}
|
|
@@ -4,6 +4,11 @@
|
|
|
4
4
|
* Identifies and swaps PII (emails, keys, IP addresses) for persistent tokens.
|
|
5
5
|
* Interoperable with TokenVault for re-hydration.
|
|
6
6
|
*/
|
|
7
|
+
import { createHash } from 'crypto';
|
|
8
|
+
/** Deterministic short hash of a value (first 6 hex chars of SHA-256). */
|
|
9
|
+
function tokenHash(value) {
|
|
10
|
+
return createHash('sha256').update(value).digest('hex').slice(0, 6);
|
|
11
|
+
}
|
|
7
12
|
/**
|
|
8
13
|
* Redactor class for identifying and masking sensitive information.
|
|
9
14
|
*/
|
|
@@ -45,7 +50,7 @@ export class Redactor {
|
|
|
45
50
|
const normalizedKey = keyName.toLowerCase().replace(/[-_]/g, '');
|
|
46
51
|
if (SENSITIVE_KEYS.some(k => normalizedKey.includes(k))) {
|
|
47
52
|
hasRedacted = true;
|
|
48
|
-
const token = `[SENSITIVE_SECRET_${
|
|
53
|
+
const token = `[SENSITIVE_SECRET_${tokenHash(node)}]`;
|
|
49
54
|
tokens.set(token, node);
|
|
50
55
|
return token;
|
|
51
56
|
}
|
|
@@ -55,42 +60,42 @@ export class Redactor {
|
|
|
55
60
|
// Redact Emails
|
|
56
61
|
text = text.replace(Redactor.PATTERNS.EMAIL, (match) => {
|
|
57
62
|
hasRedacted = true;
|
|
58
|
-
const token = `[USER_EMAIL_${
|
|
63
|
+
const token = `[USER_EMAIL_${tokenHash(match)}]`;
|
|
59
64
|
tokens.set(token, match);
|
|
60
65
|
return token;
|
|
61
66
|
});
|
|
62
67
|
// Redact IP Addresses (v4)
|
|
63
68
|
text = text.replace(Redactor.PATTERNS.IPV4, (match) => {
|
|
64
69
|
hasRedacted = true;
|
|
65
|
-
const token = `[NETWORK_IP_${
|
|
70
|
+
const token = `[NETWORK_IP_${tokenHash(match)}]`;
|
|
66
71
|
tokens.set(token, match);
|
|
67
72
|
return token;
|
|
68
73
|
});
|
|
69
74
|
// Redact IP Addresses (v6)
|
|
70
75
|
text = text.replace(Redactor.PATTERNS.IPV6, (match) => {
|
|
71
76
|
hasRedacted = true;
|
|
72
|
-
const token = `[NETWORK_IP_V6_${
|
|
77
|
+
const token = `[NETWORK_IP_V6_${tokenHash(match)}]`;
|
|
73
78
|
tokens.set(token, match);
|
|
74
79
|
return token;
|
|
75
80
|
});
|
|
76
81
|
// Redact Secrets in text (e.g. "api_key=...")
|
|
77
82
|
text = text.replace(Redactor.PATTERNS.SECRET, (match, p1) => {
|
|
78
83
|
hasRedacted = true;
|
|
79
|
-
const token = `[SENSITIVE_SECRET_${
|
|
84
|
+
const token = `[SENSITIVE_SECRET_${tokenHash(p1)}]`;
|
|
80
85
|
tokens.set(token, p1);
|
|
81
86
|
return match.replace(p1, token);
|
|
82
87
|
});
|
|
83
88
|
// Redact Credit Cards
|
|
84
89
|
text = text.replace(Redactor.PATTERNS.CREDIT_CARD, (match) => {
|
|
85
90
|
hasRedacted = true;
|
|
86
|
-
const token = `[PAYMENT_CARD_${
|
|
91
|
+
const token = `[PAYMENT_CARD_${tokenHash(match)}]`;
|
|
87
92
|
tokens.set(token, match);
|
|
88
93
|
return token;
|
|
89
94
|
});
|
|
90
95
|
// Redact Phone Numbers
|
|
91
96
|
text = text.replace(Redactor.PATTERNS.PHONE, (match) => {
|
|
92
97
|
hasRedacted = true;
|
|
93
|
-
const token = `[PHONE_NUMBER_${
|
|
98
|
+
const token = `[PHONE_NUMBER_${tokenHash(match)}]`;
|
|
94
99
|
tokens.set(token, match);
|
|
95
100
|
return token;
|
|
96
101
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"redactor.js","sourceRoot":"","sources":["../../src/middleware/redactor.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"redactor.js","sourceRoot":"","sources":["../../src/middleware/redactor.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"token-vault.d.ts","sourceRoot":"","sources":["../../src/vault/token-vault.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;GAEG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,KAAK,CAAgE;IAC7E,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAE7B;;OAEG;gBACS,KAAK,GAAE,MAAuB;IAI1C;;;;OAIG;IACI,UAAU,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI;IAQpD;;;;;OAKG;IACI,SAAS,CAAC,KAAK,EAAE,GAAG,GAAG,GAAG;IA0CjC;;;OAGG;IACI,yBAAyB,
|
|
1
|
+
{"version":3,"file":"token-vault.d.ts","sourceRoot":"","sources":["../../src/vault/token-vault.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;GAEG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,KAAK,CAAgE;IAC7E,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAE7B;;OAEG;gBACS,KAAK,GAAE,MAAuB;IAI1C;;;;OAIG;IACI,UAAU,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI;IAQpD;;;;;OAKG;IACI,SAAS,CAAC,KAAK,EAAE,GAAG,GAAG,GAAG;IA0CjC;;;OAGG;IACI,yBAAyB,KAsBtB,OAAO,MAAM,KAAG,MAAM;IAwGhC;;OAEG;IACH,OAAO,CAAC,KAAK;IASb;;;;;OAKG;IACI,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAK7C;;OAEG;IACI,KAAK,IAAI,IAAI;IAIpB;;OAEG;IACH,IAAW,IAAI,IAAI,MAAM,CAExB;CACF"}
|
|
@@ -77,28 +77,115 @@ export class TokenVault {
|
|
|
77
77
|
*/
|
|
78
78
|
createStreamingRehydrator() {
|
|
79
79
|
let buffer = '';
|
|
80
|
+
const maxTokenLen = Math.max(...[...this.vault.keys()].map(t => t.length), 0);
|
|
81
|
+
// Accumulate content fields across SSE events to reassemble split tokens
|
|
82
|
+
const contentBuffers = {};
|
|
83
|
+
const CONTENT_FIELDS = ['content', 'reasoning_content', 'partial_json'];
|
|
84
|
+
const rehydrateText = (text) => {
|
|
85
|
+
for (const [token, entry] of this.vault.entries()) {
|
|
86
|
+
text = text.split(token).join(entry.value);
|
|
87
|
+
}
|
|
88
|
+
return text;
|
|
89
|
+
};
|
|
90
|
+
const flushField = (field) => {
|
|
91
|
+
if (!contentBuffers[field])
|
|
92
|
+
return '';
|
|
93
|
+
const result = rehydrateText(contentBuffers[field]);
|
|
94
|
+
contentBuffers[field] = '';
|
|
95
|
+
return result;
|
|
96
|
+
};
|
|
80
97
|
return (chunk) => {
|
|
81
98
|
buffer += chunk;
|
|
82
|
-
//
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
//
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
99
|
+
// Detect if this is an SSE stream (contains "data: " lines)
|
|
100
|
+
const isSSE = buffer.includes('data: ');
|
|
101
|
+
if (!isSSE) {
|
|
102
|
+
// Raw text mode: hold back potential partial tokens
|
|
103
|
+
let holdBack = 0;
|
|
104
|
+
if (maxTokenLen > 0) {
|
|
105
|
+
for (const [token] of this.vault.entries()) {
|
|
106
|
+
for (let prefixLen = 1; prefixLen < token.length; prefixLen++) {
|
|
107
|
+
if (buffer.endsWith(token.substring(0, prefixLen))) {
|
|
108
|
+
holdBack = Math.max(holdBack, prefixLen);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
90
111
|
}
|
|
91
112
|
}
|
|
113
|
+
const releaseEnd = buffer.length - holdBack;
|
|
114
|
+
let text = buffer.substring(0, releaseEnd);
|
|
115
|
+
buffer = buffer.substring(releaseEnd);
|
|
116
|
+
return rehydrateText(text);
|
|
92
117
|
}
|
|
93
|
-
//
|
|
94
|
-
|
|
95
|
-
let
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
text = text.split(token).join(entry.value);
|
|
118
|
+
// SSE mode: parse each data line, accumulate content fields, rehydrate
|
|
119
|
+
// Only hold back the last segment if it doesn't end with \n (incomplete line)
|
|
120
|
+
let processable;
|
|
121
|
+
if (buffer.endsWith('\n')) {
|
|
122
|
+
processable = buffer;
|
|
123
|
+
buffer = '';
|
|
100
124
|
}
|
|
101
|
-
|
|
125
|
+
else {
|
|
126
|
+
const lastNewline = buffer.lastIndexOf('\n');
|
|
127
|
+
if (lastNewline === -1) {
|
|
128
|
+
return ''; // no complete lines yet
|
|
129
|
+
}
|
|
130
|
+
processable = buffer.substring(0, lastNewline + 1);
|
|
131
|
+
buffer = buffer.substring(lastNewline + 1);
|
|
132
|
+
}
|
|
133
|
+
const lines = processable.split('\n');
|
|
134
|
+
// Remove trailing empty string from split (the part after last \n)
|
|
135
|
+
if (lines.length > 0 && lines[lines.length - 1] === '')
|
|
136
|
+
lines.pop();
|
|
137
|
+
const outputLines = [];
|
|
138
|
+
for (const line of lines) {
|
|
139
|
+
if (!line.startsWith('data: ') || line.trim() === 'data: [DONE]') {
|
|
140
|
+
outputLines.push(line);
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
let parsed;
|
|
144
|
+
try {
|
|
145
|
+
parsed = JSON.parse(line.slice(6));
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
outputLines.push(rehydrateText(line));
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
// Look for delta content in OpenAI/ZhipuAI format
|
|
152
|
+
const delta = parsed?.choices?.[0]?.delta;
|
|
153
|
+
// Look for delta content in Anthropic format
|
|
154
|
+
const anthDelta = parsed?.delta;
|
|
155
|
+
const target = delta || anthDelta;
|
|
156
|
+
if (!target) {
|
|
157
|
+
outputLines.push('data: ' + JSON.stringify(this.rehydrate(parsed)));
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
let modified = false;
|
|
161
|
+
for (const field of CONTENT_FIELDS) {
|
|
162
|
+
const textField = field === 'content' ? 'text' : null;
|
|
163
|
+
const actualField = typeof target[field] === 'string' ? field
|
|
164
|
+
: (textField && typeof target[textField] === 'string') ? textField
|
|
165
|
+
: null;
|
|
166
|
+
if (!actualField)
|
|
167
|
+
continue;
|
|
168
|
+
const bufKey = actualField;
|
|
169
|
+
contentBuffers[bufKey] = (contentBuffers[bufKey] || '') + target[actualField];
|
|
170
|
+
const buf = contentBuffers[bufKey];
|
|
171
|
+
const lastBracket = buf.lastIndexOf('[');
|
|
172
|
+
const hasPartialToken = maxTokenLen > 0 && lastBracket >= 0 &&
|
|
173
|
+
!buf.substring(lastBracket).includes(']') &&
|
|
174
|
+
buf.length - lastBracket < maxTokenLen;
|
|
175
|
+
if (hasPartialToken) {
|
|
176
|
+
const safe = buf.substring(0, lastBracket);
|
|
177
|
+
contentBuffers[bufKey] = buf.substring(lastBracket);
|
|
178
|
+
target[actualField] = rehydrateText(safe);
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
target[actualField] = flushField(bufKey);
|
|
182
|
+
}
|
|
183
|
+
modified = true;
|
|
184
|
+
}
|
|
185
|
+
// Re-serialize with original SSE framing
|
|
186
|
+
outputLines.push('data: ' + JSON.stringify(parsed));
|
|
187
|
+
}
|
|
188
|
+
return outputLines.join('\n') + '\n';
|
|
102
189
|
};
|
|
103
190
|
}
|
|
104
191
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"token-vault.js","sourceRoot":"","sources":["../../src/vault/token-vault.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;GAEG;AACH,MAAM,OAAO,UAAU;IACb,KAAK,GAAsD,IAAI,GAAG,EAAE,CAAC;IAC5D,GAAG,CAAS;IAE7B;;OAEG;IACH,YAAY,QAAgB,EAAE,GAAG,EAAE,GAAG,IAAI;QACxC,IAAI,CAAC,GAAG,GAAG,KAAK,CAAC;IACnB,CAAC;IAED;;;;OAIG;IACI,UAAU,CAAC,MAA2B;QAC3C,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,KAAK,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,MAAM,EAAE,CAAC;YACpC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC;QACnD,CAAC;QACD,IAAI,CAAC,KAAK,EAAE,CAAC;IACf,CAAC;IAED;;;;;OAKG;IACI,SAAS,CAAC,KAAU;QACzB,qCAAqC;QACrC,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;QAExC,wEAAwE;QACxE,IAAI,MAAW,CAAC;QAChB,IAAI,CAAC;YACH,MAAM,GAAG,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;gBAClD,CAAC,CAAC,eAAe,CAAC,KAAK,CAAC;gBACxB,CAAC,CAAC,KAAK,CAAC;QACZ,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,MAAM,GAAG,KAAK,CAAC;QACjB,CAAC;QAED,MAAM,OAAO,GAAG,CAAC,IAAS,EAAO,EAAE;YACjC,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;gBAC7B,IAAI,IAAI,GAAG,IAAI,CAAC;gBAChB,iCAAiC;gBACjC,KAAK,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;oBAClD,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;gBAC7C,CAAC;gBACD,OAAO,IAAI,CAAC;YACd,CAAC;YAED,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;gBACxB,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;YACzC,CAAC;YAED,IAAI,IAAI,KAAK,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;gBAC9C,MAAM,GAAG,GAAQ,EAAE,CAAC;gBACpB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;oBAChD,GAAG,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;gBAC5B,CAAC;gBACD,OAAO,GAAG,CAAC;YACb,CAAC;YAED,OAAO,IAAI,CAAC;QACd,CAAC,CAAC;QAEF,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC;IACzB,CAAC;IAED;;;OAGG;IACI,yBAAyB;QAC9B,IAAI,MAAM,GAAG,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"token-vault.js","sourceRoot":"","sources":["../../src/vault/token-vault.ts"],"names":[],"mappings":"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"}
|
package/install.sh
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# hush 🛡️ - One-line installer
|
|
4
|
+
# Usage: curl -fsSL https://raw.githubusercontent.com/aictrl-dev/hush/master/install.sh | sh
|
|
5
|
+
|
|
6
|
+
set -e
|
|
7
|
+
|
|
8
|
+
# Colors
|
|
9
|
+
GREEN='\033[0;32m'
|
|
10
|
+
BLUE='\033[0;34m'
|
|
11
|
+
NC='\033[0m' # No Color
|
|
12
|
+
|
|
13
|
+
echo -e "${BLUE}Installing hush 🛡️ - The Semantic Security Gateway...${NC}"
|
|
14
|
+
|
|
15
|
+
# Check for Node.js
|
|
16
|
+
if ! [ -x "$(command -v node)" ]; then
|
|
17
|
+
echo 'Error: Node.js is not installed. Please install Node.js 18+ first.' >&2
|
|
18
|
+
exit 1
|
|
19
|
+
fi
|
|
20
|
+
|
|
21
|
+
# Check for npm
|
|
22
|
+
if ! [ -x "$(command -v npm)" ]; then
|
|
23
|
+
echo 'Error: npm is not installed.' >&2
|
|
24
|
+
exit 1
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
# Install hush
|
|
28
|
+
echo "Running: npm install -g @aictrl/hush"
|
|
29
|
+
npm install -g @aictrl/hush --silent
|
|
30
|
+
|
|
31
|
+
echo -e "${GREEN}Successfully installed hush!${NC}"
|
|
32
|
+
echo ""
|
|
33
|
+
echo "To get started:"
|
|
34
|
+
echo " 1. Run 'hush --dashboard' to start the gateway."
|
|
35
|
+
echo " 2. Point your tools to http://127.0.0.1:4000"
|
|
36
|
+
echo ""
|
|
37
|
+
echo "Documentation: https://github.com/aictrl-dev/hush"
|
package/logo.svg
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<svg width="600" height="200" viewBox="0 0 600 200" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<defs>
|
|
3
|
+
<style>
|
|
4
|
+
@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@700&display=swap');
|
|
5
|
+
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@500&display=swap');
|
|
6
|
+
.mono-text { font-family: 'JetBrains Mono', monospace; }
|
|
7
|
+
.sans-text { font-family: 'Inter', sans-serif; }
|
|
8
|
+
</style>
|
|
9
|
+
</defs>
|
|
10
|
+
|
|
11
|
+
<!-- ================= LOGO MARK ================= -->
|
|
12
|
+
<g transform="translate(60, 50)">
|
|
13
|
+
<path d="M 25 0 L 0 0 L 0 100 L 25 100" stroke="#a1a1aa" stroke-width="4" stroke-linecap="square" stroke-linejoin="miter" fill="none" />
|
|
14
|
+
<path d="M 75 0 L 100 0 L 100 100 L 75 100" stroke="#a1a1aa" stroke-width="4" stroke-linecap="square" stroke-linejoin="miter" fill="none" />
|
|
15
|
+
<path d="M 20 15 C 20 10, 80 10, 80 15 L 80 60 C 80 85, 50 100, 50 100 C 50 100, 20 85, 20 60 Z" fill="#18181b" />
|
|
16
|
+
<g transform="translate(50, 48) scale(0.9)">
|
|
17
|
+
<path d="M 0 -18 L 0 18 M -15.5 -9 L 15.5 9 M -15.5 9 L 15.5 -9" stroke="#fafafa" stroke-width="6" stroke-linecap="round" />
|
|
18
|
+
</g>
|
|
19
|
+
</g>
|
|
20
|
+
|
|
21
|
+
<!-- ================= TYPOGRAPHY ================= -->
|
|
22
|
+
<text x="190" y="110" class="mono-text" font-size="82" font-weight="700" fill="#18181b" letter-spacing="-0.03em">hush</text>
|
|
23
|
+
<text x="195" y="145" class="sans-text" font-size="14" font-weight="500" fill="#52525b" letter-spacing="0.3em">semantic security gateway</text>
|
|
24
|
+
|
|
25
|
+
<!-- Brand Signature: aictrl.dev -->
|
|
26
|
+
<g transform="translate(430, 160)">
|
|
27
|
+
<text class="mono-text" font-size="10" font-weight="700">
|
|
28
|
+
<tspan fill="#71717a">ai</tspan><tspan fill="#18181b">ctrl</tspan><tspan fill="#a1a1aa">.dev</tspan>
|
|
29
|
+
</text>
|
|
30
|
+
</g>
|
|
31
|
+
</svg>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aictrl/hush",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "Hush: A Semantic Security Gateway for AI Agents. Redacts PII from prompts and tool outputs locally before they hit the cloud.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -8,9 +8,6 @@
|
|
|
8
8
|
"bin": {
|
|
9
9
|
"hush": "dist/cli.js"
|
|
10
10
|
},
|
|
11
|
-
"files": [
|
|
12
|
-
"dist"
|
|
13
|
-
],
|
|
14
11
|
"scripts": {
|
|
15
12
|
"build": "tsc",
|
|
16
13
|
"start": "node dist/cli.js",
|
|
@@ -29,6 +26,10 @@
|
|
|
29
26
|
"pii",
|
|
30
27
|
"gateway"
|
|
31
28
|
],
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "https://github.com/aictrl-dev/hush"
|
|
32
|
+
},
|
|
32
33
|
"author": "AICtrl Team",
|
|
33
34
|
"license": "Apache-2.0",
|
|
34
35
|
"dependencies": {
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* E2E test harness: Hush gateway that proxies to mock upstream.
|
|
3
|
+
* Replicates the real gateway's redact -> forward -> rehydrate flow
|
|
4
|
+
* but points at a local mock instead of api.z.ai.
|
|
5
|
+
*/
|
|
6
|
+
import express from 'express';
|
|
7
|
+
import { Redactor } from '../src/middleware/redactor.js';
|
|
8
|
+
import { TokenVault } from '../src/vault/token-vault.js';
|
|
9
|
+
|
|
10
|
+
const redactor = new Redactor();
|
|
11
|
+
const vault = new TokenVault();
|
|
12
|
+
|
|
13
|
+
const app = express();
|
|
14
|
+
app.use(express.json({ limit: '50mb' }));
|
|
15
|
+
|
|
16
|
+
const GATEWAY_PORT = parseInt(process.env.GATEWAY_PORT || '4000');
|
|
17
|
+
const MOCK_PORT = parseInt(process.env.MOCK_PORT || '4111');
|
|
18
|
+
const MOCK_UPSTREAM = `http://127.0.0.1:${MOCK_PORT}/api/paas/v4/chat/completions`;
|
|
19
|
+
|
|
20
|
+
// ZhipuAI GLM route (same as real gateway, but targeting mock upstream)
|
|
21
|
+
app.post('/api/paas/v4/chat/completions', async (req, res) => {
|
|
22
|
+
const auth = req.headers['authorization'];
|
|
23
|
+
if (!auth) return res.status(401).json({ error: 'Missing Authorization' });
|
|
24
|
+
|
|
25
|
+
// 1. Redact
|
|
26
|
+
const { content: redactedBody, tokens, hasRedacted } = redactor.redact(req.body);
|
|
27
|
+
if (hasRedacted) {
|
|
28
|
+
vault.saveTokens(tokens);
|
|
29
|
+
console.log(`[E2E] Redacted ${tokens.size} PII token(s)`);
|
|
30
|
+
for (const [token] of tokens) {
|
|
31
|
+
console.log(`[E2E] ${token}`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
// 2. Forward to mock upstream
|
|
37
|
+
const response = await fetch(MOCK_UPSTREAM, {
|
|
38
|
+
method: 'POST',
|
|
39
|
+
headers: { 'Content-Type': 'application/json', 'Authorization': auth as string },
|
|
40
|
+
body: JSON.stringify(redactedBody),
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const data = await response.json();
|
|
44
|
+
|
|
45
|
+
// 3. Rehydrate
|
|
46
|
+
const rehydrated = vault.rehydrate(data);
|
|
47
|
+
res.json(rehydrated);
|
|
48
|
+
} catch (error) {
|
|
49
|
+
console.error('[E2E] Forward failed:', error);
|
|
50
|
+
res.status(500).json({ error: 'E2E gateway forward failed' });
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// Health endpoint exposes vault size
|
|
55
|
+
app.get('/health', (_req, res) => {
|
|
56
|
+
res.json({ status: 'running', vaultSize: vault.size });
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
app.listen(GATEWAY_PORT, '127.0.0.1', () => {
|
|
60
|
+
console.log(`E2E Hush Gateway listening on http://127.0.0.1:${GATEWAY_PORT}`);
|
|
61
|
+
console.log(` Upstream: ${MOCK_UPSTREAM}`);
|
|
62
|
+
});
|