From 47a2f9971dea5b2473455af6b7ca43e30fea7050 Mon Sep 17 00:00:00 2001 From: Nilton Constantino Date: Sun, 18 Jan 2026 06:43:24 +0000 Subject: [PATCH] add devtools-protocol --- crates/host-desktop/src/prometeu_runner.rs | 2 + crates/prometeu-core/src/debugger_protocol.rs | 3 + .../examples/basic_session.jsonl | 4 + .../examples/breakpoint_flow.jsonl | 2 + devtools-protocol/examples/handshake.jsonl | 2 + devtools-protocol/protocol.json | 61 +++++++++++++++ devtools-protocol/protocol.md | 77 +++++++++++++++++++ 7 files changed, 151 insertions(+) create mode 100644 devtools-protocol/examples/basic_session.jsonl create mode 100644 devtools-protocol/examples/breakpoint_flow.jsonl create mode 100644 devtools-protocol/examples/handshake.jsonl create mode 100644 devtools-protocol/protocol.json create mode 100644 devtools-protocol/protocol.md diff --git a/crates/host-desktop/src/prometeu_runner.rs b/crates/host-desktop/src/prometeu_runner.rs index 916e7d99..c5cda79f 100644 --- a/crates/host-desktop/src/prometeu_runner.rs +++ b/crates/host-desktop/src/prometeu_runner.rs @@ -115,6 +115,7 @@ impl PrometeuRunner { // Enviar Handshake let handshake = DebugResponse::Handshake { + protocol_version: DEVTOOLS_PROTOCOL_VERSION, runtime_version: "0.1".to_string(), cartridge: HandshakeCartridge { app_id: self.firmware.os.current_app_id, @@ -693,6 +694,7 @@ mod tests { let n = stream.read(&mut buf).expect("Deve ler handshake"); let resp: serde_json::Value = serde_json::from_slice(&buf[..n]).expect("Handshake deve ser JSON válido"); assert_eq!(resp["type"], "handshake"); + assert_eq!(resp["protocol_version"], DEVTOOLS_PROTOCOL_VERSION); // Envia start via JSON stream.write_all(b"{\"type\":\"start\"}\n").expect("Conexão deve estar aberta para escrita"); diff --git a/crates/prometeu-core/src/debugger_protocol.rs b/crates/prometeu-core/src/debugger_protocol.rs index 40511763..49a8b5ea 100644 --- a/crates/prometeu-core/src/debugger_protocol.rs +++ b/crates/prometeu-core/src/debugger_protocol.rs @@ -1,6 +1,8 @@ use serde::{Deserialize, Serialize}; use crate::model::AppMode; +pub const DEVTOOLS_PROTOCOL_VERSION: u32 = 1; + #[derive(Debug, Serialize, Deserialize)] #[serde(tag = "type")] pub enum DebugCommand { @@ -31,6 +33,7 @@ pub enum DebugCommand { pub enum DebugResponse { #[serde(rename = "handshake")] Handshake { + protocol_version: u32, runtime_version: String, cartridge: HandshakeCartridge, }, diff --git a/devtools-protocol/examples/basic_session.jsonl b/devtools-protocol/examples/basic_session.jsonl new file mode 100644 index 00000000..d31b1797 --- /dev/null +++ b/devtools-protocol/examples/basic_session.jsonl @@ -0,0 +1,4 @@ +{"type":"start"} +{"type":"pause"} +{"type":"getState"} +{"type":"resume"} diff --git a/devtools-protocol/examples/breakpoint_flow.jsonl b/devtools-protocol/examples/breakpoint_flow.jsonl new file mode 100644 index 00000000..1676b80d --- /dev/null +++ b/devtools-protocol/examples/breakpoint_flow.jsonl @@ -0,0 +1,2 @@ +{"type":"setBreakpoint","pc":120} +{"event":"breakpointHit","pc":120,"frame_index":10} diff --git a/devtools-protocol/examples/handshake.jsonl b/devtools-protocol/examples/handshake.jsonl new file mode 100644 index 00000000..753962b5 --- /dev/null +++ b/devtools-protocol/examples/handshake.jsonl @@ -0,0 +1,2 @@ +{"type":"handshake","protocol_version":1,"runtime_version":"0.1","cartridge":{"app_id":1,"title":"ColorSquare","app_version":"1.0.0","app_mode":"Debug"}} +{"type":"start"} diff --git a/devtools-protocol/protocol.json b/devtools-protocol/protocol.json new file mode 100644 index 00000000..7c79bdd3 --- /dev/null +++ b/devtools-protocol/protocol.json @@ -0,0 +1,61 @@ +{ + "protocol_version": 1, + "handshake": { + "runtime_to_client": { + "type": "handshake", + "fields": ["protocol_version", "runtime_version", "cartridge"] + }, + "client_to_runtime": { + "type": "start|ok" + } + }, + "requests": [ + { + "name": "pause", + "params": [] + }, + { + "name": "resume", + "params": [] + }, + { + "name": "step", + "params": [] + }, + { + "name": "stepFrame", + "params": [] + }, + { + "name": "getState", + "params": [], + "response": ["pc", "stack_top", "frame_index", "app_id"] + }, + { + "name": "setBreakpoint", + "params": ["pc"] + }, + { + "name": "clearBreakpoint", + "params": ["pc"] + } + ], + "events": [ + { + "name": "breakpointHit", + "fields": ["pc", "frame_index"] + }, + { + "name": "log", + "fields": ["level", "source", "msg"] + }, + { + "name": "telemetry", + "fields": ["frame_index", "vm_steps", "syscalls", "cycles"] + }, + { + "name": "cert", + "fields": ["rule", "used", "limit", "frame_index"] + } + ] +} diff --git a/devtools-protocol/protocol.md b/devtools-protocol/protocol.md new file mode 100644 index 00000000..8c91209f --- /dev/null +++ b/devtools-protocol/protocol.md @@ -0,0 +1,77 @@ +# Prometeu DevTools Protocol + +Este documento descreve o protocolo de comunicação entre o Runtime do Prometeu e ferramentas de desenvolvimento (como o Prometeu Debugger). + +## Visão Geral + +O protocolo é baseado em JSON, enviado via uma conexão de transporte (geralmente TCP). Cada mensagem é um objeto JSON em uma única linha (JSONL). + +## Versionamento + +O protocolo possui uma versão explícita definida no campo `protocol_version`. + +- Mudanças incompatíveis (breaking changes) incrementam a versão principal. +- Novos campos em mensagens existentes devem ser opcionais sempre que possível para manter compatibilidade retroativa. + +A versão atual do protocolo é: **1** + +## Handshake + +Ao conectar, o Runtime envia uma mensagem de handshake para o cliente. + +### Runtime -> Cliente + +```json +{ + "type": "handshake", + "protocol_version": 1, + "runtime_version": "0.1.0", + "cartridge": { + "app_id": 1, + "title": "ColorSquare", + "app_version": "1.0.0", + "app_mode": "Debug" + } +} +``` + +### Cliente -> Runtime + +O cliente deve responder com `start` ou `ok` para iniciar a sessão. + +```json +{ "type": "start" } +``` + +## Requisições (Requests) + +O cliente pode enviar as seguintes requisições para controlar a execução: + +| Nome | Parâmetros | Descrição | +|------|------------|-----------| +| `pause` | `[]` | Pausa a execução da VM. | +| `resume` | `[]` | Retoma a execução da VM. | +| `step` | `[]` | Executa uma única instrução (PC). | +| `stepFrame` | `[]` | Executa até o próximo frame. | +| `getState` | `[]` | Retorna o estado atual da VM (`pc`, `stack_top`, `frame_index`, `app_id`). | +| `setBreakpoint` | `{"pc": number}` | Define um breakpoint no endereço especificado. | +| `clearBreakpoint` | `{"pc": number}` | Remove um breakpoint no endereço especificado. | + +## Respostas (Responses) + +Algumas requisições geram respostas específicas. Por exemplo, `getState` retorna o estado da VM. + +## Eventos (Events) + +O Runtime pode enviar eventos assíncronos para o cliente: + +| Nome | Campos | Descrição | +|------|--------|-----------| +| `breakpointHit` | `pc`, `frame_index` | Emitido quando a execução atinge um breakpoint. | +| `log` | `level`, `source`, `msg` | Emitido quando o código em execução gera um log. | +| `telemetry` | `frame_index`, `vm_steps`, `syscalls`, `cycles` | Estatísticas de execução enviadas periodicamente. | +| `cert` | `rule`, `used`, `limit`, `frame_index` | Informações de certificação e limites de recursos. | + +## Exemplos + +Veja a pasta `examples/` para fluxos de mensagens completos.