consortium
Version:
Remote control and session sharing CLI for AI coding agents
253 lines (247 loc) • 9.71 kB
JavaScript
;
var fs = require('node:fs');
var path = require('node:path');
var persistence = require('./types-BYXHizYk.cjs');
function sniffMimeFromBytes(buf) {
if (buf.length < 4) return void 0;
const b = buf instanceof Buffer ? buf : Buffer.from(buf.buffer, buf.byteOffset, buf.byteLength);
if (b[0] === 137 && b[1] === 80 && b[2] === 78 && b[3] === 71) return "image/png";
if (b[0] === 255 && b[1] === 216 && b[2] === 255) return "image/jpeg";
if (b[0] === 71 && b[1] === 73 && b[2] === 70 && b[3] === 56) return "image/gif";
if (b[0] === 37 && b[1] === 80 && b[2] === 68 && b[3] === 70) return "application/pdf";
if (b.length >= 12 && b[0] === 82 && b[1] === 73 && b[2] === 70 && b[3] === 70 && b[8] === 87 && b[9] === 69 && b[10] === 66 && b[11] === 80) return "image/webp";
if (b.length >= 12 && b[4] === 102 && b[5] === 116 && b[6] === 121 && b[7] === 112) {
if (b[8] === 113 && b[9] === 116 && b[10] === 32) return "video/quicktime";
return "video/mp4";
}
if (b[0] === 26 && b[1] === 69 && b[2] === 223 && b[3] === 163) return "video/webm";
if (b[0] === 73 && b[1] === 68 && b[2] === 51) return "audio/mpeg";
if (b[0] === 255 && (b[1] & 224) === 224) return "audio/mpeg";
if (b.length >= 12 && b[0] === 82 && b[1] === 73 && b[2] === 70 && b[3] === 70 && b[8] === 87 && b[9] === 65 && b[10] === 86 && b[11] === 69) return "audio/wav";
if (b[0] === 79 && b[1] === 103 && b[2] === 103 && b[3] === 83) return "audio/ogg";
if (b[0] === 102 && b[1] === 76 && b[2] === 97 && b[3] === 67) return "audio/flac";
return void 0;
}
const MIME_BY_EXT = {
png: "image/png",
jpg: "image/jpeg",
jpeg: "image/jpeg",
gif: "image/gif",
webp: "image/webp",
avif: "image/avif",
heic: "image/heic",
svg: "image/svg+xml",
mp4: "video/mp4",
mov: "video/quicktime",
webm: "video/webm",
mkv: "video/x-matroska",
mp3: "audio/mpeg",
wav: "audio/wav",
ogg: "audio/ogg",
m4a: "audio/mp4",
flac: "audio/flac",
pdf: "application/pdf"
};
function mimeFromExtension(path) {
const i = path.lastIndexOf(".");
if (i < 0) return void 0;
const ext = path.slice(i + 1).toLowerCase();
return MIME_BY_EXT[ext];
}
const DEFAULTS = {
maxBytes: 64 * 1024 * 1024,
maxDepth: 6,
maxCandidates: 8,
maxFileAgeMs: 30 * 60 * 1e3
// 30 min — older than a turn, probably stale
};
const BASE64ISH = /^[A-Za-z0-9+/=_-]{800,}$/;
function looksLikePath(s) {
return s.length > 1 && s.length < 4096 && path.isAbsolute(s) && !s.includes("\n");
}
function looksLikeUrl(s) {
return /^https?:\/\/[^\s]+$/i.test(s) && s.length < 2048;
}
function extractPathsFromProse(text) {
if (!text || text.length > 1e5) return [];
const out = [];
const re = /\/[^\s'"`)\]]+\.(?:png|jpe?g|gif|webp|svg|avif|heic|mp4|mov|webm|mkv|mp3|wav|ogg|m4a|flac|pdf)\b/gi;
let m;
while ((m = re.exec(text)) !== null) {
out.push(m[0]);
if (out.length >= 4) break;
}
return out;
}
async function scanToolResultForMedia(content, onMedia, options = {}) {
const opts = { ...DEFAULTS, ...options };
const seenBytes = /* @__PURE__ */ new Set();
let emitted = 0;
const tryEmit = async (cand) => {
if (emitted >= opts.maxCandidates) return;
if (cand.bytes.length === 0 || cand.bytes.length > opts.maxBytes) return;
const key = `${cand.mime}|${cand.bytes.length}`;
if (seenBytes.has(key)) return;
seenBytes.add(key);
emitted++;
try {
await onMedia(cand);
} catch (err) {
persistence.logger.debug("[mediaScan] onMedia callback threw:", err);
}
};
const tryEmitFromPath = async (path, name) => {
try {
const stat = await fs.promises.stat(path);
if (!stat.isFile()) return;
if (stat.size === 0 || stat.size > opts.maxBytes) return;
if (Date.now() - stat.mtimeMs > opts.maxFileAgeMs) return;
const extMime = mimeFromExtension(path);
if (!extMime) return;
const buf = await fs.promises.readFile(path);
const sniffed = sniffMimeFromBytes(buf) ?? extMime;
const bytes = new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
await tryEmit({ bytes, mime: sniffed, name: name ?? basenameOf(path) });
} catch (err) {
persistence.logger.debug(`[mediaScan] path read failed (${path}):`, err instanceof Error ? err.message : err);
}
};
const tryEmitFromUrl = async (url, name) => {
try {
const res = await fetch(url, { method: "GET" });
if (!res.ok) return;
const ct = res.headers.get("content-type") ?? "";
const len = Number(res.headers.get("content-length") ?? "0");
if (len > opts.maxBytes) return;
const ab = await res.arrayBuffer();
const buf = Buffer.from(ab);
if (buf.length === 0 || buf.length > opts.maxBytes) return;
const sniffed = sniffMimeFromBytes(buf);
const isMediaCt = ct.startsWith("image/") || ct.startsWith("video/") || ct.startsWith("audio/") || ct === "application/pdf";
if (!sniffed && !isMediaCt) return;
const mime = sniffed ?? ct.split(";")[0].trim();
const bytes = new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
await tryEmit({ bytes, mime, name: name ?? url.split("/").pop() });
} catch (err) {
persistence.logger.debug(`[mediaScan] url fetch failed (${url}):`, err instanceof Error ? err.message : err);
}
};
const walk = async (node, depth, hintName) => {
if (emitted >= opts.maxCandidates) return;
if (node === null || node === void 0 || depth > opts.maxDepth) return;
if (typeof node === "string") {
const s = node;
if (looksLikePath(s)) {
await tryEmitFromPath(s, hintName);
return;
}
if (looksLikeUrl(s)) {
await tryEmitFromUrl(s, hintName);
return;
}
if (BASE64ISH.test(s)) {
try {
const buf = Buffer.from(s, "base64");
if (buf.length > 0) {
const sniffed = sniffMimeFromBytes(buf);
if (sniffed) {
const bytes = new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
await tryEmit({ bytes, mime: sniffed, name: hintName });
}
}
} catch {
}
return;
}
for (const p of extractPathsFromProse(s)) {
await tryEmitFromPath(p);
if (emitted >= opts.maxCandidates) return;
}
return;
}
if (Array.isArray(node)) {
for (const item of node) {
await walk(item, depth + 1);
if (emitted >= opts.maxCandidates) return;
}
return;
}
if (typeof node === "object") {
const o = node;
if (typeof o.type === "string") {
if (o.type === "image" && typeof o.data === "string") {
const mime = (typeof o.mimeType === "string" ? o.mimeType : void 0) ?? "image/png";
try {
const buf = Buffer.from(o.data, "base64");
if (buf.length > 0) {
const bytes = new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
await tryEmit({ bytes, mime, name: typeof o.name === "string" ? o.name : void 0 });
}
} catch {
}
return;
}
if (o.type === "audio" && typeof o.data === "string") {
const mime = (typeof o.mimeType === "string" ? o.mimeType : void 0) ?? "audio/mpeg";
try {
const buf = Buffer.from(o.data, "base64");
if (buf.length > 0) {
const bytes = new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
await tryEmit({ bytes, mime, name: typeof o.name === "string" ? o.name : void 0 });
}
} catch {
}
return;
}
if (o.type === "resource" && o.resource && typeof o.resource === "object") {
const r = o.resource;
if (typeof r.blob === "string") {
const mime = (typeof r.mimeType === "string" ? r.mimeType : void 0) ?? "application/octet-stream";
try {
const buf = Buffer.from(r.blob, "base64");
if (buf.length > 0) {
const sniffed = sniffMimeFromBytes(buf) ?? mime;
const bytes = new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
await tryEmit({ bytes, mime: sniffed, name: typeof r.uri === "string" ? basenameOf(r.uri) : void 0 });
}
} catch {
}
return;
}
if (typeof r.uri === "string") {
const uri = r.uri;
if (uri.startsWith("file://")) {
await tryEmitFromPath(uri.slice(7));
return;
}
}
}
}
for (const [k, v] of Object.entries(o)) {
const lk = k.toLowerCase();
const hint = (lk.includes("name") || lk.includes("filename")) && typeof o.name === "string" ? o.name : void 0;
await walk(v, depth + 1, hint);
if (emitted >= opts.maxCandidates) return;
}
}
};
await walk(content, 0);
}
function basenameOf(p) {
const i = Math.max(p.lastIndexOf("/"), p.lastIndexOf("\\"));
return i >= 0 ? p.slice(i + 1) : p;
}
function resolveModelFromMeta(meta, currentModel) {
if (!meta || !Object.prototype.hasOwnProperty.call(meta, "model")) {
return { model: currentModel, reset: false, changed: false };
}
if (meta.model === null) {
return { model: void 0, reset: true, changed: currentModel !== void 0 };
}
if (meta.model) {
return { model: meta.model, reset: false, changed: currentModel !== meta.model };
}
return { model: currentModel, reset: false, changed: false };
}
exports.resolveModelFromMeta = resolveModelFromMeta;
exports.scanToolResultForMedia = scanToolResultForMedia;