From 7dc7601102a1966d931bd8697c02d10fca571f13 Mon Sep 17 00:00:00 2001 From: Matteo Benedetto Date: Sat, 22 Aug 2026 12:37:53 +0200 Subject: [PATCH] feat(recorder): aggiunti tool di registrazione e analisi attivita BiDi (JSONL) --- extensions/firefox-bidi.ts | 153 +++++++++++++++++++++++++++++++++++++ 1 file changed, 153 insertions(+) diff --git a/extensions/firefox-bidi.ts b/extensions/firefox-bidi.ts index a7f476c..587df56 100644 --- a/extensions/firefox-bidi.ts +++ b/extensions/firefox-bidi.ts @@ -720,6 +720,159 @@ export default function (pi: ExtensionAPI) { }, }); + // ------------------------------------------------------------------------- + // Registrazione e analisi attività di navigazione (Activity Recorder JSONL) + // ------------------------------------------------------------------------- + pi.registerTool({ + name: "browser_record_start", + label: "Browser Record Start", + description: + "Avvia o riconfigura la registrazione continua degli eventi e delle attività di navigazione " + + "(URL, domini, navigazioni, caricamento DOM, errori e log di console) su un file JSONL.", + parameters: Type.Object({ + path: Type.Optional( + Type.String({ + description: + "Percorso assoluto del file JSONL (default: ~/.local/state/pi-firefox-bidi/activity.jsonl)", + }), + ), + events: Type.Optional( + Type.Array(Type.String(), { + description: "Lista di eventi BiDi da registrare (opzionale)", + }), + ), + }), + async execute(_id, params) { + await ensureBridge(); + const r = await bridge("/recorder/start", { + method: "POST", + body: JSON.stringify(params), + }); + return { + content: [ + { + type: "text", + text: `Registrazione attività attiva su file: ${r.file}`, + }, + ], + details: r, + }; + }, + }); + + pi.registerTool({ + name: "browser_record_stop", + label: "Browser Record Stop", + description: "Sospende la registrazione delle attività di navigazione su file.", + parameters: Type.Object({}), + async execute() { + await ensureBridge(); + const r = await bridge("/recorder/stop", { method: "POST" }); + return { + content: [ + { + type: "text", + text: `Registrazione attività sospesa. Eventi totali registrati: ${r.totalEvents} (${r.file})`, + }, + ], + details: r, + }; + }, + }); + + pi.registerTool({ + name: "browser_record_status", + label: "Browser Record Status", + description: "Mostra lo stato del registratore attività, il percorso del file JSONL e il numero di eventi.", + parameters: Type.Object({}), + async execute() { + await ensureBridge(); + const r = await bridge("/recorder/status"); + return { + content: [{ type: "text", text: JSON.stringify(r, null, 2) }], + details: r, + }; + }, + }); + + pi.registerTool({ + name: "browser_record_get", + label: "Browser Record Get", + description: + "Recupera le attività di navigazione registrate dal file JSONL per analisi (formato compatto o JSON grezzo).", + parameters: Type.Object({ + limit: Type.Optional( + Type.Number({ description: "Numero massimo di eventi recenti da recuperare (default 30)", default: 30 }), + ), + format: Type.Optional( + Type.Enum( + { summary: "summary", json: "json" }, + { description: "Formato: 'summary' (timeline compatta) o 'json' (oggetti completi)", default: "summary" }, + ), + ), + }), + async execute(_id, params) { + await ensureBridge(); + const r = await bridge("/recorder/get", { + method: "POST", + body: JSON.stringify({ limit: params.limit ?? 30 }), + }); + const records: any[] = r.records ?? []; + if (params.format === "json") { + return { + content: [{ type: "text", text: JSON.stringify(r, null, 2) }], + details: r, + }; + } + + if (records.length === 0) { + return { + content: [{ type: "text", text: `Nessuna attività registrata nel log (${r.file}).` }], + details: r, + }; + } + + const lines = records.map((rec) => { + const timeStr = rec.timestamp ? rec.timestamp.split("T")[1]?.slice(0, 8) : "--:--:--"; + if (rec.event === "navigation_started") { + return `[${timeStr}] 🌐 Navigazione: ${rec.url} (dominio: ${rec.domain ?? "-"})`; + } else if (rec.event === "page_loaded") { + return `[${timeStr}] ⏱ Caricato: ${rec.url ?? ""} in ${rec.durationMs ?? "?"}ms`; + } else if (rec.event === "dom_ready") { + return `[${timeStr}] 📄 DOM Ready: ${rec.url ?? ""}`; + } else if (rec.event === "console_log") { + return `[${timeStr}] 💬 [Console ${rec.level ?? "log"}]: ${rec.text ?? ""}`; + } + return `[${timeStr}] ⚡ ${rec.event}: ${rec.url ?? JSON.stringify(rec)}`; + }); + + return { + content: [ + { + type: "text", + text: `Ultime ${records.length} attività registrate (${r.file}):\n\n` + lines.join("\n"), + }, + ], + details: r, + }; + }, + }); + + pi.registerTool({ + name: "browser_record_clear", + label: "Browser Record Clear", + description: "Svuota il file di log delle attività e azzera il contatore.", + parameters: Type.Object({}), + async execute() { + await ensureBridge(); + const r = await bridge("/recorder/clear", { method: "POST" }); + return { + content: [{ type: "text", text: "Log attività svuotato con successo." }], + details: r, + }; + }, + }); + // ------------------------------------------------------------------------- // Notifica asincrona (agy/bridge → pi): webhook locale // -------------------------------------------------------------------------