Skip to content

feat(dashboard): Plugins screen (Layout A) — Phase 2 complete 🎉#30

Closed
diegosouzapw wants to merge 78 commits into
MarceloClaro:mainfrom
diegosouzapw:upstream/pr-10-screen-plugins
Closed

feat(dashboard): Plugins screen (Layout A) — Phase 2 complete 🎉#30
diegosouzapw wants to merge 78 commits into
MarceloClaro:mainfrom
diegosouzapw:upstream/pr-10-screen-plugins

Conversation

@diegosouzapw

Copy link
Copy Markdown

O que esta PR faz

Adiciona a quarta e última tela do Nexus Dashboard v3: Plugins. Configuração visual dos plugins declarados em opencode.json — toggle enabled, edição de opções com form gerado a partir de schema conhecido, preview-diff antes de apply atômico com backup automático.

Com esta PR, a fase Dashboard fica completa: 4 telas funcionais (Agents & Models, Health, Pipelines, Plugins) + Overview legado preservado.

Funcionalidades da tela

  • Tabela dos plugins atualmente declarados em opencode.json (4 internos + opcionalmente @omniroute/opencode-plugin se ativo)
  • Toggle enabled por plugin (controla presença no array plugin: [...])
  • Configure abre expansion inline com form de opções gerado a partir de schema hardcoded para plugins conhecidos
  • Pending counter + Preview Diff modal (mostra unified diff do opencode.json que será gerado)
  • Apply atômico: backup opencode.json.bak.<UTC-ts> → tempfile + os.replace → restore automático se write falhar
  • Restart-required notice persistente no header (plugins só recarregam ao reiniciar o CLI)
  • Modal "How to restart" com instruções passo-a-passo

JSONC-aware parser

opencode.json é JSONC (aceita // comments, /* block comments */, trailing commas). O parser usa state machine que respeita string literals — URLs com https:// são preservadas (não cortadas como faria um regex naïve).

Quando o arquivo tem comentários, a UI mostra banner de aviso explicando que Apply vai perder os comentários — mitigado por backup automático com timestamp.

Endpoints adicionados

Method Path Função
GET /api/plugins Lista plugins com schemas + has_comments flag
PUT /api/plugins/<id> Stage mudança (enabled e/ou options)
DELETE /api/plugins/<id>/pending Unstage
GET /api/plugins/pending Lista mudanças staged
GET /api/plugins/diff Unified diff preview
POST /api/plugins/apply Apply atômico + backup

Schemas hardcoded (5 plugins conhecidos)

  • ecosystem-sync — sem opções configuráveis
  • manus-evolve — sem opções
  • bernstein-sync — sem opções
  • antigravity-bridge — sem opções
  • @omniroute/opencode-plugin — 12 opções (providerId, displayName, baseURL, modelCacheTtl, 8 features flags em features.*)

Plugin desconhecido (não no schema) mostra "no schema available — edit opencode.json manually". Continua podendo toggle enabled.

Segurança

  • Pending pattern: nada escreve em opencode.json até Apply explícito
  • Backup obrigatório antes de qualquer write (opencode.json.bak.YYYYMMDDTHHMMSSZ)
  • Restore automático se write falhar
  • Pre-validação completa: plugin staged que não existe no config atual → 400 com lista
  • Options validadas contra schema (chaves desconhecidas rejeitadas)
  • Path traversal protegido no plugin id (regex)

Limitação conhecida (documentada na UI)

Apply destrói comentários JSONC do opencode.json. Mitigação: banner explícito quando has_comments=true + backup com timestamp. Pode ser endereçado em PR futura (parser JSONC com preservação de comentários).

Mudanças

Path Tipo Linhas
nexus/api/plugins_list.py novo 227 (JSONC parser string-state-aware)
nexus/api/plugin_changes.py novo 316 (staging + diff + apply)
nexus/dashboard/plugins.html novo 120
nexus/dashboard/assets/plugins.js novo 321
nexus/dashboard/assets/plugins.css novo 72
nexus/dashboard_server.py modified +26
.gitignore modified +3 (exclui opencode.json.bak.*, merged.tmp, pending-plugin-changes.json)

Reverter

git revert <commit-sha> — 4 commits. Backups opencode.json.bak.* permanecem no disco como safety net mesmo após revert.

Prints


🎉 Fase Dashboard completa

Com PR-6 a PR-10 mergeadas, o Nexus Dashboard v3.x ganha:

Tela PR Layout
Foundation (sidebar, API skeleton, ThreadingHTTPServer) PR-6
Agents & Models PR-7 Tabela densa com pending-changes
Health PR-8 Tabela unificada com SSE 5s
Pipelines PR-9 Tabs + drawer com terminal SSE
Plugins PR-10 Tabela + inline expansion

Próxima e última PR: fix(audit): consolidated fixes from PRs 1-10 code review — fixes pós-review (do_DELETE missing, /api/dados regression, dispatch bug em rotas com trailing slash, e 7 outros itens menores).

marce and others added 30 commits May 16, 2026 12:47
…ython-TS

Container registra 8 servicos core + 3 plugins TS. CommandRegistry bridge
para 14 comandos. PluginManager com discover_ts_plugins, register_in_container,
health_summary. 7 scripts Nexus com DI (from_container). 88/88 testes passando,
378/391 legado preservados. 100% backward compat.

docs: README v4.1.0, OPENCODE_ECOSYSTEM.md atualizados, DI_MIGRATION.md criado.
…editais-br v7.1, cache versionado e correção KeyError
…, command interfaces, and project-specific documentation.
…d refactor README

- Create GETTING_STARTED.md in PT-BR with installation guide, prerequisites,
  3 usage examples (/artigo, /reversa, /auto), and troubleshooting
- Create CONTRIBUTING.md in PT-BR with project structure, code standards,
  guides for adding agents/skills/MCPs, and PR process
- Create ROADMAP.md in PT-BR with version history, evolution cycles (evo-1
  to evo-8), and short/medium/long-term goals
- Refactor README.md: add Quick Start section after badges, add audience
  table, simplify Resumo with Detalhes Tecnicos subsection, move Diagramas
  section after Comandos Rapidos, add Documentacao links table

Co-Authored-By: Prof. Marcelo Claro <marceloclaro@gmail.com>
- GETTING_STARTED.md: guia de instalação e primeiros passos
- README.md: Quick Start, 'Para quem', diagramas movidos, resumo simplificado, tabela de docs
- CONTRIBUTING.md: guia para contribuidores com padrões de código e estrutura do projeto
- ROADMAP.md: histórico de versões e visão futura
- GLOSSARY.md: glossário de termos técnicos (MCP, DI, RAG, MASWOS, SEEKER, etc.)
- AGENTS_PTBR.md: tradução do AGENTS.md (chinês → PT-BR formal)
- TUTORIALS.md: 4 tutoriais práticos (artigo, quantum, reversa, MCPs)
- AGENTS.md: nota com link para versão em português

Co-Authored-By: Prof. Marcelo Claro <marceloclaro@gmail.com>
… rule, MCP count, link annotations

- Rename 'Quick Start' to 'Início Rápido' (PT-BR formal rule)
- Add missing '## Arquitetura Geral' heading for broken index anchor
- Remove duplicate horizontal rule between Arquitetura and Simulação sections
- Clarify MCP count in GETTING_STARTED.md (40 registered, 17 active)
- Mark TUTORIALS.md, GLOSSARY.md, AGENTS_PTBR.md links as '(em breve)'

Co-Authored-By: Prof. Marcelo Claro <marceloclaro@gmail.com>
docs: add onboarding documentation (GETTING_STARTED, CONTRIBUTING, ROADMAP) and refactor README
Conflitos em CONTRIBUTING.md, GETTING_STARTED.md, ROADMAP.md e README.md
resolvidos mantendo as versões criadas neste PR (conforme especificações
do usuário), com adições do main: header Arquitetura Geral e link Documentação
no índice.

Co-Authored-By: Prof. Marcelo Claro <marceloclaro@gmail.com>
docs: adicionar documentação de onboarding completa em PT-BR
Correções aplicadas:
- Corrigir '&' inválido em XML → '&amp;' (causava xmlParseEntityRef error)
- Adicionar markers coloridos por fase (arw-blue, arw-yellow, arw-red, arw-purple)
- Corrigir setas do ciclo para conectar corretamente todas as 5 fases
- Adicionar spokes tracejados do Health Monitor para cada fase
- Reposicionar Health Monitor com score 96/100
- Adicionar badge 'Health Score: 96/100'
- Ajustar layout para Phase 5 (VERIFICAR) ficar visível dentro do viewBox
- Corrigir loop-back de VERIFICAR → MONITORAR

Co-Authored-By: Prof. Marcelo Claro <marceloclaro@gmail.com>
…ling-svg

fix: corrigir self-healing.svg — erro XML, layout e conexões
…idade

Correções aplicadas em 6 SVGs:

academic-pipeline.svg:
- Corrigir atributo 'fontSize' → 'font-size' (XML inválido)
- Corrigir typo 'Basearado' → 'Baseado'

agent-orchestration.svg:
- Adicionar 4ª conexão do Orquestrador para Agente Jurídico
- Alinhar setas para centros reais dos boxes de agentes

mcp-architecture.svg:
- Adicionar zona de segurança para coluna Domain (3ª coluna)
- Adicionar setas Client → Server entre camadas
- Remover emojis (compatibilidade cross-browser)

mirofish-phd-auditor.svg:
- Remover emojis (compatibilidade cross-browser)

rag-strategies.svg:
- Substituir emoji circles por SVG <circle> com fill verde
- Texto CONFIRMADO agora em verde (#3fb950)

self-healing.svg:
- Sincronizar com versão corrigida da main (PR #4)

Co-Authored-By: Prof. Marcelo Claro <marceloclaro@gmail.com>
…detalhadas

- Reestruturar em 4 partes progressivas (Fundamentos → Módulos → Infraestrutura → Referência)
- Adicionar seção 'O que é o OpenCode Ecosystem?' com explicação acessível
- Adicionar seção 'Conceitos Fundamentais' (Agentes, MCP, Skills, Nexus)
- Adicionar seção 'Como Funciona — Fluxo de Trabalho' com diagrama ASCII
- Mover Comandos Rápidos para início (uso prático primeiro)
- Reordenar módulos: funcionalidades do usuário antes da infraestrutura técnica
- Adicionar explicações contextuais em cada seção técnica
- Consolidar AutoEvolve em seção própria 'Evolução Autônoma'
- Mover diagramas SVG para seção colapsável (details/summary)
- Mover comparativo para seção própria
- Preservar 100% do conteúdo técnico original

Co-Authored-By: Prof. Marcelo Claro <marceloclaro@gmail.com>
fix: melhorar fluxogramas SVG — corrigir erros, conexões e compatibilidade
…rificar MCPs (40 registrados, 17 ativos)

Co-Authored-By: Prof. Marcelo Claro <marceloclaro@gmail.com>
MarceloClaro and others added 26 commits May 24, 2026 14:16
… protocolo reprodutibilidade, 20 citacoes DOI
…jeira, 3 niveis de profundidade, 25+ paginas, 20 citacoes DOI
* chore(config): add opencode.omniroute.json.example template

Opt-in config template for routing through OmniRoute AI Gateway as an
alternative to opencode/big-pickle. Users rename or merge this file into
opencode.json to activate. Original opencode.json is not touched.

* chore(scripts): add OmniRoute tenant health-check script

4-step validation (models, providers, combos, mcp/stream) with proper
exit codes (0 healthy, 1 threshold, 2 connectivity, 3 missing deps).
Used by GETTING_STARTED.md for users to verify their tenant before
applying the example config.

* docs: add optional OmniRoute section to GETTING_STARTED

New section 5 documents how to opt-in to OmniRoute. Preserves big-pickle
as the default and explicitly states no change happens for users who
don't apply the example config.

* docs(readme): mention OmniRoute as optional gateway

One-line addition under 'Modelo base' header pointing to the new section
in GETTING_STARTED.md. big-pickle remains the highlighted default.

* feat(scripts): add apply-omniroute-config.sh JSONC-aware merger

The previous "jq -s '.[0] * {mcp: .[1].mcp}'" command documented in
GETTING_STARTED.md failed because opencode.omniroute.json.example is
JSONC (has // comments) and jq only parses strict JSON.

This script does the merge correctly via Node, using a string-state-aware
JSONC stripper (URLs like https://... are preserved). Backup of the
original opencode.json is created with a UTC timestamp.

* fix(docs): use apply-omniroute-config.sh + default to localhost baseURL

- GETTING_STARTED step 3 now invokes the dedicated script instead of
  inline node/jq (which was broken on JSONC).
- Step 2 uses <BASE_URL> placeholder instead of a hardcoded URL.
- Revert section uses dynamic backup detection.
- example baseURL default changed from a hardcoded tenant URL to
  http://localhost:20128 (opt-in to external tenants instead of opt-out).
- inline reference to jq merge command removed from the example file.

* fix(scripts): consistent curl arg handling + truncated error output

- Check 4 (/api/mcp/stream) now uses an array for the optional auth
  header, matching the style of the fetch() helper.
- Check 1 connectivity error is truncated to one line for terminal
  readability (was printing full curl stderr).

* chore: ignore opencode.json.bak.* and merged.tmp artifacts

* fix(scripts): propagate node exit code from apply-omniroute-config

bash resets $? to 0 inside "if ! cmd; then" blocks. The previous
implementation captured EXIT_CODE=$? after the negated condition,
which always returned 0 even when node had failed with code 2 (invalid
JSONC) or 3 (invalid JSON). Callers checking the exit status would
proceed as if the merge succeeded.

Fix: capture $? on a separate line outside the if, with set +e/-e
bracketing to avoid set -e tripping before the exit code is read.

Verified: invalid example → exit 2, invalid target → exit 3, happy
path → exit 0.
* feat(agents): assign omniroute/claude-opus-4-7 to 5 reviewers + 4 architects

Adds 'model: omniroute/claude-opus-4-7' frontmatter to nine quality-gate
and decision agents. Original plan called for 'banca + orientadores'
academic-review agents (5 reviewers + 4 PhD orientadores) but those exist
only as textual descriptions in criador-artigo/banca/*.md, not as OpenCode
agent files. Adjusted scope to the closest functional analogues in
agents/:

Reviewers (5):
  code-reviewer, reviewer, reversa-reviewer, ws-reviewer, security-auditor

Architects/decisions (4):
  architect, architecture-analyzer, adr-manager, contract-manager

Each file got a single line added immediately inside the frontmatter
(after the opening --- for files without a 'name:' field, or after 'name:'
when present). YAML frontmatter is preserved and parseable. Without
OmniRoute configured (PR-1 not applied), OpenCode CLI falls back to the
global default (opencode/big-pickle) — no crash.

* docs: add omniroute-model-mapping reference + CONTRIBUTING section

- references/omniroute-model-mapping.md: canonical table of agent
  categories -> recommended OmniRoute models, fallback ladder, free-tier
  preservation strategy, and explicit list of the 9 files specialized
  by this PR.
- CONTRIBUTING.md: new section 'Especializando o modelo de um agente
  (opcional)' documenting the model: frontmatter convention, when to use
  it, fallback behavior, and PR checklist.
Extends ecosystem-sync to track OmniRoute provider/combo health when
the OMNIROUTE_BASE_URL env var is set. Without the env var, the plugin
behaves identically to v3.5 (zero regression).

New behavior (opt-in via env):
- session.created: fetches /api/providers and /api/combos with 3s
  hard timeout, populates state.omniRoute.{providers, combos,
  healthyProviderCount, degradedProviderCount, activeComboCount,
  lastSync, lastError}. Soft-fail on any error.
- session.idle: log message appends OmniRoute summary.
- shell.env: exports 8 global + N per-provider env vars:
  ECOSYSTEM_OMNIROUTE_{ENABLED, BASE_URL, HEALTHY, DEGRADED,
  PROVIDER_COUNT, ACTIVE_COMBOS, LAST_SYNC, LAST_ERROR},
  ECOSYSTEM_OMNIROUTE_COMBO_SLUGS (CSV — used by /route in PR-4),
  ECOSYSTEM_OMNIROUTE_PROVIDER_<NAME>_{HEALTHY,TOTAL,CB}.

Implementation notes:
- 3 new interfaces (OmniRouteProviderHealth, OmniRouteComboInfo,
  OmniRouteSnapshot) added before EcosystemState.
- state.omniRoute is OPTIONAL — v3.5 state files load without
  migration.
- No new imports. fetch + AbortController are global in Bun 1.3
  and Node 22+ (within engines.node range).
- version default bumped to '3.6.0' in loadState.
- service tag in client.app.log calls: 'ecosystem-sync-v3.6'.
- Banner string updated to 'EcosystemSync v3.6'.

Hooks NOT modified (preserved verbatim from v3.5):
- tool.execute.before
- tool.execute.after
- session.error

File: 528 -> 749 lines, +221 net.
#4)

* feat(commands): add /route slash command for OmniRoute combo selection

Adds the /route slash command which sets OMNIROUTE_COMBO for the current
OpenCode session, allowing users to choose the routing strategy (combo)
that pipelines like /artigo, /reversa, /quantum will use.

Frontmatter declares 5 triggers (/route, /r, /combo, route, combo),
arguments (combo_slug), env vars consumed (OMNIROUTE_BASE_URL) and
exported (OMNIROUTE_COMBO). Documents 4 usage examples and 4 rules
including env_required, slug_validation, session_scope, no_side_effects.

The Python CommandRegistry is metadata-only; actual dispatch happens
in command/dispatcher.ts (TypeScript) — handler implementation belongs
there and is out of scope for this PR.

* chore(registry): register /route in TRIGGER_MAP

Single-line addition to the dict so CommandRegistry.find() can resolve
the /route command from any of its 5 declared triggers. Verified with
'import ast' parse + CommandRegistry.load() discovery.

* docs: document /route in README + TUTORIALS

- README.md commands table: +1 row for /route.
- TUTORIALS.md: new section 'Roteamento OmniRoute por sessão (/route)'
  with 5 usage examples (cost-optimized, claude-primary, auto-free, none,
  --list) and an error-handling reference table.
…dbox (#5)

Adds a new system skill that teaches agents how to invoke OmniRoute's
sandboxed Skills framework via MCP tools (skill_list, skill_run,
skill_status, skill_cancel). Progressive disclosure: SKILL.md is 2217B
(≤ 2500B budget), references/omniroute-skills-reference.md has full
schemas (6169B).

Rules enforced via 4 <rule> blocks: prefer_local, timeout_aware,
audit_required, no_secrets.

Use cases beyond local skills:
- Isolation (untrusted code, eval suites)
- Audit trail (mcp_audit table)
- Public catalog skills (eval-factuality, pii-detect, etc.)
… skeleton (#6)

* feat(dashboard): refactor dashboard_server.py to v3.0 with route dispatch

Major refactor (849 -> 597 lines, -252 net) introducing the foundation
for multi-page management screens in PR-7..PR-10:

- PAGE_ROUTES + API_HANDLERS dispatch tables at module top
- do_GET / do_PUT / do_POST / do_HEAD methods
- _dispatch_api (longest-prefix match), _serve_page, _serve_asset
  (path-traversal guarded at 2 levels), _send_{json,text,bytes} helpers
- _load_api_module() for dynamic handler registration from nexus/api/*.py
- 1 MB body cap on POST/PUT to prevent payload DoS
- ThreadingHTTPServer NOT yet — single-threaded for foundation; PR-8
  will migrate when SSE is introduced

Removed (v2.0 legacy):
- Inline HTML_TEMPLATE string (~418 lines) — overview now served from
  nexus/dashboard/index.html on disk (the file that was already there
  as the public template).
- gerar_html_estatico() simplified to read+write the on-disk template
  (no more inline generation).

Preserved:
- /dados.json legacy endpoint (external consumers may depend on it)
- All overview rendering behavior — verified visually identical
- All launcher scripts (nexus/dashboard/run_*.bat, start_dashboard.py)

Adds /api/ping endpoint as foundation validation (see nexus/api/ping.py).

* feat(api): add nexus/api/ping endpoint for foundation validation

First handler registered via the new _load_api_module() pattern.
Returns {ok, version: '3.0', omniroute_enabled, omniroute_base_url,
timestamp}. Used by nav.js footer to display OmniRoute env status
in the sidebar.

* feat(dashboard): shared assets — nav.js, styles.css, api.js

Three vanilla-JS/CSS assets used by all dashboard pages:

- nav.js: window.renderNav(activeKey) renders the 5-item sidebar
  (overview + 4 pending PRs marked with 'PR-7' etc. badges)
- styles.css: design tokens (dark theme), sidebar, table, button,
  panel styles. No external imports — no Google Fonts, no CDN.
- api.js: window.api.{get,put,post,delete,stream} minimal fetch
  client with consistent error handling and SSE EventSource helper.

Served via /assets/* (path-traversal guarded by dashboard_server.py).

* feat(dashboard): wrap index.html in layout with sidebar nav

- Body wrapped in <div class='layout'> > <div id='nav'> + <main>
- All existing v2.0 content preserved verbatim inside <main class='main'>
- Added <link rel='stylesheet' href='/assets/styles.css'> to head
- Added api.js + nav.js scripts before </body>
- Added renderNav('overview') call to highlight current page
- Chart.js CDN tag enhanced with SRI integrity hash (CWE-353
  mitigation) and crossorigin='anonymous'

* docs(dashboard): document v3.0 architecture

Architecture reference for contributors implementing PR-7..PR-10:
- File layout (pages, assets, nexus/api/ handler modules)
- Routing tables (PAGE_ROUTES, API_HANDLERS, assets, legacy)
- Design principles (CLI is source of truth, loopback only, pending
  pattern, stdlib only, reversible)
- Security model (loopback bind, path traversal, body cap, shell
  injection prevention, atomic writes, env scope)
- How to add a new page (PR-N pattern)
- Version history (v1.0 -> v2.0 -> v3.0 -> v3.1+ upcoming)
* feat(api): add Agents & Models REST endpoints (PR-7)

4 new handler modules registered via _load_api_module():

- nexus/api/agents.py             — GET /api/agents with ?category/?model/?overridden/?q filters
- nexus/api/omniroute_proxy.py    — GET /api/models, /api/combos (30s in-memory cache, soft-fail)
- nexus/api/session_combo.py      — GET/PUT /api/session/combo persists to .evolve/session-env.json
- nexus/api/agent_changes.py      — PUT/DELETE staging in .evolve/pending-agent-changes.json,
                                     GET /api/agents/{pending,diff}, POST /api/agents/apply
                                     with atomic per-file writes (tempfile + os.replace)

Soft-fail design: /api/models returns 503 with clear error when
OMNIROUTE_BASE_URL is unset. /api/agents works regardless (local-only).

Path-traversal protection: agent name validated against regex
^[a-zA-Z0-9][a-zA-Z0-9_.-]{0,127}$ before file access.

* feat(dashboard): register PR-7 handlers in dispatcher

Wires up /api/agents, /api/models, /api/combos, /api/session/combo,
/api/agents/{pending,diff,apply,<name>/{model,pending}}. Longest-prefix
match ensures /api/agents/{pending,diff,apply} resolve before the
/api/agents/ catchall for dynamic <agent_name> routes.

* feat(dashboard): Agents & Models screen frontend (Layout A)

- agents.html (149 lines): Layout A table with filters (category,
  model, overridden, search), per-row model dropdown, pending counter,
  bulk-assign select, diff modal, footer with apply/discard buttons
- agents.js (347 lines): fetch from /api/{agents,models,combos,session/combo,
  agents/pending,agents/diff}; render table; handle staging via
  /api/agents/<name>/model; preview diff in modal; apply via POST
- agents.css (50 lines): row-changed highlight, star icon for pending,
  modal layout, cost tooltip

Layout A was selected by maintainer in the 3-mockup discussion
(see _tasks/pr-7-screen-agents-models/README.md).

* chore: ignore .evolve/session-env.json and pending-agent-changes.json
* feat(api): add Health screen REST + SSE endpoints (PR-8)

3 new handler modules registered via _load_api_module():

- nexus/api/health_providers.py — GET /api/health/providers, 5s cache,
  proxies /api/providers from OmniRoute, normalizes to unified schema:
  {type:'prov'|'mcp', name, status:'ok'|'warn'|'crit'|'off', score:0-100,
   details, extra:{...}, last_check}
- nexus/api/health_mcp.py — GET /api/health/mcp, reads
  .evolve/ecosystem-state.json (managed by ecosystem-sync), normalizes
  to the same unified schema.
- nexus/api/health_stream.py — GET /api/health/stream Server-Sent
  Events. Tick 5s, lifetime cap 10min. Factory pattern: receives
  providers + mcp modules at registration time. Cleanly handles
  BrokenPipeError/ConnectionResetError. Returns sentinel (-1, None, None)
  to signal _dispatch_api that the response was already written.

* feat(dashboard): ThreadingHTTPServer + sentinel for SSE handlers

Two minimal changes enabling SSE in PR-8 without blocking concurrent
requests:

1. HTTPServer -> ThreadingHTTPServer: a single SSE connection no longer
   blocks the rest of the API. Verified by running /api/health/stream
   in parallel with /api/agents — both respond in <200ms.

2. _dispatch_api recognizes sentinel status=-1 from a handler as 'already
   wrote response, do not double-write headers/body'. Required for SSE
   handlers that take over the wfile stream.

Plus PR-8 handler registration block.

* feat(dashboard): Health screen frontend (Layout B + SSE)

Layout B (unified dense table) was selected by maintainer:

- health.html: filters (type, status, problems-only), sort dropdown,
  table with 6 columns (STATUS, TYPE, NAME, SCORE, DETAILS, LAST CHECK),
  alerts bar (CRITICAL/WARNING counts with row names), inline drawer
  for selected item with extra fields.
- health.js: connects to /api/health/stream via EventSource, applies
  filters/sort client-side, renders rows colored by status (row-ok,
  row-warn, row-crit, row-off), live-updates the detail drawer.
- health.css: status-cell coloring, dot indicators, drawer + connection
  row styles, pause indicator styling.

Layout B was selected by maintainer in the 3-mockup discussion
(see _tasks/pr-8-screen-health/README.md).
…ocess management) (#9)

* feat(api): Pipelines screen REST + SSE endpoints (PR-9)

3 new SECURITY-CRITICAL handler modules:

- nexus/api/pipelines_list.py (194 lines): GET /api/pipelines lists
  commands from command/*.md with frontmatter parser, GET
  /api/pipelines/runs lists runs from .evolve/pipeline-runs/*/meta.json
  with PID-liveness orphan detection.

- nexus/api/pipelines_run.py (387 lines): POST /api/pipelines/run.
  Strict security:
  - Allow-list: command must exist in command/*.md
  - COMMAND_TOKEN_RE blocks shell metachars before dispatch
  - Args validated per-arg against schema in frontmatter
    (type, required, pattern, length cap 4096B per arg, 16KB total)
  - subprocess uses shell=False, list-form argv
  - start_new_session=True for killpg-based cancel
  - Hard timeout MAX_DURATION_S = 8*3600 (8h)
  - MAX_GLOBAL_CONCURRENT = 8 + per-command lock unless
    allow_concurrent: true in frontmatter
  - Args passed via PIPELINE_ARGS_JSON env var (no argv quoting)
  - cwd = workspace root, env inherited (no privilege escalation)
  - Append-only output.log
  - Atomic meta.json updates

- nexus/api/pipelines_stream.py (156 lines): SSE tail of output.log
  + cancel. run_id regex r'run_\d{8}T\d{6}Z_[a-f0-9]{6}' strict.
  Cancel: SIGTERM → 5s grace → SIGKILL via os.killpg.
  Sentinel -1 for SSE handler.

* feat(dashboard): register PR-9 handlers (pipelines)

Adds dispatch routing for /api/pipelines, /api/pipelines/runs,
/api/pipelines/run, /api/pipelines/runs/<id>/output/stream,
/api/pipelines/runs/<id>/cancel. Uses unified _route_runs callable
for dynamic <run_id> routing.

* feat(dashboard): Pipelines screen frontend (Layout C)

Layout C (tabs + drawer) selected by maintainer:

- pipelines.html (134 lines): 2 tabs [Commands (14)] [Runs (N)],
  filters per tab, modal with auto-generated args form, inline drawer
  for terminal output of selected run.
- pipelines.js (298 lines): tab switching, command list rendering with
  args-summary in cells, Run button opens modal with form generated
  from frontmatter arguments[], submit calls POST /api/pipelines/run,
  redirects to Runs tab, opens drawer with SSE consumer for stdout
  stream, polling 5s for run list refresh, cancel button.
- pipelines.css (54 lines): tab styling, run row status colors,
  terminal monospace styling, modal form fields.

Layout C was selected by maintainer in the 3-mockup discussion
(see _tasks/pr-9-screen-pipelines/README.md).

* chore: ignore .evolve/pipeline-runs/ (ephemeral run logs)
* feat(api): Plugins screen REST endpoints (PR-10)

2 new handler modules:

- nexus/api/plugins_list.py (227 lines): GET /api/plugins reads
  opencode.json, strips JSONC comments via string-state machine
  (preserves URLs like https://...), returns plugins with hardcoded
  schemas for 5 known plugins (4 internal + @omniroute/opencode-plugin).
  has_comments flag warns user that Apply will lose JSONC formatting.

- nexus/api/plugin_changes.py (316 lines): pending-changes machinery
  + atomic apply:
  * PUT /api/plugins/<id> stages enabled/options in .evolve/
    pending-plugin-changes.json
  * DELETE /api/plugins/<id>/pending unstages
  * GET /api/plugins/pending lists staged changes
  * GET /api/plugins/diff renders unified diff
  * POST /api/plugins/apply: pre-validate (schema + must exist in
    current config) -> backup opencode.json.bak.<UTC-ts> -> atomic
    write via tempfile + os.replace -> restore from backup on any
    failure. Response includes restart_required + restart_hint.

Path-traversal protection: plugin id validated against regex
^[A-Za-z0-9@_./-]{1,200}$.

* feat(dashboard): register PR-10 handlers (plugins)

Adds dispatch routing for /api/plugins, /api/plugins/pending,
/api/plugins/diff, /api/plugins/apply, /api/plugins/<id>,
/api/plugins/<id>/pending. Longest-prefix match ensures specific
endpoints resolve before the /<id> catchall.

* feat(dashboard): Plugins screen frontend (Layout A)

Layout A (table + inline expansion) selected by maintainer:

- plugins.html (120 lines): table with ENABLED toggle, PLUGIN name,
  VERSION, STATUS, OPTIONS (Configure button). Inline expansion
  drawer shows configurable options with field-by-field form. Modal
  for diff preview + Apply confirmation. Modal for 'How to restart'
  instructions. Comments warning banner at top when applicable.
- plugins.js (321 lines): fetch plugins + pending; render table with
  toggle/configure UI; stage changes via PUT /api/plugins/<id>;
  preview diff in modal with colored +/- lines; Apply with backup
  notice; Discard. Field rendering supports nested keys
  (features.combos, etc.).
- plugins.css (72 lines): toggle switch styling, expanded row
  highlight, options form grid layout, modal diff styling.

Layout A was selected by maintainer in the 3-mockup discussion
(see _tasks/pr-10-screen-plugins/README.md).

* chore: ignore PR-10 artifacts (backups + pending changes)
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

MarceloClaro pushed a commit that referenced this pull request May 30, 2026
…honesta

- PE#26-#30 (5 problemas genuinamente cegos): 5/5 PASS
- ROS-SPLC, LCSM, LIA (3 problemas genuinamente cegos): 3/3 PASS
- Total acumulado: 42/42 (100%) — 30 PE + 12 Rosalind
- 558K solvers cegos adicionais
- Nota honesta: verificacao automatica, nao por revisores
- Limitacao explicita: 8/10 dim CORA-Eval sem validacao externa
- Pronto para o revisor senior
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants