'use strict'; // DSH unified parser. Three markers: // DSH - classic JSON // DSH_B64 - base64 UTF-8 JSON // DSH_FILE - raw file content const L = String.fromCharCode(10214); const R = String.fromCharCode(10215); const BS = String.fromCharCode(92); const Q = String.fromCharCode(34); const OPEN_DSH = L + 'DSH' + R; const CLOSE_DSH = L + '/DSH' + R; const OPEN_B64 = L + 'DSH_B64' + R; const CLOSE_B64 = L + '/DSH_B64' + R; const OPEN_FILE = L + 'DSH_FILE'; const CLOSE_FILE = L + '/DSH_FILE' + R; const B64_CLEAN = new RegExp('[^A-Za-z0-9+/=]', 'g'); function b64DecodeUtf8(b64) { const clean = String(b64 || '').replace(B64_CLEAN, ''); const bin = atob(clean); const bytes = new Uint8Array(bin.length); for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i); return new TextDecoder('utf-8').decode(bytes); } function tryParseJson(s) { try { return JSON.parse(s); } catch (e) { return null; } } function parseFileHeader(header) { const pIdx = header.indexOf('path'); if (pIdx < 0) return null; const eq = header.indexOf('=', pIdx); if (eq < 0) return null; const q1 = header.indexOf(Q, eq); if (q1 < 0) return null; const q2 = header.indexOf(Q, q1 + 1); if (q2 < 0) return null; return header.slice(q1 + 1, q2); } function parseOne(text, startIdx) { if (text.startsWith(OPEN_FILE, startIdx)) { const openEnd = text.indexOf(R, startIdx + OPEN_FILE.length); if (openEnd < 0) return null; const header = text.slice(startIdx + OPEN_FILE.length, openEnd); const path = parseFileHeader(header); if (!path) return null; const closeIdx = text.indexOf(CLOSE_FILE, openEnd + 1); if (closeIdx < 0) return null; let content = text.slice(openEnd + 1, closeIdx); if (content.charCodeAt(0) === 10) content = content.slice(1); return { call: { tool: 'dsh_write', args: { path: path, content: content } }, nextIdx: closeIdx + CLOSE_FILE.length }; } if (text.startsWith(OPEN_B64, startIdx)) { const closeIdx = text.indexOf(CLOSE_B64, startIdx + OPEN_B64.length); if (closeIdx < 0) return null; const raw = text.slice(startIdx + OPEN_B64.length, closeIdx); const json = b64DecodeUtf8(raw); const obj = tryParseJson(json); if (!obj || typeof obj !== 'object' || !obj.tool) return null; return { call: obj, nextIdx: closeIdx + CLOSE_B64.length }; } if (text.startsWith(OPEN_DSH, startIdx)) { const closeIdx = text.indexOf(CLOSE_DSH, startIdx + OPEN_DSH.length); if (closeIdx < 0) return null; const raw = text.slice(startIdx + OPEN_DSH.length, closeIdx); const obj = tryParseJson(raw); if (!obj || typeof obj !== 'object' || !obj.tool) return null; return { call: obj, nextIdx: closeIdx + CLOSE_DSH.length }; } return null; } function extractCalls(text, maxCalls) { const limit = Math.max(1, maxCalls | 0) || 3; const calls = []; let pos = 0; while (calls.length < limit) { const i = text.indexOf(L, pos); if (i < 0) break; const parsed = parseOne(text, i); if (!parsed) { pos = i + L.length; continue; } calls.push(parsed.call); pos = parsed.nextIdx; } return { calls: calls, consumed: pos }; } module.exports = { extractCalls: extractCalls, b64DecodeUtf8: b64DecodeUtf8 };