@agent-link/server 0.1.355 → 0.1.356
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/encryption.js +13 -0
- package/dist/encryption.js.map +1 -1
- package/dist/json-worker.d.ts +1 -0
- package/dist/json-worker.js +96 -0
- package/dist/json-worker.js.map +1 -0
- package/dist/worker-pool.d.ts +14 -0
- package/dist/worker-pool.js +137 -0
- package/dist/worker-pool.js.map +1 -0
- package/package.json +1 -1
package/dist/encryption.js
CHANGED
|
@@ -5,6 +5,7 @@ import { promisify } from 'util';
|
|
|
5
5
|
const gzip = promisify(zlib.gzip);
|
|
6
6
|
const gunzip = promisify(zlib.gunzip);
|
|
7
7
|
const { encodeBase64, decodeBase64, encodeUTF8, decodeUTF8 } = tweetnaclUtil;
|
|
8
|
+
import { isLargeMessage, isLargeOutbound, workerParseMessage, workerEncryptAndSend } from './worker-pool.js';
|
|
8
9
|
const COMPRESS_THRESHOLD = 512;
|
|
9
10
|
export function generateSessionKey() {
|
|
10
11
|
return tweetnacl.randomBytes(32);
|
|
@@ -60,6 +61,13 @@ export function decodeKey(encodedKey) {
|
|
|
60
61
|
return decodeBase64(encodedKey);
|
|
61
62
|
}
|
|
62
63
|
export async function parseMessage(data, sessionKey) {
|
|
64
|
+
// Offload large messages to Worker thread to avoid blocking the event loop
|
|
65
|
+
if (isLargeMessage(data)) {
|
|
66
|
+
const result = await workerParseMessage(data, sessionKey);
|
|
67
|
+
if (result !== null)
|
|
68
|
+
return result;
|
|
69
|
+
// Worker returned null — could be parse error or worker failure; fall through to inline
|
|
70
|
+
}
|
|
63
71
|
try {
|
|
64
72
|
const parsed = JSON.parse(data);
|
|
65
73
|
if (sessionKey && isEncrypted(parsed)) {
|
|
@@ -72,6 +80,11 @@ export async function parseMessage(data, sessionKey) {
|
|
|
72
80
|
}
|
|
73
81
|
}
|
|
74
82
|
export async function encryptAndSend(ws, msg, sessionKey) {
|
|
83
|
+
// Offload large messages to Worker thread
|
|
84
|
+
if (isLargeOutbound(msg)) {
|
|
85
|
+
await workerEncryptAndSend(ws, msg, sessionKey);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
75
88
|
try {
|
|
76
89
|
if (sessionKey) {
|
|
77
90
|
const encrypted = await encrypt(msg, sessionKey);
|
package/dist/encryption.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"encryption.js","sourceRoot":"","sources":["../src/encryption.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"encryption.js","sourceRoot":"","sources":["../src/encryption.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// Worker thread for offloading large JSON parse/stringify + encryption
|
|
2
|
+
// This runs in a separate thread so large messages don't block the event loop.
|
|
3
|
+
import { parentPort } from 'worker_threads';
|
|
4
|
+
import tweetnacl from 'tweetnacl';
|
|
5
|
+
import tweetnaclUtil from 'tweetnacl-util';
|
|
6
|
+
import zlib from 'zlib';
|
|
7
|
+
import { promisify } from 'util';
|
|
8
|
+
const gzip = promisify(zlib.gzip);
|
|
9
|
+
const gunzip = promisify(zlib.gunzip);
|
|
10
|
+
const { encodeBase64, decodeBase64, encodeUTF8, decodeUTF8 } = tweetnaclUtil;
|
|
11
|
+
const COMPRESS_THRESHOLD = 512;
|
|
12
|
+
function isEncrypted(msg) {
|
|
13
|
+
return msg !== null && typeof msg === 'object'
|
|
14
|
+
&& typeof msg.n === 'string'
|
|
15
|
+
&& typeof msg.c === 'string';
|
|
16
|
+
}
|
|
17
|
+
async function decrypt(encrypted, key) {
|
|
18
|
+
try {
|
|
19
|
+
const nonce = decodeBase64(encrypted.n);
|
|
20
|
+
const ciphertext = decodeBase64(encrypted.c);
|
|
21
|
+
const decrypted = tweetnacl.secretbox.open(ciphertext, nonce, key);
|
|
22
|
+
if (!decrypted)
|
|
23
|
+
return null;
|
|
24
|
+
if (encrypted.z) {
|
|
25
|
+
const decompressed = await gunzip(Buffer.from(decrypted));
|
|
26
|
+
return JSON.parse(decompressed.toString('utf8'));
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
return JSON.parse(encodeUTF8(decrypted));
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
async function encrypt(data, key) {
|
|
37
|
+
const nonce = tweetnacl.randomBytes(24);
|
|
38
|
+
const jsonStr = JSON.stringify(data);
|
|
39
|
+
let message;
|
|
40
|
+
let compressed = false;
|
|
41
|
+
if (jsonStr.length > COMPRESS_THRESHOLD) {
|
|
42
|
+
const compressedBuf = await gzip(Buffer.from(jsonStr, 'utf8'));
|
|
43
|
+
message = new Uint8Array(compressedBuf);
|
|
44
|
+
compressed = true;
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
message = decodeUTF8(jsonStr);
|
|
48
|
+
}
|
|
49
|
+
const encrypted = tweetnacl.secretbox(message, nonce, key);
|
|
50
|
+
const result = {
|
|
51
|
+
n: encodeBase64(nonce),
|
|
52
|
+
c: encodeBase64(encrypted),
|
|
53
|
+
};
|
|
54
|
+
if (compressed)
|
|
55
|
+
result.z = true;
|
|
56
|
+
return result;
|
|
57
|
+
}
|
|
58
|
+
// Message handlers
|
|
59
|
+
async function handleParse(raw, key) {
|
|
60
|
+
try {
|
|
61
|
+
const parsed = JSON.parse(raw);
|
|
62
|
+
if (key && isEncrypted(parsed)) {
|
|
63
|
+
return await decrypt(parsed, key);
|
|
64
|
+
}
|
|
65
|
+
return parsed;
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
async function handleSerialize(msg, key) {
|
|
72
|
+
if (key) {
|
|
73
|
+
const encrypted = await encrypt(msg, key);
|
|
74
|
+
return JSON.stringify(encrypted);
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
return JSON.stringify(msg);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
parentPort.on('message', async (task) => {
|
|
81
|
+
try {
|
|
82
|
+
const key = task.key ? decodeBase64(task.key) : null;
|
|
83
|
+
if (task.op === 'parse') {
|
|
84
|
+
const result = await handleParse(task.data, key);
|
|
85
|
+
parentPort.postMessage({ id: task.id, result, error: null });
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
const serialized = await handleSerialize(task.data, key);
|
|
89
|
+
parentPort.postMessage({ id: task.id, result: serialized, error: null });
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
parentPort.postMessage({ id: task.id, result: null, error: err.message });
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
//# sourceMappingURL=json-worker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json-worker.js","sourceRoot":"","sources":["../src/json-worker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Check if a raw inbound message is large enough to offload to Worker. */
|
|
2
|
+
export declare function isLargeMessage(raw: string): boolean;
|
|
3
|
+
/** Check if an outbound message object is large enough to offload.
|
|
4
|
+
* Avoids JSON.stringify by checking known large-payload shapes (file attachments). */
|
|
5
|
+
export declare function isLargeOutbound(msg: unknown): boolean;
|
|
6
|
+
/** Parse a large message in a Worker thread. Falls back to inline on failure. */
|
|
7
|
+
export declare function workerParseMessage(raw: string, sessionKey: Uint8Array | null): Promise<Record<string, unknown> | null>;
|
|
8
|
+
/** Serialize+encrypt a large message in a Worker thread, then send via ws. */
|
|
9
|
+
export declare function workerEncryptAndSend(ws: {
|
|
10
|
+
send: (data: string) => void;
|
|
11
|
+
readyState: number;
|
|
12
|
+
}, msg: unknown, sessionKey: Uint8Array | null): Promise<void>;
|
|
13
|
+
/** Shut down all workers. Call on server shutdown. */
|
|
14
|
+
export declare function terminateWorkerPool(): Promise<void>;
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
// Worker pool for offloading large message JSON parse/stringify + encryption.
|
|
2
|
+
// Pre-spawns a small number of Workers and dispatches tasks round-robin.
|
|
3
|
+
import { Worker } from 'worker_threads';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
import { dirname, join } from 'path';
|
|
6
|
+
import tweetnaclUtil from 'tweetnacl-util';
|
|
7
|
+
const { encodeBase64 } = tweetnaclUtil;
|
|
8
|
+
const POOL_SIZE = 2;
|
|
9
|
+
const LARGE_MESSAGE_THRESHOLD = 1_000_000; // 1MB
|
|
10
|
+
let pool = null;
|
|
11
|
+
let nextId = 0;
|
|
12
|
+
let nextWorkerIdx = 0;
|
|
13
|
+
function getWorkerPath() {
|
|
14
|
+
const thisFile = fileURLToPath(import.meta.url);
|
|
15
|
+
const thisDir = dirname(thisFile);
|
|
16
|
+
// In dev/test, import.meta.url points to src/*.ts — resolve to dist/*.js
|
|
17
|
+
if (thisFile.endsWith('.ts')) {
|
|
18
|
+
return join(thisDir, '..', 'dist', 'json-worker.js');
|
|
19
|
+
}
|
|
20
|
+
return join(thisDir, 'json-worker.js');
|
|
21
|
+
}
|
|
22
|
+
function ensurePool() {
|
|
23
|
+
if (pool)
|
|
24
|
+
return pool;
|
|
25
|
+
pool = [];
|
|
26
|
+
const workerPath = getWorkerPath();
|
|
27
|
+
for (let i = 0; i < POOL_SIZE; i++) {
|
|
28
|
+
const worker = new Worker(workerPath);
|
|
29
|
+
const pw = { worker, pending: new Map(), busy: 0 };
|
|
30
|
+
worker.on('message', (msg) => {
|
|
31
|
+
const task = pw.pending.get(msg.id);
|
|
32
|
+
if (!task)
|
|
33
|
+
return;
|
|
34
|
+
pw.pending.delete(msg.id);
|
|
35
|
+
pw.busy--;
|
|
36
|
+
if (msg.error) {
|
|
37
|
+
task.reject(new Error(msg.error));
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
task.resolve(msg.result);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
worker.on('error', (err) => {
|
|
44
|
+
// Reject all pending tasks
|
|
45
|
+
for (const [, task] of pw.pending) {
|
|
46
|
+
task.reject(err instanceof Error ? err : new Error(String(err)));
|
|
47
|
+
}
|
|
48
|
+
pw.pending.clear();
|
|
49
|
+
pw.busy = 0;
|
|
50
|
+
});
|
|
51
|
+
pool.push(pw);
|
|
52
|
+
}
|
|
53
|
+
return pool;
|
|
54
|
+
}
|
|
55
|
+
function pickWorker() {
|
|
56
|
+
const workers = ensurePool();
|
|
57
|
+
// Round-robin, skip if too many pending (>10)
|
|
58
|
+
for (let i = 0; i < workers.length; i++) {
|
|
59
|
+
const idx = (nextWorkerIdx + i) % workers.length;
|
|
60
|
+
if (workers[idx].busy < 10) {
|
|
61
|
+
nextWorkerIdx = (idx + 1) % workers.length;
|
|
62
|
+
return workers[idx];
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return null; // All workers busy — caller should fall back to inline
|
|
66
|
+
}
|
|
67
|
+
function dispatch(op, data, key) {
|
|
68
|
+
const pw = pickWorker();
|
|
69
|
+
if (!pw)
|
|
70
|
+
return Promise.reject(new Error('All workers busy'));
|
|
71
|
+
const id = nextId++;
|
|
72
|
+
const keyStr = key ? encodeBase64(key) : null;
|
|
73
|
+
return new Promise((resolve, reject) => {
|
|
74
|
+
pw.pending.set(id, { resolve, reject });
|
|
75
|
+
pw.busy++;
|
|
76
|
+
pw.worker.postMessage({ id, op, data, key: keyStr });
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
/** Check if a raw inbound message is large enough to offload to Worker. */
|
|
80
|
+
export function isLargeMessage(raw) {
|
|
81
|
+
return raw.length > LARGE_MESSAGE_THRESHOLD;
|
|
82
|
+
}
|
|
83
|
+
/** Check if an outbound message object is large enough to offload.
|
|
84
|
+
* Avoids JSON.stringify by checking known large-payload shapes (file attachments). */
|
|
85
|
+
export function isLargeOutbound(msg) {
|
|
86
|
+
if (!msg || typeof msg !== 'object')
|
|
87
|
+
return false;
|
|
88
|
+
const m = msg;
|
|
89
|
+
// Chat messages with file attachments
|
|
90
|
+
if (Array.isArray(m.files)) {
|
|
91
|
+
for (const f of m.files) {
|
|
92
|
+
if (f && typeof f === 'object' && typeof f.data === 'string') {
|
|
93
|
+
if (f.data.length > LARGE_MESSAGE_THRESHOLD)
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
// Generic large data/content fields
|
|
99
|
+
if (typeof m.data === 'string' && m.data.length > LARGE_MESSAGE_THRESHOLD)
|
|
100
|
+
return true;
|
|
101
|
+
if (typeof m.content === 'string' && m.content.length > LARGE_MESSAGE_THRESHOLD)
|
|
102
|
+
return true;
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
/** Parse a large message in a Worker thread. Falls back to inline on failure. */
|
|
106
|
+
export async function workerParseMessage(raw, sessionKey) {
|
|
107
|
+
try {
|
|
108
|
+
return await dispatch('parse', raw, sessionKey);
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
// Fallback: parse inline
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/** Serialize+encrypt a large message in a Worker thread, then send via ws. */
|
|
116
|
+
export async function workerEncryptAndSend(ws, msg, sessionKey) {
|
|
117
|
+
try {
|
|
118
|
+
const serialized = await dispatch('serialize', msg, sessionKey);
|
|
119
|
+
if (ws.readyState === 1) {
|
|
120
|
+
ws.send(serialized);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
catch (err) {
|
|
124
|
+
console.error('[workerEncryptAndSend] Worker failed, falling back to inline:', err.message);
|
|
125
|
+
// Fallback: do it inline
|
|
126
|
+
const { encryptAndSend } = await import('./encryption.js');
|
|
127
|
+
await encryptAndSend(ws, msg, sessionKey);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/** Shut down all workers. Call on server shutdown. */
|
|
131
|
+
export async function terminateWorkerPool() {
|
|
132
|
+
if (!pool)
|
|
133
|
+
return;
|
|
134
|
+
await Promise.all(pool.map(pw => pw.worker.terminate()));
|
|
135
|
+
pool = null;
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=worker-pool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker-pool.js","sourceRoot":"","sources":["../src/worker-pool.ts"],"names":[],"mappings":"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"}
|