Files
MichaelandClaude Opus 5.5 5016396998 Repris d'AJEAN : recherche d'images, aperçu du navigateur, vignettes, liens d'images
- web_images (amont v0.17.7) : recherche d'images DuckDuckGo, pendant de
  web_search ; les images du web s'affichent dans le fil avec la loupe, sans
  Referer, et disparaissent si le site les refuse. Schéma minimal pour tenir
  le budget du préambule.
- Aperçu en direct du navigateur piloté (cu_autoshot.go) : une capture après
  chaque action browser_* qui change la page, gardée en RAM, jamais envoyée au
  modèle, retirée du journal en fin de tour ; une carte dans le fil, en fondu.
- Vignettes calculées par le serveur (?thumb=, amont v0.17.9) : ~560 px au
  lieu de l'original (7 Ko au lieu de 550 Ko sur une capture), chargées deux
  par deux avec un nouvel essai ; la loupe ouvre l'original.
- Un lien vers une image ([graphique](graph.png)) l'affiche au lieu d'un
  bouton de téléchargement.
- Chrome du computer use tué avec Loki sous Linux (Pdeathsig), même en cas
  d'arrêt brutal : plus de navigateur orphelin dans le conteneur.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 10:30:05 +02:00

93 lines
2.7 KiB
Go

package loki
// cu_autoshot.go — aperçu EN DIRECT du navigateur piloté (computer use). Après
// chaque action browser_*, on capture la page pour que l'UTILISATEUR voie ce que
// fait l'IA. C'est de l'interface seulement :
// - jamais envoyé au modèle (zéro token, vision non requise) ;
// - jamais écrit sur le disque : un petit anneau en mémoire vive ;
// - retiré du journal en fin de tour (compactLogLocked), et l'UI efface la carte
// à la fin du tour. Une capture à GARDER passe toujours par
// browser_screenshot, qui l'enregistre dans le dossier de travail.
import (
"bytes"
"encoding/base64"
"fmt"
"image"
"image/jpeg"
_ "image/png"
"net/http"
"os"
"strings"
"sync"
"time"
)
const cuShotRing = 8 // captures gardées en RAM (l'UI n'affiche que la dernière)
var (
cuShotMu sync.Mutex
cuShotData = map[string][]byte{}
cuShotIDs []string
)
// cuAutoShotTool : outils après lesquels on capture (ceux qui changent la page).
func cuAutoShotTool(name string) bool {
switch name {
case "browser_open", "browser_click", "browser_click_xy", "browser_type", "browser_key", "browser_scroll", "browser_find":
return true
}
return false
}
func cuLog(format string, a ...any) { fmt.Fprintf(os.Stderr, "[cu] "+format+"\n", a...) }
// cuAutoShot capture la page en pleine résolution (JPEG de bonne qualité) et
// renvoie l'identifiant de la capture en mémoire ("" si impossible : jamais
// bloquant pour l'outil).
func cuAutoShot() string {
s, err := cdpGet()
if err != nil {
cuLog("aperçu : navigateur indisponible : %v", err)
return ""
}
raw, err := s.screenshot()
if err != nil {
cuLog("aperçu : capture impossible : %v", err)
return ""
}
img, _, err := image.Decode(bytes.NewReader(raw))
if err != nil {
cuLog("aperçu : image illisible (%d octets) : %v", len(raw), err)
return ""
}
var buf bytes.Buffer
if err := jpeg.Encode(&buf, img, &jpeg.Options{Quality: 90}); err != nil {
return ""
}
id := fmt.Sprintf("%x", time.Now().UnixNano())
cuShotMu.Lock()
cuShotData[id] = buf.Bytes()
cuShotIDs = append(cuShotIDs, id)
for len(cuShotIDs) > cuShotRing {
delete(cuShotData, cuShotIDs[0])
cuShotIDs = cuShotIDs[1:]
}
cuShotMu.Unlock()
return id
}
// handleCUShot (GET ?id=) : une capture en base64 (JSON, pour traverser aussi le
// tunnel chiffré du relais). 404 si elle est déjà sortie de l'anneau.
func handleCUShot(w http.ResponseWriter, r *http.Request) {
id := strings.TrimSpace(r.URL.Query().Get("id"))
cuShotMu.Lock()
b := cuShotData[id]
cuShotMu.Unlock()
if b == nil {
sendJSON(w, 404, map[string]any{"ok": false, "error": "capture expirée"})
return
}
sendJSON(w, 200, map[string]any{"ok": true, "mime": "image/jpeg", "data": base64.StdEncoding.EncodeToString(b)})
}