diff --git a/.plans/opencc-agent-proxy-plan.md b/.plans/opencc-agent-proxy-plan.md new file mode 100644 index 0000000..fd0ba00 --- /dev/null +++ b/.plans/opencc-agent-proxy-plan.md @@ -0,0 +1,217 @@ +# Plan - PoC `cc-agent-proxy` + programme Lua `opencc` + +> A executer ailleurs, pas sur cette machine. Deploiement HTTPS, reverse proxy, firewall et exposition publique geres separement par Guillaume. + +## 1. Objectif + +Piloter un agent OpenCode depuis une turtle ComputerCraft sur Minecraft / ATM10. + +```text +Turtle --HTTP + token--> cc-agent-proxy --HTTP Basic Auth--> opencode serve +opencc.lua Node/TypeScript proxy OpenCode headless +``` + +## 2. Faisabilite + +`opencode serve` lance un serveur HTTP headless exposant une spec OpenAPI 3.1 sur `/doc`. + +Routes utiles pour le PoC : + +| Methode | Route | Usage | +|---|---|---| +| `GET` | `/global/health` | healthcheck amont | +| `POST` | `/session` | cree une session | +| `POST` | `/session/:id/message` | envoie un message et attend la reponse | +| `POST` | `/session/:id/prompt_async` | envoie en asynchrone | +| `GET` | `/session/:id/message` | liste les messages | +| `GET` | `/event` | flux SSE | + +Conclusion : le proxy n'est pas strictement necessaire, mais il rend le client Lua beaucoup plus simple. + +## 3. Pourquoi garder le proxy + +- Expose une API turtle simple : `POST /ask { prompt, sessionId? }` vers `{ sessionId, reply }`. +- Gere le Basic Auth OpenCode cote Node. +- Garde cote turtle un token Bearer simple. +- Aplatit les reponses OpenCode pour limiter RAM et complexite Lua. +- Permet plus tard async, polling, logs, quotas ou allowlist sans changer le programme Lua. + +## 4. Partie A - `cc-agent-proxy` + +Creer un dossier `cc-agent-proxy/` dans le repo. + +Stack : + +- Node 20+. +- TypeScript strict. +- Fastify. +- Vitest. +- Zod. +- Config via env validee, avec `.env.example`. + +Arborescence : + +```text +cc-agent-proxy/ + package.json + tsconfig.json + .env.example + src/ + config.ts + opencode.ts + auth.ts + server.ts + index.ts + test/ + config.test.ts + opencode.test.ts + auth.test.ts + server.test.ts +``` + +Config : + +| Variable | Defaut | Role | +|---|---|---| +| `PROXY_HOST` | `0.0.0.0` | interface d'ecoute | +| `PROXY_PORT` | `7070` | port du proxy | +| `PROXY_TOKEN` | requis | secret turtle <-> proxy | +| `OPENCODE_BASE_URL` | `http://127.0.0.1:4096` | URL OpenCode | +| `OPENCODE_USERNAME` | `opencode` | user Basic Auth | +| `OPENCODE_PASSWORD` | requis | password Basic Auth | +| `OPENCODE_MODEL` | optionnel | modele a passer a OpenCode | + +Routes proxy : + +| Methode | Route | Auth | Usage | +|---|---|---|---| +| `GET` | `/health` | non | etat proxy + `opencode.health()` | +| `POST` | `/ask` | oui | cree/reutilise session, envoie prompt, retourne reponse | +| `POST` | `/session` | oui, optionnel | cree une session explicite | + +Client OpenCode : + +- `health()` appelle `GET /global/health`. +- `createSession(title?)` appelle `POST /session`. +- `sendMessage(sessionId, text)` appelle `POST /session/:id/message`. +- `Authorization: Basic base64(username:password)`. +- Body message : `{ parts: [{ type: "text", text }], model?: ... }`. +- Extraire les `parts` texte de la reponse. +- A valider au premier run reel : format exact des `parts`. + +Auth proxy : + +- Accepter `Authorization: Bearer `. +- Accepter aussi `X-Proxy-Token` pour simplicite CC si besoin. +- Comparaison constant-time avec `crypto.timingSafeEqual`. +- Reponse `401` si absent ou invalide. +- Ne jamais logger le token. + +Tests Node en scope du PoC : + +- `config.test.ts` : valeurs par defaut, erreurs si secrets manquants. +- `opencode.test.ts` : `fetch` mocke pour health/session/message/parsing. +- `auth.test.ts` : token valide, invalide, absent. +- `server.test.ts` : `app.inject()` pour `/health`, `/ask`, `401` sans token. + +## 5. Partie B - programme Lua `opencc` + +Creer : + +```text +apis/libopencc.lua +programs/opencc.lua +tests/opencc.lua +``` + +Programme : + +- `opencc --help`. +- `opencc --version`. +- `opencc "prompt"` ou prompt interactif via `read()`. +- Appelle `http://:/ask`. +- Header `Content-Type: application/json`. +- Header `Authorization: Bearer `. +- Body `textutils.serialiseJSON({ prompt = prompt, sessionId = sessionId })`. +- Parse la reponse JSON. +- Affiche `reply`. +- Memorise `sessionId` pour reutilisation si on choisit une session persistante. + +Config Lua : + +- Utiliser `settings` ComputerCraft en priorite : +- `opencc.proxy_url`. +- `opencc.proxy_token`. +- `opencc.session_id`. +- `opencc.timeout`, si applicable. +- Autoriser un fallback hardcode uniquement pour PoC local, mais ne pas committer de secret reel. +- Stockage session possible dans un petit fichier local, par exemple `/.opencc-session`, si plus simple que `settings`. + +Tests Lua : + +- En scope si simple : tester `libopencc` sans vraie requete HTTP via injection d'un faux client `http`. +- Tester construction URL, headers, body JSON, parsing succes, parsing erreur. +- Hors scope PoC si trop couteux : test end-to-end avec vrai proxy Node et vrai OpenCode. +- Le test reel HTTP se fera manuellement via CraftOS-PC puis en jeu. + +Note repo : + +- Le reseau existant `apis/net.lua` est oriente modem/rednet/router, pas HTTP sortant. +- Pour ce PoC, garder `http.post` direct cote Lua est le bon choix. +- Le packaging existant du repo est `ccpm` / `packages/*/ccpm.json`, pas `cube`. +- Ne pas integrer au packaging tant que le PoC n'est pas valide ; ajouter ensuite un package dedie type `tos-agent` si utile. + +## 6. Pre-requis CC:Tweaked / ATM10 + +- API `http` activee. +- Hote/IP du proxy autorise dans les `http.rules` cote serveur ATM10. +- Pas de CORS, car ce n'est pas un navigateur. +- Prompts courts pour le PoC, car `POST /session/:id/message` peut etre long. + +Evolution hors PoC : + +- Proxy utilise `prompt_async`. +- Turtle appelle `POST /ask` puis poll `GET /ask/:id/status`. +- Le proxy ecoute eventuellement `/event` SSE cote OpenCode. + +## 7. Ordre d'execution + +1. Bootstrap `cc-agent-proxy`. +2. Implementer `config.ts` + tests. +3. Implementer `opencode.ts` + tests avec `fetch` mocke. +4. Implementer `auth.ts`, `server.ts`, `/health`, `/ask` + tests `app.inject()`. +5. Valider manuellement avec `opencode serve` + `curl`. +6. Implementer `apis/libopencc.lua` + `programs/opencc.lua`. +7. Ajouter `tests/opencc.lua` si l'injection HTTP reste simple. +8. Tester via CraftOS-PC. +9. Tester en jeu sur ATM10 apres configuration `http.rules`. +10. Documenter les commandes de lancement et les pre-requis. + +## 8. Validation manuelle + +```bash +OPENCODE_SERVER_PASSWORD=xxx opencode serve \ + --hostname 127.0.0.1 \ + --port 4096 +``` + +```bash +curl -s http://localhost:7070/health +``` + +```bash +curl -s -X POST http://localhost:7070/ask \ + -H "Authorization: Bearer $PROXY_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"prompt":"dis bonjour"}' +``` + +## 9. Points de vigilance + +- Format reel des `parts` OpenCode a valider au premier run. +- OpenCode doit avoir un provider authentifie. +- Timeouts longs cote agent vs limites HTTP CC:Tweaked. +- `http.rules` cote serveur ATM10. +- `PROXY_TOKEN` jamais committe. +- `OPENCODE_PASSWORD` jamais committe. +- Le proxy doit rester minimal tant que le PoC n'a pas prouve la boucle complete.