UNPKG

@platformos/pos-cli

Version:
101 lines (91 loc) 3.45 kB
// platformos.logs.stream - streaming logs via SSE with polling import log from '../log.js'; import { resolveAuth } from '../auth.js'; import Gateway from '../../lib/proxy.js'; function matchesFilter(row, filter) { if (!filter) return true; const type = (row.error_type || row.type || '').toString().toLowerCase(); return type.includes(String(filter).toLowerCase()); } const streamTool = { description: 'Real-time log streaming using polling and SSE. Optional filter by type.', inputSchema: { type: 'object', additionalProperties: false, properties: { env: { type: 'string' }, url: { type: 'string' }, email: { type: 'string' }, token: { type: 'string' }, endpoint: { type: 'string' }, interval: { type: 'integer', minimum: 250 }, filter: { type: 'string' }, startLastId: { type: 'string', description: 'Starting last id (default 0)' }, maxDuration: { type: 'integer', description: 'Optional max duration ms' } } }, streamHandler: async (params, { writer, Gateway: GatewayOverride, ...ctx } = {}) => { const auth = await resolveAuth(params, ctx); const baseUrl = params?.endpoint ? params.endpoint : auth.url; const GatewayCtor = GatewayOverride || Gateway; const gateway = new GatewayCtor({ url: baseUrl, token: auth.token, email: auth.email }); const interval = Number(params?.interval) || 3000; const filter = params?.filter; let lastId = (params && params.startLastId != null) ? String(params.startLastId) : '0'; const seen = new Set(); const started = Date.now(); const maxDuration = params?.maxDuration && Number(params.maxDuration) > 0 ? Number(params.maxDuration) : null; let elapsed = 0; let doneTimer = null; if (maxDuration) { doneTimer = setTimeout(() => { try { writer({ event: 'done', data: '' }); } catch (_) {} }, maxDuration); } writer({ event: 'data', data: JSON.stringify({ type: 'info', message: 'logs.stream started', env: params?.env || auth.source, interval }) }); const tick = async () => { try { const resp = await gateway.logs({ lastId }); const list = (resp && resp.logs) || []; if (list.length > 0) { let maxId = lastId; for (const row of list) { if (seen.has(row.id)) continue; seen.add(row.id); if (matchesFilter(row, filter)) { writer({ event: 'data', data: JSON.stringify(row) }); } const curr = Number(row.id); if (!Number.isNaN(curr)) { const prev = Number(maxId); if (Number.isNaN(prev) || curr > prev) maxId = String(row.id); } } lastId = maxId; } } catch (e) { writer({ event: 'error', data: String(e) }); } }; let timer = null; const schedule = () => { timer = setTimeout(async function run() { await tick(); if (maxDuration) { elapsed += interval; if (elapsed >= maxDuration) { writer({ event: 'done', data: '' }); return; } } schedule(); }, interval); }; // initial tick immediately await tick(); schedule(); // return a promise that never resolves; http-server will end when streamHandler resolves/rejects return new Promise(() => {}); } }; export default streamTool;