agent-nuvira 1.95.0 → 1.97.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.
- package/dist/web-dashboard/server.d.ts.map +1 -1
- package/dist/web-dashboard/server.js +181 -0
- package/dist/web-dashboard/server.js.map +1 -1
- package/package.json +1 -1
- package/src/web-dashboard/public/assets/index-B47v8pr1.css +1 -0
- package/src/web-dashboard/public/assets/index-woI2W4xZ.js +207 -0
- package/src/web-dashboard/public/assets/index-woI2W4xZ.js.map +1 -0
- package/src/web-dashboard/public/index.html +2 -2
- package/src/web-dashboard/public/assets/index-BODJHuQY.css +0 -1
- package/src/web-dashboard/public/assets/index-BqXGfwKH.js +0 -178
- package/src/web-dashboard/public/assets/index-BqXGfwKH.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/web-dashboard/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,YAAY,EAAmC,MAAM,WAAW,CAAC;AA6B1E,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,WAAW,EAAoB,MAAM,mBAAmB,CAAC;AA2JlE,4DAA4D;AAC5D,wBAAgB,mBAAmB,IAAI,OAAO,CAE7C;AAED,4FAA4F;AAC5F,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAOxD;AAsHD,6DAA6D;AAC7D,wBAAgB,eAAe,CAAC,KAAK,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,CAKrG;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/web-dashboard/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -320,6 +320,105 @@ export function broadcastTyping(event) {
|
|
|
320
320
|
catch { /* client disconnected */ }
|
|
321
321
|
}
|
|
322
322
|
}
|
|
323
|
+
// ─── ZIP Generation (no external dependencies) ─────────────────────────────
|
|
324
|
+
/** CRC-32 lookup table (precomputed). */
|
|
325
|
+
const CRC_TABLE = new Uint32Array(256);
|
|
326
|
+
for (let i = 0; i < 256; i++) {
|
|
327
|
+
let c = i;
|
|
328
|
+
for (let j = 0; j < 8; j++) {
|
|
329
|
+
c = (c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1);
|
|
330
|
+
}
|
|
331
|
+
CRC_TABLE[i] = c;
|
|
332
|
+
}
|
|
333
|
+
function crc32(buf) {
|
|
334
|
+
let crc = 0xFFFFFFFF;
|
|
335
|
+
for (let i = 0; i < buf.length; i++) {
|
|
336
|
+
crc = CRC_TABLE[(crc ^ buf[i]) & 0xFF] ^ (crc >>> 8);
|
|
337
|
+
}
|
|
338
|
+
return (crc ^ 0xFFFFFFFF) >>> 0;
|
|
339
|
+
}
|
|
340
|
+
/** DOS date/time format for ZIP headers. */
|
|
341
|
+
function dosDateTime(date) {
|
|
342
|
+
const time = ((date.getHours() & 0x1F) << 11) | ((date.getMinutes() & 0x3F) << 5) | ((date.getSeconds() >> 1) & 0x1F);
|
|
343
|
+
const d = ((date.getFullYear() - 1980) & 0x7F) << 9 | ((date.getMonth() + 1) & 0xF) << 5 | (date.getDate() & 0x1F);
|
|
344
|
+
return { time, date: d };
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Generate a ZIP file from an array of { name, content } entries.
|
|
348
|
+
* Supports text files only (no compression — stored method).
|
|
349
|
+
* Returns a Buffer suitable for HTTP response.
|
|
350
|
+
*/
|
|
351
|
+
function generateZip(files) {
|
|
352
|
+
const now = new Date();
|
|
353
|
+
const { time, date } = dosDateTime(now);
|
|
354
|
+
const entries = [];
|
|
355
|
+
const parts = [];
|
|
356
|
+
let offset = 0;
|
|
357
|
+
// Local file headers + file data.
|
|
358
|
+
for (const file of files) {
|
|
359
|
+
const nameBytes = new TextEncoder().encode(file.name);
|
|
360
|
+
const dataBytes = new TextEncoder().encode(file.content);
|
|
361
|
+
const crc = crc32(dataBytes);
|
|
362
|
+
// Local file header (30 + nameLen).
|
|
363
|
+
const header = new ArrayBuffer(30 + nameBytes.length);
|
|
364
|
+
const hv = new DataView(header);
|
|
365
|
+
hv.setUint32(0, 0x04034b50, true); // signature
|
|
366
|
+
hv.setUint16(4, 20, true); // version needed
|
|
367
|
+
hv.setUint16(6, 0, true); // flags
|
|
368
|
+
hv.setUint16(8, 0, true); // compression method (stored)
|
|
369
|
+
hv.setUint16(10, time, true); // mod time
|
|
370
|
+
hv.setUint16(12, date, true); // mod date
|
|
371
|
+
hv.setUint32(14, crc, true); // crc-32
|
|
372
|
+
hv.setUint32(18, dataBytes.length, true); // compressed size
|
|
373
|
+
hv.setUint32(22, dataBytes.length, true); // uncompressed size
|
|
374
|
+
hv.setUint16(26, nameBytes.length, true); // name length
|
|
375
|
+
hv.setUint16(28, 0, true); // extra field length
|
|
376
|
+
new Uint8Array(header).set(nameBytes, 30);
|
|
377
|
+
entries.push({ name: nameBytes, data: dataBytes, crc, offset });
|
|
378
|
+
parts.push(new Uint8Array(header), dataBytes);
|
|
379
|
+
offset += header.byteLength + dataBytes.length;
|
|
380
|
+
}
|
|
381
|
+
// Central directory.
|
|
382
|
+
const centralStart = offset;
|
|
383
|
+
for (const entry of entries) {
|
|
384
|
+
const ch = new ArrayBuffer(46 + entry.name.length);
|
|
385
|
+
const cv = new DataView(ch);
|
|
386
|
+
cv.setUint32(0, 0x02014b50, true); // signature
|
|
387
|
+
cv.setUint16(4, 20, true); // version made by
|
|
388
|
+
cv.setUint16(6, 20, true); // version needed
|
|
389
|
+
cv.setUint16(8, 0, true); // flags
|
|
390
|
+
cv.setUint16(10, 0, true); // compression (stored)
|
|
391
|
+
cv.setUint16(12, time, true); // mod time
|
|
392
|
+
cv.setUint16(14, date, true); // mod date
|
|
393
|
+
cv.setUint32(16, entry.crc, true); // crc-32
|
|
394
|
+
cv.setUint32(20, entry.data.length, true); // compressed size
|
|
395
|
+
cv.setUint32(24, entry.data.length, true); // uncompressed size
|
|
396
|
+
cv.setUint16(28, entry.name.length, true); // name length
|
|
397
|
+
cv.setUint16(30, 0, true); // extra field length
|
|
398
|
+
cv.setUint16(32, 0, true); // file comment length
|
|
399
|
+
cv.setUint16(34, 0, true); // disk number start
|
|
400
|
+
cv.setUint16(36, 0, true); // internal file attributes
|
|
401
|
+
cv.setUint32(38, 0, true); // external file attributes
|
|
402
|
+
cv.setUint32(42, entry.offset, true); // local header offset
|
|
403
|
+
new Uint8Array(ch).set(entry.name, 46);
|
|
404
|
+
parts.push(new Uint8Array(ch));
|
|
405
|
+
offset += ch.byteLength;
|
|
406
|
+
}
|
|
407
|
+
// End of central directory.
|
|
408
|
+
const centralSize = offset - centralStart;
|
|
409
|
+
const eocd = new ArrayBuffer(22);
|
|
410
|
+
const ev = new DataView(eocd);
|
|
411
|
+
ev.setUint32(0, 0x06054b50, true); // signature
|
|
412
|
+
ev.setUint16(4, 0, true); // disk number
|
|
413
|
+
ev.setUint16(6, 0, true); // central dir disk
|
|
414
|
+
ev.setUint16(8, entries.length, true); // entries on this disk
|
|
415
|
+
ev.setUint16(10, entries.length, true); // total entries
|
|
416
|
+
ev.setUint32(12, centralSize, true); // central dir size
|
|
417
|
+
ev.setUint32(16, centralStart, true); // central dir offset
|
|
418
|
+
ev.setUint16(20, 0, true); // comment length
|
|
419
|
+
parts.push(new Uint8Array(eocd));
|
|
420
|
+
return Buffer.concat(parts.map((p) => Buffer.from(p.buffer, p.byteOffset, p.byteLength)));
|
|
421
|
+
}
|
|
323
422
|
let activePipeline = null; // goal/description of current pipeline
|
|
324
423
|
let activeNodes = [];
|
|
325
424
|
let activeEdges = [];
|
|
@@ -2775,6 +2874,88 @@ function handleRequest(req, res) {
|
|
|
2775
2874
|
// dashboard passes the FULL map it read; the running gateway re-reads
|
|
2776
2875
|
// config per inbound, so changes apply without a restart). Named
|
|
2777
2876
|
// WhatsApp contacts are also synced into the bridge contacts file
|
|
2877
|
+
// POST /api/admin/gateway/conversations/export — bulk export as ZIP.
|
|
2878
|
+
// Rides on gateway.manage (admin + operator).
|
|
2879
|
+
if (pathname === '/api/admin/gateway/conversations/export' && req.method === 'POST') {
|
|
2880
|
+
void (async () => {
|
|
2881
|
+
const session = adminSessions.validate(bearerToken(req));
|
|
2882
|
+
if (!session) {
|
|
2883
|
+
writeJson(res, 401, { ok: false, error: 'Not authenticated — log in first.' });
|
|
2884
|
+
return;
|
|
2885
|
+
}
|
|
2886
|
+
if (!roleCan(session.role, 'gateway.manage')) {
|
|
2887
|
+
writeJson(res, 403, { ok: false, error: `Access denied — role '${session.role}' cannot export conversations.` });
|
|
2888
|
+
return;
|
|
2889
|
+
}
|
|
2890
|
+
const body = await readJsonBody(req);
|
|
2891
|
+
const keys = body?.keys;
|
|
2892
|
+
if (!Array.isArray(keys) || keys.length === 0) {
|
|
2893
|
+
writeJson(res, 400, { ok: false, error: 'Missing or invalid "keys" array.' });
|
|
2894
|
+
return;
|
|
2895
|
+
}
|
|
2896
|
+
try {
|
|
2897
|
+
const { GatewayChatStore, CHAT_HISTORY_TTL_MS } = await import('../gateway/chat-store.js');
|
|
2898
|
+
const store = new GatewayChatStore();
|
|
2899
|
+
const allConvs = store.getAllConversations();
|
|
2900
|
+
// Resolve contact names.
|
|
2901
|
+
let contactLookup = {};
|
|
2902
|
+
try {
|
|
2903
|
+
const { readContactsFile } = await import('../gateway/whatsapp/contacts.js');
|
|
2904
|
+
const { whatsappSessionDir } = await import('../gateway/whatsapp/session.js');
|
|
2905
|
+
const contacts = readContactsFile(whatsappSessionDir());
|
|
2906
|
+
for (const [name, digits] of Object.entries(contacts)) {
|
|
2907
|
+
if (name && digits)
|
|
2908
|
+
contactLookup[digits] = name;
|
|
2909
|
+
}
|
|
2910
|
+
}
|
|
2911
|
+
catch { /* best-effort */ }
|
|
2912
|
+
const keySet = new Set(keys);
|
|
2913
|
+
const convs = allConvs.filter((c) => keySet.has(c.key));
|
|
2914
|
+
if (convs.length === 0) {
|
|
2915
|
+
writeJson(res, 404, { ok: false, error: 'No matching conversations found.' });
|
|
2916
|
+
return;
|
|
2917
|
+
}
|
|
2918
|
+
// Build ZIP entries (manual ZIP64-compatible for text files).
|
|
2919
|
+
const files = [];
|
|
2920
|
+
const now = new Date();
|
|
2921
|
+
const dateStr = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`;
|
|
2922
|
+
for (const conv of convs) {
|
|
2923
|
+
const [platform, ...rest] = conv.key.split(':');
|
|
2924
|
+
const channelId = rest.join(':');
|
|
2925
|
+
const cleanId = channelId.replace(/[^\d]/g, '');
|
|
2926
|
+
const contactName = contactLookup[cleanId] || contactLookup[channelId] || channelId;
|
|
2927
|
+
const lines = [
|
|
2928
|
+
`Conversation with ${contactName} (${platform || 'unknown'})`,
|
|
2929
|
+
`Exported: ${now.toLocaleString()}`,
|
|
2930
|
+
`Messages: ${conv.messages.length}`,
|
|
2931
|
+
'─'.repeat(50),
|
|
2932
|
+
'',
|
|
2933
|
+
];
|
|
2934
|
+
for (const m of conv.messages) {
|
|
2935
|
+
const role = m.role === 'user' ? contactName : 'Agent';
|
|
2936
|
+
const ts = new Date(m.ts).toLocaleString();
|
|
2937
|
+
lines.push(`[${ts}] ${role}:`);
|
|
2938
|
+
lines.push(m.content);
|
|
2939
|
+
lines.push('');
|
|
2940
|
+
}
|
|
2941
|
+
const safeName = `${platform || 'unknown'}-${contactName.replace(/[^\w]/g, '_')}-${channelId.replace(/[^\w]/g, '_')}.txt`;
|
|
2942
|
+
files.push({ name: safeName, content: lines.join('\n') });
|
|
2943
|
+
}
|
|
2944
|
+
// Generate ZIP manually (no external deps).
|
|
2945
|
+
const zipBuf = generateZip(files);
|
|
2946
|
+
res.writeHead(200, {
|
|
2947
|
+
'Content-Type': 'application/zip',
|
|
2948
|
+
'Content-Disposition': `attachment; filename="conversations-${dateStr}.zip"`,
|
|
2949
|
+
'Content-Length': zipBuf.length,
|
|
2950
|
+
});
|
|
2951
|
+
res.end(zipBuf);
|
|
2952
|
+
}
|
|
2953
|
+
catch (err) {
|
|
2954
|
+
writeJson(res, 500, { ok: false, error: `Export failed: ${err instanceof Error ? err.message : String(err)}` });
|
|
2955
|
+
}
|
|
2956
|
+
})();
|
|
2957
|
+
return;
|
|
2958
|
+
}
|
|
2778
2959
|
// GET /api/admin/gateway/conversations — paginated conversation list.
|
|
2779
2960
|
// Rides on gateway.manage (admin + operator).
|
|
2780
2961
|
if (pathname === '/api/admin/gateway/conversations' && req.method === 'GET') {
|