amalgm 0.1.36 → 0.1.37
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/package.json +1 -1
- package/runtime/scripts/amalgm-mcp/artifacts/supervisor.js +29 -2
- package/runtime/scripts/amalgm-mcp/events/executor.js +6 -0
- package/runtime/scripts/amalgm-mcp/events/rest.js +13 -0
- package/runtime/scripts/amalgm-mcp/events/tools.js +13 -0
- package/runtime/scripts/amalgm-mcp/fs/rest.js +6 -0
- package/runtime/scripts/amalgm-mcp/server/http.js +9 -0
- package/runtime/scripts/amalgm-mcp/state/db.js +25 -0
- package/runtime/scripts/amalgm-mcp/state/snapshot.js +10 -0
- package/runtime/scripts/amalgm-mcp/tasks/executor.js +9 -3
- package/runtime/scripts/amalgm-mcp/tasks/tools.js +13 -0
- package/runtime/scripts/amalgm-mcp/tests/workspace-store.test.js +84 -0
- package/runtime/scripts/amalgm-mcp/toolbox/store.js +15 -0
- package/runtime/scripts/amalgm-mcp/workspace/rest.js +162 -22
- package/runtime/scripts/amalgm-mcp/workspace/store.js +278 -0
- package/runtime/scripts/chat-core/adapters/codex.js +44 -4
- package/runtime/scripts/chat-core/contract.js +57 -0
- package/runtime/scripts/chat-core/engine.js +5 -5
- package/runtime/scripts/chat-core/server.js +17 -4
- package/runtime/scripts/chat-core/sse.js +8 -1
- package/runtime/scripts/chat-core/stores.js +3 -1
- package/runtime/scripts/chat-core/tooling/active-memory.js +396 -0
- package/runtime/scripts/chat-core/tooling/system-prompt.js +3 -0
- package/runtime/scripts/local-gateway.js +158 -0
|
@@ -46,12 +46,34 @@ const pty = loadPty();
|
|
|
46
46
|
const AMALGM_DIR = process.env.AMALGM_DIR || path.join(os.homedir(), '.amalgm');
|
|
47
47
|
const STATE_FILE = path.join(AMALGM_DIR, 'runtime-state.json');
|
|
48
48
|
const ARTIFACTS_FILE = path.join(AMALGM_DIR, 'artifacts.json');
|
|
49
|
+
const LOG_DIR = path.join(AMALGM_DIR, 'logs');
|
|
49
50
|
const BIND_HOST = process.env.AMALGM_BIND_HOST || '127.0.0.1';
|
|
50
51
|
const OWNER = process.env.AMALGM_RUNTIME_SOURCE || 'local';
|
|
51
52
|
const VERSION = process.env.npm_package_version || process.env.AMALGM_RUNTIME_VERSION || '';
|
|
52
53
|
const DEFAULT_CWD = process.env.AMALGM_DEFAULT_CWD || os.homedir();
|
|
53
54
|
const PORT = Number.parseInt(process.env.AMALGM_GATEWAY_PORT || '28781', 10);
|
|
54
55
|
const RUNTIME_TOKEN_HEADER = 'x-amalgm-runtime-token';
|
|
56
|
+
const DEFAULT_DIAGNOSTIC_LOG_TAIL_BYTES = 256 * 1024;
|
|
57
|
+
const MAX_DIAGNOSTIC_LOG_TAIL_BYTES = 2 * 1024 * 1024;
|
|
58
|
+
|
|
59
|
+
const DIAGNOSTIC_LOG_FILES = Object.freeze({
|
|
60
|
+
daemon: 'daemon.log',
|
|
61
|
+
'chat-server': 'chat-server.log',
|
|
62
|
+
'local-gateway': 'local-gateway.log',
|
|
63
|
+
'amalgm-mcp': 'amalgm-mcp.log',
|
|
64
|
+
'fs-watcher': 'fs-watcher.log',
|
|
65
|
+
'port-monitor': 'port-monitor.log',
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const DIAGNOSTIC_LOG_ALIASES = Object.freeze({
|
|
69
|
+
chat: 'chat-server',
|
|
70
|
+
mcp: 'amalgm-mcp',
|
|
71
|
+
gateway: 'local-gateway',
|
|
72
|
+
events: 'daemon',
|
|
73
|
+
'event-tunnel': 'daemon',
|
|
74
|
+
'chat-tunnel': 'daemon',
|
|
75
|
+
tunnel: 'daemon',
|
|
76
|
+
});
|
|
55
77
|
|
|
56
78
|
const SERVICE_PORTS = {
|
|
57
79
|
gateway: PORT,
|
|
@@ -314,6 +336,137 @@ function sendJson(res, statusCode, payload) {
|
|
|
314
336
|
res.end(JSON.stringify(payload));
|
|
315
337
|
}
|
|
316
338
|
|
|
339
|
+
function diagnosticLogServices() {
|
|
340
|
+
return Object.keys(DIAGNOSTIC_LOG_FILES);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function normalizeDiagnosticLogService(value) {
|
|
344
|
+
let service = String(value || 'daemon').trim().toLowerCase();
|
|
345
|
+
if (service.endsWith('.log')) service = service.slice(0, -4);
|
|
346
|
+
service = service.replace(/[^a-z0-9_-]/g, '');
|
|
347
|
+
return DIAGNOSTIC_LOG_ALIASES[service] || service;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
function parseDiagnosticTailBytes(value) {
|
|
351
|
+
const parsed = Number.parseInt(String(value || ''), 10);
|
|
352
|
+
if (!Number.isFinite(parsed) || parsed < 0) return DEFAULT_DIAGNOSTIC_LOG_TAIL_BYTES;
|
|
353
|
+
return Math.min(parsed, MAX_DIAGNOSTIC_LOG_TAIL_BYTES);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function parseDiagnosticLineLimit(value) {
|
|
357
|
+
const parsed = Number.parseInt(String(value || ''), 10);
|
|
358
|
+
if (!Number.isFinite(parsed) || parsed <= 0) return null;
|
|
359
|
+
return Math.min(parsed, 5000);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
function redactDiagnosticLogContent(content) {
|
|
363
|
+
return String(content)
|
|
364
|
+
.replace(/(authorization\s*[:=]\s*)(?:Bearer\s+)?[A-Za-z0-9._~+/=-]{8,}/gi, '$1[REDACTED]')
|
|
365
|
+
.replace(/\bBearer\s+[A-Za-z0-9._~+/=-]{20,}\b/gi, 'Bearer [REDACTED]')
|
|
366
|
+
.replace(/\b(?:sk|pk|rk|ghp|github_pat|glpat|xox[baprs])[-_][A-Za-z0-9_./+=-]{16,}\b/g, '[REDACTED_TOKEN]')
|
|
367
|
+
.replace(
|
|
368
|
+
/((?:api[_-]?key|apikey|token|secret|password|cookie|set-cookie)[^:=\n]{0,32}[:=]\s*["']?)[^\s"',;}]+/gi,
|
|
369
|
+
'$1[REDACTED]',
|
|
370
|
+
);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
function applyDiagnosticLineLimit(content, lineLimit) {
|
|
374
|
+
if (!lineLimit) return { content, lineTruncated: false };
|
|
375
|
+
const lines = String(content).split(/\r?\n/);
|
|
376
|
+
if (lines.length <= lineLimit) return { content, lineTruncated: false };
|
|
377
|
+
return {
|
|
378
|
+
content: lines.slice(-lineLimit).join('\n'),
|
|
379
|
+
lineTruncated: true,
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
async function readDiagnosticLogTail(logPath, maxBytes) {
|
|
384
|
+
const stats = await fs.promises.stat(logPath);
|
|
385
|
+
if (!stats.isFile()) {
|
|
386
|
+
const error = new Error('Diagnostic log path is not a file');
|
|
387
|
+
error.statusCode = 400;
|
|
388
|
+
throw error;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
const bytes = Math.min(stats.size, maxBytes);
|
|
392
|
+
if (bytes === 0) {
|
|
393
|
+
return {
|
|
394
|
+
stats,
|
|
395
|
+
bytes,
|
|
396
|
+
content: '',
|
|
397
|
+
byteTruncated: false,
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
const handle = await fs.promises.open(logPath, 'r');
|
|
402
|
+
try {
|
|
403
|
+
const buffer = Buffer.alloc(bytes);
|
|
404
|
+
await handle.read(buffer, 0, bytes, stats.size - bytes);
|
|
405
|
+
return {
|
|
406
|
+
stats,
|
|
407
|
+
bytes,
|
|
408
|
+
content: buffer.toString('utf8'),
|
|
409
|
+
byteTruncated: stats.size > bytes,
|
|
410
|
+
};
|
|
411
|
+
} finally {
|
|
412
|
+
await handle.close();
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
async function handleDiagnosticLogs(req, res, url) {
|
|
417
|
+
if (req.method !== 'GET') {
|
|
418
|
+
res.writeHead(405, { Allow: 'GET' });
|
|
419
|
+
res.end();
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
const service = normalizeDiagnosticLogService(
|
|
424
|
+
url.searchParams.get('service') || url.searchParams.get('name'),
|
|
425
|
+
);
|
|
426
|
+
const filename = DIAGNOSTIC_LOG_FILES[service];
|
|
427
|
+
if (!filename) {
|
|
428
|
+
sendJson(res, 400, {
|
|
429
|
+
error: 'Unknown diagnostic log service',
|
|
430
|
+
availableServices: diagnosticLogServices(),
|
|
431
|
+
});
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
const tailBytes = parseDiagnosticTailBytes(url.searchParams.get('tailBytes'));
|
|
436
|
+
const lineLimit = parseDiagnosticLineLimit(url.searchParams.get('lines'));
|
|
437
|
+
const logPath = path.join(LOG_DIR, filename);
|
|
438
|
+
|
|
439
|
+
try {
|
|
440
|
+
const tail = await readDiagnosticLogTail(logPath, tailBytes);
|
|
441
|
+
const lineLimited = applyDiagnosticLineLimit(tail.content, lineLimit);
|
|
442
|
+
sendJson(res, 200, {
|
|
443
|
+
service,
|
|
444
|
+
path: logPath,
|
|
445
|
+
exists: true,
|
|
446
|
+
size: tail.stats.size,
|
|
447
|
+
mtime: tail.stats.mtime.toISOString(),
|
|
448
|
+
tailBytes: tail.bytes,
|
|
449
|
+
truncated: tail.byteTruncated || lineLimited.lineTruncated,
|
|
450
|
+
content: redactDiagnosticLogContent(lineLimited.content),
|
|
451
|
+
});
|
|
452
|
+
} catch (error) {
|
|
453
|
+
if (error && error.code === 'ENOENT') {
|
|
454
|
+
sendJson(res, 404, {
|
|
455
|
+
service,
|
|
456
|
+
path: logPath,
|
|
457
|
+
exists: false,
|
|
458
|
+
error: 'Diagnostic log file not found',
|
|
459
|
+
});
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
const status = typeof error?.statusCode === 'number' ? error.statusCode : 500;
|
|
463
|
+
sendJson(res, status, {
|
|
464
|
+
service,
|
|
465
|
+
error: error instanceof Error ? error.message : 'Failed to read diagnostic log',
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
317
470
|
async function readBody(req) {
|
|
318
471
|
const chunks = [];
|
|
319
472
|
for await (const chunk of req) chunks.push(Buffer.from(chunk));
|
|
@@ -760,6 +913,11 @@ const server = http.createServer(async (req, res) => {
|
|
|
760
913
|
return;
|
|
761
914
|
}
|
|
762
915
|
|
|
916
|
+
if (url.pathname === '/diagnostics/logs') {
|
|
917
|
+
await handleDiagnosticLogs(req, res, url);
|
|
918
|
+
return;
|
|
919
|
+
}
|
|
920
|
+
|
|
763
921
|
if (url.pathname === '/pty/session' || url.pathname === '/pty/resize') {
|
|
764
922
|
await handlePtyHttp(req, res, url.pathname);
|
|
765
923
|
return;
|