23 KiB
GRILLED_PLAN - cc-agent-proxy + programme Lua opencc
Compagnon de
.plans/opencc-agent-proxy-plan.md. Chaque question a une recommandation par defaut et un blocTODO: answerque tu remplis. Un prochain/grill-melira ce fichier et le condensera en plan v2 pret a executer.Convention :
- Remplace
TODO: answerpar ta reponse (1-3 lignes suffisent).- Si tu choisis une option listee, ecris
Option N+ raison breve.- Si tu inventes une 5e option, ecris-la in extenso.
- Si une question devient sans objet apres une reponse majeure, ecris
N/A - voir Q<n>.- Laisse
TODO: answer (optionnel)tel quel si pas d'avis.
Regles de base du PoC
- Une turtle, un OpenCode, un proxy, une reponse utilisable. Rien de plus.
- Contraintes explicites > design generique.
- Pas de packaging tant que la boucle n'a pas tourne in-game.
- Aucun secret commit.
- La forme des reponses OpenCode est suspecte tant qu'on ne l'a pas vue en vrai.
A. Les 8 questions determinantes
A repondre en premier. Le reste devient souvent trivial une fois ces choix trances.
A1. Critere d'acceptation du PoC
Quand dit-on "c'est fini" ?
- Option 1 - curl roundtrip :
curl /askretourne une vraie reponse OpenCode. Pas d'in-game. - Option 2 - CraftOS-PC roundtrip :
opencc "salut"repond dans l'emulateur, en pointant sur le proxy local. - Option 3 - in-game ATM10 :
openccmarche sur une vraie turtle apres confighttp.rules. - Option 4 - conversation in-game : Option 3 + verifier que la session persistante garde le contexte sur plusieurs prompts.
Recommandation : Option 4. C'est ce qui prouve que le pipeline tient bout
en bout. 1-3 sont des jalons intermediaires. Premier prompt anodin (dis bonjour) avant prompts libres.
Pourquoi ca compte : decide si async, multi-turtle, logs riches, quotas, packaging et UX terminale sont hors scope.
TODO: answer
A2. Autorite et workspace d'OpenCode
Le proxy parle a un opencode serve qui a, potentiellement, acces complet a
des fichiers et a un shell. Une turtle qui prompt = un acteur exterieur qui
peut indirectement declencher des actions cote hote.
- Option 1 - workspace dedie jetable :
opencode servelance dans un dir temporaire (/tmp/opencc-poc) sans secrets, sans repo perso. - Option 2 - workspace projet dedie : un dossier
~/opencc-workspace/vide ou minimal, persistant entre runs. - Option 3 - repo perso : non, trop risque.
- Option 4 - sandbox container : OpenCode dans Docker avec mount limite. Plus de cout pour le PoC.
Recommandation : Option 1. Workspace jetable, recree a chaque session de
test. Documenter le dir exact dans .env.
Pourquoi ca compte : c'est la vraie frontiere de confiance, plus que le token Lua.
TODO: answer
A3. Cycle de vie de la session
- Option 1 - session unique partagee : le proxy cree une session au boot, toutes les turtles tapent dedans. Conflits de memoire conversationnelle.
- Option 2 - session par turtle, persistee cote turtle : la turtle stocke
sessionIddanssettings; le proxy se contente de relayer. - Option 3 - session ephemere par prompt : chaque
/askcree+detruit. Pas de continuite. Plus simple, perd le contexte. - Option 4 - session par turtle, mappee cote proxy : table
turtleId -> sessionIdcote proxy. Necessite d'identifier la turtle.
Recommandation : Option 2. Le proxy reste sans etat ; la turtle envoie
sessionId optionnel, le proxy en cree une si absent et le renvoie dans la
reponse.
TODO: answer
A4. Sync vs async pour /ask
POST /session/:id/message peut bloquer longtemps. CC:Tweaked a des
timeouts HTTP (~30s typiques, depend du serveur).
- Option 1 - sync stricte : le proxy attend la fin et renvoie. Risque de timeout cote turtle.
- Option 2 - async + polling :
POST /ask->jobId,GET /ask/:jobIdpour poll. Plus robuste, plus complexe. - Option 3 - keepalive / chunked : inutile en HTTP CC standard.
- Option 4 - sync now, async later : on commence en sync (prompts courts
pour PoC), on documente la limite, on bascule async si on l'atteint. API
/askreste compatible.
Recommandation : Option 4.
TODO: answer
A5. Topologie de deploiement
Le plan source dit "deploiement HTTPS, RP, firewall, exposition publique geres separement par Guillaume".
- Option 1 - tout sur ta machine perso :
opencode serve+cc-agent-proxyen localhost, expose via tunnel (Cloudflare, ngrok). - Option 2 - VPS dedie : meme hote distant, HTTPS via Caddy/nginx.
- Option 3 - homelab : Option 2 mais chez toi, derriere ton RP existant.
- Option 4 - deux hotes separes : OpenCode sur GPU dedie, proxy ailleurs.
Conditionne : auth (A6), OPENCODE_BASE_URL, qui termine TLS.
Recommandation : Option 3 si tu as un homelab, sinon Option 1. Proxy + opencode sur le meme hote, loopback entre les deux, proxy expose en HTTPS.
TODO: answer
A6. Modele d'authentification turtle <-> proxy
- Option 1 - token unique partage : un seul
PROXY_TOKEN, meme valeur sur toutes les turtles viasettings. Pas de revocation granulaire. - Option 2 - token par turtle : table de tokens cote proxy. Permet revocation par turtle.
- Option 3 - HMAC signe : turtle envoie
os.getComputerID()+ HMAC. Plus complexe, peu de gain sans rotation. - Option 4 - allowlist IP : impossible, la turtle sort via le proxy HTTP du serveur Minecraft.
Recommandation : Option 1. Helper isTokenValid(token) pour permettre
une table plus tard sans refactor.
TODO: answer
A7. Forme de la reponse renvoyee a la turtle
OpenCode retourne parts, tool calls, code blocks. La turtle a peu de RAM.
- Option 1 - texte plat :
{ reply }. Tout ce qui n'est pas texte est jete. - Option 2 - texte + meta :
{ reply, model, tokens, sessionId }. - Option 3 - parts brutes : la turtle parse. Trop couteux.
- Option 4 - texte plat + troncature :
{ reply, sessionId, truncated }avecMAX_REPLY_CHARS(ex. 4 KiB). Logs proxy gardent le complet.
Recommandation : Option 4.
TODO: answer
A8. Scope minimal v1 (in et out)
- Option 1 - hello world : turtle envoie "ping", recoit "pong". Une seule route, sync, pas de session persistante.
- Option 2 - conversation 1 turtle / 1 session : echange sur plusieurs prompts, sessionId persistante.
- Option 3 - + outils OpenCode : agent peut lire/ecrire un fichier dans le workspace.
- Option 4 - + multi-turtle : deux turtles en parallele sans collision.
Recommandation : Option 2.
Hors-scope explicite du PoC : async, multi-turtles, package ccpm, deploiement
auto, Docker, quotas, UI terminale riche, streaming OpenCode (/event).
TODO: answer
B. Scope, contraintes de temps, cleanup
B1. Limite de temps et plan de bascule
Si la forme de l'API OpenCode bloque, qu'est-ce qu'on fait ?
Recommandation : une seule passe d'implementation. Si l'API OpenCode bloque, on arrete d'abstraire et on capture le contrat reel via curl avant de coder.
TODO: answer
B2. Rollback / cleanup post-PoC
Comment on nettoie une fois la demo faite ?
Recommandation : stop proxy, stop opencode serve, supprimer sessions de
test, rotation du PROXY_TOKEN et du OPENCODE_PASSWORD utilises.
TODO: answer
C. OpenCode upstream : contrat et comportement
C1. Version OpenCode et Basic Auth verifies au premier run
OpenCode evolue. Le plan suppose Basic Auth active. A valider.
Recommandation : enregistrer opencode --version utilise lors de la
validation ; pinger /global/health avec Basic Auth via curl avant de coder
le client.
TODO: answer
C2. Spec exacte de POST /session/:id/message
Plan : { parts: [{ type: "text", text }], model? }. Marque comme inconnu.
Recommandation : curl http://localhost:4096/doc une fois OpenCode lance,
capturer un sample reel de reponse, le commiter sanitize dans
cc-agent-proxy/test/fixtures/.
TODO: answer
C3. Provider et modele LLM
OPENCODE_MODEL optionnel. Quel provider Guillaume utilise (anthropic,
openai, ollama local) ?
Recommandation : laisser defaut OpenCode, ne passer model que si
OPENCODE_MODEL est defini dans .env.
TODO: answer
C4. Timeout amont vers OpenCode
Recommandation : REQUEST_TIMEOUT_MS configurable, defaut 60s. Au dela ->
504 cote proxy.
TODO: answer
C5. Exposition des erreurs OpenCode au turtle
Recommandation : pas de stack trace ni secret. Renvoyer
{ error: "...", code: "..." } avec message public court ; detail dans les
logs proxy uniquement.
TODO: answer
C6. SessionId auto-creation et recovery si stale
Deux comportements distincts :
sessionIdabsent dans la requete -> proxy en cree un (recommande oui).sessionIdfourni mais 404 amont -> ?- Option A : recreer silencieusement.
- Option B : renvoyer
{ error: "session_expired" }, laisser la turtle decider (opencc --new).
Recommandation : Option B. Auto-recreation cacherait une perte de contexte deroutante.
TODO: answer
C7. Comportement quand OpenCode est down
- 502/503 brutal ?
- Retry interne avec backoff ?
/healthreflete-t-il l'etat upstream ?
Recommandation : pas de retry auto, 502 + JSON { error: "upstream_down" }.
/health inclut un champ opencode: "up"/"down".
TODO: answer
C8. Gestion des parts non-textuelles (tools, code blocks)
Recommandation : concatener les parts texte, ignorer les non-textuelles dans
la reply, mais logger un compteur cote proxy (parts_text, parts_tool,
parts_other).
TODO: answer
D. Proxy API shape
D1. Routes minimales du proxy
Plan : /health, /ask, /session.
/sessionobligatoire ou implicite via/ask?/healthinclut upstream ?
Recommandation :
GET /health->{ proxy: "ok", opencode: "up"/"down" }.POST /ask-> obligatoire, body{ prompt, sessionId? }, response{ sessionId, reply, truncated }.POST /session-> garder, retourne{ sessionId }. Pratique pour initialiser ou nommer une session ; ne casse pas le PoC si on ne l'expose pas a la turtle.
TODO: answer
D2. Codes HTTP stables pour la turtle
Recommandation :
200succes400requete invalide (prompt vide, body malforme)401token absent/invalide413prompt trop long502OpenCode down504OpenCode timeout500erreur proxy inattendue
TODO: answer
D3. Limites taille de requete
- Max prompt length ?
- Max body size HTTP ?
Recommandation : prompt max 8 KiB (MAX_PROMPT_CHARS env), body max
32 KiB cote Fastify. Pas de rate-limit dans le PoC.
TODO: answer
D4. Request IDs / correlation
Recommandation : requestId auto-genere cote proxy, present dans tous les
logs, non expose dans la reponse JSON (juste dans header x-request-id pour
debug optionnel).
TODO: answer
E. Stack Node / TypeScript
E1. Runtime et gestionnaire de paquets
Recommandation : Node 20+, pnpm. Sinon npm si tu prefers vanilla.
TODO: answer
E2. Strictness TypeScript
Recommandation : "strict": true + noUncheckedIndexedAccess.
exactOptionalPropertyTypes evite (bruit pour peu de valeur en PoC).
TODO: answer
E3. Framework HTTP et validation
Plan : Fastify + Zod.
Recommandation : Fastify + Zod + fastify-type-provider-zod pour
validation automatique des routes. Confirme ou propose autre.
TODO: answer
E4. Logging
Recommandation : Pino (default Fastify). LOG_LEVEL=info par defaut,
debug en dev. Flag LOG_BODIES=true pour inclure prompt/reply ; off
par defaut. Jamais logger token ni OPENCODE_PASSWORD. Logs : status,
duree, sessionId (12 premiers chars), requestId, longueurs prompt/reply.
TODO: answer
E5. Process / lancement / deploiement
tsxen dev,node dist/index.jsaprestscen prod ?- Dockerfile ? systemd ? pm2 ?
- Bind
0.0.0.0? Shutdown gracieux ?
Recommandation : tsx en dev, node dist en prod, pas de Docker, pas de
systemd dans le repo (tu deploies a la main). 0.0.0.0 car expose derriere
RP. Shutdown : app.close() sur SIGTERM, rien de plus.
TODO: answer
E6. Tests Node
Plan : vitest, fetch mocke, app.inject(). Pas d'objectif de couverture %.
Recommandation : couvrir /ask happy path, 401, OpenCode 5xx mock,
prompt vide, prompt trop long, sessionId 404 amont. Fixture OpenCode reelle
(cf C2) utilisee dans opencode.test.ts.
TODO: answer
E7. Variables d'env (en plus du tableau du plan)
A ajouter :
LOG_LEVEL(info/debug)LOG_BODIES(true/false)MAX_REPLY_CHARS(defaut 4096)MAX_PROMPT_CHARS(defaut 8192)REQUEST_TIMEOUT_MS(defaut 60000)OPENCODE_WORKSPACE(info uniquement, documente le dir A2)
Recommandation : oui, ajouter les six. .env.example les liste, jamais de
secret reel.
TODO: answer
E8. CORS, body parsing, etat en memoire
- CORS : non, ce n'est pas un navigateur.
- Body : JSON only.
- Etat : stateless (cf A3 Option 2).
Recommandation : garder tel quel. Confirme ou contredit.
TODO: answer
F. Auth, secrets, exposition publique
F1. Generation du PROXY_TOKEN
Recommandation : genere a la main (openssl rand -hex 32), ecrit dans
.env. Si absent au boot, le proxy refuse de demarrer (fail-fast).
TODO: answer
F2. HTTPS vs HTTP cote turtle
ATM10 accepte les deux si dans http.rules. Token nu en HTTP = leak.
Recommandation : HTTPS uniquement, meme en homelab, via le RP existant.
TODO: answer
F3. Saisie du token sur la turtle
Le secret ne doit jamais etre dans un fichier Lua commit.
Recommandation : settings set opencc.proxy_token <token> + settings.save()
manuellement dans l'environnement de chaque turtle. Documenter dans le
quickstart.
TODO: answer
F4. Token en query string ?
Recommandation : non, header uniquement. Les query strings se retrouvent dans les logs RP/proxy.
TODO: answer
F5. Headers acceptes : Bearer et X-Proxy-Token
Plan : les deux pour simplicite CC.
Recommandation : accepter Authorization: Bearer <token> ET X-Proxy-Token,
documenter Bearer comme prefere. X-Proxy-Token reste seulement si CC pose
souci avec Authorization (a tester).
TODO: answer
F6. Comparaison constant-time
Recommandation : crypto.timingSafeEqual apres normalisation (longueur
egalisee) pour eviter le throw sur taille differente.
TODO: answer
F7. Rotation du token
Recommandation : hors scope PoC. Redemarrer proxy + reconfigurer turtles = acceptable.
TODO: answer
F8. Rate-limit et allowlist IP
Recommandation : ni l'un ni l'autre dans l'app. Si exposition publique le demande, c'est au RP / firewall de gerer (donc en dehors du repo).
TODO: answer
F9. Basic Auth OpenCode
Recommandation : credentials uniquement via env (OPENCODE_USERNAME,
OPENCODE_PASSWORD), valides au boot, sinon fail-fast.
TODO: answer
G. Programme Lua opencc
G1. Emplacement et packaging
Plan : apis/libopencc.lua, programs/opencc.lua, tests/opencc.lua.
Recommandation : pas de packages/tos-agent/ccpm.json avant validation
in-game (cf I3). Fichiers nus dans apis/ et programs/ pendant le PoC.
TODO: answer
G2. Source de config (settings vs fichier vs CLI)
Clefs settings :
opencc.proxy_url(URL complete HTTPS recommandee)opencc.proxy_tokenopencc.session_id(auto-remplie)opencc.timeout(optionnel)
Recommandation : settings uniquement + flags CLI (--url, --token)
qui overrident pour le debug. Si proxy_url ou proxy_token manque,
message clair + exit non-zero. Pas de host/port separes.
TODO: answer
G3. Format CLI
opencc --help,--versionopencc "prompt"(argv) ou interactif viaread()si pas d'arg- REPL multi-tour ?
Recommandation : pas de REPL. Un prompt par invocation. Le contexte vient
de la session_id persistante.
TODO: answer
G4. Persistance et reset de la session
- Persistance :
settings.set('opencc.session_id', id); settings.save()ou fichier dedie ? - Reset :
opencc --new?--reset-session?
Recommandation : settings (coherence G2) ; flag opencc --new qui
efface opencc.session_id et cree une nouvelle session via le prochain
/ask.
TODO: answer
G5. Affichage de la reponse
- Print brut ?
- Word-wrap manuel ?
- Pagination ?
- Print metadata (sessionId, model) ?
Recommandation : print + word-wrap simple sur largeur terminal. Pas de
pagination. Pas de metadata sauf en mode --verbose (a ajouter plus tard).
TODO: answer
G6. Erreurs reseau et http.rules
http.post peut renvoyer nil (handshake ko, host bloque par http.rules)
ou un response status != 200.
Recommandation :
nil-> "proxy injoignable. Verifieopencc.proxy_urlet que l'hote est autorise danshttp.rules".401-> "token invalide".413-> "prompt trop long".502/504-> "agent indisponible / trop lent".- Autre -> message generique + status code.
TODO: answer
G7. Timeout HTTP CC
http.post n'a pas de parametre timeout standard. CC:Tweaked >= 1.97 a
http.request avec timeout ; a verifier sur ATM10.
Recommandation : exposer opencc.timeout. Si CC l'accepte, on l'utilise ;
sinon documenter la limite et compter sur A4 Option 4 pour la suite.
TODO: answer
G8. Pattern factory et JSON
Repo : apis/libccpm.lua est une factory avec opts.http injectable. Meme
chose pour libopencc ? Encodage JSON via textutils.serialiseJSON ?
Recommandation : factory : local createOpencc = require('/apis/libopencc'); local opencc = createOpencc({ http = http, settings = settings, computerCraftTimeout = ... }). JSON via textutils.serialiseJSON / unserialiseJSON.
TODO: answer
G9. Trim / multiline / vide
Recommandation : trim final, refus si prompt vide apres trim. Multiline preserve.
TODO: answer
G10. Tests Lua
Plan : injection d'un faux http via libtest.
Recommandation : tests/opencc.lua avec libtest, faux http qui verifie
URL, headers (Bearer + Content-Type), body JSON, parsing succes, parsing
erreur. Hors-scope : E2E reel avec vrai proxy.
TODO: answer
H. Tests et validation
H1. Fixture OpenCode capturee
Recommandation : oui (cf C2). Sanitize, store dans
cc-agent-proxy/test/fixtures/session-message.json. Sert de reference pour
opencode.test.ts.
TODO: answer
H2. E2E manuel vs automatise
Recommandation : manuel pour le PoC. Documenter les commandes (cf H7).
TODO: answer
H3. CraftOS-PC headless avant l'in-game
Recommandation : oui si just craftos --headless peut faire http.post.
Premier roundtrip en local avant ATM10.
TODO: answer
H4. Pre-requis ATM10 a documenter
A documenter :
httpenable cote serveur.- Hote du proxy autorise dans
http.rules(allow*.tondomaine.tld:443ou IP). - DNS / IP joignable depuis le serveur Minecraft.
- Token + URL definis dans les settings de la turtle.
- Provider LLM authentifie cote OpenCode.
Recommandation : checklist dans docs/opencc-quickstart.md ecrite apres
la premiere validation reelle.
TODO: answer
H5. Hooks just check et just test
pre-commit = just check test (Lua + luacheck). Faut-il y mettre les
tests Node ?
Recommandation : non. Recipe separee just node-check (eslint+prettier)
et just node-test (vitest) dans un sous-Justfile de cc-agent-proxy/.
Eviter une dependance implicite Node pour les commits Lua.
TODO: answer
H6. CI
Repo : pas de CI publique declaree, juste hooks locaux.
Recommandation : pas de CI pour le PoC. Plus tard, eventuellement un workflow
GH Actions node-test + lua-check.
TODO: answer
H7. Transcript de validation manuelle
Recommandation : apres premier roundtrip reussi, enregistrer les commandes
exactes et outputs sanitize dans docs/opencc-quickstart.md, incluant un
deuxieme /ask avec le meme sessionId pour prouver la persistance.
TODO: answer
I. Architecture / coherence repo
I1. Reuse de apis/net.lua
Recommandation : non. net.lua est modem/rednet. http.post direct
cote Lua est le bon choix.
TODO: answer
I2. Reuse de apis/eventloop.lua
Recommandation : non. Le PoC est request/response synchrone. Re-evaluer si A4 passe en async (G1 post-PoC).
TODO: answer
I3. Packaging ccpm
Recommandation : post-PoC. Apres validation in-game, creer
packages/tos-agent/ccpm.json :
files:apis/libopencc.lua,programs/opencc.luadependencies:tos-coreautostart: aucun- Ajouter dans
packages/index.json.
Declencheur : PoC reussi + au moins une raison de reinstaller/partager.
TODO: answer
I4. ADR
Recommandation : ADR-0012 "external agent bridge via HTTPS proxy" ecrit apres validation. Justifie la presence d'un sous-dossier Node dans un repo Lua.
TODO: answer
I5. Conformite CLAUDE.md
- Bump
_VERSIONsur chaque module Lua modifie. --version/--helpsuropencc.- Lua : 2-space indent,
;,local function. - Node : suit Prettier default.
Recommandation : appliquer tel quel.
TODO: answer
J. Post-PoC (evolutions, hors scope mais a ne pas peindre dans un coin)
Reponds seulement si avis fort.
J1. Mode async
Trigger : reponses LLM frequemment > timeout CC/proxy.
Recommandation : ajouter POST /ask?async=1 + GET /ask/:jobId/status.
Backward-compatible. opencc apprend a poll.
TODO: answer (optionnel)
J2. SSE / WebSocket pour streaming
Pas de SSE en CC:Tweaked, mais http.websocket existe.
Recommandation : proxy WS dedie si streaming devient utile. Hors-scope PoC.
TODO: answer (optionnel)
J3. Multi-turtle isolation et identite
Trigger : > 1 turtle/utilisateur sur le meme proxy.
Recommandation : avec A3 Option 2 c'est deja gratuit niveau sessions. Pour
quotas, ajouter turtleId (= os.getComputerID()) au body et a la table de
tokens.
TODO: answer (optionnel)
J4. Outils OpenCode (lecture/ecriture fichiers)
Recommandation : sandbox stricte (mount lecture-seule + un dir scratch). Hors-scope PoC.
TODO: answer (optionnel)
J5. Replacement de http.post par apis/net.lua
Recommandation : probablement jamais. net.lua est routeur/rednet, pas
sortie HTTP.
TODO: answer (optionnel)
J6. Observabilite (metrics, traces)
Recommandation : ignorer en PoC. Pino logs suffisent. Prometheus exporter plus tard si quotas.
TODO: answer (optionnel)
K. Inconnues a valider au premier run
Pas de reponse texte ; coche au fur et a mesure.
- Version exacte d'OpenCode utilisee (
opencode --version). - Format reel des
partsretournees parPOST /session/:id/message. - Comportement OpenCode si
modelest passe vs absent. - Basic Auth vraiment necessaire dans la config de Guillaume.
- Timeout effectif
http.postcote CC sur ATM10 (et si CC:Tweaked version supporte un parametre timeout). - Reaction OpenCode si on hammer une session morte (404 ? autre ?).
- Limite de longueur d'un message accepte par OpenCode.
- Cas ou le provider LLM est down : OpenCode renvoie quoi ?
- CraftOS-PC headless peut-il faire
http.postHTTPS vers le proxy en local ?
L. Quoi repondre en priorite
Si tu veux aller vite, reponds dans cet ordre. Le reste decoule.
- A1 - critere d'acceptation
- A2 - workspace OpenCode (frontiere de confiance)
- A3 - cycle session
- A4 - sync vs async
- A5 - topologie deploiement
- A6 - auth turtle <-> proxy
- A7 - forme de la reponse
- A8 - scope v1
Une fois ces 8 trances, on peut souvent ignorer les sections B-J pour le "go/no-go" et juste valider les recommandations par defaut. Les detailler n'apporte de la valeur que si tu veux devier d'une recommandation precise.