Skip to content

feat: multi-provider support + setup hardening + UI redesign#4

Merged
DavidsonGomes merged 43 commits intoEvolutionAPI:developfrom
NeritonDias:codex-auth
Apr 12, 2026
Merged

feat: multi-provider support + setup hardening + UI redesign#4
DavidsonGomes merged 43 commits intoEvolutionAPI:developfrom
NeritonDias:codex-auth

Conversation

@NeritonDias
Copy link
Copy Markdown
Contributor

@NeritonDias NeritonDias commented Apr 12, 2026

Summary / Resumo

Adds complete multi-provider AI support to EvoNexus, allowing users to switch between Anthropic (native Claude), OpenAI (GPT-5.x via Codex OAuth or API key), and OpenRouter (200+ models) — all configurable from a redesigned dashboard. Includes a ground-up rewrite of the setup wizard for clean VPS installs, a professional UI redesign of onboarding/login/providers screens, and massive image optimization (271MB to 1.7MB).

Adiciona suporte completo multi-provider ao EvoNexus, permitindo alternar entre Anthropic (Claude nativo), OpenAI (GPT-5.x via Codex OAuth ou API key) e OpenRouter (200+ modelos) — tudo configuravel pelo dashboard redesenhado. Inclui reescrita completa do setup wizard para instalacao limpa em VPS, redesign profissional da UI de onboarding/login/providers, e otimizacao massiva de imagens (271MB para 1.7MB).


1. Multi-Provider Architecture / Arquitetura Multi-Provider

Provider Toggle System

  • Toggle switch (on/off) per provider in the dashboard — only one active at a time
  • Setting active_provider to "none" disables all providers
  • When no provider is active, agent sessions are blocked with a clear terminal message: "No AI provider is active. Go to Providers to configure."
  • Backend: POST /providers/active now accepts "none" as valid provider_id
  • Claude-bridge validates active provider before spawning any CLI process

Toggle on/off por provider no dashboard. Quando nenhum esta ativo, sessoes sao bloqueadas com mensagem clara no terminal.

OpenAI Codex OAuth

  • Browser OAuth flow: user clicks login in dashboard, authorizes in OpenAI, copies callback URL back
  • Device Auth flow: alternative for environments where browser redirect is blocked
  • Tokens saved in correct Codex format (~/.codex/auth.json) with auth_mode: "Chatgpt"
  • Auto-detects Codex auth vs API key — removes OPENAI_API_KEY when OAuth is active
  • Dashboard endpoints: auth-start, auth-complete, device-start, device-poll, status, logout

Fluxo OAuth completo via dashboard — browser OAuth + device auth. Tokens salvos no formato correto do Codex.

Agent Persona Enforcement

  • Non-Anthropic providers (GPT, Gemini) don't follow agent system prompts as strongly as Claude
  • Fix: --system-prompt replaces the entire default prompt (instead of --append-system-prompt which was too weak)
  • Reads agent definition from .claude/agents/{agent}.md and injects it with strong enforcement instructions
  • Result: GPT responds as Oracle, Clawdia, etc. — not as "Claude" or generic assistant

GPT/Gemini agora incorporam a persona do agente corretamente via --system-prompt que substitui o prompt padrao.

Clean Environment Whitelist

  • Spawned processes only inherit 22 whitelisted system vars (HOME, PATH, SHELL, etc.) + provider env vars
  • Prevents stale/cached OPENAI_API_KEY from process.env leaking into spawned sessions
  • Default OPENAI_MODEL to gpt-5.4 when missing
  • Root detection: skips --dangerously-skip-permissions for root users (Claude/OpenClaude reject it)

Ambiente limpo com whitelist de vars — previne vazamento de API keys obsoletas.


2. Setup Hardening / Robustez do Setup (setup.py)

Auto-Install Prerequisites

All prerequisites are auto-installed without user intervention:

  • apt-get update && apt-get upgrade with DEBIAN_FRONTEND=noninteractive (no prompts, clean output)
  • build-essential (g++ required for node-pty compilation)
  • Node.js 24.x LTS (upgraded from 22.x maintenance)
  • uv (Python package manager) — installed for SUDO_USER, not root, fixing .venv symlink permissions
  • Claude Code CLI + OpenClaude via npm global
  • All install output suppressed — only clean status lines shown (Installing... -> checkmark)

Todos os pre-requisitos instalados automaticamente. Output limpo sem logs verbosos.

SSL & Nginx Auto-Configuration

  • Certbot as default (changed from self-signed) — attempts Let's Encrypt first
  • Auto-reuse existing certbot certs (idempotent re-runs)
  • Self-signed fallback if certbot fails (compatible with Cloudflare Full mode)
  • SSL key permissions auto-fixed (chmod 600) after generation
  • IPv6 listeners on ports 80 and 443 (listen [::]:80 / listen [::]:443 ssl)
  • Firewall auto-config: opens ports via ufw + iptables
  • Removes default nginx site, enables evonexus site

Certbot como padrao, self-signed como fallback. IPv6, firewall e permissoes SSL configurados automaticamente.

Permission Handling (sudo)

  • uv sync runs as SUDO_USER (not root) so .venv symlinks point to user's Python
  • chown -R fixes workspace ownership before starting services
  • chmod +x .venv/bin/ ensures Python binaries are executable after chown
  • Services started via su - $SUDO_USER to avoid root-owned processes
  • getent passwd fallback with try/except for IndexError safety

Permissoes tratadas corretamente quando rodando com sudo — uv, .venv e servicos rodam como o usuario original.

Service Startup

  • Generates start-services.sh with nohup for persistent background processes
  • Cleans stale ~/.claude-code-web/sessions.json on every start (prevents agent persona issues)
  • Health check on ports 32352 (terminal-server) and 8080 (dashboard) after startup
  • Stops any existing services (systemd, background processes) before starting new ones

3. Dashboard Onboarding / Onboarding do Dashboard

Onboarding Flow Restored

  • Problem: In remote mode (domain+SSL), setup.py generated workspace.yaml with empty owner/company. The dashboard checked if the file existed (configured: true) and skipped Step 1 (name, company, timezone, language).
  • Fix: /api/config/workspace-status now reads the YAML and checks if owner field is actually filled. Empty owner = configured: false = Step 1 is shown.
  • Setup.tsx always sends workspace data when Step 1 was displayed, updating workspace.yaml.

Onboarding restaurado — verifica se owner esta preenchido, nao apenas se o arquivo existe.

Post-Setup Flow

  • After creating admin account, user is automatically redirected to /providers to configure AI provider
  • No more guessing where to go after setup

Dead Route Removal

  • Removed /chat Quick Actions from Overview (route didn't exist in router)
  • Replaced with Agents and Providers quick action links
  • Removed unused Sun, MessageSquare imports from lucide-react

4. UI/UX Redesign / Redesign Visual

Setup & Login Pages — Complete Rewrite

Animated Background:

  • Canvas API neural network mesh with ~80 particles connected by edges
  • Particles move with individual velocities, edges appear/disappear by distance
  • requestAnimationFrame loop, responsive to window resize
  • Subtle green (rgba(0, 255, 167, 0.25)) on deep dark (#080c14)

Card Design:

  • Solid card (#0b1018) with intentional borders (#152030), no backdrop-blur
  • Custom SVG logomark (layered prism) — not a generic library icon
  • Tab-style step navigation instead of numbered circles
  • Header separated from form by border

Form UX:

  • Proper autocomplete attributes on all inputs (username, email, new-password, current-password)
  • Labels: 11px uppercase with 0.08em tracking for hierarchy
  • Inputs: #0f1520 background, #1e2a3a borders, green focus ring
  • Flat solid CTA button (#00FFA7) with hover (#00e69a) and active (#00cc88) states
  • Loading spinner integrated in button during submission

What was removed (AI cliches):

  • Sparkles icon (trademark of "made by AI")
  • Gradient orbs with blur (generic glassmorphism)
  • Grid overlay (overused pattern)
  • Animated ping on step indicator
  • Lucide icons inside inputs (cluttered)
  • Gradient CTA buttons (over-styled)

Background animado real via Canvas API (mesh neural), card solido sem glass, sem cliches de IA, tipografia profissional.

Providers Page — Redesign

  • List layout instead of card grid — cleaner, more scannable
  • Toggle switch component with proper role="switch" and aria-checked
  • Compact status bar with install status + counters
  • Provider color dots for visual identity
  • Consistent modal styling matching Setup/Login pages
  • Removed Star icon, Zap icon, and decorative badges

5. Image Optimization / Otimizacao de Imagens

Agent Avatars (PNG to WebP)

  • 38 avatar files: 271 MB to 1.7 MB (99.4% reduction)
  • Resized from 2048x2048 to 512x512
  • All references updated in agent-meta.ts

Documentation & Screenshots (PNG to WebP)

Directory Files Before After Reduction
docs/imgs/ 23 2.8 MB 916 KB 67%
public/ 5 2.0 MB 540 KB 73%
site/public/assets/ 6 2.1 MB 583 KB 72%
dashboard/frontend/src/assets/hero 1 44 KB 16 KB 64%

Total: 167 files changed, 1,533 insertions(+), 732 deletions(-) 40 commits on codex-auth branch


Commits (40)

Features

  • 04ad2f2 feat: OpenAI Codex OAuth via Dashboard + Nginx auto-config
  • 453f1cd feat: enforce agent persona for non-Anthropic providers
  • b12633c feat(setup): system update, Node.js 24.x LTS, certbot as default SSL
  • 045e62f feat: setup hardening, remove /chat, redirect to /providers, images to WebP
  • 5f05d99 feat(ui): premium dark glassmorphism redesign for Setup + Login
  • 240afe1 feat(ui): professional redesign — canvas network bg, no AI cliches
  • bd57cb2 feat: provider toggle switch + session blocking when none active

Critical Fixes

  • 51ff3f5 fix(critical): whitelist env vars instead of spreading process.env
  • 2cd3e53 fix(critical): add default OPENAI_MODEL when missing
  • ad34dfa fix(critical): remove OPENAI_API_KEY from process.env before spawn
  • 5d8e54f fix(critical): start services as SUDO_USER, not root
  • f99b2b7 fix(critical): chown workspace BEFORE starting services
  • 4e3a10d fix(critical): run uv sync as SUDO_USER, not root
  • e6df10d fix(critical): ...options spread was overwriting agent with undefined
  • cc70472 fix(critical): use --system-prompt instead of --append-system-prompt

Performance

  • 5ee1622 perf: optimize agent avatars — 271MB PNG to 1.7MB WebP (99.4% reduction)

Setup & Permissions

  • b0617b1 fix: add build-essential prereq + nginx SSL/IPv6/firewall improvements
  • 6f40ce5 fix: install uv for SUDO_USER, not root — .venv was not created
  • 71d878e fix: uv PATH resolution before verification + clean install output
  • 4ad21d7 fix: restore onboarding when owner is empty + clean setup output
  • b076527 fix: chmod +x .venv/bin/ after chown to fix Permission denied
  • ec7414d fix: skip --dangerously-skip-permissions when running as root

Summary by Sourcery

Add multi-provider AI selection with OpenAI Codex OAuth support, harden the VPS setup wizard, and redesign the dashboard authentication and provider screens while optimizing image assets.

New Features:

  • Introduce provider toggling and activation tracking in the dashboard with support for disabling all providers.
  • Add OpenAI Codex OAuth and device auth flows, including status and logout endpoints and a dashboard modal UI.
  • Support OpenAI Codex OAuth tokens in the terminal bridge, preferring OAuth over stale API keys and enforcing agent personas via system prompts.
  • Add a dashboard access configuration flow in setup, including Nginx+SSL provisioning and firewall rules for remote HTTPS access.
  • Auto-start and health-check terminal server and dashboard services via a generated startup script, redirecting first-time users to provider configuration.

Bug Fixes:

  • Prevent stale environment variables and missing OPENAI_MODEL from breaking non-Anthropic sessions by whitelisting env vars and applying sane defaults.
  • Ensure setup and services run as the non-root sudo user with correct ownership and executable permissions for virtualenv binaries.
  • Restore onboarding step 1 by checking for a non-empty workspace owner instead of only file existence.
  • Ensure terminal sessions are blocked when no provider is active and that agent names are consistently propagated from the session.
  • Fix Codex/OpenAI auth detection by removing conflicting OPENAI_API_KEY when auth.json is present and updating terminal URLs for proxied access.

Enhancements:

  • Redesign the Providers page into a compact list with accessible toggle switches, streamlined actions, and OpenAI auth status indicators.
  • Completely restyle the Setup and Login pages with a canvas-based animated background, solid cards, and improved form UX and accessibility.
  • Refine provider configuration flows to auto-fill sensible defaults, simplify env var editing, and surface test results more cleanly.
  • Improve setup prerequisite handling with automated system updates, build-essential installation, and quieter npm/uv operations.
  • Enhance workspace persona generation when owner name is missing and update various dashboard stats/quick-links for clarity.

Documentation:

  • Update dashboard and integration documentation screenshots to use optimized WebP assets and reflect the redesigned UI.
  • Clarify getting-started instructions, including shallow clone commands and updated dashboard imagery.

Chores:

  • Convert avatars, marketing images, and docs screenshots from large PNGs to smaller WebP files and update references across the app.

claude added 30 commits April 11, 2026 16:54
## OpenAI Authentication (2 methods)

### Browser OAuth (works with ANY OpenAI org)
1. Dashboard generates PKCE authorize URL (redirect_uri=localhost:1455)
2. User opens link, authorizes on OpenAI
3. Browser redirects to localhost:1455 (shows error page — expected)
4. User copies the callback URL from browser address bar
5. User pastes URL into dashboard text field
6. Backend extracts code, exchanges for tokens, saves ~/.codex/auth.json

### Device Auth (for orgs that allow it)
1. Dashboard requests device code from OpenAI
2. Shows user_code + verification URL
3. User opens URL, enters code
4. Dashboard polls until authorized
5. Exchanges for tokens, saves auth.json

## Nginx Auto-Config
- New configure_access() in setup.py
- Asks: local (localhost:8080) or domain with SSL
- Auto-generates Nginx config with:
  - HTTP → HTTPS redirect
  - / → localhost:8080 (dashboard)
  - /terminal/ → localhost:32352 (terminal-server WebSocket)
- Certbot auto or manual SSL cert paths

## Setup Wizard Changes
- OpenAI provider: choice between API Key (GPT-4.x) or Codex OAuth (GPT-5.x)
- Codex OAuth: "authenticate via Dashboard → Providers → Login com OpenAI"
- Gemini, Bedrock, Vertex marked as "coming soon"
- Removed codex_auth as separate provider (unified into openai)

## Backend (providers.py)
- POST /api/providers/openai/auth-start — PKCE + authorize URL
- POST /api/providers/openai/auth-complete — token exchange from callback URL
- POST /api/providers/openai/device-start — device code request
- POST /api/providers/openai/device-poll — poll for authorization
- GET /api/providers/openai/status — check auth.json validity
- POST /api/providers/openai/logout — remove auth.json

## Frontend (Providers.tsx)
- Auth modal with 2 tabs: Browser OAuth / Device Auth
- Browser OAuth: link + paste callback URL field
- Device Auth: code display + auto-polling
- Auth status badge + logout button on OpenAI card

## claude-bridge.js
- Auto-detects ~/.codex/auth.json
- Removes OPENAI_API_KEY from env vars when auth.json exists
- Prevents API key from overriding OAuth token

https://claude.ai/code/session_01EsVmzavc8eQK3TwPKMyzFq
OAuth endpoints don't need Flask-Login auth — PKCE state in session
provides security. The @login_required decorator was blocking requests
even when the user was logged into the dashboard.

Also updated setup.py:
- Domain mode: configure Nginx first, then build+start, redirect to dashboard
- Simplified provider list (3 active + 3 coming soon)
- OpenAI: auth choice (API key or Codex OAuth via dashboard)

https://claude.ai/code/session_01EsVmzavc8eQK3TwPKMyzFq
Prerequisites (Node.js, npm, uv, Claude Code) are now checked with
an offer to install automatically. The setup only fails if the user
declines installation AND the tool is required.

- Node.js: installs via nodesource setup script
- uv: installs via astral.sh install script
- Claude Code: installs via npm
- Each tool: asks "Install X? (Y/n)" before proceeding

https://claude.ai/code/session_01EsVmzavc8eQK3TwPKMyzFq
AgentTerminal.tsx: In production, connect to terminal-server via
/terminal/ path on same origin (proxied by Nginx) instead of direct
port 32352 which fails behind HTTPS proxy.

setup.py: Install terminal-server npm deps before starting. Verify
both services are running after startup. Use proper cwd for
terminal-server so config paths resolve correctly.

https://claude.ai/code/session_01EsVmzavc8eQK3TwPKMyzFq
The old format used {"openai-codex": {"type":"oauth","access":...}}
which OpenClaude doesn't recognize. The correct format is:
{
  "auth_mode": "Chatgpt",
  "tokens": {
    "access_token": "...",
    "refresh_token": "...",
    "id_token": "...",
    "account_id": "..."
  },
  "last_refresh": "2026-04-11T..."
}

Also extracts chatgpt_account_id from the JWT access token payload
and updates openai_status to handle both old and new format.

https://claude.ai/code/session_01EsVmzavc8eQK3TwPKMyzFq
ROOT CAUSE: The spawn() call spreads process.env into the child
environment. If OPENAI_API_KEY exists in the host shell (even stale),
it overrides the Codex OAuth auth.json, causing "Incorrect API key"
errors. The previous fix only deleted from the providerEnv local
object, which had no effect on inherited process.env vars.

FIX: Clone process.env into baseEnv, then DELETE OPENAI_API_KEY
from baseEnv when Codex OAuth auth.json exists. This ensures the
spawned openclaude process has NO OPENAI_API_KEY in its environment,
forcing it to use ~/.codex/auth.json instead.

Also returns active provider name from _loadProviderConfig() so
startSession() can make provider-specific decisions.

https://claude.ai/code/session_01EsVmzavc8eQK3TwPKMyzFq
Terminal-server npm install and service startup now run for ALL modes
(local and remote), not just remote. The user should never need to
manually install deps or start services after make setup.

https://claude.ai/code/session_01EsVmzavc8eQK3TwPKMyzFq
/tmp files created by sudo become root-owned, causing permission
errors when services restart as non-root user.

https://claude.ai/code/session_01EsVmzavc8eQK3TwPKMyzFq
When setup runs with sudo, services were started as root. This causes:
1. --dangerously-skip-permissions blocked (Claude/OpenClaude refuse root)
2. chdir failures (wrong home directory)
3. auth.json not found (looks in /root/.codex/ instead of user home)

Fix: detect SUDO_USER env var and use su - to start services as the
original user who ran sudo. Also chown the workspace to that user.

https://claude.ai/code/session_01EsVmzavc8eQK3TwPKMyzFq
The previous approach of {...process.env} then deleting OPENAI_API_KEY
still leaked the stale key somehow. Nuclear fix: build a CLEAN env
from scratch with only whitelisted system vars (HOME, PATH, USER, etc.)
plus the provider-specific vars. Nothing else.

This guarantees that NO stale OPENAI_API_KEY, ANTHROPIC_API_KEY, or
any other env var from the host shell leaks into the spawned CLI.

https://claude.ai/code/session_01EsVmzavc8eQK3TwPKMyzFq
…auth failure

THE BUG: OpenClaude requires OPENAI_MODEL env var to properly resolve
Codex OAuth authentication. Without it, OpenClaude falls back to API
key auth and fails with "OPENAI_API_KEY is required".

The providers.json had OPENAI_MODEL="" (empty) which was filtered out
by the v!='' check, leaving the spawned process with NO model set.

FIX: claude-bridge.js now defaults OPENAI_MODEL to gpt-5.4-mini when
the active provider is openai/codex_auth and no model is configured.
Also updated providers.example.json to have gpt-5.4-mini as default.

Confirmed working: the exact same command with OPENAI_MODEL=gpt-5.4-mini
succeeds from CLI — "Olá!" response from Oracle agent.

https://claude.ai/code/session_01EsVmzavc8eQK3TwPKMyzFq
… systemd

The .venv created by 'uv sync' (run as root via sudo) was owned by root.
When services started as ubuntu (via su -), uv couldn't access .venv.

Fix: Move chown -R to run IMMEDIATELY after all installs complete,
BEFORE starting services. Also disable any existing evonexus.service
to prevent conflicts with old processes.

https://claude.ai/code/session_01EsVmzavc8eQK3TwPKMyzFq
When using OpenAI, OpenRouter, Gemini, or any non-Anthropic provider,
GPT models don't follow agent system prompts as strongly as Claude.
The model knows about the agent but identifies as "Claude" instead.

Fix: append a persona enforcement instruction via --append-system-prompt
when the active provider is NOT Anthropic and an agent is loaded. This
forces the model to fully embody the agent persona.

Only applied for non-Anthropic providers — Claude handles agent
personas natively without reinforcement.

https://claude.ai/code/session_01EsVmzavc8eQK3TwPKMyzFq
Converted all 38 agent avatar images from 2048x2048 PNG to 512x512 WebP:
- Total size: 271MB → 1.7MB
- Per image: ~7MB → ~40KB average
- Quality: 85% WebP (visually identical at 512px)

Updated agent-meta.ts references from .png to .webp.

https://claude.ai/code/session_01EsVmzavc8eQK3TwPKMyzFq
su - user -c '... &' doesn't persist background processes — they die
when su exits. Now creates start-services.sh that uses nohup + direct
.venv/bin/python (no uv wrapper) for reliable background execution.

https://claude.ai/code/session_01EsVmzavc8eQK3TwPKMyzFq
When uv sync runs as root, .venv/bin/python symlinks to
/root/.local/share/uv/python/ which is inaccessible to non-root users.
Now runs uv sync via su - SUDO_USER so the venv uses the user's Python.

https://claude.ai/code/session_01EsVmzavc8eQK3TwPKMyzFq
Claude Code and OpenClaude block this flag for root users with:
"cannot be used with root/sudo privileges for security reasons"

Fix: detect root via process.getuid() === 0 and skip the flag.
The trust prompt is handled by the existing PTY auto-accept code
in claude-bridge.js (detects "Do you trust the files" and sends Enter).

This enables EvoNexus to work on VPS instances where root is the
only available user.

https://claude.ai/code/session_01EsVmzavc8eQK3TwPKMyzFq
The agent name was sometimes null because options.agent wasn't set
when reconnecting to existing sessions. Now uses session.agentName
as fallback, which is set when the session is created via
POST /api/sessions/for-agent.

https://claude.ai/code/session_01EsVmzavc8eQK3TwPKMyzFq
THE BUG: In server.js startClaude(), the call to startSession() had:
  { agent: agentForSession, ..., ...options }
Since ...options was AFTER agent:, and options.agent could be undefined
(when reconnecting to persisted sessions), it OVERWROTE the agent
with undefined. The --agent flag was never passed to openclaude.

FIX: Move ...options BEFORE agent: so agentForSession always wins:
  { ...options, agent: agentForSession, ... }

Also added explicit logging of agent name and spawn args for debugging.

https://claude.ai/code/session_01EsVmzavc8eQK3TwPKMyzFq
Old sessions in ~/.claude-code-web/sessions.json were being restored
with stale processes that didn't have --agent or --append-system-prompt.
Now start-services.sh deletes sessions.json before starting the
terminal-server.

https://claude.ai/code/session_01EsVmzavc8eQK3TwPKMyzFq
--append-system-prompt is too weak for GPT models — they ignore the
appended persona instruction and respond as "OpenClaude" instead of
the agent persona.

--system-prompt REPLACES the default system prompt entirely, making
the agent persona the primary instruction. Now reads the agent .md
file body and passes it as the system prompt with a persona enforcement
suffix.

Confirmed working: GPT-5.4 responds "Sou Oracle — o ponto de entrada
do EvoNexus" with --system-prompt.

https://claude.ai/code/session_01EsVmzavc8eQK3TwPKMyzFq
claude added 10 commits April 12, 2026 00:15
- Add build-essential as first prerequisite check (node-pty requires g++)
- Auto-install via apt or yum if missing
- Remove 2>/dev/null from terminal-server npm install to surface errors
- Nginx: self-signed SSL as default (Cloudflare Full compatible)
- Nginx: IPv6 listeners on ports 80 and 443
- Nginx: auto-open firewall ports (ufw + iptables)
- Nginx: remove default site, auto-enable evonexus site

https://claude.ai/code/session_01EsVmzavc8eQK3TwPKMyzFq
- Add apt-get update/upgrade at start of check_prerequisites()
  (DEBIAN_FRONTEND=noninteractive to avoid interactive prompts)
- Upgrade Node.js from 22.x (maintenance) to 24.x (active LTS)
- Change SSL default from self-signed to certbot (Let's Encrypt)
- Reuse existing certbot cert if found (idempotent re-runs)
- Auto-fallback to self-signed if certbot fails (DNS not ready)
- Fix SSL key permissions (chmod 600) after generation
- Remove 2>/dev/null from certbot to surface errors

https://claude.ai/code/session_01EsVmzavc8eQK3TwPKMyzFq
When running `sudo make setup`, uv was installed in /root/.local/bin/
but `uv sync` runs as SUDO_USER (ubuntu) who didn't have uv.
Result: .venv never created, dashboard backend fails to start (502).

Fix: install uv via `su - $SUDO_USER` so it lands in the user's
~/.local/bin/. Also add user's uv path to root's PATH for the rest
of setup, and remove 2>/dev/null from uv sync to surface errors.

https://claude.ai/code/session_01EsVmzavc8eQK3TwPKMyzFq
…o WebP

Setup fixes:
- Fix getent IndexError with try/except fallback to /home/{user}
- Remove 2>/dev/null from frontend npm build (surface errors)

Dashboard UX:
- Remove dead /chat Quick Actions from Overview
- Replace with Agents and Providers quick actions
- Redirect to /providers after first-time setup completion

Image optimization (PNG → WebP, ~70% reduction):
- docs/imgs/: 23 images (2.8MB → 916KB)
- public/: 5 images (2.0MB → 540KB)
- site/public/assets/: 6 images (2.1MB → 583KB)
- dashboard hero: 44KB → 16KB
- Updated all references in docs, README, site

https://claude.ai/code/session_01EsVmzavc8eQK3TwPKMyzFq
uv install was failing verification because:
1. uv installed at /home/ubuntu/.local/bin/ (via su - ubuntu)
2. _check_tool re-verified as root without that path in PATH
3. PATH update happened AFTER _check_tool returned — too late

Fix: resolve user home and add ~/.local/bin to PATH BEFORE calling
_check_tool, so the post-install verification finds the binary.

Also: suppress verbose apt output during system update and
build-essential install — show only clean status messages.

https://claude.ai/code/session_01EsVmzavc8eQK3TwPKMyzFq
Onboarding fix:
- workspace-status endpoint now checks if owner field is filled,
  not just if workspace.yaml exists. Remote mode creates the file
  with empty owner/company, so Setup.tsx Step 1 was being skipped.
- Setup.tsx always sends workspace data when Step 1 was shown,
  so owner/company get written to workspace.yaml on first login.

Clean setup output:
- All package installs (npm, nginx, certbot, build-essential)
  redirect stdout/stderr to /dev/null
- Use \r carriage return to overwrite "Installing..." with checkmark
- No more raw apt/npm output cluttering the terminal
- Only clean status lines: Installing... → ✓ installed

https://claude.ai/code/session_01EsVmzavc8eQK3TwPKMyzFq
Old avatar PNGs (271MB) remain in git history even after WebP
conversion. Shallow clone skips history and downloads only ~5-10MB.

https://claude.ai/code/session_01EsVmzavc8eQK3TwPKMyzFq
Complete visual overhaul of onboarding and login screens:

Setup.tsx:
- Animated gradient orbs background (green/blue, floating animation)
- Subtle grid pattern overlay for depth
- Glassmorphism card with backdrop-blur, border glow, top highlight
- Step indicator with animated ping on active step
- Lucide icons inside all input fields (User, Building2, Globe, etc.)
- Gradient CTA button with hover glow effect
- Slide transitions between steps (left/right)
- Loading spinner with branded colors
- Features bar footer (38 AI Agents, 137 Skills, Multi-Provider)
- Sparkles icon in header and CTA

Login.tsx:
- Same animated background for visual consistency
- Glassmorphism card matching Setup design
- Icon-enhanced inputs (User, KeyRound)
- Gradient button with loading spinner
- fadeInUp entrance animation

Design language: Dark glassmorphism with Evolution palette (#00FFA7
accent, #060a13 base), Inter font, uppercase tracking labels,
white/[0.03-0.08] transparency layers.

https://claude.ai/code/session_01EsVmzavc8eQK3TwPKMyzFq
Complete rewrite following frontend-design skill guidelines:
"Avoid generic AI patterns — no sparkles icons, no cliche purple
gradients, no predictable symmetric layouts."

Changes from previous version:
- Removed Sparkles icon (AI cliche)
- Removed gradient orbs/blur (generic glassmorphism)
- Removed grid overlay (overused pattern)
- Removed animated ping on step indicator
- Removed lucide icons inside inputs (cluttered)
- Removed gradient CTA buttons (over-styled)

New design approach:
- Canvas-based animated network mesh (particles + edges,
  neural-network aesthetic that is unique to EvoNexus)
- Solid card with intentional borders, no backdrop-blur
- Custom SVG logomark (layered prism, not a generic icon)
- Tab-style step nav instead of numbered circles
- Flat solid CTA button (#00FFA7) with proper hover states
- Proper autocomplete attributes on all inputs
- Minimal footer with stats as data, not decoration
- Labels use 11px uppercase tracking for hierarchy
- Dark palette: #080c14 base, #0b1018 card, #152030 borders

Follows Vercel Web Interface Guidelines:
- focus:outline with ring, never outline-none alone
- autocomplete on all inputs
- active voice, specific button labels
- proper label hierarchy

https://claude.ai/code/session_01EsVmzavc8eQK3TwPKMyzFq
Backend:
- POST /providers/active now accepts "none" to disable all providers
- Validation allows "none" as a valid provider_id

Claude-bridge:
- Block session start when active provider is "none"
- Show clear message: "No AI provider is active" with instructions
- Prevent any CLI from spawning without an active provider

Frontend (Providers.tsx redesign):
- Toggle switch (on/off) replaces Activate button
- Turning off sets active_provider to "none"
- Turning on sets active_provider to that provider
- List layout instead of card grid (cleaner, scannable)
- Same visual language as Setup/Login (dark palette, borders,
  uppercase labels, Inter font)
- Removed Star icon and badge decorations
- Compact status bar with install status + counts
- Consistent modal styling with Setup page inputs

https://claude.ai/code/session_01EsVmzavc8eQK3TwPKMyzFq
@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented Apr 12, 2026

Reviewer's Guide

Implements full multi-provider AI support (Anthropic/OpenRouter/OpenAI with Codex OAuth), hardens the VPS setup pipeline (prereq auto-install, SSL/nginx/firewall, permissions, services), redesigns Setup/Login/Providers UI, restores onboarding flow, and aggressively optimizes images/assets to WebP while tightening provider env handling and agent persona enforcement for non-Anthropic models.

Sequence diagram for OpenAI Codex OAuth via dashboard

sequenceDiagram
  actor User
  participant Browser
  participant Frontend as Providers_page
  participant Backend as Providers_API
  participant OpenAIAuth as OpenAI_auth_server
  participant OpenAIToken as OpenAI_token_endpoint
  participant FS as File_system

  User->>Browser: Open /providers and click Login on OpenAI
  Browser->>Frontend: Click event
  Frontend->>Backend: POST /api/providers/openai/auth-start
  Backend->>Backend: Generate PKCE
  Backend->>Backend: Store code_verifier in session
  Backend-->>Frontend: authorize_url
  Frontend-->>User: Show "Open OpenAI Login" link
  User->>OpenAIAuth: Open authorize_url and login
  OpenAIAuth-->>User: Redirect to callback URL (with code)
  User->>Frontend: Paste callback URL into modal
  Frontend->>Backend: POST /api/providers/openai/auth-complete
  Backend->>Backend: Extract code from URL
  Backend->>Backend: Load code_verifier from session
  Backend->>OpenAIToken: POST /oauth/token (code, code_verifier)
  OpenAIToken-->>Backend: access_token, refresh_token, id_token
  Backend->>FS: Write ~/.codex/auth.json (auth_mode Chatgpt)
  Backend->>FS: Update config/providers.json (active_provider=openai)
  Backend-->>Frontend: {status: ok, message}
  Frontend->>Backend: GET /api/providers/openai/status
  Backend-->>Frontend: {authenticated: true, method: codex_oauth}
  Frontend-->>User: Show OAuth badge and OpenAI as active provider
Loading

Sequence diagram for starting an agent session with provider validation and persona enforcement

sequenceDiagram
  actor User
  participant Browser
  participant Frontend as Agent_terminal_UI
  participant TermServer as Terminal_server
  participant Bridge as Claude_bridge
  participant ProvCfg as providers.json
  participant FS as File_system
  participant CLI as Claude_or_OpenClaude_CLI

  User->>Browser: Open agent terminal (select agent)
  Browser->>Frontend: Load AgentTerminal
  Frontend->>TermServer: WS startClaude(options.agent)

  TermServer->>Bridge: startSession(sessionId, options)
  Bridge->>ProvCfg: Read providers.json
  ProvCfg-->>Bridge: {active_provider, providers}
  Bridge->>Bridge: Determine active provider
  alt no active provider ("none" or missing)
    Bridge-->>TermServer: Error "No AI provider is active"
    TermServer-->>Frontend: WS message (error + guidance)
    Frontend-->>User: Show error, link to Providers
  else provider active
    Bridge->>FS: Read agent markdown (.claude/agents/{agent}.md)
    FS-->>Bridge: Agent definition (or fallback)
    Bridge->>Bridge: Build cleanEnv (whitelist system vars)
    Bridge->>Bridge: If provider is openai and no OPENAI_MODEL, set gpt-5.4
    Bridge->>CLI: spawn(cli_command, [--agent, --system-prompt], env=cleanEnv+provider_env)
    CLI-->>Bridge: Stream terminal output
    Bridge-->>TermServer: onOutput(data)
    TermServer-->>Frontend: WS output
    Frontend-->>User: Render terminal and agent persona responses
  end
Loading

Class diagram for providers configuration and OpenAI Codex auth file

classDiagram
  class ProvidersConfig {
    +string active_provider
    +Map~string,ProviderEntry~ providers
  }

  class ProviderEntry {
    +string id
    +string name
    +string description
    +string cli_command
    +Map~string,string~ env_vars
    +bool has_config
    +bool installed
    +bool requires_logout
    +string default_model
    +string default_base_url
    +string default_region
    +bool coming_soon
  }

  class OpenAIAuthFile {
    +string auth_mode
    +Tokens tokens
    +string last_refresh
  }

  class Tokens {
    +string access_token
    +string refresh_token
    +string id_token
    +string account_id
  }

  ProvidersConfig "1" o-- "*" ProviderEntry : providers
  OpenAIAuthFile "1" o-- "1" Tokens : tokens
Loading

File-Level Changes

Change Details Files
Redesigned Providers dashboard with single-active-provider toggles, Codex OAuth controls, and streamlined provider config/test UI.
  • Replaced provider card grid with compact list rows including install/config status and an accessible toggle switch that maps to POST /providers/active, supporting provider_id='none'.
  • Added OpenAI Codex OAuth/browser and device auth flows to the frontend including status badges, login/logout buttons, and an auth modal that talks to new /providers/openai/* endpoints.
  • Simplified provider env-var editing, defaulting, and testing flows, while removing decorative icons and tightening styling to match the new dark UI system.
dashboard/frontend/src/pages/Providers.tsx
Significantly hardened setup.py to auto-install dependencies, configure HTTPS/nginx/firewall, choose access mode, and correctly manage permissions and services.
  • Introduced _check_tool and expanded check_prerequisites to auto-install build-essential, Node.js 24.x, uv, Claude CLI, and OpenClaude with quiet output and centralized missing-tool handling.
  • Added configure_access() to choose local vs domain+SSL access, auto-provision certs via certbot (with self-signed/ manual fallback), write IPv6-enabled nginx config, and open necessary firewall ports.
  • Reworked main() to support remote quick-setup (minimal questions, remote defaults), optional local full setup, and new provider selection that deprecates Codex Auth as a separate provider in favor of OpenAI with API key vs Codex OAuth.
  • Fixed sudo/permission issues by running uv sync and services as SUDO_USER, chowning the workspace and logs before service start, chmod +x .venv/bin, generating a start-services.sh that starts terminal-server and Flask via nohup, and adding simple HTTP health checks on ports 32352/8080.
setup.py
Restored and improved onboarding setup flow and redirected post-setup users to Providers configuration.
  • Changed /api/config/workspace-status to parse workspace.yaml and only return configured=true when an owner value is present, not just when the file exists.
  • Updated Setup.tsx to always send workspace data when Step 1 is shown, to validate and advance steps client-side, and to redirect to /providers after successful admin creation.
  • Adjusted Overview quick actions to link to /agents and /providers instead of the non-existent /chat route.
dashboard/backend/routes/config.py
dashboard/frontend/src/pages/Setup.tsx
dashboard/frontend/src/pages/Overview.tsx
Implemented OpenAI Codex OAuth backend endpoints and Codex token persistence in the correct auth.json format, plus provider activation semantics for disabling providers.
  • Added OPENAI_* constants, CODEX_AUTH_FILE handling, and _save_codex_auth() to write ~/.codex/auth.json in the Chatgpt tokens schema, including JWT decoding to derive chatgpt_account_id.
  • Introduced /api/providers/openai/auth-start, /auth-complete, /device-start, /device-poll, /status, and /logout endpoints implementing PKCE browser OAuth and device auth flows, updating providers.json active_provider to openai upon success and cleaning up on logout.
  • Relaxed /api/providers/active to accept provider_id='none' to disable all providers and updated get/set logic accordingly.
dashboard/backend/routes/providers.py
Strengthened terminal-server provider handling with clean env whitelisting, Codex OAuth precedence, default model injection, root-safety, and agent persona enforcement for non-Anthropic providers.
  • Updated claude-bridge to load providers.json including active provider id, treat missing config as anthropic defaults, and, when Codex auth.json is present for openai/codex_auth, delete OPENAI_API_KEY before spawn so OpenClaude uses OAuth tokens.
  • Added agent-aware argument construction where non-anthropic providers get a --system-prompt built from the agent markdown (after frontmatter) that strongly enforces the agent persona instead of relying on --append-system-prompt.
  • Built a whitelisted environment for spawned processes (22-ish system vars plus provider env), removed spreading process.env, added default OPENAI_MODEL=gpt-5.4 for OpenAI when missing, and suppressed --dangerously-skip-permissions when running as root while still auto-accepting trust prompts.
  • Ensured terminal-server always propagates the resolved agent name into startSession options and adjusted terminal URLs to go through /terminal behind the reverse proxy.
dashboard/terminal-server/src/claude-bridge.js
dashboard/terminal-server/src/server.js
dashboard/frontend/src/components/AgentTerminal.tsx
Applied a full visual overhaul to Setup and Login flows with a canvas-based neural mesh background and modern, non-glassmorphic cards and forms.
  • Implemented a reusable NetworkCanvas component in both Setup.tsx and Login.tsx that draws an animated mesh of particles and connections using the Canvas API and requestAnimationFrame.
  • Rebuilt Setup and Login cards with a new logomark, solid dark card, typographic hierarchy, updated input/button styles, proper autocomplete attributes, and multi-step/tab navigation for workspace vs account steps.
  • Simplified loading states, tightened error presentation, and updated language defaults (e.g., ptBR) while aligning copy with the new flow.
dashboard/frontend/src/pages/Setup.tsx
dashboard/frontend/src/pages/Login.tsx
Optimized image assets and agent avatars to WebP, drastically reducing bundle size and updating references across docs, site, and dashboard.
  • Converted 38 large PNG agent avatars to 512x512 WebP and rewired all avatar paths in agent-meta.ts to .webp variants.
  • Swapped all dashboard docs screenshots, README images, site hero assets, and config screenshots from .png to .webp, updating markdown and TS imports accordingly.
  • Adjusted git clone instructions in README and docs to use --depth 1 for shallower clones.
dashboard/frontend/src/lib/agent-meta.ts
docs/dashboard/*.md
docs/*.md
README.md
site/src/pages/Home.tsx
public/*.webp
docs/imgs/*.webp
site/public/assets/*.webp

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 4 issues, and left some high level feedback:

  • In setup.py, several os.system calls interpolate user-provided values like the domain directly into shell commands (certbot, openssl, nginx, su); consider validating/sanitizing these inputs or switching to subprocess with argument lists to avoid potential command injection issues.
  • The default OpenAI model differs between setup (e.g. 'gpt-4.1' in choose_provider) and the terminal bridge fallback ('gpt-5.4'); it would be good to centralize this default so the UX and behavior are consistent across configuration and runtime.
  • The /api/providers/openai/status endpoint is unauthenticated and exposes whether Codex auth is configured; if this information should be restricted, consider adding login_required or otherwise limiting who can call it.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In setup.py, several os.system calls interpolate user-provided values like the domain directly into shell commands (certbot, openssl, nginx, su); consider validating/sanitizing these inputs or switching to subprocess with argument lists to avoid potential command injection issues.
- The default OpenAI model differs between setup (e.g. 'gpt-4.1' in choose_provider) and the terminal bridge fallback ('gpt-5.4'); it would be good to centralize this default so the UX and behavior are consistent across configuration and runtime.
- The /api/providers/openai/status endpoint is unauthenticated and exposes whether Codex auth is configured; if this information should be restricted, consider adding login_required or otherwise limiting who can call it.

## Individual Comments

### Comment 1
<location path="dashboard/backend/routes/providers.py" line_range="362-363" />
<code_context>
+# ── OpenAI Auth Flow ──────────────────────────────
+
+
+@bp.route("/api/providers/openai/auth-start", methods=["POST"])
+def openai_auth_start():
+    """Generate PKCE + authorize URL for Browser OAuth flow."""
+    code_verifier = secrets.token_urlsafe(64)
</code_context>
<issue_to_address>
**🚨 issue (security):** OpenAI OAuth endpoints should likely be protected with authentication to avoid unauthenticated token writes and provider changes

These new OpenAI OAuth/device auth endpoints (`/api/providers/openai/auth-start`, `/auth-complete`, `/device-start`, `/device-poll`, `/status`) aren’t protected with `@login_required` but can write to `~/.codex/auth.json` and switch `active_provider` to `openai`. This allows unauthenticated users or CSRF to trigger auth flows and change provider state.

Please gate the mutating endpoints behind `@login_required` (or equivalent) so only authenticated dashboard users can initiate/complete these flows and modify provider state. If they must be reusable from the CLI, consider tightening their scope or adding extra validation to prevent unauthenticated state changes.
</issue_to_address>

### Comment 2
<location path="dashboard/backend/routes/config.py" line_range="21-23" />
<code_context>
+    try:
+        content = config_path.read_text(encoding="utf-8")
+        import yaml
+        data = yaml.safe_load(content) or {}
+        ws = data.get("workspace", data)
+        owner = (ws.get("owner") or "").strip()
+        return jsonify({"configured": bool(owner)})
+    except Exception:
</code_context>
<issue_to_address>
**issue (bug_risk):** Workspace status now checks `owner` key, but CLI setup writes `owner_name`, which may incorrectly mark configured workspaces as unconfigured

This logic only treats the workspace as configured when `workspace.owner` is non-empty, but the CLI currently writes `owner_name` (and existing YAML uses `owner_name`). As a result, workspaces created via `setup.py` may now be reported as `configured: false` even though they’re valid.

To avoid this regression, either read both keys (e.g. `owner = (ws.get("owner") or ws.get("owner_name") or "").strip()`) or update the CLI to write the same key this endpoint expects so they stay consistent.
</issue_to_address>

### Comment 3
<location path="dashboard/terminal-server/src/claude-bridge.js" line_range="64-65" />
<code_context>
       }
-      return { cli_command: cliCommand, env_vars: envVars };
+      return { cli_command: cliCommand, env_vars: envVars, active };
     } catch (err) {
       console.warn(`[provider] Could not read providers.json: ${err.message}`);
       return { cli_command: 'claude', env_vars: {} };
</code_context>
<issue_to_address>
**issue (bug_risk):** Fallback return on providers.json read error does not set `active`, which now causes sessions to be blocked

In the success path `_loadProviderConfig` now returns `{ cli_command, env_vars, active }`, and `startSession` checks `providerConfig.active` to decide whether to block:

```js
if (!providerConfig.active || providerConfig.active === 'none') {
  // abort
}
```

But in the `catch` branch you still return only `{ cli_command: 'claude', env_vars: {} }`, so `active` is `undefined` and `startSession` will always treat this as "no provider active" and block, instead of falling back to Claude.

To keep the previous behavior, the error path should also set `active` to the default provider, e.g.:

```js
return { cli_command: 'claude', env_vars: {}, active: 'anthropic' };
```
</issue_to_address>

### Comment 4
<location path="dashboard/frontend/src/pages/Providers.tsx" line_range="90-93" />
<code_context>
+function NetworkCanvas() {
+  const ref = useRef<HTMLCanvasElement>(null)
+
+  useEffect(() => {
+    const canvas = ref.current
+    if (!canvas) return
</code_context>
<issue_to_address>
**suggestion (performance):** Device auth polling interval is hard-coded instead of using server-provided `interval`

The `/device-start` response includes an `interval` value:

```json
{"user_code": ..., "verification_url": ..., "interval": X, ...}
```

Consider storing this `interval` when you set `deviceCode` and using it in `setInterval(pollDeviceAuth, interval)` instead of the fixed `5000` to respect the server’s recommended polling cadence and avoid unnecessary requests.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread dashboard/backend/routes/providers.py
Comment thread dashboard/backend/routes/config.py Outdated
Comment thread dashboard/terminal-server/src/claude-bridge.js
Comment thread dashboard/frontend/src/pages/Providers.tsx
claude added 2 commits April 12, 2026 03:25
Merge origin/develop into codex-auth. Single conflict in
site/src/pages/Home.tsx: upstream added EVO_NEXUS.png import.
Resolved by converting to WebP and keeping all our WebP imports.

https://claude.ai/code/session_01EsVmzavc8eQK3TwPKMyzFq
Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New security issues found

Comment thread setup.py
"""Check if a tool is installed. If not, offer to install it."""
try:
result = subprocess.run(["claude", "--version"], capture_output=True, text=True, timeout=5)
result = subprocess.run(cmd, capture_output=True, text=True, timeout=10)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security (python.lang.security.audit.dangerous-subprocess-use-audit): Detected subprocess function 'run' without a static string. If this data can be controlled by a malicious actor, it may be an instance of command injection. Audit the use of this call to ensure it is not controllable by an external resource. You may consider using 'shlex.escape()'.

Source: opengrep

Comment thread setup.py
ret = os.system(f"{install_cmd} > /dev/null 2>&1")
# Re-check after install
try:
result = subprocess.run(cmd, capture_output=True, text=True, timeout=10)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security (python.lang.security.audit.dangerous-subprocess-use-audit): Detected subprocess function 'run' without a static string. If this data can be controlled by a malicious actor, it may be an instance of command injection. Audit the use of this call to ensure it is not controllable by an external resource. You may consider using 'shlex.escape()'.

Source: opengrep

Comment thread setup.py
npm_ok = False
for cmd in ["npm", "npm.cmd"]:
try:
result = subprocess.run([cmd, "--version"], capture_output=True, text=True, timeout=5)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security (python.lang.security.audit.dangerous-subprocess-use-audit): Detected subprocess function 'run' without a static string. If this data can be controlled by a malicious actor, it may be an instance of command injection. Audit the use of this call to ensure it is not controllable by an external resource. You may consider using 'shlex.escape()'.

Source: opengrep

Comment thread setup.py
os.system(f"cd {WORKSPACE} && uv sync -q 2>/dev/null")
_sudo_user = os.environ.get("SUDO_USER", "")
if _sudo_user and os.getuid() == 0:
os.system(f"su - {_sudo_user} -c 'cd {WORKSPACE} && uv sync -q'")
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security (python.lang.security.audit.dangerous-system-call-tainted-env-args): Found user-controlled data used in a system call. This could allow a malicious actor to execute commands. Use the 'subprocess' module instead, which is easier to use without accidentally exposing a command injection vulnerability.

Source: opengrep

Comment thread setup.py
sudo_user = os.environ.get("SUDO_USER", "")
if sudo_user and os.getuid() == 0:
print(f" {DIM}Fixing file ownership for {sudo_user}...{RESET}")
os.system(f"chown -R {sudo_user}:{sudo_user} {WORKSPACE}")
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security (python.lang.security.audit.dangerous-system-call-tainted-env-args): Found user-controlled data used in a system call. This could allow a malicious actor to execute commands. Use the 'subprocess' module instead, which is easier to use without accidentally exposing a command injection vulnerability.

Source: opengrep

Comment thread setup.py
logs_dir = WORKSPACE / "logs"
logs_dir.mkdir(exist_ok=True)
if sudo_user and os.getuid() == 0:
os.system(f"chown -R {sudo_user}:{sudo_user} {logs_dir}")
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security (python.lang.security.audit.dangerous-system-call-tainted-env-args): Found user-controlled data used in a system call. This could allow a malicious actor to execute commands. Use the 'subprocess' module instead, which is easier to use without accidentally exposing a command injection vulnerability.

Source: opengrep

Comment thread setup.py
os.chmod(startup_script, 0o755)

if sudo_user:
os.system(f"su - {sudo_user} -c '{startup_script}'")
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security (python.lang.security.audit.dangerous-system-call-tainted-env-args): Found user-controlled data used in a system call. This could allow a malicious actor to execute commands. Use the 'subprocess' module instead, which is easier to use without accidentally exposing a command injection vulnerability.

Source: opengrep

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New security issues found

Comment thread setup.py
"""Check if a tool is installed. If not, offer to install it."""
try:
result = subprocess.run(["claude", "--version"], capture_output=True, text=True, timeout=5)
result = subprocess.run(cmd, capture_output=True, text=True, timeout=10)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security (python.lang.security.audit.dangerous-subprocess-use-audit): Detected subprocess function 'run' without a static string. If this data can be controlled by a malicious actor, it may be an instance of command injection. Audit the use of this call to ensure it is not controllable by an external resource. You may consider using 'shlex.escape()'.

Source: opengrep

Comment thread setup.py
ret = os.system(f"{install_cmd} > /dev/null 2>&1")
# Re-check after install
try:
result = subprocess.run(cmd, capture_output=True, text=True, timeout=10)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security (python.lang.security.audit.dangerous-subprocess-use-audit): Detected subprocess function 'run' without a static string. If this data can be controlled by a malicious actor, it may be an instance of command injection. Audit the use of this call to ensure it is not controllable by an external resource. You may consider using 'shlex.escape()'.

Source: opengrep

Comment thread setup.py
npm_ok = False
for cmd in ["npm", "npm.cmd"]:
try:
result = subprocess.run([cmd, "--version"], capture_output=True, text=True, timeout=5)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security (python.lang.security.audit.dangerous-subprocess-use-audit): Detected subprocess function 'run' without a static string. If this data can be controlled by a malicious actor, it may be an instance of command injection. Audit the use of this call to ensure it is not controllable by an external resource. You may consider using 'shlex.escape()'.

Source: opengrep

Comment thread setup.py
os.system(f"cd {WORKSPACE} && uv sync -q 2>/dev/null")
_sudo_user = os.environ.get("SUDO_USER", "")
if _sudo_user and os.getuid() == 0:
os.system(f"su - {_sudo_user} -c 'cd {WORKSPACE} && uv sync -q'")
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security (python.lang.security.audit.dangerous-system-call-tainted-env-args): Found user-controlled data used in a system call. This could allow a malicious actor to execute commands. Use the 'subprocess' module instead, which is easier to use without accidentally exposing a command injection vulnerability.

Source: opengrep

Comment thread setup.py
sudo_user = os.environ.get("SUDO_USER", "")
if sudo_user and os.getuid() == 0:
print(f" {DIM}Fixing file ownership for {sudo_user}...{RESET}")
os.system(f"chown -R {sudo_user}:{sudo_user} {WORKSPACE}")
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security (python.lang.security.audit.dangerous-system-call-tainted-env-args): Found user-controlled data used in a system call. This could allow a malicious actor to execute commands. Use the 'subprocess' module instead, which is easier to use without accidentally exposing a command injection vulnerability.

Source: opengrep

Comment thread setup.py
logs_dir = WORKSPACE / "logs"
logs_dir.mkdir(exist_ok=True)
if sudo_user and os.getuid() == 0:
os.system(f"chown -R {sudo_user}:{sudo_user} {logs_dir}")
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security (python.lang.security.audit.dangerous-system-call-tainted-env-args): Found user-controlled data used in a system call. This could allow a malicious actor to execute commands. Use the 'subprocess' module instead, which is easier to use without accidentally exposing a command injection vulnerability.

Source: opengrep

Comment thread setup.py
os.chmod(startup_script, 0o755)

if sudo_user:
os.system(f"su - {sudo_user} -c '{startup_script}'")
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security (python.lang.security.audit.dangerous-system-call-tainted-env-args): Found user-controlled data used in a system call. This could allow a malicious actor to execute commands. Use the 'subprocess' module instead, which is easier to use without accidentally exposing a command injection vulnerability.

Source: opengrep

1. Security: add @login_required to all OpenAI OAuth endpoints
   (auth-start, auth-complete, device-start, device-poll, status)
   Previously unauthenticated users could trigger auth flows and
   write to ~/.codex/auth.json.

2. Bug risk: workspace-status checks both 'owner' and 'owner_name'
   CLI setup writes owner_name, dashboard writes owner. Now both
   keys are checked so workspaces from either source are detected.

3. Bug risk: claude-bridge catch fallback returns active: 'anthropic'
   Previously the error path returned {cli_command, env_vars} without
   active, causing startSession to treat it as "none" and block all
   sessions when providers.json was unreadable.

4. Performance: device auth polling uses server-provided interval
   Instead of hardcoded 5000ms, now uses deviceCode.interval from
   the /device-start response (default 5s if not provided).

https://claude.ai/code/session_01EsVmzavc8eQK3TwPKMyzFq
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.

3 participants