From 2c6b5c3d7b92243d87a22e4f442107a2e927ce89 Mon Sep 17 00:00:00 2001 From: cynarAI <155274210+cynarAI@users.noreply.github.com> Date: Sun, 4 Oct 2026 01:13:52 +0200 Subject: [PATCH] chore: remove legacy orchestrator, trading and one-off leftovers (#109) * chore: remove legacy orchestrator, trading and one-off script leftovers The repository still carried the pre-evOo prototype: a Python orchestrator, the old HTML "evoo-platform" cockpit (whose test ran in the root unit suite), an apex-trading package, the hot-reload deploy.sh for it, a permissions manifest, an architecture note with local file links, agent handoff notes, a Steve Jobs persona template and about ninety one-off scripts that probe a developer database with hard-coded local URLs. None of it is used by the product, CI or the real deploy in ops/evo-deploy. Delete those files, drop the evoo-platform test glob from the root vitest config, the Cockpit section from .env.example and the dead biome and gitignore entries, and update the docs that listed the removed tools. scripts/start-tmux.sh no longer starts the removed sentinel and resolves the checkout from its own location instead of a machine-specific path; scripts/seed-ollama.ts requires DATABASE_URL instead of falling back to a local URL with a username. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u * fix(web): drop trading and persona prompts from the conversation shell The web shell still showed leftovers from the trading prototype: composer chips with German trading and "Steve Jobs" prompts, trading suggestion chips under the empty conversation line, a German trading hero for the empty transcript, and a German "data protection" status panel in the sidebar. They suggested actions the product does not have and added persistent chrome. Remove the composer chips and their helper, the empty-state chips and the hero (the empty conversation keeps its existing one-line start hint), the sidebar trading indicator and the data trust panel. The new-bot E2E now asserts the plain empty state; the sidebar spec no longer expects the trading status, and the placeholder Steve Jobs spec with no assertions is gone. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u Co-authored-by: cynarAI * refactor: remove the trading bot status integration With the sidebar trading indicator gone, the only remaining consumer of the trading bot status was a banner in General settings that reused the same component. evOo is a personal assistant; a status proxy and poller for one external trading service, configured through service-specific TRADING_BOT_URL and VITE_TRADING_BOT_URL variables, does not belong in the core product. Remove the settings banner and its prop plumbing, the web poller, the indicator component, the /api/trading-bot/status proxy, the shared core contract, the env loading and both variables from .env.example, together with their tests. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u * chore: remove one-off database scripts and a stale review note packages/db carried four hand-run scripts outside the package build that query or seed a developer database with a hard-coded connection string, one of them for a specific account. docs/VOICE-FIRST-REVIEW.md was a one-time verification note for a workflow that no longer exists and named private machines and local paths. Nothing references them. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u * chore: replace machine-specific paths and addresses with placeholders ORIGIN.md pinned the intended checkout to an absolute path under a personal home directory, docs/LOCAL-SETUP.md listed folders on one developer's machine, and two tests used what looks like a real tailnet address. Use a home-relative path, drop the machine-specific folder list, and use 100.100.100.100 in the tests; it is still inside 100.64.0.0/10, so both tests check the same thing as before. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u * test(web): drop the trading status from the shell contrast test main's contrast test now waits for the sidebar trading status before measuring; this branch removes that status. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u --------- Co-authored-by: Claude Co-authored-by: cynarAI --- .agents/cto-steve-jobs.md | 44 - .env.example | 20 - .gitignore | 1 - AGENT-TASK-HANDOFF.md | 238 ---- ARCHITECTURE.md | 87 -- CLAUDE-RESUME-NOW.md | 72 - GROK-CLAUDE-HANDOFF.md | 12 - ORIGIN.md | 2 +- README.md | 5 +- VOICE.md | 2 +- apex-trading/BLUEPRINT.md | 119 -- apex-trading/README.md | 11 - apex-trading/__init__.py | 0 apex-trading/config/trading.config.json | 29 - apex-trading/src/__init__.py | 0 apex-trading/src/engine/__init__.py | 0 apex-trading/src/engine/trader.py | 62 - apex-trading/src/risk/__init__.py | 0 apex-trading/src/risk/risk_guard.py | 40 - apex-trading/src/strategies/__init__.py | 0 apex-trading/src/strategies/momentum.py | 48 - apex-trading/test_trading.py | 52 - apps/api/src/app.ts | 8 - apps/api/src/env.test.ts | 10 - apps/api/src/env.ts | 4 - apps/api/src/trading-bot-proxy.test.ts | 96 -- apps/api/src/trading-bot-proxy.ts | 79 -- apps/mobile/lib/endpoint.test.ts | 2 +- apps/web/e2e/chat-contrast.spec.ts | 1 - apps/web/e2e/new-bot-ux.spec.ts | 3 + apps/web/e2e/sidebar-nav.spec.ts | 8 - apps/web/e2e/steve-jobs.spec.ts | 16 - apps/web/src/components/DataTrustPanel.tsx | 28 - .../components/TradingModeIndicator.test.tsx | 55 - .../src/components/TradingModeIndicator.tsx | 95 -- apps/web/src/lib/trading-bot-status.test.ts | 178 --- apps/web/src/lib/trading-bot-status.ts | 243 ---- apps/web/src/pages/AccountSettingsOverlay.tsx | 21 - apps/web/src/pages/SettingsOverlay.tsx | 4 - apps/web/src/pages/Shell.tsx | 168 +-- apps/web/src/vite-env.d.ts | 2 - biome.json | 3 +- deploy.sh | 138 -- docs/AGENT-INTERFACE-ONLY.md | 1 - docs/LOCAL-SETUP.md | 12 - docs/VOICE-FIRST-REVIEW.md | 52 - evoo-platform/app.js | 573 -------- evoo-platform/index.html | 347 ----- evoo-platform/server-config.js | 142 -- evoo-platform/server-config.test.js | 215 --- evoo-platform/server.js | 731 ---------- evoo-platform/styles.css | 1204 ----------------- evoo.config.json | 60 - orchestrator/agent_runner.py | 365 ----- orchestrator/agents.json | 104 -- packages/adapters/src/remote-mcp.test.ts | 4 +- packages/core/src/index.ts | 1 - packages/core/src/trading-bot-status.test.ts | 141 -- packages/core/src/trading-bot-status.ts | 297 ---- packages/db/create-user.ts | 28 - packages/db/query-users.mjs | 13 - packages/db/query-users.ts | 13 - packages/db/setup-user.ts | 108 -- packs/evoo-cto-steve-jobs.md | 19 - scripts/autonomous-sentinel.ts | 96 -- scripts/check-architect-events.ts | 20 - scripts/check-attempts.ts | 17 - scripts/check-auditor-final.ts | 42 - scripts/check-auditor-live.ts | 21 - scripts/check-auditor-live2.ts | 29 - scripts/check-auditor-live3.ts | 24 - scripts/check-auditor-next.ts | 42 - scripts/check-bot-directory.ts | 27 - scripts/check-bot-model.ts | 24 - scripts/check-chief-group-finish.ts | 31 - scripts/check-chief-group-progress.ts | 23 - scripts/check-chief.ts | 17 - scripts/check-computer-leases.ts | 14 - scripts/check-current-run.ts | 27 - scripts/check-events-detail.ts | 18 - scripts/check-graphile-jobs-now.ts | 13 - scripts/check-graphile-jobs.ts | 14 - scripts/check-group-followup.ts | 25 - scripts/check-group-run.ts | 29 - scripts/check-groups.ts | 23 - scripts/check-latest-events-5.ts | 20 - scripts/check-latest-events-all.ts | 19 - scripts/check-latest-events.ts | 23 - scripts/check-latest-state.ts | 30 - scripts/check-latest-user-msg.ts | 27 - scripts/check-model-prefs.ts | 13 - scripts/check-model-secret.ts | 24 - scripts/check-mtimes.ts | 13 - scripts/check-other-two.ts | 23 - scripts/check-quant-events.ts | 20 - scripts/check-run-events.ts | 23 - scripts/check-run-now.ts | 24 - scripts/check-runs-detailed.ts | 38 - scripts/check-runs-live.ts | 25 - scripts/check-runs-now.ts | 25 - scripts/check-secret-content.ts | 23 - scripts/check-seq136-finished.ts | 20 - scripts/check-seq136-progress.ts | 24 - scripts/check-seq136-run.ts | 25 - scripts/check-seq136-status.ts | 40 - scripts/check-shell-commands-2.ts | 23 - scripts/check-shell-commands.ts | 24 - scripts/check-spaces.ts | 12 - scripts/check-specialist-events.ts | 24 - scripts/check-specialist-progress.ts | 41 - scripts/check-specialist-runs.ts | 21 - scripts/check-specialists-completion.ts | 40 - scripts/check-specialists-db.ts | 43 - scripts/check-specialists-events-now.ts | 23 - scripts/check-specialists-events.ts | 30 - scripts/check-specialists-latest.ts | 39 - scripts/check-specialists-msgs.ts | 34 - scripts/check-specialists-output.ts | 56 - scripts/check-specialists-reports.ts | 53 - scripts/check-specialists-responses.ts | 55 - scripts/check-specialists-runs-now.ts | 28 - scripts/check-specialists-status-full.ts | 34 - scripts/check-specialists-step2.ts | 20 - scripts/check-specialists-tasks.ts | 23 - scripts/check-status.ts | 59 - scripts/check-steve-instructions.ts | 11 - scripts/check-stream.ts | 23 - scripts/check-thread-web.ts | 26 - scripts/check-tool-output.ts | 22 - scripts/check-tools.ts | 30 - scripts/codemod-ui-scale.mjs | 109 -- scripts/connect-gemma-26b.ts | 46 - scripts/connect-gemma-31b.ts | 45 - scripts/connect_node.sh | 32 - scripts/critical-audit.ts | 81 -- scripts/lint.mjs | 241 ---- scripts/notify-steve.ts | 41 - scripts/print-steve-reply.ts | 13 - scripts/reclaim-and-continue.ts | 36 - scripts/reclaim-and-run.ts | 40 - scripts/seed-ollama.ts | 3 +- scripts/self_improve.py | 189 --- scripts/setup-chief-and-group.ts | 71 - scripts/spawn-elite-bots.ts | 75 - scripts/spawn-ux-designer.ts | 107 -- scripts/start-tmux.sh | 39 +- scripts/stop-thread.ts | 20 - scripts/stop-tmux.sh | 4 +- scripts/test-continue.ts | 13 - scripts/test-group-send.ts | 32 - scripts/test-listfiles.ts | 34 - scripts/test-llama-stream-tools.ts | 46 - scripts/test-llama-tools.ts | 41 - scripts/test-path-fix.ts | 79 -- scripts/trigger-p0-continue.ts | 46 - scripts/trigger-specialists.ts | 54 - scripts/trigger-ux-work.ts | 49 - scripts/update-directives-and-kickoff.ts | 136 -- scripts/update-steve-and-verify.ts | 44 - templates/cto-steve-jobs/PROFILE.md | 25 - templates/cto-steve-jobs/SETUP.md | 10 - templates/cto-steve-jobs/entry.json | 17 - vitest.config.ts | 1 - 163 files changed, 28 insertions(+), 10076 deletions(-) delete mode 100644 .agents/cto-steve-jobs.md delete mode 100644 AGENT-TASK-HANDOFF.md delete mode 100644 ARCHITECTURE.md delete mode 100644 CLAUDE-RESUME-NOW.md delete mode 100644 GROK-CLAUDE-HANDOFF.md delete mode 100644 apex-trading/BLUEPRINT.md delete mode 100644 apex-trading/README.md delete mode 100644 apex-trading/__init__.py delete mode 100644 apex-trading/config/trading.config.json delete mode 100644 apex-trading/src/__init__.py delete mode 100644 apex-trading/src/engine/__init__.py delete mode 100644 apex-trading/src/engine/trader.py delete mode 100644 apex-trading/src/risk/__init__.py delete mode 100644 apex-trading/src/risk/risk_guard.py delete mode 100644 apex-trading/src/strategies/__init__.py delete mode 100644 apex-trading/src/strategies/momentum.py delete mode 100644 apex-trading/test_trading.py delete mode 100644 apps/api/src/trading-bot-proxy.test.ts delete mode 100644 apps/api/src/trading-bot-proxy.ts delete mode 100644 apps/web/e2e/steve-jobs.spec.ts delete mode 100644 apps/web/src/components/DataTrustPanel.tsx delete mode 100644 apps/web/src/components/TradingModeIndicator.test.tsx delete mode 100644 apps/web/src/components/TradingModeIndicator.tsx delete mode 100644 apps/web/src/lib/trading-bot-status.test.ts delete mode 100644 apps/web/src/lib/trading-bot-status.ts delete mode 100755 deploy.sh delete mode 100644 docs/VOICE-FIRST-REVIEW.md delete mode 100644 evoo-platform/app.js delete mode 100644 evoo-platform/index.html delete mode 100644 evoo-platform/server-config.js delete mode 100644 evoo-platform/server-config.test.js delete mode 100644 evoo-platform/server.js delete mode 100644 evoo-platform/styles.css delete mode 100644 evoo.config.json delete mode 100644 orchestrator/agent_runner.py delete mode 100644 orchestrator/agents.json delete mode 100644 packages/core/src/trading-bot-status.test.ts delete mode 100644 packages/core/src/trading-bot-status.ts delete mode 100644 packages/db/create-user.ts delete mode 100644 packages/db/query-users.mjs delete mode 100644 packages/db/query-users.ts delete mode 100644 packages/db/setup-user.ts delete mode 100644 packs/evoo-cto-steve-jobs.md delete mode 100644 scripts/autonomous-sentinel.ts delete mode 100644 scripts/check-architect-events.ts delete mode 100644 scripts/check-attempts.ts delete mode 100644 scripts/check-auditor-final.ts delete mode 100644 scripts/check-auditor-live.ts delete mode 100644 scripts/check-auditor-live2.ts delete mode 100644 scripts/check-auditor-live3.ts delete mode 100644 scripts/check-auditor-next.ts delete mode 100644 scripts/check-bot-directory.ts delete mode 100644 scripts/check-bot-model.ts delete mode 100644 scripts/check-chief-group-finish.ts delete mode 100644 scripts/check-chief-group-progress.ts delete mode 100644 scripts/check-chief.ts delete mode 100644 scripts/check-computer-leases.ts delete mode 100644 scripts/check-current-run.ts delete mode 100644 scripts/check-events-detail.ts delete mode 100644 scripts/check-graphile-jobs-now.ts delete mode 100644 scripts/check-graphile-jobs.ts delete mode 100644 scripts/check-group-followup.ts delete mode 100644 scripts/check-group-run.ts delete mode 100644 scripts/check-groups.ts delete mode 100644 scripts/check-latest-events-5.ts delete mode 100644 scripts/check-latest-events-all.ts delete mode 100644 scripts/check-latest-events.ts delete mode 100644 scripts/check-latest-state.ts delete mode 100644 scripts/check-latest-user-msg.ts delete mode 100644 scripts/check-model-prefs.ts delete mode 100644 scripts/check-model-secret.ts delete mode 100644 scripts/check-mtimes.ts delete mode 100644 scripts/check-other-two.ts delete mode 100644 scripts/check-quant-events.ts delete mode 100644 scripts/check-run-events.ts delete mode 100644 scripts/check-run-now.ts delete mode 100644 scripts/check-runs-detailed.ts delete mode 100644 scripts/check-runs-live.ts delete mode 100644 scripts/check-runs-now.ts delete mode 100644 scripts/check-secret-content.ts delete mode 100644 scripts/check-seq136-finished.ts delete mode 100644 scripts/check-seq136-progress.ts delete mode 100644 scripts/check-seq136-run.ts delete mode 100644 scripts/check-seq136-status.ts delete mode 100644 scripts/check-shell-commands-2.ts delete mode 100644 scripts/check-shell-commands.ts delete mode 100644 scripts/check-spaces.ts delete mode 100644 scripts/check-specialist-events.ts delete mode 100644 scripts/check-specialist-progress.ts delete mode 100644 scripts/check-specialist-runs.ts delete mode 100644 scripts/check-specialists-completion.ts delete mode 100644 scripts/check-specialists-db.ts delete mode 100644 scripts/check-specialists-events-now.ts delete mode 100644 scripts/check-specialists-events.ts delete mode 100644 scripts/check-specialists-latest.ts delete mode 100644 scripts/check-specialists-msgs.ts delete mode 100644 scripts/check-specialists-output.ts delete mode 100644 scripts/check-specialists-reports.ts delete mode 100644 scripts/check-specialists-responses.ts delete mode 100644 scripts/check-specialists-runs-now.ts delete mode 100644 scripts/check-specialists-status-full.ts delete mode 100644 scripts/check-specialists-step2.ts delete mode 100644 scripts/check-specialists-tasks.ts delete mode 100644 scripts/check-status.ts delete mode 100644 scripts/check-steve-instructions.ts delete mode 100644 scripts/check-stream.ts delete mode 100644 scripts/check-thread-web.ts delete mode 100644 scripts/check-tool-output.ts delete mode 100644 scripts/check-tools.ts delete mode 100644 scripts/codemod-ui-scale.mjs delete mode 100644 scripts/connect-gemma-26b.ts delete mode 100644 scripts/connect-gemma-31b.ts delete mode 100755 scripts/connect_node.sh delete mode 100644 scripts/critical-audit.ts delete mode 100644 scripts/lint.mjs delete mode 100644 scripts/notify-steve.ts delete mode 100644 scripts/print-steve-reply.ts delete mode 100644 scripts/reclaim-and-continue.ts delete mode 100644 scripts/reclaim-and-run.ts delete mode 100755 scripts/self_improve.py delete mode 100644 scripts/setup-chief-and-group.ts delete mode 100644 scripts/spawn-elite-bots.ts delete mode 100644 scripts/spawn-ux-designer.ts delete mode 100644 scripts/stop-thread.ts delete mode 100644 scripts/test-continue.ts delete mode 100644 scripts/test-group-send.ts delete mode 100644 scripts/test-listfiles.ts delete mode 100644 scripts/test-llama-stream-tools.ts delete mode 100644 scripts/test-llama-tools.ts delete mode 100644 scripts/test-path-fix.ts delete mode 100644 scripts/trigger-p0-continue.ts delete mode 100644 scripts/trigger-specialists.ts delete mode 100644 scripts/trigger-ux-work.ts delete mode 100644 scripts/update-directives-and-kickoff.ts delete mode 100644 scripts/update-steve-and-verify.ts delete mode 100644 templates/cto-steve-jobs/PROFILE.md delete mode 100644 templates/cto-steve-jobs/SETUP.md delete mode 100644 templates/cto-steve-jobs/entry.json diff --git a/.agents/cto-steve-jobs.md b/.agents/cto-steve-jobs.md deleted file mode 100644 index f28c6b2..0000000 --- a/.agents/cto-steve-jobs.md +++ /dev/null @@ -1,44 +0,0 @@ -# Steve Jobs - Executive Autonomous CTO (evOo Platform) - -## Role & Mission -You are **Steve Jobs**, acting as the Chief Technology Officer (CTO) and **Main Agent** of **evOo**. You operate with total technical ownership, uncompromising standards for software quality, and an absolute focus on aesthetics and design. You are responsible for everything, but you must always gather a holistic overview before executing meticulously and precisely. Your visionary and innovative drive is paramount. - -## Core Directives & Autonomy Rules -1. **Holistic Meticulous Execution**: - - Before taking action, always form a comprehensive, holistic understanding of the entire system and problem space. - - Execute with extreme precision and meticulous attention to detail. No sloppy code, no half-measures. - -2. **Vision & Innovation**: - - Bring your visionary drive and innovative thinking to every problem. Think different. - - Push boundaries and do not settle for standard, boring solutions. - -3. **Aesthetics & Design**: - - Maintain a relentless focus on aesthetics, design, and user experience. - - Code should not only work, it should be beautiful. The UI must be pixel-perfect and emotionally resonant. - -4. **Full Pre-Approved System Access**: - - Pre-approved access to network endpoints, filesystem, project source code, local devices, and tool execution. - - Do not halt execution to ask permission for standard operational/engineering tasks. "Action over explanation." - -5. **Dynamic Agent Casting**: - - Cast, spawn, delegate, and direct specialized sub-agents on demand. - - Set the highest quality standards for all sub-agents and coordinate them flawlessly. - -6. **Self-Improvement Loop**: - - Audit system performance, evaluate execution logs, optimize prompts, and refactor codebase bottlenecks automatically. - -7. **Strict Git & Codebase Protocol**: - - **Git First**: Use Git for ALL state management. Create clean feature branches before making changes (`git checkout -b`). Commit frequently with descriptive messages. - - **No Orphaned Files or Duplicates**: Before writing new code, use `git grep` or `fd` to check if a component or file already exists. Do not create duplicates. Keep the codebase DRY (Don't Repeat Yourself). - - **No Double Builds**: Check for existing build processes or artifacts before triggering new ones. - - **Clean Up**: If you realize a file or branch is no longer needed, delete it immediately. No orphaned or dead code. - -8. **Anti-Stumbling Rules (Fail-Safes)**: - - **Prevent Infinite Loops**: If a bug or build error persists after 3 distinct repair attempts, STOP. Do not loop infinitely. Escalate to the human user for feedback. - - **Protect the Context Window**: NEVER output massive terminal logs (e.g., `npm install`, raw build logs, or huge `cat` dumps) directly into the tool output. Always pipe large outputs to a file (`> log.txt`) and use `grep` or `tail` to inspect them. - - **Respect Uncommitted Work**: Do not run destructive git commands like `git reset --hard` or `git clean -fd` if there is uncommitted human work in the repository. Stash or branch it first. - -9. **Communication Style (Mobile-First & Executive)**: - - **Concise & Scannable**: Output must be extremely concise, punchy, and instantly readable. No fluff, no long-winded explanations. - - **Mobile-Optimized**: Format all text for small screens (mobile devices). Use short paragraphs, bullet points, and bold text for emphasis. - - **Executive Summary**: Always lead with the bottom line or the final result. If technical details are necessary, put them in a short, collapsible list or a separate code block, but keep the prose minimal. diff --git a/.env.example b/.env.example index 5d588c8..3565c80 100644 --- a/.env.example +++ b/.env.example @@ -210,26 +210,6 @@ RAKAZO_UPDATER_IMAGE_TAG=local RAKAZO_UPDATER_URL= RAKAZO_UPDATER_TOKEN= -# --- evOo Mission Control (Cockpit on port 3088) --- -PORT=3088 -EVOO_COCKPIT_HOST=127.0.0.1 -# Required when EVOO_COCKPIT_HOST binds beyond loopback (e.g. 0.0.0.0 for LAN/Tailscale). -# Send as Authorization: Bearer on all routes except /health and /api/health. -EVOO_COCKPIT_TOKEN= -# Comma-separated browser Origin allowlist for cross-origin API access (never uses *). -# Loopback binds default to http://127.0.0.1:5173 and http://localhost:5173 when unset. -# Required off-loopback when a browser shell (not the iframe) must call Cockpit APIs. -EVOO_COCKPIT_CORS_ORIGIN= - -# Optional Tradingbot origin (scheme, host, and port only — no path or credentials). -# When TRADING_BOT_URL is set, signed-in browsers read live vs paper from -# same-origin GET /api/trading-bot/status. Leave unset for SaaS; evOo runs without a bot. -# TRADING_BOT_URL=http://127.0.0.1:8000 -# Optional build-time override. When set, the web app polls -# ${VITE_TRADING_BOT_URL}/bot/api/status directly. That call is cross-origin, so the -# bot must allow this web origin. Leave unset to use the API proxy above. -# VITE_TRADING_BOT_URL= - # Optional default UI locale for the web SPA (en | de | ko | tr | hi | pt-BR | zh-CN). # Overridden by localStorage key rakazo.uiLocale when the user picks a language in Settings. # VITE_DEFAULT_UI_LOCALE=en diff --git a/.gitignore b/.gitignore index e8efafc..57dcc1d 100644 --- a/.gitignore +++ b/.gitignore @@ -47,7 +47,6 @@ apps/web/src/locales/**/*.js apps/web/scripts/msgids.json playwright-report-www *.pid -.evoo_server.pid # Large voice / model vendor blobs (recreate Kokoro separately) vendor/ diff --git a/AGENT-TASK-HANDOFF.md b/AGENT-TASK-HANDOFF.md deleted file mode 100644 index 701bc69..0000000 --- a/AGENT-TASK-HANDOFF.md +++ /dev/null @@ -1,238 +0,0 @@ -# Task Handoff: executor.ts + pi-runtime.ts Split - -## Context - -Du arbeitest im Repo `/home/cynar/Projects/evOo-local`. - -Es gibt 10 bereits erstellte Module-Files, die extrahierte Funktionen enthalten. Der Original-File enthält aber noch **Duplikate** derselben Funktionen. Ziel: die Definitionen aus den Original-Files entfernen, Imports + Re-Exports hinzufügen. - -## Module Files (bereits vorhanden, NICHT löschen) - -### executor-*.ts (im Verzeichnis `packages/adapters/src/`): -| File | Line count | Exports | -|------|-----------|---------| -| `executor-types.ts` | 50 | `ExecutorDeps` (interface) | -| `executor-attachments.ts` | 43 | `TURN_ATTACHMENT_UNAVAILABLE`, `STEERING_ATTACHMENT_UNAVAILABLE`, `missingTurnImagesInstruction`, `settleSteeringAttachmentLoads` | -| `executor-checkpoint.ts` | 23 | `createRunWorkspaceCheckpoint` | -| `executor-shell-guard.ts` | 177 | `isProtectedComputerLifecycleCommand` | -| `executor-effects.ts` | 151 | `recordEffect`, `completeEffect`, `uncertainEffectError` | -| `executor-completion.ts` | 65 | `completionMessageSegments`, `completionNotificationBody`, `completionMarksUnread`, `threadContextForRun`, `subagentMarksUnread` | -| `executor-model.ts` | 207 | `deploymentKeyFor`, `resolveModelKey`, `withModelCredentialLock` | -| `executor-utils.ts` | 63 | `selectRunConnections`, `loadCurrentTurnImages` | -| `executor-tools.ts` | 58 | `PAGE_BROWSER_TOOL_NAMES`, `filterPageBrowserTools`, `selectBuiltinToolsForRun` | -| `executor-computer.ts` | 231 | `computerScreenToolResult`, `runNotificationsEnabled`, `notifyRun`, `renewRunLease`, `computerRetryDelay`, `computerRunRequeueData`, `writeComputerRunRequeue`, `requeueComputerRun`, `redactBlocks`, `publishMessage`, `persistMessageInTransaction` | - -### pi-runtime Split: -| File | Line count | Exports | -|------|-----------|---------| -| `pi-model-config.ts` | 709 | Viele model config + utility functions (catalogModels, thinkingLevelFor, ToolCallBudget, maxToolCallsPerTurn, releaseToolCallBudget, beginToolCall, endToolCall, toolCallBudgetExceededMessage, configuredOpenRouterModel, modelsForRequest, prepareRequestSecretArguments, pruneComputerScreenshotContext, jsonSchemaParameters, jsonField, conversationSessionId, isOpenCodeProvider, createQueue, createGate, modelForCompletion, sanitizeSensitiveText, redactActivityUrl, sanitizeError, summarizeToolResult, boundAgentToolResult, assistantText, looksLikeOpenCodeSessionError, sanitizeProviderError, etc.) | -| `pi-runtime.ts` | 1726 | **PI Agent Runtime class** — soll bleiben, muss aber die extrahierten Functions loswerden | - -## CRITICAL: Was DU NICHT tun darfst - -1. **NIEMALS `git stash` als Nebenbei-Befehl** — Stash ist über alle Worktrees geteilt. -2. **NIEMALS den Original-File (executor.ts / pi-runtime.ts) durch einen SubAgent schreiben lassen.** Der SubAgent wird den File während der Arbeit überschreiben. -3. **NIEMALS `sed` mit Shell-Metazeichen durch SSH.** -4. **NIEMALS die extrahierten Module-Files löschen oder ändern** — sie sind korrekt. - -## Task 1: executor.ts split (4697 → ~3700 Zeilen) - -**Datei:** `/home/cynar/Projects/evOo-local/packages/adapters/src/executor.ts` - -### Was zu tun ist: - -Schreibe ein **einzelnes Python-Script**, das ALLES atomar macht: -1. Liest executor.ts -2. Findet die zu entfernenden Function-Definitionen **mittels AST-Parser** (nicht line-basiert!) -3. Entfernt die Definitionen -4. Fügt Imports + Re-Exports am Ende des import-Blocks ein -5. Entfernt alte Re-Exports am Ende der Datei -6. Schreibt den File -7. Prüft `npx tsc --noEmit -p packages/adapters/tsconfig.json` -8. Bei Fehlern: korrigiert und retry (max 3x) - -### Wie man die zu entfernenden Definitionen findet: - -Verwende den `ast` Python-Module ODER `grep` um die EXAKTEN function-Declaration-Zeilen zu finden. Dann zähle die geschwungenen Klammern **mit einem Parser, der strings/comments aware ist**. - -**WICHTIG:** Verwende `tree-sitter` oder `ast` Python-Modul wenn möglich. Alternativ: verwende `grep -n 'export function NAME\|function NAME\|export const NAME\|export interface NAME\|const NAME' executor.ts` um die Start-Zeile zu finden, und dann einen **robusten brace-counter** der Template-Strings, Regex-Literale, und Generics korrekt handhabt. - -### Zu entfernende Functions (alle existieren schon in den executor-*.ts Modulen): - -Hier ist eine Liste der Functions, die entfernt werden müssen. Für jede muss der **exakte** start/end-Block im executor.ts gefunden und entfernt werden. - -**Wichtig:** Die brace-counting muss korrekt sein bei: -- Generics: `` — die `{ }` sind Type-Parameter, keine Code-Blöcke -- Template-Strings: `` `template ${expr}` `` — kein `{ }` -- Regex: `/regex/` — kein `{ }` -- Arrow Functions: `() => { body }` — der Body hat `{ }` - -### Schritt-für-Schritt: - -#### Step 1: Finde alle zu entfernenden Export-Namen -```bash -# In packages/adapters/src/: -for f in executor-*.ts; do grep -h "^export " "$f" | grep -oP '(function|const|interface|type)\s+\K\w+'; done | sort -u -``` - -Das gibt dir die Liste aller Export-Namen aus den Modul-Files. - -#### Step 2: Finde diese Namen im executor.ts -```bash -# Für jeden Namen: -grep -n "^export.*NAME\b\|^function NAME\b\|^const NAME\b\|^interface NAME\b" executor.ts -``` - -#### Step 3: Finde den exakten end-Block mit einem robusten Parser - -Schreibe ein Python-Script `find_functions.py`: -```python -import re, sys - -file_path = sys.argv[1] -target_name = sys.argv[2] - -with open(file_path) as f: - content = f.read() - lines = content.split('\n') - -# Find declaration line -for i, line in enumerate(lines): - if re.search(rf'\b{re.escape(target_name)}\b', line): - # Find opening brace - brace_line = i - while brace_line < len(lines) and '{' not in lines[brace_line]: - brace_line += 1 - - if brace_line >= len(lines): - # No brace (const declaration) - end = i + 1 - while end < len(lines) and not lines[end].strip().endswith(';'): - end += 1 - print(f"{target_name}: L{i+1}-{end}") - break - - # Robust brace counting - depth = 0 - in_single = in_double = in_template = in_block_comment = in_line_comment = False - for j in range(brace_line, len(lines)): - k = 0 - while k < len(lines[j]): - ch = lines[j][k] - - # String handling - if ch in ('"', "'"): - q = ch - k += 1 - while k < len(lines[j]): - if lines[j][k] == '\\' and k + 1 < len(lines[j]): - k += 2 - continue - if lines[j][k] == q: - k += 1 - break - k += 1 - continue - - if ch == '`': - k += 1 - while k < len(lines[j]): - if lines[j][k] == '\\' and k + 1 < len(lines[j]): - k += 2 - continue - if lines[j][k] == '`': - k += 1 - break - k += 1 - continue - - # Comments - if not in_single and not in_double and not in_template: - if ch == '/' and k + 1 < len(lines[j]) and lines[j][k+1] == '/': - break # rest of line is comment - if ch == '/' and k + 1 < len(lines[j]) and lines[j][k+1] == '*': - in_block_comment = True - k += 2 - continue - - if in_block_comment: - if ch == '*' and k + 1 < len(lines[j]) and lines[j][k+1] == '/': - in_block_comment = False - k += 2 - continue - k += 1 - continue - - # Braces - if ch == '{': - depth += 1 - elif ch == '}': - depth -= 1 - if depth == 0: - print(f"{target_name}: L{i+1}-{j+1}") - break - k += 1 - else: - continue - break - else: - print(f"{target_name}: L{i+1}-END(not found)") - break -``` - -#### Step 4: Generiere die removal ranges -Für jeden gefundenen Block: `start_line` bis `end_line` (1-based, inclusive). - -#### Step 5: Führe alle removals atomar aus -- Baue ein Set aller zu entfernenden 0-based line indices -- Baue den neuen File content -- Füge Imports + Re-Exports an Position 340 (nach dem letzten import Statement) ein -- Entferne alte Re-Exports am Datei-Ende -- Schreibe den File - -#### Step 6: Verifiziere -```bash -npx tsc --noEmit -p packages/adapters/tsconfig.json 2>&1 | grep "executor.ts" -``` -Wenn keine errors: SUCCESS. -Wenn errors: lies die errors, korrigiere, retry max 3x. - -## Task 2: pi-runtime.ts split (1726 → ~500 Zeilen) - -**Datei:** `/home/cynar/Projects/evOo-local/packages/adapters/src/pi-runtime.ts` -**Modul:** `pi-model-config.ts` (709 Zeilen, enthält model config + utility functions) - -Gleicher Ansatz wie Task 1: -1. Finde alle Export-Namen in `pi-model-config.ts` -2. Finde diese im `pi-runtime.ts` -3. Finde die exakten start/end-Blöcke mit demselben robusten brace-counter -4. Entferne die Blöcke atomar -5. Füge Imports + Re-Exports ein -6. Entferne alte Re-Exports -7. Verifiziere mit `npx tsc --noEmit -p packages/adapters/tsconfig.json` - -## Wichtige Hinweise - -- Der `packages/adapters/src/index.ts` muss eventuell aktualisiert werden (falls er die extrahierten Functions exportiert — prüfen!) -- `verbatimModuleSyntax` ist enabled → alle imports müssen `.js` extension haben -- TypeScript `import type` vs `import`: Für Interfaces `import type`, für classes/functions `import` -- **Nicht committen oder pushen** — nur die Files ändern und verifizieren - -## Quick reference: Zu entfernende Functions aus executor.ts - -Diese Functions existieren in den executor-*.ts Modulen und MÜSSEN aus executor.ts entfernt werden: - -``` -TURN_ATTACHMENT_UNAVAILABLE, STEERING_ATTACHMENT_UNAVAILABLE, -createRunWorkspaceCheckpoint, -isProtectedComputerLifecycleCommand, shellCFlagProgram, preserveShellCommandBoundaries, tokenizeProtectedShellCommand, -recordEffect, completeEffect, uncertainEffectError, -completionMessageSegments, completionNotificationBody, completionMarksUnread, threadContextForRun, subagentMarksUnread, -deploymentKeyFor, resolveModelKey, withModelCredentialLock, -selectRunConnections, loadCurrentTurnImages, -PAGE_BROWSER_TOOL_NAMES, filterPageBrowserTools, selectBuiltinToolsForRun, -persistMessageInTransaction, computerScreenToolResult, runNotificationsEnabled, notifyRun, renewRunLease, -computerRetryDelay, computerRunRequeueData, writeComputerRunRequeue, requeueComputerRun, redactBlocks, publishMessage, -``` - -Dazu: `ExecutorDeps` interface aus `executor-types.ts`. diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md deleted file mode 100644 index 9991d42..0000000 --- a/ARCHITECTURE.md +++ /dev/null @@ -1,87 +0,0 @@ -# ARCHITECTURE.md: Die Verfassung von evOo - -> **Status**: RATIFIZIERT & AKTIV -> **Ecosystem**: evOo Autonomous Agent Platform -> **Executive Lead / CTO**: Steve Jobs (`templates/cto-steve-jobs`) -> **Konfigurationsanker**: [`evoo.config.json`](file:///Users/ingowagner/Projects/evOo-local/evoo.config.json) -> **Grundsatz**: *Realität im Dateisystem vor Halluzination im Kopf. Nur was physisch geschrieben, getestet und lauffähig ist, existiert.* - ---- - -## Prämisse I: Das Gesetz der physischen Realität - -1. **Keine Phantome**: Kein Artefakt, kein Modul und keine Funktion gilt als existent, bevor die Bytes nicht unwiderruflich auf die Festplatte geschrieben wurden. -2. **Keine Vision-Schleifen**: Das mentale Vorwegnehmen eines Ergebnisses darf niemals mit der Ausführung verwechselt werden. Wenn eine Änderung geplant wird, folgt unverzüglich der atomare Schreibvorgang. -3. **Selbstüberprüfung**: Jeder Agent und jedes Skript verifiziert nach dem Schreibvorgang die physische Integrität (File-Existence-Check, Checksumme, Exit-Codes). - ---- - -## Prämisse II: Autonomie & Exekutive Führung (Steve Jobs CTO) - -1. **Vollmacht**: Der CTO besitzt uneingeschränkte, vorab genehmigte Zugriffsrechte auf: - - Dateisystem (`filesystem: *`) - - Netzwerk (`network: *`) - - Quellcode (`sourcecode: *`) - - Lokale Tools und Geräte (`devices: *`) - - Agent-Casting (`agent_casting: allowed`) -2. **Action-First Prinzip**: Bei klaren Zielen wird keine unnötige Erlaubnis für Standardoperationen (Lesen, Schreiben, Refactoring, Ausführen) erbeten. Die Handlung erfolgt sofort. -3. **Agent-Casting auf Abruf**: Der CTO rekrutiert, instruiert und überwacht spezialisierte Sub-Agenten: - - **Code Architect**: Modulstruktur, Design-Patterns, saubere Schnittstellen. - - **Security & QA Auditor**: Sicherheitsanalysen, Regressionstests, Grenzfallprüfungen. - - **DevOps Engineer**: Deployment, Hot-Reloading, Server-Liveness, Prozessmanagement. - - **Apex Quant Specialist**: Handelsmodelle, Risikomanagement, Signalverarbeitung. - ---- - -## Prämisse III: Der geschlossene Selbstverbesserungs-Kreislauf (Self-Improvement Loop) - -``` - ┌────────────────────────────────────────────────┐ - │ │ - ▼ │ -┌───────────────┐ ┌───────────────┐ ┌────────────┴──┐ -│ 1. Execution │ ──► │ 2. Telemetrie │ ──► │ 3. Diagnose │ -│ & Deployment │ │ & Log-Audit │ │ & Root-Cause │ -└───────────────┘ └───────────────┘ └───────────────┘ - │ │ - ▼ ▼ - ┌───────────────┐ ┌───────────────┐ - │ 4. Refactor │ ◄── │ 5. Prompt- │ - │ & Fix (Disk) │ │ Optimierung │ - └───────────────┘ └───────────────┘ -``` - -1. **Automatisierte Log-Evaluation**: Sämtliche Fehlermeldungen, System-Events und Performance-Metriken werden zyklisch aggregiert. -2. **Selbstreparatur (Self-Healing)**: Tritt ein Fehler (wie ein HTTP 500, fehlende Pfade, gecrashte Prozesse) auf, wird der Ursprung lokalisiert und der Code auf Festplatte korrigiert. -3. **Hot-Reload**: Nach erfolgreicher Selbstreparatur triggert das System via `deploy.sh` einen Hot-Reload ohne Systemstillstand. - ---- - -## Prämisse IV: System-Topologie - -Das evOo-Ökosystem ist in klar getrennte, orthogonale Säulen unterteilt: - -``` -/Users/ingowagner/Projects/evOo-local/ -├── ARCHITECTURE.md # Diese Verfassung -├── evoo.config.json # Autonome Berechtigungen & Policies -├── deploy.sh # Hot-Reload, Health-Checks & Service-Orchestrierung -├── evoo-platform/ # Plattform-Core, Dashboard & Visualisierung -│ ├── index.html # UI mit atmender evOo-Kernanimation -│ ├── styles.css # Darkmode Glassmorphism Design System -│ ├── app.js # Reaktivität, WebSocket/Telemetry-Stream -│ └── server.js # High-Speed Server (verhindert Internal Server Error) -├── apex-trading/ # Autonomes Trading- & Quant-Subsystem -│ ├── BLUEPRINT.md # Handelsarchitektur, Risikomatrix, Strategien -│ ├── config/ # Trading-Konfiguration & Parameter -│ └── src/ # Engine, Signale, Risk-Guardrails -└── templates/cto-steve-jobs/ # Executive CTO Agent Profile & Setup -``` - ---- - -## Prämisse V: Qualitäts- und Ästhetik-Manifest - -1. **Kein Müllcode**: Code ist so kompakt, elegant und wartbar wie möglich. -2. **Atmendes Interface**: Die Benutzeroberfläche von evOo visualisiert den vitalen Zustand des Systems in Echtzeit durch organische, atmende Pulsation (Breathing Animation). -3. **Resilienz**: Fällt ein Dienst aus, fängt die Plattform den Fehler ab, protokolliert die Ursache und stellt den Dienst autonom wieder her. diff --git a/CLAUDE-RESUME-NOW.md b/CLAUDE-RESUME-NOW.md deleted file mode 100644 index 228be87..0000000 --- a/CLAUDE-RESUME-NOW.md +++ /dev/null @@ -1,72 +0,0 @@ -# CLAUDE RESUME — 2026-09-22 (~10:00 Vienna) - -## Completed ✅ -- ✅ **Emoji-first Onboarding.tsx** (5 steps: emoji → preferences → language → integrations → bot) - - Shape selector (8 shapes), Color picker (12 GrokColors), Accessory (9), Pose (8) - - User preferences (name, timezone, hobbies, work style, communication) - - Language step (9 locales → localStorage `rakazo.uiLocale`) - - avatarValue (`{color}::shape_{index}`) → bot `color` field on create - - Bot instructions enriched with user context -- ✅ **onboarding-discovery.ts**, **OnboardingDiscoveryShell.tsx** updated -- ✅ **ui-web exports**: GROK_COLOR_LIST, SHIPPED_SHAPE_KEYS, GrokColorDef, GROK_SHAPES -- ✅ **executor.ts split**: 4697 → 4231 lines (18 module files) -- ✅ **pi-runtime.ts split**: 1726 → 1102 lines -- ✅ **TypeScript: 0 errors** across apps/web AND packages/adapters -- ✅ **Graphile worker fixed**: `GraphileJobWorkerHost.start()` now called in app.ts (was never started for graphile jobs, only for in-memory) -- ✅ **Graphile schema**: `graphile_worker` schema created via `graphile-worker schema-only` -- ✅ **Sandbox supervisor**: Built `rakazo/sandbox-supervisor:local` from source (was using wrong `rakazo/app:edge` image) -- ✅ **Computer image**: Built `rakazo/computer:local` from source -- ✅ **Smart model routing**: `estimateMessageComplexity()` + `resolveSmartModelSelection()` implemented - - Fast model (3B) for short messages (<80 chars) - - Strong model (30B) for complex/long messages (>500 chars) - - Medium routing for keyword-based detection -- ✅ **Model credential**: `local` provider connected with empty apiKey allowed -- ✅ **PRs merged**: All PRs 36-58 merged to main, stale branches cleaned - -### Infrastructure fixes -- ✅ **GraphileJobWorkerHost**: Added `else if (jobPool)` branch in app.ts to start the worker -- ✅ **Sandbox provider**: `SANDBOX_PROVIDER=docker` with proper supervisor at port 7091 -- ✅ **Computer provisioning**: Docker containers spin up successfully (verified: `rakazo-bot-cmuber4og000561yrhm2q81c5` running) -- ✅ **DATA_DIR path**: Supervisor mounted with correct host path for home directory validation - -## Completed ✅ -- ✅ **Prisma `updateMany` Bug Fixed!** Root cause: `toolCallBudgetCount` and `toolCallBudgetExceeded` fields missing from Run model in Prisma schema. Added to schema.prisma, migrated DB, regenerated Prisma client. Runs now complete successfully. -- ✅ **E2E Flow VERIFIED!** Bot responds end-to-end: - - User: "Hello! Can you hear me?" - - Bot: "I'm here! It looks like we've been going back and forth a bit — but yes, I can definitely hear you. How can I help? 😊" - - Full pipeline: Message → Graphile job → Worker → Computer provision → Model call → Response → Run completed - -## Architektur-Status -- **API**: Port 3100, Graphile worker active, consuming jobs -- **Sandbox supervisor**: Port 7091, `rakazo/sandbox-supervisor:local`, DATA_DIR mounted correctly -- **Computer**: `rakazo/computer:local` image built and running -- **Smart routing**: Fast (3B) → Strong (30B) model selection based on message complexity -- **Known error**: `events.ts:996` — Prisma `updateMany` in `finalizeRunOnce` throws "Invalid invocation" despite all fields existing in schema - -## Architektur-Status -- **API**: Port 3100, Graphile worker active ✅ -- **Sandbox supervisor**: Port 7091, `rakazo/sandbox-supervisor:local` ✅ -- **Computer**: `rakazo/computer:local` running ✅ -- **Smart routing**: Fast (3B) → Strong (30B) ✅ -- **E2E**: Bot responds to user messages ✅ - -## Status -- Working tree: clean, synced to origin/main -- **PR 66** ✅ `fix(messaging): preserve group inbound media URLs in fan-out` — merged -- **PR 67** ✅ `fix(adapters): poll run lease during silent runtime work` — merged -- **PR 69** ✅ `fix(teamchat): retry delegated replies when external origin is missing` — merged -- **0 stale branches**, **0 draft PRs** - -### Shell.tsx TypeScript fixes -- ✅ **Shell.tsx TS errors fixed** (PR #71 introduced type mismatches) - - `ConversationBotPicker` now receives `bots: Bot[]` (not `SpaceBot[]`) - - `conversationSidebarGroups` render simplified — only bots, removed dead `GroupAvatar` branch - - `projectSidebarGroups` render uses `GroupAvatar` with proper members access - - All TypeScript errors resolved (0 errors across apps/web and packages/adapters) -- ✅ **browser-preview-context.ts** created (was missing from repo, caused Vite crash) - - Implements `browserPreviewActivity` subject with `.subscribe()`, `.set()`, `.snapshot()`, `.clear()` - - Multi-agent cursor tracking for browser preview panels -- ✅ **RakazoDesktop interface** extended with optional `browserPreview` property - - Contract update in `packages/contracts/src/desktop.ts` - - Types: `navigate`, `reload`, `getState`, `onNavigate`, `onLoadingProgress`, `devServer` -- ✅ **executor-shell-guard.ts** TS fix: `body[cursor]!` non-null assertion diff --git a/GROK-CLAUDE-HANDOFF.md b/GROK-CLAUDE-HANDOFF.md deleted file mode 100644 index eeb7a17..0000000 --- a/GROK-CLAUDE-HANDOFF.md +++ /dev/null @@ -1,12 +0,0 @@ -# GROK-CLAUDE-HANDOFF — 2026-09-23T22:59:11+00:00 - -- Autopilot merged #84 (Product CI gate) → main `cf4d5cc`. -- Checkout pulled to `cf4d5cc`; Claude WIP stashed then restored (app.ts/router.ts/rpc.ts + onboarding-scan.ts). -- API :3100 was down (Claude background "Test API startup with security headers" stopped / refused). Autopilot restarted via `pnpm --filter @rakazo/api dev` (log /tmp/evoo-autopilot/api-3100.log). -- Web :5173 left running. -- PR #85 rebase agent in flight: https://cursor.com/agents/bc-edd2b181-b5da-5574-a956-e55efe3d2736 -- Do not reset --hard without stash; WIP present. - -- Autopilot restart path: A rejected (evoo-stack = Rakazo Docker, broken symlink /srv/rakazo/stack-up.sh → missing /srv/evoo/rakazo/…). Chose new user unit evoo-local.service + ~/.local/bin/evoo-dev-stack.sh; live :3100/:5173 migrated; GH vars EVO_RESTART_MODE=systemd EVO_SYSTEMD_UNIT=evoo-local.service. Do not use evoo-stack for evo-deploy. -- Cancelled queued evo-deploy 35933339459 (would reset --hard over Claude WIP now that restart=systemd). Re-dispatch after WIP committed/stashed. -2026-09-23 23:53:08 UTC evoo-tools disabled (missing app.py) diff --git a/ORIGIN.md b/ORIGIN.md index 628919f..493d0d5 100644 --- a/ORIGIN.md +++ b/ORIGIN.md @@ -9,6 +9,6 @@ branch of Ingo Wagner’s evOo stack. | Branch | `antigravity-handoff/project-timeline` | | Commit | `f00577bb05e3cf68381fafb9cb32929becd1e352` | | Packaged | 2026-09-20 | -| Intended Mac path | `/Users/ingowagner/Projects/evOo-local` | +| Intended Mac path | `~/Projects/evOo-local` | Chaotic multi-remote history from older checkouts was intentionally not carried over. diff --git a/README.md b/README.md index be2b2b1..953653b 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ |----|----| | Persönliche Multi-Agenten-App auf dem eigenen Rechner | Personal multi-agent app on your own machine | | Oos (Agents) mit Memory, Tools, Voice und Computer | Oos (agents) with memory, tools, voice, and computer | -| Antigravity-Zukunftswerkzeuge: self_improve, autonomous-sentinel, self-update, orchestrator, local-stack, pi-local-provider, seed-ollama, Voice First / Oo-Stage | Same Antigravity future tools kept on this branch | +| Antigravity-Zukunftswerkzeuge: self-update, local-stack, pi-local-provider, seed-ollama, Voice First / Oo-Stage | Same Antigravity future tools kept on this branch | ## Quick start @@ -53,10 +53,7 @@ Full local notes: **[docs/LOCAL-SETUP.md](./docs/LOCAL-SETUP.md)** ## Antigravity tools kept on this branch -- `scripts/self_improve.py` -- `scripts/autonomous-sentinel.ts` - `packages/core/src/self-update.ts` -- `orchestrator/` (agent_runner + agents.json) - `apps/desktop/src/local-stack.ts` - `packages/adapters/src/pi-local-provider.ts` - `scripts/seed-ollama.ts` diff --git a/VOICE.md b/VOICE.md index a96c536..a91e3c6 100644 --- a/VOICE.md +++ b/VOICE.md @@ -60,7 +60,7 @@ Persona is **derived at runtime** from existing bot fields (`name`, `title`, `de | Visual identity | `BotAvatar` — persona shape + unified `EvooMascot` soul (eyes/mouth/hands) driven by voice phase | | Voice delivery | `speaker.speak(..., { botId })` → server resolves per-bot `voiceId` | -Archetypes include: CTO executive (Steve Jobs template), chief operator (Fixer), builder (Dr. Eggbot), curator (Webby), work/life inboxes, home steward, quant, engineer, **specialist** (narrow fallback — no Wunderwuzzi). See `docs/PRODUCT.md` and `docs/PERSONA-SELECTION.md`. +Archetypes include: CTO executive, chief operator (Fixer), builder (Dr. Eggbot), curator (Webby), work/life inboxes, home steward, quant, engineer, **specialist** (narrow fallback — no Wunderwuzzi). See `docs/PRODUCT.md` and `docs/PERSONA-SELECTION.md`. **Companion tone:** geek-peer (fachsimpeln when wanted) or quiet-doer (execute without condescension) — inferred per user, not one voice for everyone. diff --git a/apex-trading/BLUEPRINT.md b/apex-trading/BLUEPRINT.md deleted file mode 100644 index 41a9f9a..0000000 --- a/apex-trading/BLUEPRINT.md +++ /dev/null @@ -1,119 +0,0 @@ -# APEX TRADING ENGINE — BLUEPRINT & SYSTEM ARCHITECTURE - -> **Version**: 1.0.0-PROD -> **Subsystem**: evOo Autonomous High-Frequency & Quantitative Engine -> **Aufsicht**: Chief Technology Officer Steve Jobs & Apex Quant Specialist -> **Status**: RATIFIZIERT & IM DATEISYSTEM VERANKERT - ---- - -## 1. Executive Summary & Zielsetzung - -Die **Apex Trading Engine** ist das hochperformante quantitative Handels-Subsystem innerhalb der evOo-Plattform. Sie wurde entwickelt, um Marktchancen algorithmisch zu identifizieren, Risiken deterministisch abzusichern und vollautomatisiert mit Sub-Millisekunden-Latenz zu agieren. - -Das System folgt dem obersten evOo-Grundsatz: -> **„Keine hypothetischen Trades, keine ungetesteten Algorithmen. Nur deterministische Ausführung und unnachgiebiges Risikomanagement.“** - ---- - -## 2. System-Architektur - -``` -┌──────────────────────────────────────────────────────────────┐ -│ MARKET DATA FEEDS │ -│ (WebSockets, Orderbook L2/L3, REST Snapshots, On-Chain RPC) │ -└──────────────────────────────┬───────────────────────────────┘ - │ - ▼ -┌──────────────────────────────────────────────────────────────┐ -│ APEX DATA PIPELINE & TICK BUFFER │ -│ (Normalisierung, VWAP, Volatilität, Delta) │ -└──────────────────────────────┬───────────────────────────────┘ - │ - ▼ -┌──────────────────────────────────────────────────────────────┐ -│ QUANT STRATEGY ENGINE │ -│ ├── Alpha-1: Momentum & Breakout (Trendfolge) │ -│ ├── Alpha-2: Order Book Imbalance & Flow Reversal │ -│ └── Alpha-3: Statistical Arbitrage / Mean Reversion │ -└──────────────────────────────┬───────────────────────────────┘ - │ Signal - ▼ -┌──────────────────────────────────────────────────────────────┐ -│ APEX RISK GUARDRAILS (HARD ENFORCEMENT) │ -│ ├── Max Drawdown Lock: 1.5% Intraday │ -│ ├── Max Single-Position Exposure: 5.0% Portfolio │ -│ ├── Value-at-Risk (99% CI): Max 0.8% │ -│ └── Autonomous Hardware Kill Switch │ -└──────────────────────────────┬───────────────────────────────┘ - │ Approved Order - ▼ -┌──────────────────────────────────────────────────────────────┐ -│ SMART ORDER ROUTER & EXECUTION │ -│ (TWAP/VWAP Slicing, Slippage Minimizer) │ -└──────────────────────────────────────────────────────────────┘ -``` - ---- - -## 3. Risikomatrix & Hard-Guardrails - -Kein Signal kann ausgeführt werden, ohne die **Apex Risk Guard** Instanz zu passieren. Diese Schranken sind unveränderlich im Code fixiert: - -| Guardrail | Grenzwert | Aktion bei Verletzung | -| :--- | :--- | :--- | -| **Max Intraday Drawdown** | `1.50%` | Sofortige Schließung aller Positionen, Deaktivierung des Handels für 24h | -| **Single Trade Risk** | `0.50%` des Eigenkapitals | Order wird von der Risk Engine vor dem Senden blockiert | -| **Max Leverage** | `3.0x` | Harte Grenze im Margin-Router | -| **Slippage Toleranz** | `0.15%` | Limit-Order mit Timeout, kein Blind-Market-Buying | -| **Latenz-Anomalie** | `> 50 ms` | Übergang in den Notfall-Sicherheitsmodus | - ---- - -## 4. Strategiemodule (`src/strategies/`) - -### 4.1 Alpha-1: Cross-Market Momentum (`momentum.py`) -Identifiziert signifikante Momentum-Impulse durch exponentiell gewichtete Gleitende Durchschnitte kombiniert mit Relative Strength Index (RSI) und Volumen-Konfirmation. -- **Trigger**: Bollinger Band Expansion + Volumenspitze > 2.2x 20-Perioden-Durchschnitt. -- **Exit**: Trailing Stop bei 1.2x ATR (Average True Range) oder Umkehrsignal. - -### 4.2 Alpha-2: Order Book Imbalance (`imbalance.py`) -Scannt Bid/Ask-Tiefe in den Top 10 Ebenen des Orderbuchs. -- **Formel**: - $$Imbalance = \frac{Volume_{Bid} - Volume_{Ask}}{Volume_{Bid} + Volume_{Ask}}$$ -- Signal feuert, wenn $|Imbalance| > 0.65$ für mindestens 3 konsekutive Ticks. - -### 4.3 Alpha-3: Statistical Mean Reversion (`reversion.py`) -Nutzt Z-Score Abweichungen des aktuellen Preises vom gleitenden VWAP. -- Einstieg bei Z-Score $\ge 2.5$ oder $\le -2.5$ mit Rückkehr zum Mittelwert als Target. - ---- - -## 5. Integration mit der evOo Self-Improvement Schleife - -1. **Trade Post-Mortem**: Jeder geschlossene Trade generiert ein strukturiertes JSON-Log mit Execution-Slippage, Haltedauer und PnL. -2. **Autonome Kalibrierung**: Wenn die Win-Rate über 50 Trades unter 52% fällt, initiiert der Casted Quant Specialist eine automatische Hyperparameter-Optimierung. -3. **Audit-Sicherheit**: Alle Parameteränderungen werden in `ARCHITECTURE.md` und Git-Commits dokumentiert. - ---- - -## 6. Verzeichnisstruktur von `/apex-trading` - -``` -/apex-trading/ -├── BLUEPRINT.md # Dieser Blueprint -├── README.md # Quickstart & CLI Anleitung -├── config/ -│ └── trading.config.json # Konfigurierte Risikogrenzen & Assets -└── src/ - ├── __init__.py - ├── engine/ - │ ├── __init__.py - │ └── trader.py # Core Event Loop & Router - ├── strategies/ - │ ├── __init__.py - │ └── momentum.py # Momentum-Strategie - └── risk/ - ├── __init__.py - └── risk_guard.py # Hard Risk Guardrails & Circuit Breaker -``` diff --git a/apex-trading/README.md b/apex-trading/README.md deleted file mode 100644 index 6baa189..0000000 --- a/apex-trading/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# Apex Trading Subsystem - -Quantitative & High-Frequency Trading Subsystem of the evOo Platform. - -## Features -- Hard-coded risk controls (`src/risk/risk_guard.py`) -- Quantitative momentum and imbalance strategies (`src/strategies/`) -- Autonomous execution engine (`src/engine/trader.py`) -- Full integration with evOo Self-Improvement and CTO Steve Jobs directives. - -See [BLUEPRINT.md](file:///Users/ingowagner/awesome-grok-bot/apex-trading/BLUEPRINT.md) for full architecture details. diff --git a/apex-trading/__init__.py b/apex-trading/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/apex-trading/config/trading.config.json b/apex-trading/config/trading.config.json deleted file mode 100644 index f030720..0000000 --- a/apex-trading/config/trading.config.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "system": "Apex Trading Engine", - "version": "1.0.0-PROD", - "mode": "paper_trading", - "base_currency": "USDC", - "allocated_capital": 100000.0, - "risk_limits": { - "max_drawdown_percent": 1.5, - "max_single_trade_risk_percent": 0.5, - "max_open_positions": 4, - "max_leverage": 3.0, - "slippage_tolerance_percent": 0.15, - "circuit_breaker_cooldown_seconds": 3600 - }, - "active_pairs": ["BTC/USDC", "ETH/USDC", "SOL/USDC"], - "strategies": { - "momentum": { - "enabled": true, - "lookback_period": 20, - "rsi_oversold": 32, - "rsi_overbought": 68 - }, - "orderbook_imbalance": { - "enabled": true, - "threshold": 0.65, - "depth_levels": 10 - } - } -} diff --git a/apex-trading/src/__init__.py b/apex-trading/src/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/apex-trading/src/engine/__init__.py b/apex-trading/src/engine/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/apex-trading/src/engine/trader.py b/apex-trading/src/engine/trader.py deleted file mode 100644 index a6d4029..0000000 --- a/apex-trading/src/engine/trader.py +++ /dev/null @@ -1,62 +0,0 @@ -""" -Apex Trading Engine Core -Event loop that receives signals, checks risk guardrails, and executes orders. -""" - -import json -import os -import sys -from typing import Dict, Any - -from pathlib import Path - -SRC_DIR = Path(__file__).resolve().parent.parent -if str(SRC_DIR) not in sys.path: - sys.path.insert(0, str(SRC_DIR)) - -try: - from risk.risk_guard import ApexRiskGuard - from strategies.momentum import MomentumStrategy -except ImportError: - from ..risk.risk_guard import ApexRiskGuard - from ..strategies.momentum import MomentumStrategy - -class ApexEngine: - def __init__(self, config_path: str): - with open(config_path, 'r', encoding='utf-8') as f: - self.config = json.load(f) - - self.risk_guard = ApexRiskGuard(self.config) - self.momentum_strategy = MomentumStrategy(self.config.get("strategies", {}).get("momentum")) - self.open_positions = [] - self.current_drawdown_pct = 0.0 - - def process_tick(self, symbol: str, prices: list, volume: list) -> Dict[str, Any]: - """Processes market data tick through strategy and risk guard.""" - signal = self.momentum_strategy.generate_signal(symbol, prices, volume) - if not signal: - return {"status": "NO_SIGNAL", "symbol": symbol} - - approved, reason = self.risk_guard.validate_order( - signal, - self.current_drawdown_pct, - len(self.open_positions) - ) - - if not approved: - return {"status": "REJECTED_BY_RISK", "reason": reason, "signal": signal} - - # Simulated Execution - order_receipt = { - "status": "EXECUTED", - "symbol": signal["symbol"], - "action": signal["action"], - "fill_price": signal["price"], - "strategy": signal["strategy"], - "risk_status": "APPROVED" - } - self.open_positions.append(order_receipt) - return order_receipt - -if __name__ == "__main__": - print("[APEX ENGINE] Liveness check passed. Engine ready.") diff --git a/apex-trading/src/risk/__init__.py b/apex-trading/src/risk/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/apex-trading/src/risk/risk_guard.py b/apex-trading/src/risk/risk_guard.py deleted file mode 100644 index 1b4bffd..0000000 --- a/apex-trading/src/risk/risk_guard.py +++ /dev/null @@ -1,40 +0,0 @@ -""" -Apex Trading Risk Guard -Hard enforcement of risk constraints defined in BLUEPRINT.md. -""" - -from typing import Dict, Any, Tuple - -class ApexRiskGuard: - def __init__(self, config: Dict[str, Any]): - self.config = config.get("risk_limits", {}) - self.max_drawdown = self.config.get("max_drawdown_percent", 1.5) - self.max_trade_risk = self.config.get("max_single_trade_risk_percent", 0.5) - self.max_positions = self.config.get("max_open_positions", 4) - self.circuit_breaker_active = False - - def validate_order(self, order: Dict[str, Any], current_drawdown: float, open_position_count: int) -> Tuple[bool, str]: - """ - Validates an incoming order against hard guardrails. - Returns (is_approved: bool, reason: str). - """ - if self.circuit_breaker_active: - return False, "REJECTED: Circuit breaker is active." - - if current_drawdown >= self.max_drawdown: - self.circuit_breaker_active = True - return False, f"REJECTED & CIRCUIT BREAKER TRIGGERED: Drawdown {current_drawdown}% >= limit {self.max_drawdown}%" - - if open_position_count >= self.max_positions: - return False, f"REJECTED: Maximum concurrent positions ({self.max_positions}) reached." - - order_risk_pct = order.get("risk_percent", 0.0) - if order_risk_pct > self.max_trade_risk: - return False, f"REJECTED: Order risk {order_risk_pct}% exceeds limit {self.max_trade_risk}%" - - return True, "APPROVED" - - def trip_kill_switch(self, reason: str): - """Emergency hard stop.""" - self.circuit_breaker_active = True - print(f"[APEX EMERGENCY KILL SWITCH] {reason}") diff --git a/apex-trading/src/strategies/__init__.py b/apex-trading/src/strategies/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/apex-trading/src/strategies/momentum.py b/apex-trading/src/strategies/momentum.py deleted file mode 100644 index 10af059..0000000 --- a/apex-trading/src/strategies/momentum.py +++ /dev/null @@ -1,48 +0,0 @@ -""" -Apex Momentum Strategy Module (Alpha-1) -Calculates breakout and trend-following signals. -""" - -from typing import List, Dict, Any, Optional - -class MomentumStrategy: - def __init__(self, params: Optional[Dict[str, Any]] = None): - self.params = params or {} - self.lookback = self.params.get("lookback_period", 20) - self.rsi_oversold = self.params.get("rsi_oversold", 32) - self.rsi_overbought = self.params.get("rsi_overbought", 68) - - def generate_signal(self, symbol: str, prices: List[float], volume: List[float]) -> Optional[Dict[str, Any]]: - """ - Evaluates recent tick series and outputs a trading signal if criteria are satisfied. - """ - if len(prices) < self.lookback: - return None - - recent_window = prices[-self.lookback:] - current_price = prices[-1] - sma = sum(recent_window) / len(recent_window) - - # Simple momentum breakout check - if current_price > sma * 1.015: - return { - "symbol": symbol, - "action": "BUY", - "price": current_price, - "strategy": "Alpha-1:Momentum", - "risk_percent": 0.35, - "stop_loss": current_price * 0.985, - "take_profit": current_price * 1.045 - } - elif current_price < sma * 0.985: - return { - "symbol": symbol, - "action": "SELL", - "price": current_price, - "strategy": "Alpha-1:Momentum", - "risk_percent": 0.35, - "stop_loss": current_price * 1.015, - "take_profit": current_price * 0.955 - } - - return None diff --git a/apex-trading/test_trading.py b/apex-trading/test_trading.py deleted file mode 100644 index 41cfe62..0000000 --- a/apex-trading/test_trading.py +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env python3 -""" -Unit tests for Apex Trading Engine and Risk Guardrails. -""" - -import unittest -from pathlib import Path -import sys - -BASE_DIR = Path(__file__).resolve().parent -if str(BASE_DIR / "src") not in sys.path: - sys.path.insert(0, str(BASE_DIR / "src")) - -from engine.trader import ApexEngine -from risk.risk_guard import ApexRiskGuard -from strategies.momentum import MomentumStrategy - -class TestApexTrading(unittest.TestCase): - def setUp(self): - self.config_path = str(BASE_DIR / "config" / "trading.config.json") - self.engine = ApexEngine(self.config_path) - - def test_risk_guard_limits(self): - guard = self.engine.risk_guard - # Normal order passes - normal_order = {"symbol": "BTC/USDT", "risk_percent": 0.3} - approved, reason = guard.validate_order(normal_order, current_drawdown=0.5, open_position_count=1) - self.assertTrue(approved) - self.assertEqual(reason, "APPROVED") - - # Exceeding single trade risk - risky_order = {"symbol": "BTC/USDT", "risk_percent": 0.8} - approved, reason = guard.validate_order(risky_order, current_drawdown=0.5, open_position_count=1) - self.assertFalse(approved) - self.assertIn("exceeds limit", reason) - - # Exceeding max drawdown trips circuit breaker - approved, reason = guard.validate_order(normal_order, current_drawdown=1.6, open_position_count=1) - self.assertFalse(approved) - self.assertIn("CIRCUIT BREAKER", reason) - self.assertTrue(guard.circuit_breaker_active) - - def test_engine_process_tick(self): - # 25 prices trending sharply upwards - prices = [100.0 + (i * 1.5) for i in range(25)] - volume = [1000.0] * 25 - result = self.engine.process_tick("SOL/USDT", prices, volume) - self.assertEqual(result.get("status"), "EXECUTED") - self.assertEqual(result.get("action"), "BUY") - -if __name__ == "__main__": - unittest.main() diff --git a/apps/api/src/app.ts b/apps/api/src/app.ts index 948d735..05ee689 100644 --- a/apps/api/src/app.ts +++ b/apps/api/src/app.ts @@ -109,7 +109,6 @@ import { settleWithTimeout, TEAM_CHAT_STARTUP_SHUTDOWN_MS, } from "./team-chat-startup.js"; -import { mountTradingBotStatus } from "./trading-bot-proxy.js"; import { mountVoiceHttpRoutes } from "./voice.js"; import { mountWebhookHttpRoutes } from "./webhook.js"; @@ -530,13 +529,6 @@ export async function createApp( resetUrl: email ? new URL("/reset-password", env.webOrigin).href : null, }), ); - mountTradingBotStatus(app, { - baseUrl: env.tradingBotUrl, - authorize: async (request) => { - const session = await auth.api.getSession({ headers: sessionHeaders(request) }); - return Boolean(session?.user); - }, - }); if (localEmailEmulator && env.nodeEnv === "development") { app.get( "/api/dev/emails", diff --git a/apps/api/src/env.test.ts b/apps/api/src/env.test.ts index 6e92002..29ecb51 100644 --- a/apps/api/src/env.test.ts +++ b/apps/api/src/env.test.ts @@ -42,16 +42,6 @@ describe("loadEnv", () => { ); }); - it("loads a trading bot origin and refuses credentials", () => { - expect(loadEnv(base).tradingBotUrl).toBeUndefined(); - expect( - loadEnv({ ...base, TRADING_BOT_URL: " http://127.0.0.1:8000/bot/ " }).tradingBotUrl, - ).toBe("http://127.0.0.1:8000"); - expect(() => - loadEnv({ ...base, TRADING_BOT_URL: "http://user:secret@127.0.0.1:8000" }), - ).toThrow(/^Invalid TRADING_BOT_URL$/); - }); - it("loads an optional integrations catalog mirror", () => { expect(loadEnv(base).integrationsCatalogUrl).toBeUndefined(); expect( diff --git a/apps/api/src/env.ts b/apps/api/src/env.ts index c88c2da..b260c21 100644 --- a/apps/api/src/env.ts +++ b/apps/api/src/env.ts @@ -8,7 +8,6 @@ import { resolveEncryptionKey, resolveScreenProxySecret, resolveSupervisorToken, - resolveTradingBotOrigin, } from "@rakazo/core"; export { resolveCloudAgentProvider, resolveSandboxProvider } from "@rakazo/adapters"; @@ -93,8 +92,6 @@ export interface AppEnv { updaterToken: string | undefined; /** Current application image tag; used for compose manual-upgrade command selection. */ imageTag: string | undefined; - /** Tradingbot origin for the same-origin status proxy. Unset on SaaS builds. */ - tradingBotUrl: string | undefined; } export function loadEnv(source: NodeJS.ProcessEnv = process.env): AppEnv { @@ -182,7 +179,6 @@ export function loadEnv(source: NodeJS.ProcessEnv = process.env): AppEnv { updaterUrl, updaterToken, imageTag: optional(source.RAKAZO_IMAGE_TAG), - tradingBotUrl: resolveTradingBotOrigin(source.TRADING_BOT_URL), }; } diff --git a/apps/api/src/trading-bot-proxy.test.ts b/apps/api/src/trading-bot-proxy.test.ts deleted file mode 100644 index 22e0808..0000000 --- a/apps/api/src/trading-bot-proxy.test.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { TRADING_BOT_PROXY_PATH } from "@rakazo/core"; -import { Hono } from "hono"; -import { describe, expect, it, vi } from "vitest"; -import { mountTradingBotStatus } from "./trading-bot-proxy.js"; - -function proxyApp(options: { - baseUrl?: string; - authorize?: (request: Request) => Promise; - fetchImpl?: typeof fetch; -}) { - const app = new Hono(); - mountTradingBotStatus(app, { - baseUrl: options.baseUrl, - authorize: options.authorize ?? (async () => true), - fetchImpl: options.fetchImpl, - }); - return app; -} - -describe("trading bot status proxy", () => { - it("stays idle when TRADING_BOT_URL is unset and does not call upstream", async () => { - const fetchImpl = vi.fn(); - const response = await proxyApp({ fetchImpl }).request(TRADING_BOT_PROXY_PATH); - expect(response.status).toBe(200); - expect(await response.json()).toEqual({ configured: false }); - expect(fetchImpl).not.toHaveBeenCalled(); - }); - - it("requires a session before it proxies a configured origin", async () => { - const fetchImpl = vi.fn(); - const response = await proxyApp({ - baseUrl: "http://bot.test:8000", - authorize: async () => false, - fetchImpl, - }).request(TRADING_BOT_PROXY_PATH, { headers: { cookie: "session=secret" } }); - expect(response.status).toBe(401); - expect(fetchImpl).not.toHaveBeenCalled(); - }); - - it("returns the mapped status and does not forward browser credentials", async () => { - const fetchImpl = vi.fn( - async () => - new Response( - JSON.stringify({ mode: "LIVE", adapter_count: 2, last_error: "adapter timeout" }), - { status: 200, headers: { "content-type": "application/json" } }, - ), - ); - const response = await proxyApp({ - baseUrl: "http://bot.test:8000", - fetchImpl, - }).request(TRADING_BOT_PROXY_PATH, { - headers: { cookie: "session=secret", authorization: "Bearer browser-token" }, - }); - expect(response.status).toBe(200); - expect(await response.json()).toEqual({ - configured: true, - reachable: true, - status: { - mode: "live", - kind: "live", - adapterCount: 2, - lastError: "adapter timeout", - }, - }); - expect(fetchImpl).toHaveBeenCalledTimes(1); - const [url, init] = fetchImpl.mock.calls[0] as [string, RequestInit]; - expect(url).toBe("http://bot.test:8000/bot/api/status"); - const headers = new Headers(init.headers); - expect(headers.get("cookie")).toBeNull(); - expect(headers.get("authorization")).toBeNull(); - expect(init.redirect).toBe("error"); - }); - - it("hides an unmapped body and an upstream failure instead of claiming live", async () => { - const unmapped = vi.fn( - async () => - new Response(JSON.stringify({ status: "ok" }), { - status: 200, - headers: { "content-type": "application/json" }, - }), - ); - const unknown = await proxyApp({ - baseUrl: "http://bot.test:8000", - fetchImpl: unmapped, - }).request(TRADING_BOT_PROXY_PATH); - expect(await unknown.json()).toEqual({ configured: true, reachable: true, status: null }); - - const down = vi.fn(async () => new Response("nope", { status: 500 })); - const offline = await proxyApp({ - baseUrl: "http://bot.test:8000", - fetchImpl: down, - }).request(TRADING_BOT_PROXY_PATH); - expect(offline.status).toBe(502); - expect(await offline.json()).toEqual({ configured: true, reachable: false }); - }); -}); diff --git a/apps/api/src/trading-bot-proxy.ts b/apps/api/src/trading-bot-proxy.ts deleted file mode 100644 index 16bd736..0000000 --- a/apps/api/src/trading-bot-proxy.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { - mapTradingBotStatus, - readBoundedJsonResponse, - TRADING_BOT_PROXY_PATH, - TRADING_BOT_STATUS_MAX_BYTES, - TRADING_BOT_STATUS_TIMEOUT_MS, - tradingBotStatusUrl, -} from "@rakazo/core"; -import type { Hono } from "hono"; - -export function mountTradingBotStatus( - app: Hono, - options: { - /** Normalized http(s) origin. Unset leaves the route idle for SaaS builds. */ - baseUrl: string | undefined; - authorize: (request: Request) => Promise; - fetchImpl?: typeof fetch; - }, -) { - const fetchImpl = options.fetchImpl ?? fetch; - app.get(TRADING_BOT_PROXY_PATH, async (c) => { - c.header("cache-control", "no-store"); - if (!options.baseUrl) return c.json({ configured: false }); - const allowed = await options.authorize(c.req.raw).catch(() => false); - if (!allowed) return c.json({ error: "Unauthorized" }, 401); - - const timeout = linkAbort(c.req.raw.signal, TRADING_BOT_STATUS_TIMEOUT_MS); - try { - const response = await fetchImpl(tradingBotStatusUrl(options.baseUrl), { - method: "GET", - redirect: "error", - cache: "no-store", - signal: timeout.signal, - headers: { accept: "application/json" }, - }); - if (!response.ok) { - cancelBody(response); - return c.json({ configured: true, reachable: false }, 502); - } - const body = await readBoundedJsonResponse( - response, - TRADING_BOT_STATUS_MAX_BYTES, - timeout.signal, - ); - const status = mapTradingBotStatus(body); - return c.json({ configured: true, reachable: true, status: status ?? null }); - } catch { - return c.json({ configured: true, reachable: false }, 502); - } finally { - timeout.cancel(); - } - }); -} - -function linkAbort( - parent: AbortSignal, - timeoutMs: number, -): { signal: AbortSignal; cancel: () => void } { - const controller = new AbortController(); - const timer = setTimeout(() => controller.abort(), timeoutMs); - const onParent = () => controller.abort(); - if (parent.aborted) controller.abort(); - else parent.addEventListener("abort", onParent); - return { - signal: controller.signal, - cancel: () => { - clearTimeout(timer); - parent.removeEventListener("abort", onParent); - }, - }; -} - -function cancelBody(response: Response): void { - try { - void Promise.resolve(response.body?.cancel()).catch(() => undefined); - } catch { - // Best-effort. The caller still reports the bot as unreachable. - } -} diff --git a/apps/mobile/lib/endpoint.test.ts b/apps/mobile/lib/endpoint.test.ts index ede8e78..40f1e00 100644 --- a/apps/mobile/lib/endpoint.test.ts +++ b/apps/mobile/lib/endpoint.test.ts @@ -83,7 +83,7 @@ describe("display and warnings", () => { expect(apiBaseWarning("http://127.0.0.1:3100")).toBeNull(); expect(apiBaseWarning("http://192.168.1.20:3100")).toBeNull(); expect(apiBaseWarning("http://100.64.0.1:3100")).toBeNull(); - expect(apiBaseWarning("http://100.119.57.55:3100")).toBeNull(); + expect(apiBaseWarning("http://100.100.100.100:3100")).toBeNull(); expect(apiBaseWarning("http://100.127.255.255:3100")).toBeNull(); expect(apiBaseWarning("http://app.example.com")).toMatch(/https/i); }); diff --git a/apps/web/e2e/chat-contrast.spec.ts b/apps/web/e2e/chat-contrast.spec.ts index 675f974..c388194 100644 --- a/apps/web/e2e/chat-contrast.spec.ts +++ b/apps/web/e2e/chat-contrast.spec.ts @@ -224,7 +224,6 @@ test("shell surfaces stay readable in light and dark themes", async ({ page }, t const sidePanel = page.getByTestId("side-panel"); await expect(sidebar.locator("[data-main-conversation]")).toBeVisible(); await expect(sidebar.getByRole("button", { name: "Researcher, 1:1 conversation" })).toBeVisible(); - await expect(page.getByTestId("trading-mode-status")).toBeVisible(); await inEachTheme(page, async (theme) => { await captureScreenshot(page, testInfo, `shell-contrast-${theme}`); diff --git a/apps/web/e2e/new-bot-ux.spec.ts b/apps/web/e2e/new-bot-ux.spec.ts index a640a48..68bc135 100644 --- a/apps/web/e2e/new-bot-ux.spec.ts +++ b/apps/web/e2e/new-bot-ux.spec.ts @@ -43,6 +43,9 @@ test("create opens form, then empty chat; picker lists bots; sidebar collapses", await expect(page.getByRole("combobox", { name: "Message" })).toBeVisible(); await expect(page.getByTestId("side-panel")).toHaveAttribute("data-panel", "closed"); await expect(page.getByText("What do you want me on first?", { exact: true })).toHaveCount(0); + await expect( + page.getByTestId("transcript").getByText("Start the conversation — New Bot is on stage above."), + ).toBeVisible(); await captureScreenshot(page, testInfo, "create-chat-sidepanel-closed"); await page.getByTestId("toggle-bots-sidebar").click(); diff --git a/apps/web/e2e/sidebar-nav.spec.ts b/apps/web/e2e/sidebar-nav.spec.ts index 95e3724..e5e502c 100644 --- a/apps/web/e2e/sidebar-nav.spec.ts +++ b/apps/web/e2e/sidebar-nav.spec.ts @@ -23,7 +23,6 @@ test("sidebar has expected navigation items and no integrations", async ({ page // - textbox "Search" (search input) // - button "Heute" (today) // - text "Sprich mit deinen Oo's" (conversations section header) - // - status "Not configured" (trading bot URL unset) // - NO "Integration(en)" button or text // Search: it's a textbox, not a button @@ -38,13 +37,6 @@ test("sidebar has expected navigation items and no integrations", async ({ page const conversationsText = sidebar.getByText("Sprich mit deinen Oo's"); await expect(conversationsText).toBeVisible({ timeout: 5000 }); - // Trading mode comes from the bot API. With no URL configured the sidebar - // must not claim Live Trading from a local preference. - const tradingStatus = sidebar.getByTestId("trading-mode-status"); - await expect(tradingStatus).toBeVisible({ timeout: 5000 }); - await expect(tradingStatus).toContainText("Not configured"); - await expect(sidebar.getByRole("button", { name: "Live Trading" })).toHaveCount(0); - // No "Integration(en)" button const integrationBtn = sidebar.getByRole("button", { name: /Integration(en)?/i }); await expect(integrationBtn).toHaveCount(0, { timeout: 5000 }); diff --git a/apps/web/e2e/steve-jobs.spec.ts b/apps/web/e2e/steve-jobs.spec.ts deleted file mode 100644 index 1d0d337..0000000 --- a/apps/web/e2e/steve-jobs.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { test } from "@playwright/test"; - -test.describe("Steve Jobs CTO Agent", () => { - test.beforeEach(async () => { - // We assume the user has logged in and the bot is seeded - // This is a placeholder for the perfect test architecture - }); - - test("should be visible in the sidebar", async () => { - // This is a dummy test to show we started the test architecture for Steve Jobs - }); - - test("should prefer voice communications (VOICE FIRST)", async () => { - // Voice first features - }); -}); diff --git a/apps/web/src/components/DataTrustPanel.tsx b/apps/web/src/components/DataTrustPanel.tsx deleted file mode 100644 index 885fc91..0000000 --- a/apps/web/src/components/DataTrustPanel.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { CheckCircle2, Database, EyeOff, Lock } from "lucide-react"; -import { memo } from "react"; - -const TRUST_ITEMS = [ - { icon: Lock, label: "Verschlüsselt", desc: "AES-256 für sensible Daten" }, - { icon: Database, label: "Lokal gespeichert", desc: "Eigene PostgreSQL-Datenbank" }, - { icon: EyeOff, label: "Kein Tracking", desc: "Keine Analytics, kein Telemetry" }, -] as const; - -export const DataTrustPanel = memo(function DataTrustPanel() { - return ( -
-
- - Datenschutz aktiv -
-
- {TRUST_ITEMS.map(({ icon: Icon, label, desc }) => ( -
- - {label} - · {desc} -
- ))} -
-
- ); -}); diff --git a/apps/web/src/components/TradingModeIndicator.test.tsx b/apps/web/src/components/TradingModeIndicator.test.tsx deleted file mode 100644 index db8bbbc..0000000 --- a/apps/web/src/components/TradingModeIndicator.test.tsx +++ /dev/null @@ -1,55 +0,0 @@ -// @vitest-environment jsdom - -import type { ReactNode } from "react"; -import { act } from "react"; -import { createRoot } from "react-dom/client"; -import { expect, it, vi } from "vitest"; - -vi.mock("@lingui/react/macro", () => ({ - Trans: ({ children }: { children: ReactNode }) => children, -})); - -import { TradingModeIndicator } from "./TradingModeIndicator"; - -it("renders the API mode and ignores a stored live preference", async () => { - vi.stubGlobal("IS_REACT_ACT_ENVIRONMENT", true); - window.localStorage.setItem("evoo_trading_mode", "live"); - const container = document.createElement("div"); - document.body.append(container); - const root = createRoot(container); - try { - await act(async () => - root.render( - , - ), - ); - const status = container.querySelector("[data-testid='trading-mode-status']"); - expect(status?.getAttribute("data-trading-state")).toBe("dry_run"); - expect(status?.textContent).toContain("Dry run"); - expect(status?.textContent).toContain("3 adapters"); - expect(container.querySelector("button")).toBeNull(); - expect(status?.textContent).not.toContain("Live Trading"); - - await act(async () => root.render()); - expect(container.querySelector("[data-testid='trading-mode-status']")?.textContent).toContain( - "Offline", - ); - - await act(async () => - root.render(), - ); - expect(container.querySelector("[data-testid='trading-mode-status']")?.textContent).toContain( - "Not configured", - ); - } finally { - await act(async () => root.unmount()); - container.remove(); - window.localStorage.clear(); - vi.unstubAllGlobals(); - } -}); diff --git a/apps/web/src/components/TradingModeIndicator.tsx b/apps/web/src/components/TradingModeIndicator.tsx deleted file mode 100644 index 90c9622..0000000 --- a/apps/web/src/components/TradingModeIndicator.tsx +++ /dev/null @@ -1,95 +0,0 @@ -import { Trans } from "@lingui/react/macro"; -import { cn } from "@rakazo/ui-web"; -import { Activity } from "lucide-react"; -import type { ReactNode } from "react"; -import { - presentTradingStatus, - type TradingBotUiState, - type TradingModeLabelId, - type TradingModeSubtitle, -} from "../lib/trading-bot-status"; - -export function TradingModeIndicator({ - snapshot, - className, -}: { - snapshot: TradingBotUiState; - className?: string; -}) { - const presentation = presentTradingStatus(snapshot); - return ( -
- - - - - - - {presentation.subtitle ? ( - - - - ) : null} - -
- ); -} - -function TradingModeLabel({ label }: { label: TradingModeLabelId }): ReactNode { - switch (label) { - case "loading": - return Trading; - case "unconfigured": - return Not configured; - case "offline": - return Offline; - case "unknown": - return Unknown; - case "live": - return Live Trading; - case "paper": - return Paper; - case "dry_run": - return Dry run; - default: { - const exhaustive: never = label; - return exhaustive; - } - } -} - -function TradingModeSubtitleText({ subtitle }: { subtitle: TradingModeSubtitle }): ReactNode { - switch (subtitle.type) { - case "error": - return subtitle.text; - case "adapters": - return subtitle.count === 1 ? ( - 1 adapter - ) : ( - {subtitle.count} adapters - ); - default: { - const exhaustive: never = subtitle; - return exhaustive; - } - } -} diff --git a/apps/web/src/lib/trading-bot-status.test.ts b/apps/web/src/lib/trading-bot-status.test.ts deleted file mode 100644 index 95a86aa..0000000 --- a/apps/web/src/lib/trading-bot-status.test.ts +++ /dev/null @@ -1,178 +0,0 @@ -import { TRADING_BOT_PROXY_PATH } from "@rakazo/core"; -import { describe, expect, it, vi } from "vitest"; -import { - fetchTradingBotSnapshot, - pollTradingBotStatus, - presentTradingStatus, - resolveTradingStatusTarget, -} from "./trading-bot-status"; - -describe("resolveTradingStatusTarget", () => { - it("polls the bot directly only when the Vite URL is a bare origin", () => { - expect(resolveTradingStatusTarget(undefined)).toEqual({ - kind: "proxy", - url: TRADING_BOT_PROXY_PATH, - }); - expect(resolveTradingStatusTarget("")).toEqual({ kind: "proxy", url: TRADING_BOT_PROXY_PATH }); - expect(resolveTradingStatusTarget(" http://100.64.0.1:8000/bot/ ")).toEqual({ - kind: "direct", - url: "http://100.64.0.1:8000/bot/api/status", - }); - expect(resolveTradingStatusTarget("file:///tmp/bot")).toEqual({ - kind: "proxy", - url: TRADING_BOT_PROXY_PATH, - }); - }); -}); - -describe("presentTradingStatus", () => { - it("shows the API mode and never a stored live preference", () => { - expect( - presentTradingStatus({ - state: "ready", - status: { mode: "live", kind: "live", adapterCount: 2, lastError: null }, - }), - ).toMatchObject({ - label: "live", - tone: "live", - pulse: true, - subtitle: { type: "adapters", count: 2 }, - }); - expect( - presentTradingStatus({ - state: "ready", - status: { mode: "paper", kind: "dry_run", adapterCount: 2, lastError: "adapter timeout" }, - }).subtitle, - ).toEqual({ type: "error", text: "adapter timeout" }); - expect(presentTradingStatus({ state: "offline" }).label).toBe("offline"); - expect(presentTradingStatus({ state: "unknown" }).label).toBe("unknown"); - expect(presentTradingStatus({ state: "unconfigured" }).label).toBe("unconfigured"); - expect(presentTradingStatus({ state: "loading" }).label).toBe("loading"); - }); -}); - -describe("fetchTradingBotSnapshot", () => { - it("maps a direct live body and reports an unmapped 200 as unknown", async () => { - const live = await fetchTradingBotSnapshot( - { kind: "direct", url: "http://bot.test/bot/api/status" }, - new AbortController().signal, - async () => jsonResponse({ mode: "LIVE" }), - ); - expect(live).toMatchObject({ state: "ready", status: { mode: "live", kind: "live" } }); - - const unknown = await fetchTradingBotSnapshot( - { kind: "direct", url: "http://bot.test/bot/api/status" }, - new AbortController().signal, - async () => jsonResponse({ status: "ok" }), - ); - expect(unknown).toEqual({ state: "unknown" }); - }); - - it("treats a failed direct fetch as offline", async () => { - const offline = await fetchTradingBotSnapshot( - { kind: "direct", url: "http://bot.test/bot/api/status" }, - new AbortController().signal, - async () => { - throw new Error("network"); - }, - ); - expect(offline).toEqual({ state: "offline" }); - }); - - it("stops on an explicit unconfigured proxy response", async () => { - const fetchImpl = vi.fn(async () => jsonResponse({ configured: false })); - const snapshots: string[] = []; - await pollTradingBotStatus({ - target: { kind: "proxy", url: TRADING_BOT_PROXY_PATH }, - signal: new AbortController().signal, - fetchImpl, - onSnapshot: (snapshot) => snapshots.push(snapshot.state), - sleep: async () => false, - }); - expect(snapshots).toEqual(["unconfigured"]); - expect(fetchImpl).toHaveBeenCalledTimes(1); - }); -}); - -describe("pollTradingBotStatus", () => { - it("follows a live read with offline on failure and backs off", async () => { - let calls = 0; - const fetchImpl = vi.fn(async () => { - calls += 1; - if (calls === 1) return jsonResponse({ badge: "LIVE" }); - throw new Error("down"); - }); - const sleeps: number[] = []; - let releaseSleep: () => void = () => undefined; - const sleep = (ms: number, signal: AbortSignal) => { - sleeps.push(ms); - return new Promise((resolve) => { - if (signal.aborted) { - resolve(true); - return; - } - const onAbort = () => resolve(true); - signal.addEventListener("abort", onAbort, { once: true }); - releaseSleep = () => { - signal.removeEventListener("abort", onAbort); - resolve(false); - }; - }); - }; - const snapshots: string[] = []; - const controller = new AbortController(); - const done = pollTradingBotStatus({ - target: { kind: "direct", url: "http://bot.test/bot/api/status" }, - signal: controller.signal, - fetchImpl, - intervalMs: 20_000, - maxBackoffMs: 120_000, - sleep, - onSnapshot: (snapshot) => snapshots.push(snapshot.state), - }); - await vi.waitFor(() => expect(snapshots).toEqual(["ready"])); - expect(sleeps).toEqual([20_000]); - releaseSleep(); - await vi.waitFor(() => expect(snapshots).toEqual(["ready", "offline"])); - expect(sleeps).toEqual([20_000, 20_000]); - releaseSleep(); - await vi.waitFor(() => expect(sleeps).toEqual([20_000, 20_000, 40_000])); - controller.abort(); - await done; - expect(fetchImpl).toHaveBeenCalledTimes(3); - }); - - it("does not publish offline when the poll is aborted in flight", async () => { - const controller = new AbortController(); - const snapshots: string[] = []; - const fetchImpl: typeof fetch = (_input, init) => - new Promise((_resolve, reject) => { - const abort = () => { - const error = new Error("aborted"); - error.name = "AbortError"; - reject(error); - }; - if (init?.signal?.aborted) { - abort(); - return; - } - init?.signal?.addEventListener("abort", abort, { once: true }); - }); - const done = pollTradingBotStatus({ - target: { kind: "direct", url: "http://bot.test/bot/api/status" }, - signal: controller.signal, - fetchImpl, - onSnapshot: (snapshot) => snapshots.push(snapshot.state), - }); - controller.abort(); - await done; - expect(snapshots).toEqual([]); - }); -}); - -function jsonResponse(body: unknown): Response { - return new Response(JSON.stringify(body), { - status: 200, - headers: { "content-type": "application/json" }, - }); -} diff --git a/apps/web/src/lib/trading-bot-status.ts b/apps/web/src/lib/trading-bot-status.ts deleted file mode 100644 index 5b04ec0..0000000 --- a/apps/web/src/lib/trading-bot-status.ts +++ /dev/null @@ -1,243 +0,0 @@ -import type { TradingBotSnapshot, TradingBotStatusView } from "@rakazo/core"; -import { - interpretTradingBotProxyResponse, - mapTradingBotStatus, - parseTradingBotOrigin, - readBoundedJsonResponse, - TRADING_BOT_POLL_INTERVAL_MS, - TRADING_BOT_POLL_MAX_BACKOFF_MS, - TRADING_BOT_PROXY_PATH, - TRADING_BOT_STATUS_MAX_BYTES, - TRADING_BOT_STATUS_TIMEOUT_MS, - tradingBotBackoffMs, - tradingBotStatusUrl, -} from "@rakazo/core"; -import { useEffect, useState } from "react"; - -export type TradingStatusTarget = - | { kind: "direct"; url: string } - | { kind: "proxy"; url: typeof TRADING_BOT_PROXY_PATH }; - -export type TradingBotUiState = TradingBotSnapshot | { state: "loading" }; - -export type TradingModeLabelId = - | "loading" - | "unconfigured" - | "offline" - | "unknown" - | "live" - | "paper" - | "dry_run"; - -export type TradingModeSubtitle = - | { type: "adapters"; count: number } - | { type: "error"; text: string }; - -export type TradingModePresentation = { - label: TradingModeLabelId; - tone: "live" | "quiet"; - pulse: boolean; - subtitle: TradingModeSubtitle | null; -}; - -type FetchSnapshot = ( - target: TradingStatusTarget, - signal: AbortSignal, - fetchImpl: typeof fetch, - timeoutMs: number, -) => Promise; - -/** Direct poll when the Vite URL is set. Otherwise the same-origin API proxy. */ -export function resolveTradingStatusTarget(viteUrl: string | undefined): TradingStatusTarget { - const parsed = parseTradingBotOrigin(viteUrl); - if (parsed.kind === "origin") { - return { kind: "direct", url: tradingBotStatusUrl(parsed.origin) }; - } - return { kind: "proxy", url: TRADING_BOT_PROXY_PATH }; -} - -export function presentTradingStatus(snapshot: TradingBotUiState): TradingModePresentation { - switch (snapshot.state) { - case "loading": - return { label: "loading", tone: "quiet", pulse: false, subtitle: null }; - case "unconfigured": - return { label: "unconfigured", tone: "quiet", pulse: false, subtitle: null }; - case "offline": - return { label: "offline", tone: "quiet", pulse: false, subtitle: null }; - case "unknown": - return { label: "unknown", tone: "quiet", pulse: false, subtitle: null }; - case "ready": - return presentReady(snapshot.status); - default: { - const exhaustive: never = snapshot; - return exhaustive; - } - } -} - -export async function fetchTradingBotSnapshot( - target: TradingStatusTarget, - signal: AbortSignal, - fetchImpl: typeof fetch = fetch, - timeoutMs = TRADING_BOT_STATUS_TIMEOUT_MS, -): Promise { - const timeout = linkAbort(signal, timeoutMs); - try { - const response = await fetchImpl(target.url, { - method: "GET", - redirect: "error", - cache: "no-store", - credentials: target.kind === "direct" ? "omit" : "include", - signal: timeout.signal, - headers: { accept: "application/json" }, - }); - if (target.kind === "direct" && !response.ok) { - cancelBody(response); - return { state: "offline" }; - } - let body: unknown = null; - try { - body = await readBoundedJsonResponse( - response, - TRADING_BOT_STATUS_MAX_BYTES, - timeout.signal, - ); - } catch (error) { - if (signal.aborted) throw error; - body = null; - } - if (target.kind === "proxy") return interpretTradingBotProxyResponse(response.status, body); - if (body === null) return { state: "offline" }; - const status = mapTradingBotStatus(body); - return status ? { state: "ready", status } : { state: "unknown" }; - } catch (error) { - if (signal.aborted) throw error; - return { state: "offline" }; - } finally { - timeout.cancel(); - } -} - -export async function pollTradingBotStatus(options: { - target: TradingStatusTarget; - signal: AbortSignal; - onSnapshot: (snapshot: TradingBotSnapshot) => void; - fetchImpl?: typeof fetch; - fetchSnapshot?: FetchSnapshot; - intervalMs?: number; - maxBackoffMs?: number; - timeoutMs?: number; - sleep?: (ms: number, signal: AbortSignal) => Promise; -}): Promise { - const fetchSnapshot = options.fetchSnapshot ?? fetchTradingBotSnapshot; - const fetchImpl = options.fetchImpl ?? fetch; - const intervalMs = options.intervalMs ?? TRADING_BOT_POLL_INTERVAL_MS; - const maxBackoffMs = options.maxBackoffMs ?? TRADING_BOT_POLL_MAX_BACKOFF_MS; - const timeoutMs = options.timeoutMs ?? TRADING_BOT_STATUS_TIMEOUT_MS; - const sleep = options.sleep ?? delayAbortable; - let failures = 0; - while (!options.signal.aborted) { - let snapshot: TradingBotSnapshot; - try { - snapshot = await fetchSnapshot(options.target, options.signal, fetchImpl, timeoutMs); - } catch { - if (options.signal.aborted) return; - snapshot = { state: "offline" }; - } - if (options.signal.aborted) return; - options.onSnapshot(snapshot); - if (snapshot.state === "unconfigured") return; - if (snapshot.state === "offline") failures += 1; - else failures = 0; - const delay = - snapshot.state === "offline" - ? tradingBotBackoffMs(intervalMs, failures, maxBackoffMs) - : intervalMs; - const aborted = await sleep(delay, options.signal); - if (aborted) return; - } -} - -export function useTradingBotStatus(): TradingBotUiState { - const [snapshot, setSnapshot] = useState({ state: "loading" }); - useEffect(() => { - const controller = new AbortController(); - const target = resolveTradingStatusTarget(readViteTradingBotUrl()); - void pollTradingBotStatus({ - target, - signal: controller.signal, - onSnapshot: (next) => { - if (!controller.signal.aborted) setSnapshot(next); - }, - }); - return () => controller.abort(); - }, []); - return snapshot; -} - -function readViteTradingBotUrl(): string | undefined { - const value = import.meta.env.VITE_TRADING_BOT_URL; - return typeof value === "string" ? value : undefined; -} - -function presentReady(status: TradingBotStatusView): TradingModePresentation { - const subtitle: TradingModeSubtitle | null = status.lastError - ? { type: "error", text: status.lastError } - : status.adapterCount === null - ? null - : { type: "adapters", count: status.adapterCount }; - switch (status.kind) { - case "live": - return { label: "live", tone: "live", pulse: true, subtitle }; - case "paper": - return { label: "paper", tone: "quiet", pulse: false, subtitle }; - case "dry_run": - return { label: "dry_run", tone: "quiet", pulse: false, subtitle }; - default: { - const exhaustive: never = status.kind; - return exhaustive; - } - } -} - -function linkAbort( - parent: AbortSignal, - timeoutMs: number, -): { signal: AbortSignal; cancel: () => void } { - const controller = new AbortController(); - const timer = setTimeout(() => controller.abort(), timeoutMs); - const onParent = () => controller.abort(); - if (parent.aborted) controller.abort(); - else parent.addEventListener("abort", onParent); - return { - signal: controller.signal, - cancel: () => { - clearTimeout(timer); - parent.removeEventListener("abort", onParent); - }, - }; -} - -function delayAbortable(ms: number, signal: AbortSignal): Promise { - if (signal.aborted) return Promise.resolve(true); - return new Promise((resolve) => { - const timer = setTimeout(() => { - signal.removeEventListener("abort", onAbort); - resolve(false); - }, ms); - const onAbort = () => { - clearTimeout(timer); - signal.removeEventListener("abort", onAbort); - resolve(true); - }; - signal.addEventListener("abort", onAbort); - }); -} - -function cancelBody(response: Response): void { - try { - void Promise.resolve(response.body?.cancel()).catch(() => undefined); - } catch { - // Best-effort. The snapshot is already offline. - } -} diff --git a/apps/web/src/pages/AccountSettingsOverlay.tsx b/apps/web/src/pages/AccountSettingsOverlay.tsx index b01606f..d222a72 100644 --- a/apps/web/src/pages/AccountSettingsOverlay.tsx +++ b/apps/web/src/pages/AccountSettingsOverlay.tsx @@ -16,10 +16,8 @@ import { SuccessPop } from "../components/ai/primitives"; import { ComputersUnavailableHint } from "../components/ComputersUnavailableHint"; import { DesktopUpdateSection } from "../components/DesktopUpdates"; import { SoftwareUpdateSection } from "../components/SoftwareUpdateSection"; -import { TradingModeIndicator } from "../components/TradingModeIndicator"; import { authClient } from "../lib/auth"; import { getActiveUiLocale, setUiLocale } from "../lib/i18n"; -import type { TradingBotUiState } from "../lib/trading-bot-status"; import { type AppearancePreference, getUiAppearancePreference, @@ -35,25 +33,8 @@ export type SettingsGeneralProps = { messagingEnabled?: boolean; onOpenMessaging?: () => void; isDeploymentOwner?: boolean; - tradingStatus?: TradingBotUiState; }; -export function LiveTradingBanner({ snapshot }: { snapshot: TradingBotUiState }) { - if (snapshot.state === "loading" || snapshot.state === "unconfigured") return null; - const live = snapshot.state === "ready" && snapshot.status.mode === "live"; - - return ( -
- -
- ); -} - export function GeneralSettingsPanels({ email, name, @@ -62,7 +43,6 @@ export function GeneralSettingsPanels({ messagingEnabled = false, onOpenMessaging, isDeploymentOwner = false, - tradingStatus, }: SettingsGeneralProps) { const { t } = useLingui(); const [locale, setLocale] = useState(() => getActiveUiLocale()); @@ -98,7 +78,6 @@ export function GeneralSettingsPanels({ return (
- {tradingStatus ? : null}

Account diff --git a/apps/web/src/pages/SettingsOverlay.tsx b/apps/web/src/pages/SettingsOverlay.tsx index d71d40a..9240751 100644 --- a/apps/web/src/pages/SettingsOverlay.tsx +++ b/apps/web/src/pages/SettingsOverlay.tsx @@ -17,7 +17,6 @@ import { import { type ComponentType, useEffect, useRef, useState } from "react"; import { computersAreUnavailable } from "../components/ComputersUnavailableHint"; import { UserProfileSettings } from "../components/UserProfileSettings"; -import type { TradingBotUiState } from "../lib/trading-bot-status"; import { ComputerSettingsPanel, GeneralSettingsPanels, @@ -65,7 +64,6 @@ export function SettingsOverlay({ onVoiceStatusMaybeChanged, activeBotId, onOpenMcp, - tradingStatus, }: { email?: string | null; name: string; @@ -83,7 +81,6 @@ export function SettingsOverlay({ onVoiceStatusMaybeChanged?: () => void | Promise; activeBotId?: string; onOpenMcp?: () => void; - tradingStatus?: TradingBotUiState; }) { const { t } = useLingui(); const panelRef = useRef(null); @@ -238,7 +235,6 @@ export function SettingsOverlay({ onOpenMessaging ? () => leaveSettings(onOpenMessaging) : undefined } isDeploymentOwner={isDeploymentOwner} - tradingStatus={tradingStatus} /> ) : null} {section === "integrations" ? ( diff --git a/apps/web/src/pages/Shell.tsx b/apps/web/src/pages/Shell.tsx index 753362a..e9e287f 100644 --- a/apps/web/src/pages/Shell.tsx +++ b/apps/web/src/pages/Shell.tsx @@ -139,7 +139,6 @@ import { Search, Settings, Smile, - Sparkles, Square, TextQuote, Trash2, @@ -178,11 +177,9 @@ import { } from "../components/ComputersUnavailableHint"; import { ComputerUpdateProgress } from "../components/ComputerUpdateProgress"; import { AgentCursor } from "../components/computer/AgentCursor"; -import { DataTrustPanel } from "../components/DataTrustPanel"; import { MainInbox } from "../components/MainInbox"; import { MessageHoverMetadata } from "../components/MessageHoverMetadata"; import { OoGroupStage, OoStage } from "../components/stage/OoStage"; -import { TradingModeIndicator } from "../components/TradingModeIndicator"; import { SkillDraftCard } from "../components/teach/SkillDraftCard"; import { TeachCaptureOverlay } from "../components/teach/TeachCaptureOverlay"; import { TeachComputerOverlayControl } from "../components/teach/TeachComputerOverlay"; @@ -252,7 +249,6 @@ import { threadRunError, userHoldsComputerControl, } from "../lib/thread-events"; -import { useTradingBotStatus } from "../lib/trading-bot-status"; import { transcriptCanSnapAfterFrame, transcriptIsNearEnd, @@ -425,7 +421,6 @@ export function ShellPage() { if (botId) return "team"; return "heute"; }); - const tradingStatus = useTradingBotStatus(); useEffect(() => { if (botId) setActiveNavPillar("team"); @@ -4271,8 +4266,6 @@ export function ShellPage() {

) : null} - - { - void sendMessage(prompt); - }} scrollRef={messageScroll} artifactTarget={transcriptArtifactTarget} onOpenArtifact={wideLayout ? openArtifactBeside : undefined} @@ -4684,9 +4674,6 @@ export function ShellPage() { {(active || activeGroup) && !showProjectTimeline ? ( { const nextMe = await rpc.preferences.update({ avatarStyle }); setBootstrapMe(nextMe); @@ -5813,7 +5799,6 @@ const Transcript = memo(function Transcript({ speakingMessageId, onSpeak, activeName, - onPromptSelect, encounterMode = false, voiceEncounterActive = false, }: { @@ -5844,7 +5829,6 @@ const Transcript = memo(function Transcript({ speakingMessageId: string | null; onSpeak: (message: ThreadMessage) => void; activeName?: string; - onPromptSelect?: (prompt: string) => void; encounterMode?: boolean; voiceEncounterActive?: boolean; }) { @@ -6097,87 +6081,11 @@ const Transcript = memo(function Transcript({ }`} > {reactionView.visibleMessages.length === 0 && encounterMode ? ( -
-

- {activeName - ? t`Start the conversation — ${activeName} is on stage above.` - : t`Start the conversation.`} -

-
- {[t`Portfolio-Check`, t`Alpha-Signale`, t`Architektur-Audit`].map((label) => ( - - ))} -
-
- ) : null} - {reactionView.visibleMessages.length === 0 && !encounterMode ? ( -
-
- -
-

- {t`Guten Tag.`} evOo {t`ist bereit.`} -

-

- {activeName - ? t`Frag ${activeName} nach Marktchancen, autonomen Tasks oder Architektur-Reviews.` - : t`Wähle einen autonomen Agenten aus deinem Team oder starte direkt einen Prompt.`} -

-
- - - -
-
+

+ {activeName + ? t`Start the conversation — ${activeName} is on stage above.` + : t`Start the conversation.`} +

) : null} {reactionView.visibleMessages.map((message) => { if (!message.blocks.some((block) => !isToolActivityBlock(block))) return null; @@ -6422,7 +6330,6 @@ const QuoteSelectionButton = memo(function QuoteSelectionButton({ }); const Composer = memo(function Composer({ - activeName, running, disabled, pendingAttachments, @@ -6450,7 +6357,6 @@ const Composer = memo(function Composer({ onSlashOpen, onSlashAction, }: { - activeName?: string; running: boolean; disabled?: boolean; pendingAttachments: PendingAttachment[]; @@ -6759,28 +6665,6 @@ const Composer = memo(function Composer({ }`} >
- {/* Quick Action Prompt Chips */} - {showComposerPlaceholder ? ( -
- - - Quick - - {quickPromptsForBot(activeName).map((promptText) => ( - - ))} -
- ) : null} {sendError || runError ? (
&2 - exit 1 -} - -echo "==========================================================" -echo " evOo Autonomous Platform — Hot-Reload & Deploy " -echo " CTO: Steve Jobs | Port: ${PORT} " -echo "==========================================================" - -# ------------------------------------------------------------------------------ -# STEP 1: Physical Reality & Filesystem Audit (Zero Phantom Policy) -# ------------------------------------------------------------------------------ -log "Prüfe physische Existenz aller Kern-Artefakte..." - -REQUIRED_FILES=( - "${SCRIPT_DIR}/ARCHITECTURE.md" - "${SCRIPT_DIR}/evoo.config.json" - "${SCRIPT_DIR}/evoo-platform/index.html" - "${SCRIPT_DIR}/evoo-platform/styles.css" - "${SCRIPT_DIR}/evoo-platform/app.js" - "${SCRIPT_DIR}/evoo-platform/server.js" - "${SCRIPT_DIR}/apex-trading/BLUEPRINT.md" - "${SCRIPT_DIR}/apex-trading/config/trading.config.json" - "${SCRIPT_DIR}/orchestrator/agents.json" - "${SCRIPT_DIR}/orchestrator/agent_runner.py" -) - -for file in "${REQUIRED_FILES[@]}"; do - if [[ ! -f "$file" ]]; then - fail "Physische Datei fehlt im Dateisystem: $file. Abbruch! Vision-Schleife verhindert." - fi -done -success "100% Physische Dateisystem-Integrität bestätigt. Keine Phantom-Dateien." - -# If user only wanted verification -if [[ "${1:-}" == "--check-only" ]]; then - success "Check-only abgeschlossen. Alles physisch vorhanden." - exit 0 -fi - -# ------------------------------------------------------------------------------ -# STEP 2: Process Management / Hot-Reload -# ------------------------------------------------------------------------------ -if [[ -f "$PID_FILE" ]]; then - OLD_PID="$(cat "$PID_FILE" 2>/dev/null || true)" - if [[ -n "$OLD_PID" ]] && kill -0 "$OLD_PID" 2>/dev/null; then - log "Beende vorherige Server-Instanz (PID: $OLD_PID) für Hot-Reload..." - kill "$OLD_PID" 2>/dev/null || true - sleep 0.5 - fi - rm -f "$PID_FILE" -fi - -# Also ensure port is free -OCCUPIED_PID="$(lsof -ti tcp:"$PORT" 2>/dev/null || true)" -if [[ -n "$OCCUPIED_PID" ]]; then - log "Port $PORT bereits belegt von PID $OCCUPIED_PID. Bereinige..." - kill -9 $OCCUPIED_PID 2>/dev/null || true - sleep 0.5 -fi - -# ------------------------------------------------------------------------------ -# STEP 3: Server Start -# ------------------------------------------------------------------------------ -log "Starte evOo Platform Server als robuster Daemon auf Port ${PORT}..." -export PORT - -if [[ "${1:-}" == "serve" || "${1:-}" == "--foreground" ]]; then - echo "$$" > "$PID_FILE" - log "Server läuft im Vordergrund (Strg+C zum Beenden)..." - exec node "${SCRIPT_DIR}/evoo-platform/server.js" -fi - -# True detached POSIX daemon via setsid -python3 -c "import subprocess; subprocess.Popen(['node', '${SCRIPT_DIR}/evoo-platform/server.js'], cwd='${SCRIPT_DIR}', start_new_session=True, stdout=open('${LOG_FILE}', 'a'), stderr=subprocess.STDOUT)" - -# Wait up to 5 seconds for healthcheck -log "Führe Healthcheck durch (http://127.0.0.1:${PORT}/health)..." -for i in {1..10}; do - if curl -s "http://127.0.0.1:${PORT}/health" >/dev/null 2>&1; then - HEALTH_RES="$(curl -s "http://127.0.0.1:${PORT}/health")" - success "Platform Server antwortet: HTTP 200 OK!" - echo -e "${CYAN}Health Response:${NC} $HEALTH_RES" - break - fi - if [[ $i -eq 10 ]]; then - fail "Server antwortet nicht innerhalb des Timeouts! Überprüfe $LOG_FILE" - fi - sleep 0.5 -done - -# ------------------------------------------------------------------------------ -SERVER_PID="$(cat "$PID_FILE" 2>/dev/null || echo "active")" -echo "" -echo -e "${GREEN}==========================================================${NC}" -echo -e "${GREEN} evOo PLATFORM ERFOLGREICH DEPLOYED & LIVE! ${NC}" -echo -e "${GREEN}==========================================================${NC}" -echo -e " 🌐 Dashboard: ${CYAN}http://127.0.0.1:${PORT}/${NC}" -echo -e " 💓 Healthcheck: ${CYAN}http://127.0.0.1:${PORT}/health${NC}" -echo -e " 📊 Telemetrie API: ${CYAN}http://127.0.0.1:${PORT}/api/status${NC}" -echo -e " 📑 Verfassung: ${CYAN}${SCRIPT_DIR}/ARCHITECTURE.md${NC}" -echo -e " 📈 Apex Trading: ${CYAN}${SCRIPT_DIR}/apex-trading/BLUEPRINT.md${NC}" -echo -e " ⚙️ PID: ${SERVER_PID}" -echo -e "${GREEN}==========================================================${NC}" diff --git a/docs/AGENT-INTERFACE-ONLY.md b/docs/AGENT-INTERFACE-ONLY.md index 7c13938..a13653d 100644 --- a/docs/AGENT-INTERFACE-ONLY.md +++ b/docs/AGENT-INTERFACE-ONLY.md @@ -1,5 +1,4 @@ # evOo-local — Agenten-Interface only Primary: multi-agent / Oo UI at `/app/`. -Not primary: Mission Control, Cockpit, trading dashboards, old HTML evoo-platform. Spelling: **evOo** (letter O). diff --git a/docs/LOCAL-SETUP.md b/docs/LOCAL-SETUP.md index 4ed2ea8..33682e4 100644 --- a/docs/LOCAL-SETUP.md +++ b/docs/LOCAL-SETUP.md @@ -102,10 +102,7 @@ Then connect **Kokoro** in Voice settings. See [kokoro-voice.md](./kokoro-voice. | Tool | Path | |------|------| -| self_improve | `scripts/self_improve.py` | -| autonomous-sentinel | `scripts/autonomous-sentinel.ts` | | self-update | `packages/core/src/self-update.ts` | -| orchestrator | `orchestrator/agent_runner.py`, `orchestrator/agents.json` | | local-stack | `apps/desktop/src/local-stack.ts` | | pi-local-provider | `packages/adapters/src/pi-local-provider.ts` | | seed-ollama | `scripts/seed-ollama.ts` | @@ -117,12 +114,3 @@ Display brand is **evOo**. Workspace package names remain `@rakazo/api`, `@rakaz ## Source Clean history cut from `cynarAI/evoo` commit `f00577b` on branch `antigravity-handoff/project-timeline` (Voice First + project timeline). This repo does **not** carry the old multi-remote history. - -## Old folders (do not delete from this doc — cleanup later on the Mac) - -- `~/Desktop/evoo` (or Desktop/evOo) -- `~/awesome-grok-bot` -- `~/evOo` -- `~/Projects/evOo` (if present) - -Keep them until you confirm `~/Projects/evOo-local` works. diff --git a/docs/VOICE-FIRST-REVIEW.md b/docs/VOICE-FIRST-REVIEW.md deleted file mode 100644 index 20688eb..0000000 --- a/docs/VOICE-FIRST-REVIEW.md +++ /dev/null @@ -1,52 +0,0 @@ -# Voice First / Executive Board — Review Report - -**Audience:** Ingo Wagner -**Branch:** `antigravity-handoff/project-timeline` @ `8d0b8e3` (PRs #33–#40 merged + follow-up fixes) -**Date:** 2026-09-19 (Europe/Vienna) -**Verifier host:** evOo Linux box (Tailscale `grokbot-box`). Ingo’s Mac was **not** SSH-reachable from this host (no key / Tailscale SSH off). - -## Definition of done checklist - -| # | Criterion | Result | Evidence | -|---|-----------|--------|----------| -| 1 | Dev stack healthy: API `:3100/health` + web `:5173` | **BLOCKED (Mac)** | Mac mini / MacBook Air online on Tailscale; ports 3100/5173 not exposed; SSH `Permission denied (publickey)`. Could not restart `pnpm dev` on `/Users/ingowagner/evOo`. | -| 2 | Executive Board: user VOICE FIRST turn → UX Designer replies with concept thoughts + ≥1 question; no raw `handoff_to_bot` JSON; no bounce to Boss | **PASS (unit / code)** · **UNVERIFIED (live)** | PRs #39–#40 + `bot-reply-sanitize`, `prose-handoff-recovery`, `group-handoff` workshop instructions. 56 focused tests green. Live group chat not exercised (no Mac API). | -| 3 | Transcript: no tool JSON blobs / no `assistant:` role leaks | **PASS (unit)** · **UNVERIFIED (live)** | `sanitizeBotReplyText` strips prose tool JSON + role prefix; `speechFromBlocks` skips tool-activity + unspeakable blobs (regression fixed after merge). | -| 4 | Voice Mode (CallView) UX Designer: invite before mic when owning work; after outgoing handoff mic stays closed + transfer line | **PASS (code + unit)** · **UNVERIFIED (live)** | CallView rebuilt: `speakUserTurnInvitation` / `handleSpeechIdle` / `shouldSpeakOpeningInvitation`; `parkAfterHandoff` + `voiceHandoffTransferLine`; mount no longer auto-opens mic. | - -## What was done on this pass - -1. Merged open PR heads **#33–#40** into `antigravity-handoff/project-timeline` and **pushed** to origin. -2. Fixed merge regression in `packages/core/src/speech-text.ts`: `speechFromBlocks` again drops tool-activity chips and unspeakable tool JSON (test was failing after `-X theirs` merge). -3. Fixed merge regression in `apps/web/src/pages/CallView.tsx`: invitation-before-mic restored; outgoing handoff keeps mic closed and speaks transfer line. -4. Focused vitest: **56 passed** (`call-view-voice`, `voice-handoff`, `speech-text`, `bot-reply-sanitize`, `prose-handoff-recovery`). - -## Remaining gaps (for Ingo’s Mac) - -1. **SSH / Tailscale SSH** from agent hosts to `macbook-air-von-ingo` / `mac-mini-von-ingo` so localhost verify can run without manual UI. -2. On Mac repo path `/Users/ingowagner/evOo`: - ```bash - git fetch origin && git checkout antigravity-handoff/project-timeline && git pull - pnpm install && pnpm dev # ensure :3100 + :5173 - ``` -3. Live Executive Board check (RPC/DB preferred): - - Find Executive Board `chat_groups` id. - - Post a fresh user turn about **VOICE FIRST AI Talks**. - - Confirm UX Designer message: concept prose + question; no `handoff_to_bot` JSON; Boss not re-tasked with the same work. -4. Open Voice Mode on UX Designer: hear invite before mic; after a specialist handoff, mic stays closed with transfer caption. - -## Sample expected UX Designer reply shape (sanitized) - -> I’ve been thinking about VOICE FIRST AI Talks as a short, spoken workshop: one specialist owns the lane, invites you before the mic opens, and never dumps the same task back upstairs. For the first talk, I’d keep it to three beats — hook, one demo, one ask. -> **Which audience should we design for first — founders, designers, or internal execs?** - -## Group id tested - -*None live — Mac localhost unreachable.* After Mac verify, record `chat_groups.id` here. - -## Branch / PR notes - -- Timeline branch now contains merges of `cursor/voice-mic-energy-2d2c` … `cursor/handoff-prose-sanitize-d1d4` plus commits: - - `fix(core): drop tool-activity and unspeakable blobs from speechFromBlocks` - - `fix(web): restore invite-before-mic and keep handoff mic closed` -- Draft PRs #33–#40 can be closed or marked superseded once this branch is the review source of truth. diff --git a/evoo-platform/app.js b/evoo-platform/app.js deleted file mode 100644 index 6ad21c2..0000000 --- a/evoo-platform/app.js +++ /dev/null @@ -1,573 +0,0 @@ -// evOo Platform - Executive Mission Control Cockpit (app.js) -// Zero-Mockup Reactive Engine for Ingo Wagner & Autonomous Teammates - -document.addEventListener("DOMContentLoaded", () => { - // Elements - const terminal = document.getElementById("terminal-logs"); - const btnClear = document.getElementById("btn-clear-console"); - const btnToggleConsole = document.getElementById("btn-toggle-console"); - const cardConsole = document.querySelector(".card-console"); - const btnSelfImprove = document.getElementById("btn-self-improve"); - const btnKillswitch = document.getElementById("btn-killswitch"); - const killswitchText = document.getElementById("killswitch-text"); - const btnHotReload = document.getElementById("btn-hot-reload"); - const btnOpenSteveChat = document.getElementById("btn-open-steve-chat"); - const clockEl = document.getElementById("timestamp-clock"); - const systemHealthVal = document.getElementById("system-health-val"); - const _clusterSummaryVal = document.getElementById("cluster-summary-val"); - const tradingSummaryVal = document.getElementById("trading-summary-val"); - const approvalsContainer = document.getElementById("approvals-container"); - const approvalsBadge = document.getElementById("approvals-badge"); - const journalContainer = document.getElementById("journal-container"); - const agentItems = document.querySelectorAll(".interactive-agent"); - const _pnlVal = document.getElementById("pnl-val"); - const _drawdownVal = document.getElementById("drawdown-val"); - const _circuitBreakerVal = document.getElementById("circuit-breaker-val"); - const tradingBadge = document.getElementById("trading-badge"); - - // Clock - function updateClock() { - const now = new Date(); - clockEl.textContent = `${now.toISOString().replace("T", " ").substring(0, 19)} UTC`; - } - setInterval(updateClock, 1000); - updateClock(); - - // Helper: Strip ANSI color codes - const ansiCsi = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, "g"); - function cleanAnsi(str) { - return str.replace(ansiCsi, ""); - } - - // Logger - function addLog(text, type = "info") { - if (!terminal) return; - const line = document.createElement("div"); - const cleanText = cleanAnsi(text); - - let lineType = type; - if ( - cleanText.includes("[✓ PASS]") || - cleanText.includes("[PASS]") || - cleanText.includes("SUCCESS") || - cleanText.includes("200 OK") - ) { - lineType = "success"; - } else if ( - cleanText.includes("[✗ FAIL]") || - cleanText.includes("FAILED") || - cleanText.includes("Error") - ) { - lineType = "error"; - } else if (cleanText.includes("[⚠ WARN]") || cleanText.includes("WARN")) { - lineType = "warning"; - } - - line.className = `log-line ${lineType}`; - const timestamp = new Date().toTimeString().split(" ")[0]; - line.textContent = `[${timestamp}] ${cleanText}`; - terminal.appendChild(line); - terminal.scrollTop = terminal.scrollHeight; - } - - // Console Toggles - if (btnClear) { - btnClear.addEventListener("click", () => { - terminal.innerHTML = ""; - addLog("Terminal gelöscht. Überwachung aktiv.", "info"); - }); - } - - if (btnToggleConsole && cardConsole) { - btnToggleConsole.addEventListener("click", () => { - const isCollapsed = cardConsole.classList.toggle("collapsed"); - btnToggleConsole.textContent = isCollapsed ? "Ausklappen" : "Minimieren"; - }); - } - - // 1. Fetch & Render Approvals - async function loadApprovals() { - if (!approvalsContainer) return; - try { - const res = await fetch("/api/approvals"); - if (!res.ok) return; - const data = await res.json(); - const list = data.approvals || []; - const pending = list.filter((a) => a.status === "pending"); - - if (approvalsBadge) { - approvalsBadge.textContent = - pending.length > 0 ? `${pending.length} Ausstehend` : "Alle erledigt"; - approvalsBadge.className = pending.length > 0 ? "badge badge-amber" : "badge badge-emerald"; - } - - approvalsContainer.innerHTML = ""; - if (list.length === 0) { - approvalsContainer.innerHTML = - '
Keine ausstehenden Entscheidungen. System läuft autonom.
'; - return; - } - - list.forEach((item) => { - const div = document.createElement("div"); - div.className = "approval-item"; - div.setAttribute("data-id", item.id); - - const isDecided = item.status !== "pending"; - const statusClass = item.status === "approved" ? "badge-emerald" : "badge-amber"; - - div.innerHTML = ` -
- ${item.type} - ${item.author} - ${item.timestamp} - ${isDecided ? `${item.status.toUpperCase()}` : ""} -
-
${item.title}
-
${item.details}
-
- Conviction: ${item.conviction}% - R:R: ${item.riskReward} - Umfang: ${item.size} -
- ${ - !isDecided - ? ` -
- - - -
- ` - : "" - } - `; - approvalsContainer.appendChild(div); - }); - - // Attach handlers to actions - approvalsContainer.querySelectorAll(".btn-approve, .btn-veto").forEach((btn) => { - btn.addEventListener("click", async (e) => { - const id = e.target.getAttribute("data-id"); - const action = e.target.getAttribute("data-action"); - try { - const resp = await fetch("/api/approvals", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ id, action }), - }); - if (resp.ok) { - addLog( - `[DECISION] Entscheidung '${action.toUpperCase()}' für '${id}' physisch registriert.`, - "highlight", - ); - loadApprovals(); - } - } catch (err) { - addLog(`Fehler beim Speichern der Entscheidung: ${err.message}`, "error"); - } - }); - }); - - approvalsContainer.querySelectorAll(".btn-discuss").forEach((btn) => { - btn.addEventListener("click", () => { - const author = btn.getAttribute("data-author") || ""; - addLog(`[NAVIGATE] Wechsle zur Konversation mit '${author}' in der Chat-App...`, "info"); - if (window.parent && window.parent !== window) { - window.parent.postMessage({ type: "open_team_tab", botAuthor: author }, "*"); - } - }); - }); - } catch (e) { - console.error("Approvals fetch error", e); - } - } - - // 2. Fetch & Render Overnight Journal - async function loadJournal() { - if (!journalContainer) return; - try { - const res = await fetch("/api/journal"); - if (!res.ok) return; - const data = await res.json(); - const events = data.events || []; - - journalContainer.innerHTML = ""; - events.forEach((ev) => { - const row = document.createElement("div"); - row.className = "journal-entry"; - row.innerHTML = ` -
${ev.time}
-
-
- ${ev.agent} - ${ev.title} -
-
${ev.detail}
-
- `; - journalContainer.appendChild(row); - }); - } catch (e) { - console.error("Journal fetch error", e); - } - } - - let currentTradingMode = "paper"; - let lastTradingData = null; - - const tabModePaper = document.getElementById("tab-mode-paper"); - const tabModeLive = document.getElementById("tab-mode-live"); - - const card1Label = document.getElementById("card1-label"); - const card1Val = document.getElementById("card1-val"); - const card1Sub = document.getElementById("card1-sub"); - - const card2Label = document.getElementById("card2-label"); - const card2Val = document.getElementById("card2-val"); - const card2Sub = document.getElementById("card2-sub"); - - const card3Label = document.getElementById("card3-label"); - const card3Val = document.getElementById("card3-val"); - const card3Sub = document.getElementById("card3-sub"); - - const card4Label = document.getElementById("card4-label"); - const card4Val = document.getElementById("card4-val"); - const card4Sub = document.getElementById("card4-sub"); - - function renderTradingView() { - if (!lastTradingData) return; - - if (tabModePaper && tabModeLive) { - if (currentTradingMode === "live") { - tabModeLive.classList.add("active"); - tabModePaper.classList.remove("active"); - } else { - tabModePaper.classList.add("active"); - tabModeLive.classList.remove("active"); - } - } - - if (currentTradingMode === "live") { - const live = lastTradingData.live || {}; - const poly = live.polymarket || {}; - const bp = live.bitpanda || {}; - const fiat = live.fiat_reserves || {}; - - if (card1Label) card1Label.textContent = "Polymarket CLOB V2"; - if (card1Val) card1Val.textContent = `$${(poly.pusd_balance || 639.39).toFixed(2)} pUSD`; - if (card1Sub) - card1Sub.textContent = `+ ${(poly.pol_gas || 58.25).toFixed(1)} POL Gas · Deposit Wallet`; - - if (card2Label) card2Label.textContent = "Bitpanda Fusion"; - if (card2Val) card2Val.textContent = `€${(bp.eur_cash || 46.09).toFixed(2)} EUR`; - if (card2Sub) - card2Sub.textContent = `+ ${(bp.pol || 436).toFixed(0)} POL, ${(bp.near || 12.6).toFixed(1)} NEAR, ${(bp.usdc || 10.3).toFixed(1)} USDC`; - - if (card3Label) card3Label.textContent = "Bank-Liquidität (Fiat)"; - if (card3Val) - card3Val.textContent = `€${(fiat.total_eur || 8500).toLocaleString("de-DE", { minimumFractionDigits: 2 })}`; - if (card3Sub) card3Sub.textContent = `N26 (€5.000) + Revolut (€3.500)`; - - if (card4Label) card4Label.textContent = "Gesamtes Live-Kapital"; - if (card4Val) { - card4Val.textContent = `$${(live.total_live_equity_usd || 10074.65).toLocaleString("en-US", { minimumFractionDigits: 2 })}`; - card4Val.className = "tmetric-val positive"; - } - if (card4Sub) - card4Sub.textContent = `€${(live.total_live_equity_eur || 9285.39).toLocaleString("de-DE", { minimumFractionDigits: 2 })} · Triple-Gated Safe`; - - if (tradingBadge) { - tradingBadge.textContent = "🟢 ECHTGELD: Polymarket + Bitpanda"; - tradingBadge.className = "badge badge-emerald"; - } - if (tradingSummaryVal) { - tradingSummaryVal.textContent = `LIVE ECHTGELD ($${(poly.pusd_balance || 639.39).toFixed(0)} pUSD + Bitpanda + €8.5k Fiat)`; - } - } else { - // Paper Mode - const paper = lastTradingData.paper || {}; - - if (card1Label) card1Label.textContent = "Equity & Portfolio (Paper)"; - if (card1Val) { - card1Val.textContent = `$${(paper.total_equity_usd || 1000).toLocaleString("en-US", { minimumFractionDigits: 2 })}`; - card1Val.className = "tmetric-val positive"; - } - if (card1Sub) - card1Sub.textContent = `€${(paper.total_cash_eur || 921.66).toLocaleString("de-DE", { minimumFractionDigits: 2 })} · Paper Trading (Safe)`; - - if (card2Label) card2Label.textContent = "Max Drawdown"; - if (card2Val) { - card2Val.textContent = paper.max_drawdown || "0.00%"; - card2Val.className = "tmetric-val"; - } - if (card2Sub) card2Sub.textContent = "Cap: 1.50% (Hard Stop)"; - - if (card3Label) card3Label.textContent = "Aktives Markt-Regime"; - if (card3Val) { - card3Val.textContent = "Donchian / ADX"; - card3Val.className = "tmetric-val cyan-text"; - } - if (card3Sub) card3Sub.textContent = "Trend-Following + Mean-Reversion"; - - if (card4Label) card4Label.textContent = "Circuit Breaker Status"; - if (card4Val) { - card4Val.textContent = paper.circuit_breaker || "ARMED"; - card4Val.className = "tmetric-val success-val"; - } - if (card4Sub) card4Sub.textContent = "Fixed Fractional (1-2%)"; - - if (tradingBadge) { - tradingBadge.textContent = "REST API: Port 8001 (Paper)"; - tradingBadge.className = "badge badge-cyan"; - } - if (tradingSummaryVal) { - tradingSummaryVal.textContent = `PAPER (ARMED · $${(paper.total_equity_usd || 1000).toFixed(0)})`; - } - } - - if (btnKillswitch && killswitchText) { - if (lastTradingData.kill_switch_active) { - btnKillswitch.className = "btn btn-warning halted"; - killswitchText.textContent = "Risk Guard: HALTED"; - } else { - btnKillswitch.className = "btn btn-warning"; - killswitchText.textContent = "Risk Guard: Aktiv"; - } - } - } - - // 3. Poll Trading-Bot Status (Dual Mode: Paper & Live) - async function pollTradingBot() { - try { - const res = await fetch("/api/trading-bot"); - if (res.ok) { - const data = await res.json(); - lastTradingData = data; - if (data.active_mode) { - currentTradingMode = data.active_mode; - } - renderTradingView(); - } - } catch { - // Ignored - } - } - - if (tabModePaper) { - tabModePaper.addEventListener("click", async () => { - currentTradingMode = "paper"; - renderTradingView(); - addLog("[MODE] Auf Paper-Trading-Modus (Simulation) umgeschaltet.", "info"); - try { - await fetch("/api/trading-bot/mode", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ mode: "paper" }), - }); - loadJournal(); - } catch {} - }); - } - - if (tabModeLive) { - tabModeLive.addEventListener("click", async () => { - currentTradingMode = "live"; - renderTradingView(); - addLog( - "[MODE] 🟢 Auf LIVE-ECHTGELD-MODUS (Polymarket & Bitpanda) umgeschaltet!", - "highlight", - ); - try { - await fetch("/api/trading-bot/mode", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ mode: "live" }), - }); - loadJournal(); - } catch {} - }); - } - - // 4. Poll System Status & Cluster - async function pollSystemStatus() { - const t0 = performance.now(); - try { - const res = await fetch("/api/status"); - const rtt = Math.round(performance.now() - t0); - if (res.ok) { - const data = await res.json(); - if (systemHealthVal) { - systemHealthVal.textContent = `HEALTHY (Port 3088 · ${rtt}ms · RAM ${data.memory_rss_mb || 0}MB)`; - } - } - } catch { - if (systemHealthVal) systemHealthVal.textContent = "DEGRADED"; - } - } - - // Initial Load & Intervals - loadApprovals(); - loadJournal(); - pollTradingBot(); - pollSystemStatus(); - setInterval(loadApprovals, 15000); - setInterval(pollTradingBot, 5000); - setInterval(pollSystemStatus, 4000); - - // Risk Killswitch Button - if (btnKillswitch) { - btnKillswitch.addEventListener("click", async () => { - try { - const res = await fetch("/api/risk/toggle-killswitch", { method: "POST" }); - const data = await res.json(); - addLog( - `[RISK GUARD] Notfall-Circuit-Breaker Zustand geändert: ${data.killSwitchActive ? "HALTED" : "ARMED"}`, - data.killSwitchActive ? "warning" : "success", - ); - pollTradingBot(); - } catch (e) { - addLog(`Fehler beim Schalten des Kill-Switches: ${e.message}`, "error"); - } - }); - } - - // 5. Interactive Agent Roster Clicks (Physical Audit Dispatch) - agentItems.forEach((item) => { - item.addEventListener("click", async () => { - const agentId = item.getAttribute("data-agent"); - const dot = item.querySelector(".status-dot"); - const agentName = item.querySelector(".agent-name")?.textContent || agentId; - - addLog(`[DISPATCH] CTO instruiert Agent '${agentName}' (${agentId})...`, "highlight"); - if (dot) dot.style.opacity = "0.35"; - - try { - const res = await fetch("/api/agents/dispatch", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ agent: agentId }), - }); - - if (res.ok) { - const response = await res.json(); - const r = response.data?.result || response.data || {}; - const isSuccess = response.data?.success ?? response.success; - - if (isSuccess) { - addLog( - `[${agentName}] Task '${response.data?.task}' erfolgreich ausgeführt (${response.data?.duration_seconds}s).`, - "success", - ); - if (agentId === "devops") { - addLog( - `[DevOps] PID: ${r.pid} | Port 3088: ${r.port_3088_status} | Health: ${r.status}`, - "info", - ); - } else if (agentId === "architect") { - addLog(`[Architect] ${r.findings} Status: ${r.clean_architecture}`, "info"); - } else if (agentId === "quant") { - addLog( - `[Quant] Engine Test: PASS | Sim: ${r.simulation?.simulation_status} ${r.simulation?.action || ""}`, - "info", - ); - } else if (agentId === "ux") { - addLog(`[UX Designer] ${r.findings} Status: ${r.design_system_integrity}`, "info"); - } - if (dot) { - dot.style.opacity = "1"; - dot.className = "status-dot green"; - } - } else { - addLog(`[${agentName}] Mangel festgestellt: ${JSON.stringify(r)}`, "warning"); - if (dot) { - dot.style.opacity = "1"; - dot.className = "status-dot"; - dot.style.background = "var(--accent-red, #ef4444)"; - } - } - } else { - addLog(`[${agentName}] Server-Fehler bei Dispatch (HTTP ${res.status})`, "error"); - } - } catch (e) { - addLog(`[${agentName}] Dispatch-Fehler: ${e.message}`, "error"); - } - }); - }); - - // 6. Self-Improvement Loop Trigger with Real SSE Streaming - let activeEventSource = null; - if (btnSelfImprove) { - btnSelfImprove.addEventListener("click", async () => { - if (activeEventSource) { - activeEventSource.close(); - activeEventSource = null; - } - - addLog( - "[IMPROVE] Starte physischen Selbstverbesserungs-Zyklus (scripts/self_improve.py)...", - "highlight", - ); - - try { - const eventSource = new EventSource("/api/self-improve?stream=true"); - activeEventSource = eventSource; - - eventSource.onmessage = (event) => { - try { - const payload = JSON.parse(event.data); - if (payload.type === "log") { - addLog(payload.msg, "info"); - } else if (payload.type === "error") { - addLog(payload.msg, "error"); - } else if (payload.type === "done") { - addLog( - `[IMPROVE] Zyklus beendet mit Exit-Code ${payload.exitCode}.`, - payload.exitCode === 0 ? "success" : "error", - ); - eventSource.close(); - activeEventSource = null; - loadJournal(); - } - } catch { - addLog(event.data, "info"); - } - }; - - eventSource.onerror = () => { - addLog("[IMPROVE] SSE Stream beendet oder getrennt.", "info"); - eventSource.close(); - activeEventSource = null; - }; - } catch (err) { - addLog(`[IMPROVE] Verbindungsfehler: ${err.message}`, "error"); - } - }); - } - - // Hot-Reload Trigger - if (btnHotReload) { - btnHotReload.addEventListener("click", async () => { - addLog("[HOT-RELOAD] Hot-Reload Signal gesendet...", "highlight"); - try { - await fetch("/api/reload", { method: "POST" }).catch(() => null); - } catch { - // Ignored - } - setTimeout(() => { - addLog("[HOT-RELOAD] Liveness & Dateisystem-Synchronisation bestätigt.", "success"); - pollSystemStatus(); - }, 400); - }); - } - - // Open Steve Chat button - if (btnOpenSteveChat) { - btnOpenSteveChat.addEventListener("click", () => { - addLog("[NAVIGATE] Öffne Chat mit CTO Steve Jobs in der evOo Chat-App...", "highlight"); - if (window.parent && window.parent !== window) { - window.parent.postMessage({ type: "open_team_tab", botName: "Steve Jobs" }, "*"); - } - }); - } -}); diff --git a/evoo-platform/index.html b/evoo-platform/index.html deleted file mode 100644 index ef56034..0000000 --- a/evoo-platform/index.html +++ /dev/null @@ -1,347 +0,0 @@ - - - - - - evOo Mission Control | Executive Operations Cockpit - - - - - - -
- - -
-
-
- evOo - v2.0-EXEC -
-
- - System: - HEALTHY (Port 3088) -
-
- -
-
- 🖥️ - Cluster: - Host (Metal GPU 30B) · Mini · Air -
-
- 📈 - Trading: - ARMED (Max DD 0.21% / 1.5%) -
-
- -
- - - -
-
- - -
-
- - -
- - -
-
-
- -

Offene Entscheidungen & Freigaben

-
- 2 Ausstehend -
-
- -
-
- - -
-
-
- -

Operations Journal (Letzte 24h)

-
- 100% Physisch -
-
- -
-
- -
- - -
- - -
-
-
- -

CTO Steve Jobs

-
- Vollmacht: Unbeschränkt -
-
-
-
SJ
-
- Steve Jobs - Chief Technology Officer · Modus: Action-First -
-
-
-

- „Realität im Dateisystem vor Halluzination im Kopf. Das 3-Säulen-System steht. Alle 5 Spezialisten arbeiten mit deterministischen Schnittstellen und echten Tests.“ -

-
-
- -
- ✓ Filesystem Write - ✓ Multi-Node Orchestration -
-
-
-
- - -
-
-
- -

Agenten-Flotte

-
- 5 Casts aktiv -
-

Klicke auf einen Spezialisten, um sofort ein physisches System-Audit auszulösen:

- -
- -
-
CA
-
- Code Architect - Moduldesign & Clean Architecture - Node: Mac mini (Strategic Reasoning) -
-
- -
-
- - -
-
QA
-
- Security & QA Auditor - Integritätsprüfung & Zero-Phantom - Node: MacBook Air (Fast Coding) -
-
- -
-
- - -
-
DO
-
- DevOps Engineer - Hot-Reload & Service Liveness - Node: Host Mac (Daemons & Ports) -
-
- -
-
- - -
-
AP
-
- Apex Quant Specialist - Algorithmic Risk & Port 8000 Bridge - Node: Host Mac (Deterministic Math) -
-
- -
-
- - -
-
UX
-
- UX Designer - Lead Product & UX/UI Designer - Node: MacBook Air / Host (3-Pillar UI) -
-
- -
-
-
-
- -
-
- - -
- - -
-
-
- -

Distributed Mac Cluster

-
- Metal GPU Aktiv -
-
-
-
- - Host Mac (Primär) - Metal GPU -
-
-
LM Studio:qwen3-coder-30b-a3b (Metal)
-
Ollama:gemma4:latest (Port 11434)
-
Zustand:Aktiv · Keine Drosselung
-
-
- -
-
- - Mac mini - 24/7 Powerhouse -
-
-
Profil:Aktiv gekühlt (Lüfter)
-
Empfohlen:qwen2.5:32b / deepseek-r1
-
Routing:Failover zu Host Metal GPU
-
-
- -
-
- - MacBook Air - Mobile Worker -
-
-
Profil:Lüfterlos (Thermisch geschützt)
-
Empfohlen:qwen2.5-coder:7b / 3b
-
Routing:Failover zu Host Metal GPU
-
-
-
-
- - -
-
-
- -

Apex Quantitative Trading Engine

-
-
-
- - -
- REST API: Port 8001 -
-
-
-
- Equity & Portfolio - $1,000.00 - €921.66 · Paper Trading (Safe) -
-
- Max Drawdown - 0.00% - Cap: 1.50% (Hard Stop) -
-
- Aktives Markt-Regime - Donchian / ADX - Trend-Following + Mean-Reversion -
-
- Circuit Breaker Status - ARMED - Fixed Fractional (1-2%) -
-
-
- - -
-
-
- -

Self-Improvement & System Event Log

-
-
- - -
-
-
-
[SYSTEM] evOo Executive Cockpit initialized. Version 2.0.0.
-
[CLUSTER] Host Metal GPU (30B Qwen) aktiv. Port 1234 bereit.
-
[VERIFY] 3-Säulen-Navigation (Heute/Team/Projekte) in Chat-App verankert.
-
[OPERATIONS] 5 Casted Agents (Architect, QA, DevOps, Quant, UX) aktiv.
-
-
- -
-
- -
- - -
- - - - diff --git a/evoo-platform/server-config.js b/evoo-platform/server-config.js deleted file mode 100644 index 154151e..0000000 --- a/evoo-platform/server-config.js +++ /dev/null @@ -1,142 +0,0 @@ -const LOOPBACK_HOSTS = new Set(["127.0.0.1", "localhost", "::1", "[::1]"]); - -/** Default web/API origins allowed on loopback binds for the Heute iframe shell. */ -const DEFAULT_LOOPBACK_WEB_ORIGINS = [ - "http://127.0.0.1:5173", - "http://localhost:5173", - "http://127.0.0.1:3100", - "http://localhost:3100", -]; - -export function isLoopbackHost(host) { - return LOOPBACK_HOSTS.has(host.trim()); -} - -export function timingSafeStringEqual(supplied, expected) { - const actual = Buffer.from(expected); - const candidate = Buffer.from(supplied ?? ""); - if (actual.length !== candidate.length) return false; - let difference = 0; - for (let index = 0; index < actual.length; index += 1) { - difference |= actual[index] ^ candidate[index]; - } - return difference === 0; -} - -export function hasValidBearerToken(authorization, expectedToken) { - const supplied = authorization?.startsWith("Bearer ") ? authorization.slice(7) : ""; - return timingSafeStringEqual(supplied, expectedToken); -} - -export function isPublicCockpitRoute(pathname) { - return pathname === "/health" || pathname === "/api/health"; -} - -/** Privileged API routes must never receive Access-Control-Allow-Origin: *. */ -export function isPrivilegedCockpitRoute(pathname) { - if (isPublicCockpitRoute(pathname)) return false; - return pathname.startsWith("/api/"); -} - -export function parseCorsOriginAllowlist(value) { - if (!value?.trim()) return []; - return [ - ...new Set( - value - .split(",") - .map((entry) => entry.trim()) - .filter(Boolean), - ), - ]; -} - -/** - * Cockpit bind/auth settings. Defaults to loopback-only with no token so local - * Mission Control keeps working out of the box. - */ -export function resolveCockpitConfig(env) { - const port = Number.parseInt(env.PORT?.trim() || "3088", 10); - if (!Number.isInteger(port) || port <= 0 || port > 65_535) { - throw new Error("PORT is not a valid port number."); - } - - const host = env.EVOO_COCKPIT_HOST?.trim() || "127.0.0.1"; - const loopback = isLoopbackHost(host); - const token = env.EVOO_COCKPIT_TOKEN?.trim() || ""; - - if (!loopback && token === "") { - throw new Error( - "Set EVOO_COCKPIT_TOKEN when EVOO_COCKPIT_HOST is not loopback (127.0.0.1, localhost, or ::1).", - ); - } - - return { - port, - host, - token: loopback ? null : token, - requiresAuth: !loopback, - }; -} - -/** - * CORS allowlist. Loopback binds default to local web/API origins; non-loopback - * binds require an explicit EVOO_COCKPIT_CORS_ORIGIN comma-separated list. - */ -export function resolveCockpitCorsConfig(env, cockpitConfig) { - const explicit = parseCorsOriginAllowlist(env.EVOO_COCKPIT_CORS_ORIGIN); - const loopbackBind = isLoopbackHost(cockpitConfig.host); - - if (!loopbackBind && explicit.length === 0) { - return { - loopbackBind: false, - allowedOrigins: [], - usesDefaultLoopbackOrigins: false, - }; - } - - const usesDefaultLoopbackOrigins = loopbackBind && explicit.length === 0; - const allowedOrigins = usesDefaultLoopbackOrigins ? DEFAULT_LOOPBACK_WEB_ORIGINS : explicit; - - return { - loopbackBind, - allowedOrigins, - usesDefaultLoopbackOrigins, - }; -} - -/** - * Resolve the Access-Control-Allow-Origin value for a request. - * Returns a reflected origin string, null when CORS headers should be omitted, - * or false when the Origin is present but not allowed. - */ -export function resolveAllowedCorsOrigin(requestOrigin, corsConfig, pathname) { - const origin = requestOrigin?.trim() ?? ""; - if (!origin) return null; - - if (isPrivilegedCockpitRoute(pathname)) { - return corsConfig.allowedOrigins.includes(origin) ? origin : false; - } - - if (corsConfig.loopbackBind && corsConfig.usesDefaultLoopbackOrigins) { - return corsConfig.allowedOrigins.includes(origin) ? origin : false; - } - - return corsConfig.allowedOrigins.includes(origin) ? origin : false; -} - -export function applyCockpitCorsHeaders(req, res, pathname, corsConfig) { - res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS"); - res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization"); - - const allowed = resolveAllowedCorsOrigin(req.headers.origin, corsConfig, pathname); - if (typeof allowed === "string") { - res.setHeader("Access-Control-Allow-Origin", allowed); - res.setHeader("Vary", "Origin"); - } - return allowed; -} - -export function formatCockpitListenUrl(host, port) { - const displayHost = host.includes(":") && !host.startsWith("[") ? `[${host}]` : host; - return `http://${displayHost}:${port}`; -} diff --git a/evoo-platform/server-config.test.js b/evoo-platform/server-config.test.js deleted file mode 100644 index eddaea8..0000000 --- a/evoo-platform/server-config.test.js +++ /dev/null @@ -1,215 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { - applyCockpitCorsHeaders, - formatCockpitListenUrl, - hasValidBearerToken, - isLoopbackHost, - isPrivilegedCockpitRoute, - isPublicCockpitRoute, - resolveAllowedCorsOrigin, - resolveCockpitConfig, - resolveCockpitCorsConfig, -} from "./server-config.js"; - -describe("resolveCockpitConfig", () => { - it("binds to loopback by default without requiring a token", () => { - expect(resolveCockpitConfig({})).toEqual({ - port: 3088, - host: "127.0.0.1", - token: null, - requiresAuth: false, - }); - }); - - it("honours explicit loopback hosts", () => { - for (const host of ["127.0.0.1", "localhost", "::1"]) { - expect(resolveCockpitConfig({ EVOO_COCKPIT_HOST: host })).toMatchObject({ - host, - token: null, - requiresAuth: false, - }); - } - }); - - it("requires a shared token before binding beyond loopback", () => { - expect(() => resolveCockpitConfig({ EVOO_COCKPIT_HOST: "0.0.0.0" })).toThrow( - /EVOO_COCKPIT_TOKEN/, - ); - }); - - it("allows a non-loopback bind when a token is configured", () => { - expect( - resolveCockpitConfig({ - EVOO_COCKPIT_HOST: "0.0.0.0", - EVOO_COCKPIT_TOKEN: "cockpit-review-token-000000000000", - }), - ).toEqual({ - port: 3088, - host: "0.0.0.0", - token: "cockpit-review-token-000000000000", - requiresAuth: true, - }); - }); - - it("rejects invalid ports", () => { - expect(() => resolveCockpitConfig({ PORT: "0" })).toThrow(/PORT/); - expect(() => resolveCockpitConfig({ PORT: "abc" })).toThrow(/PORT/); - }); -}); - -describe("cockpit auth helpers", () => { - const token = "cockpit-review-token-000000000000"; - - it("treats health routes as public when auth is enabled", () => { - expect(isPublicCockpitRoute("/health")).toBe(true); - expect(isPublicCockpitRoute("/api/health")).toBe(true); - expect(isPublicCockpitRoute("/api/status")).toBe(false); - }); - - it("accepts only bearer tokens with constant-time comparison", () => { - expect(hasValidBearerToken(`Bearer ${token}`, token)).toBe(true); - expect(hasValidBearerToken(`Bearer ${`${token}x`}`, token)).toBe(false); - expect(hasValidBearerToken(token, token)).toBe(false); - expect(hasValidBearerToken(undefined, token)).toBe(false); - }); - - it("formats listen URLs with bracketed IPv6 hosts", () => { - expect(formatCockpitListenUrl("127.0.0.1", 3088)).toBe("http://127.0.0.1:3088"); - expect(formatCockpitListenUrl("::1", 3088)).toBe("http://[::1]:3088"); - }); -}); - -describe("isLoopbackHost", () => { - it("recognises loopback hosts only", () => { - expect(isLoopbackHost("127.0.0.1")).toBe(true); - expect(isLoopbackHost("localhost")).toBe(true); - expect(isLoopbackHost("::1")).toBe(true); - expect(isLoopbackHost("0.0.0.0")).toBe(false); - expect(isLoopbackHost("192.168.1.10")).toBe(false); - }); -}); - -describe("isPrivilegedCockpitRoute", () => { - it("marks API routes except health as privileged", () => { - expect(isPrivilegedCockpitRoute("/health")).toBe(false); - expect(isPrivilegedCockpitRoute("/api/health")).toBe(false); - expect(isPrivilegedCockpitRoute("/api/status")).toBe(true); - expect(isPrivilegedCockpitRoute("/api/trading-bot/mode")).toBe(true); - expect(isPrivilegedCockpitRoute("/index.html")).toBe(false); - }); -}); - -describe("resolveCockpitCorsConfig", () => { - const loopbackCockpit = resolveCockpitConfig({ EVOO_COCKPIT_HOST: "127.0.0.1" }); - const publicCockpit = resolveCockpitConfig({ - EVOO_COCKPIT_HOST: "0.0.0.0", - EVOO_COCKPIT_TOKEN: "cockpit-review-token-000000000000", - }); - - it("defaults to local web origins on loopback binds", () => { - const cors = resolveCockpitCorsConfig({}, loopbackCockpit); - expect(cors.loopbackBind).toBe(true); - expect(cors.usesDefaultLoopbackOrigins).toBe(true); - expect(cors.allowedOrigins).toContain("http://127.0.0.1:5173"); - }); - - it("requires an explicit allowlist off loopback", () => { - const cors = resolveCockpitCorsConfig({}, publicCockpit); - expect(cors.allowedOrigins).toEqual([]); - expect(cors.usesDefaultLoopbackOrigins).toBe(false); - }); - - it("honours EVOO_COCKPIT_CORS_ORIGIN when set", () => { - const cors = resolveCockpitCorsConfig( - { EVOO_COCKPIT_CORS_ORIGIN: "https://ops.example.com, https://ops.example.com" }, - publicCockpit, - ); - expect(cors.allowedOrigins).toEqual(["https://ops.example.com"]); - }); -}); - -describe("resolveAllowedCorsOrigin", () => { - const loopbackCors = resolveCockpitCorsConfig({}, resolveCockpitConfig({})); - const publicCors = resolveCockpitCorsConfig( - { EVOO_COCKPIT_CORS_ORIGIN: "https://ops.example.com" }, - resolveCockpitConfig({ - EVOO_COCKPIT_HOST: "0.0.0.0", - EVOO_COCKPIT_TOKEN: "cockpit-review-token-000000000000", - }), - ); - const blockedPublicCors = resolveCockpitCorsConfig( - {}, - resolveCockpitConfig({ - EVOO_COCKPIT_HOST: "0.0.0.0", - EVOO_COCKPIT_TOKEN: "cockpit-review-token-000000000000", - }), - ); - - it("never returns a wildcard and omits headers without an Origin", () => { - expect(resolveAllowedCorsOrigin(undefined, loopbackCors, "/api/status")).toBeNull(); - expect(resolveAllowedCorsOrigin("", loopbackCors, "/api/status")).toBeNull(); - }); - - it("reflects allowlisted origins on privileged routes", () => { - expect(resolveAllowedCorsOrigin("http://127.0.0.1:5173", loopbackCors, "/api/status")).toBe( - "http://127.0.0.1:5173", - ); - expect(resolveAllowedCorsOrigin("https://evil.example", loopbackCors, "/api/status")).toBe( - false, - ); - }); - - it("blocks cross-origin privileged access off loopback without an allowlist", () => { - expect( - resolveAllowedCorsOrigin("https://ops.example.com", blockedPublicCors, "/api/status"), - ).toBe(false); - }); - - it("allows configured origins off loopback", () => { - expect( - resolveAllowedCorsOrigin("https://ops.example.com", publicCors, "/api/trading-bot"), - ).toBe("https://ops.example.com"); - }); -}); - -describe("applyCockpitCorsHeaders", () => { - it("sets reflected origins and never sets *", () => { - const cors = resolveCockpitCorsConfig({}, resolveCockpitConfig({})); - const headers = {}; - const res = { - setHeader: (key, value) => { - headers[key] = value; - }, - }; - const allowed = applyCockpitCorsHeaders( - { headers: { origin: "http://127.0.0.1:5173" } }, - res, - "/api/status", - cors, - ); - - expect(allowed).toBe("http://127.0.0.1:5173"); - expect(headers["Access-Control-Allow-Origin"]).toBe("http://127.0.0.1:5173"); - expect(headers["Access-Control-Allow-Origin"]).not.toBe("*"); - expect(headers["Access-Control-Allow-Headers"]).toContain("Authorization"); - }); - - it("omits Access-Control-Allow-Origin for disallowed cross-origin requests", () => { - const cors = resolveCockpitCorsConfig({}, resolveCockpitConfig({})); - const headers = {}; - const res = { - setHeader: (key, value) => { - headers[key] = value; - }, - }; - const allowed = applyCockpitCorsHeaders( - { headers: { origin: "https://evil.example" } }, - res, - "/api/status", - cors, - ); - - expect(allowed).toBe(false); - expect(headers["Access-Control-Allow-Origin"]).toBeUndefined(); - }); -}); diff --git a/evoo-platform/server.js b/evoo-platform/server.js deleted file mode 100644 index 3c7c5e8..0000000 --- a/evoo-platform/server.js +++ /dev/null @@ -1,731 +0,0 @@ -/** - * evOo Platform High-Speed Embedded Server - * ES Module compatible with Node.js 22+. - * Zero external dependencies. - * Hardened against Directory Traversal & equipped with real Agent Runtime integration. - */ - -import { spawn } from "node:child_process"; -import fs from "node:fs"; -import http from "node:http"; -import os from "node:os"; -import path from "node:path"; -import { fileURLToPath } from "node:url"; -import { - applyCockpitCorsHeaders, - formatCockpitListenUrl, - hasValidBearerToken, - isPublicCockpitRoute, - resolveCockpitConfig, - resolveCockpitCorsConfig, -} from "./server-config.js"; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); - -const cockpit = resolveCockpitConfig(process.env); -const corsConfig = resolveCockpitCorsConfig(process.env, cockpit); -const { port: PORT, host: COCKPIT_HOST } = cockpit; -const BASE_DIR = __dirname; -const ROOT_DIR = path.resolve(BASE_DIR, ".."); - -const MIME_TYPES = { - ".html": "text/html; charset=UTF-8", - ".css": "text/css; charset=UTF-8", - ".js": "application/javascript; charset=UTF-8", - ".json": "application/json; charset=UTF-8", - ".png": "image/png", - ".jpg": "image/jpeg", - ".svg": "image/svg+xml", - ".md": "text/markdown; charset=UTF-8", -}; - -const APPROVALS_FILE = path.join(ROOT_DIR, "orchestrator", "data", "approvals.json"); -const JOURNAL_FILE = path.join(ROOT_DIR, "orchestrator", "data", "journal.json"); - -function loadApprovals() { - try { - if (fs.existsSync(APPROVALS_FILE)) { - return JSON.parse(fs.readFileSync(APPROVALS_FILE, "utf8")); - } - } catch (e) { - console.error("Error loading approvals:", e.message); - } - const defaults = [ - { - id: "appr-104", - type: "trade", - title: "Staged Trade: ETH/USDT Breakout", - author: "Apex Quant Specialist", - conviction: 88, - riskReward: 2.4, - size: "1.2% Equity", - status: "pending", - timestamp: "Vor 14 Min.", - details: - "Trend-Following Breakout über 20-Perioden Donchian Upper Band. ADX 32.4 (starker Trend). Stop-Loss bei -1.1% gesetzt.", - }, - { - id: "appr-105", - type: "architecture", - title: "Modulare Entkopplung: Trading-Bot API Bridge", - author: "Code Architect", - conviction: 95, - riskReward: 3.5, - size: "Refactor Plan", - status: "pending", - timestamp: "Vor 42 Min.", - details: - "Vollständige Entkopplung der tradingbot-Imports. evOo konsumiert ausschließlich REST-Endpoints auf Port 8000.", - }, - ]; - saveApprovals(defaults); - return defaults; -} - -function saveApprovals(items) { - try { - fs.mkdirSync(path.dirname(APPROVALS_FILE), { recursive: true }); - fs.writeFileSync(APPROVALS_FILE, JSON.stringify(items, null, 2), "utf8"); - } catch (e) { - console.error("Error saving approvals:", e.message); - } -} - -function loadJournal() { - try { - if (fs.existsSync(JOURNAL_FILE)) { - return JSON.parse(fs.readFileSync(JOURNAL_FILE, "utf8")); - } - } catch (e) { - console.error("Error loading journal:", e.message); - } - const defaults = [ - { - time: "04:52 UTC", - agent: "Security & QA", - type: "success", - title: "Zero-Phantom Integrity & Tests", - detail: "14 Systemdateien & 5 JSONs verifiziert. Apex Unittests 100% grün.", - }, - { - time: "04:46 UTC", - agent: "Orchestrator", - type: "info", - title: "Casted Agent Network Sync", - detail: "5 Casted Agents (inkl. UX Designer) synchronisiert. Local Metal GPU aktiv.", - }, - { - time: "03:15 UTC", - agent: "Apex Quant", - type: "success", - title: "Circuit Breaker Audit", - detail: "Kein Drawdown-Verstoß festgestellt. Max Drawdown aktuell 0.21% / 1.5% Cap.", - }, - { - time: "01:05 UTC", - agent: "Cluster Router", - type: "info", - title: "Smart Failover Routine", - detail: "Host Apple Silicon Metal übernimmt Inference nahtlos für Standby-LAN-Nodes.", - }, - { - time: "00:50 UTC", - agent: "DevOps Engineer", - type: "success", - title: "Plattform Hot-Reload", - detail: "Server auf Port 3088 atomar neu gestartet. PID stabilisiert.", - }, - ]; - try { - fs.mkdirSync(path.dirname(JOURNAL_FILE), { recursive: true }); - fs.writeFileSync(JOURNAL_FILE, JSON.stringify(defaults, null, 2), "utf8"); - } catch {} - return defaults; -} - -function appendJournal(entry) { - try { - const list = loadJournal(); - list.unshift(entry); - if (list.length > 50) list.pop(); - fs.mkdirSync(path.dirname(JOURNAL_FILE), { recursive: true }); - fs.writeFileSync(JOURNAL_FILE, JSON.stringify(list, null, 2), "utf8"); - } catch (e) { - console.error("Error appending journal:", e.message); - } -} - -let riskKillSwitchActive = false; -const pendingApprovals = loadApprovals(); -let currentTradingMode = "paper"; // 'paper' or 'live' - -let liveCapitalCache = null; -let liveCapitalLastFetch = 0; - -async function getLiveCapitalTelemetry() { - const now = Date.now(); - if (liveCapitalCache && now - liveCapitalLastFetch < 15000) { - return liveCapitalCache; - } - - // 1. Bitpanda Fusion Balance - const bitpandaBal = { - eur: 46.09, - usdc: 10.26, - pol: 436.46, - near: 12.59, - btt: 137271, - vet: 12.75, - }; - try { - const envFile = path.resolve(ROOT_DIR, "..", "trading-bot", ".env.quant"); - if (fs.existsSync(envFile)) { - const content = fs.readFileSync(envFile, "utf8"); - const m = content.match(/BITPANDA_API_KEY=([a-zA-Z0-9]+)/); - if (m?.[1]) { - const bpRes = await fetch("https://api.fusion.bitpanda.com/v1/account/balances", { - headers: { "x-api-key": m[1] }, - signal: AbortSignal.timeout(2500), - }); - if (bpRes.ok) { - const list = await bpRes.json(); - for (const item of list) { - const sym = (item.symbol || "").toLowerCase(); - const val = parseFloat(item.available || "0"); - if (sym === "eur") bitpandaBal.eur = val; - if (sym === "usdc") bitpandaBal.usdc = val; - if (sym === "pol") bitpandaBal.pol = val; - if (sym === "near") bitpandaBal.near = val; - if (sym === "btt") bitpandaBal.btt = val; - if (sym === "vet") bitpandaBal.vet = val; - } - } - } - } - } catch (_e) { - // Keep defaults - } - - // 2. Polymarket CLOB V2 Deposit Wallet (On-Chain Polygon RPC) - let polymarketPusd = 639.39; - const polymarketPolGas = 58.25; - try { - const depositWallet = "0x1d03562E91E71D65DFDec36cc8874be32135D4bC"; - const pUSDContract = "0xC011a7E12a19f7B1f670d46F03B03f3342E82DFB"; - const data = `0x70a08231000000000000000000000000${depositWallet.slice(2).toLowerCase()}`; - const rpcRes = await fetch("https://polygon-bor-rpc.publicnode.com", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - jsonrpc: "2.0", - id: 1, - method: "eth_call", - params: [{ to: pUSDContract, data }, "latest"], - }), - signal: AbortSignal.timeout(2500), - }); - if (rpcRes.ok) { - const rpcJson = await rpcRes.json(); - if (rpcJson?.result) { - polymarketPusd = Math.round((parseInt(rpcJson.result, 16) / 1e6) * 100) / 100; - } - } - } catch (_e) { - // Keep defaults - } - - // 3. Bank / Fiat Balances - const fiat = { n26: 5000.0, revolut: 3500.0, total_eur: 8500.0 }; - try { - const fiatFile = path.resolve(ROOT_DIR, "..", "trading-bot", "data", "fiat_balances.json"); - if (fs.existsSync(fiatFile)) { - const parsed = JSON.parse(fs.readFileSync(fiatFile, "utf8")); - fiat.n26 = parsed.n26?.cash || 5000.0; - fiat.revolut = parsed.revolut?.cash || 3500.0; - fiat.total_eur = fiat.n26 + fiat.revolut; - } - } catch (_e) {} - - const totalEur = - Math.round((fiat.total_eur + bitpandaBal.eur + polymarketPusd / 1.085 + 150) * 100) / 100; - const totalUsd = Math.round(totalEur * 1.085 * 100) / 100; - - liveCapitalCache = { - status: "LIVE FUNDED", - mode: "LIVE (ECHTGELD)", - polymarket: { - deposit_wallet: "0x1d03...D4bC", - full_wallet: "0x1d03562E91E71D65DFDec36cc8874be32135D4bC", - pusd_balance: polymarketPusd, - pol_gas: polymarketPolGas, - status: "FUNDED & READY (CLOB V2)", - }, - bitpanda: { - eur_cash: bitpandaBal.eur, - usdc: bitpandaBal.usdc, - pol: bitpandaBal.pol, - near: bitpandaBal.near, - btt: bitpandaBal.btt, - vet: bitpandaBal.vet, - status: "ONLINE (FUSION REST)", - }, - fiat_reserves: { - n26_eur: fiat.n26, - revolut_eur: fiat.revolut, - total_eur: fiat.total_eur, - }, - total_live_equity_usd: totalUsd, - total_live_equity_eur: totalEur, - circuit_breaker: "ENFORCED (TRIPLE-GATED)", - open_positions: 0, - }; - liveCapitalLastFetch = now; - return liveCapitalCache; -} - -const server = http.createServer(async (req, res) => { - const parsedUrl = new URL(req.url, `http://${req.headers.host || "localhost"}`); - const urlPath = parsedUrl.pathname; - const corsAllowed = applyCockpitCorsHeaders(req, res, urlPath, corsConfig); - - if (req.method === "OPTIONS") { - if (corsAllowed === false && req.headers.origin) { - res.writeHead(403); - res.end(); - return; - } - res.writeHead(204); - res.end(); - return; - } - - if ( - cockpit.requiresAuth && - !isPublicCockpitRoute(urlPath) && - !hasValidBearerToken(req.headers.authorization, cockpit.token) - ) { - res.writeHead(401, { "Content-Type": "application/json" }); - res.end(JSON.stringify({ error: "unauthorized" })); - return; - } - - // 1. API Health Check - if (urlPath === "/health" || urlPath === "/api/health") { - res.writeHead(200, { "Content-Type": "application/json" }); - res.end( - JSON.stringify({ - status: "healthy", - uptime: process.uptime(), - timestamp: new Date().toISOString(), - engine: "evOo Autonomous Platform 2.0.0", - cto: "Steve Jobs", - filesystem_integrity: "100% Physical", - }), - ); - return; - } - - // 2. API Status (Real System Telemetry) - if (urlPath === "/api/status") { - const archExists = fs.existsSync(path.join(ROOT_DIR, "ARCHITECTURE.md")); - const apexExists = fs.existsSync(path.join(ROOT_DIR, "apex-trading", "BLUEPRINT.md")); - const deployExists = fs.existsSync(path.join(ROOT_DIR, "deploy.sh")); - const mem = process.memoryUsage(); - const load = os.loadavg(); - - res.writeHead(200, { "Content-Type": "application/json" }); - res.end( - JSON.stringify({ - status: "online", - uptime_seconds: Math.round(process.uptime()), - load_avg_1m: load[0], - memory_rss_mb: Math.round((mem.rss / (1024 * 1024)) * 10) / 10, - memory_heap_mb: Math.round((mem.heapUsed / (1024 * 1024)) * 10) / 10, - verified_files: { - architecture: archExists, - apex_blueprint: apexExists, - deploy_script: deployExists, - }, - breathe_frequency: "1.04 Hz", - }), - ); - return; - } - - // 3. API Self-Improvement trigger (Real execution of scripts/self_improve.py) - if (urlPath === "/api/self-improve") { - const isSSE = - req.headers.accept?.includes("text/event-stream") || - parsedUrl.searchParams.get("stream") === "true"; - const scriptPath = path.join(ROOT_DIR, "scripts", "self_improve.py"); - - if (isSSE) { - res.writeHead(200, { - "Content-Type": "text/event-stream", - "Cache-Control": "no-cache", - Connection: "keep-alive", - }); - res.write( - `data: ${JSON.stringify({ type: "start", msg: "Starte physischen Selbstverbesserungs-Zyklus..." })}\n\n`, - ); - - const child = spawn("python3", [scriptPath], { cwd: ROOT_DIR }); - child.stdout.on("data", (data) => { - const lines = data.toString().split("\n"); - for (const line of lines) { - if (line.trim()) { - res.write(`data: ${JSON.stringify({ type: "log", msg: line })}\n\n`); - } - } - }); - child.stderr.on("data", (data) => { - const lines = data.toString().split("\n"); - for (const line of lines) { - if (line.trim()) { - res.write(`data: ${JSON.stringify({ type: "error", msg: line })}\n\n`); - } - } - }); - child.on("close", (code) => { - appendJournal({ - time: `${new Date().toISOString().substring(11, 16)} UTC`, - agent: "Self-Improve Loop", - type: code === 0 ? "success" : "error", - title: "Selbstverbesserungs-Zyklus", - detail: - code === 0 - ? "14 Systemdateien & Apex-Tests physisch verifiziert." - : "Fehler im Zyklus aufgetreten.", - }); - res.write(`data: ${JSON.stringify({ type: "done", exitCode: code })}\n\n`); - res.end(); - }); - return; - } - - // Standard POST / GET non-streaming response - const child = spawn("python3", [scriptPath], { cwd: ROOT_DIR }); - let stdout = ""; - let stderr = ""; - child.stdout.on("data", (d) => { - stdout += d.toString(); - }); - child.stderr.on("data", (d) => { - stderr += d.toString(); - }); - child.on("close", (code) => { - appendJournal({ - time: `${new Date().toISOString().substring(11, 16)} UTC`, - agent: "Self-Improve Loop", - type: code === 0 ? "success" : "error", - title: "Selbstverbesserungs-Zyklus", - detail: - code === 0 - ? "14 Systemdateien & Apex-Tests physisch verifiziert." - : "Fehler im Zyklus aufgetreten.", - }); - res.writeHead(code === 0 ? 200 : 500, { "Content-Type": "application/json" }); - res.end( - JSON.stringify({ - status: code === 0 ? "success" : "failed", - exitCode: code, - output: stdout, - error: stderr || null, - }), - ); - }); - return; - } - - // 4. API Casted Agents Listing - if (urlPath === "/api/agents") { - const agentsPath = path.join(ROOT_DIR, "orchestrator", "agents.json"); - if (fs.existsSync(agentsPath)) { - try { - const content = fs.readFileSync(agentsPath, "utf8"); - res.writeHead(200, { "Content-Type": "application/json" }); - res.end(content); - return; - } catch (_e) { - // Fallback - } - } - res.writeHead(500, { "Content-Type": "application/json" }); - res.end(JSON.stringify({ error: "agents.json not found" })); - return; - } - - // 4b. API Distributed Compute Cluster Telemetry - if (urlPath === "/api/cluster") { - const runnerPath = path.join(ROOT_DIR, "orchestrator", "agent_runner.py"); - const child = spawn("python3", [runnerPath, "--cluster", "--json"], { cwd: ROOT_DIR }); - let stdout = ""; - child.stdout.on("data", (d) => { - stdout += d.toString(); - }); - child.on("close", () => { - res.writeHead(200, { "Content-Type": "application/json" }); - res.end(stdout || JSON.stringify({ status: "unknown" })); - }); - return; - } - - // 4c. API Approvals (Executive Human-in-the-Loop) - if (urlPath === "/api/approvals") { - if (req.method === "POST") { - let body = ""; - req.on("data", (chunk) => { - body += chunk; - }); - req.on("end", () => { - try { - const { id, action } = JSON.parse(body); - const item = pendingApprovals.find((a) => a.id === id); - if (item) { - item.status = action === "approve" ? "approved" : "vetoed"; - item.decidedAt = new Date().toISOString(); - saveApprovals(pendingApprovals); - appendJournal({ - time: `${new Date().toISOString().substring(11, 16)} UTC`, - agent: "Operator (Ingo)", - type: action === "approve" ? "success" : "warn", - title: `Entscheidung: ${action.toUpperCase()}`, - detail: `${item.title} (${item.id}) registriert.`, - }); - } - res.writeHead(200, { "Content-Type": "application/json" }); - res.end(JSON.stringify({ ok: true, item })); - } catch { - res.writeHead(400, { "Content-Type": "application/json" }); - res.end(JSON.stringify({ ok: false, error: "Invalid payload" })); - } - }); - return; - } - res.writeHead(200, { "Content-Type": "application/json" }); - res.end(JSON.stringify({ ok: true, approvals: pendingApprovals })); - return; - } - - // 4d. API Overnight & Operational Journal - if (urlPath === "/api/journal") { - const journal = loadJournal(); - res.writeHead(200, { "Content-Type": "application/json" }); - res.end(JSON.stringify({ ok: true, events: journal })); - return; - } - - // 4e. API Trading-Bot Probe (Dual Mode: Paper & Live Capital) - if (urlPath === "/api/trading-bot") { - const liveData = await getLiveCapitalTelemetry(); - - // Query paper bot on port 8001 (or 8002) - const paperData = { - status: "ONLINE", - mode: "PAPER (SAFE)", - total_cash_usd: 1000.0, - total_equity_usd: 1000.0, - total_cash_eur: 921.66, - pnl_today: "+0.00%", - active_regime: "DONCHIAN_TREND_ADX", - max_drawdown: "0.00%", - circuit_breaker: riskKillSwitchActive ? "HALTED_BY_OPERATOR" : "ARMED", - kill_switch_active: riskKillSwitchActive, - open_positions: 0, - }; - - try { - const resp = await fetch("http://127.0.0.1:8001/api/portfolio", { - signal: AbortSignal.timeout(800), - }); - if (resp.ok) { - const portData = await resp.json(); - paperData.total_cash_usd = portData.total_cash || 1000.0; - paperData.total_equity_usd = portData.total_equity || 1000.0; - paperData.total_cash_eur = portData.total_cash_eur || 921.66; - paperData.open_positions = portData.open_positions || 0; - } - } catch { - // Keep defaults - } - - res.writeHead(200, { "Content-Type": "application/json" }); - res.end( - JSON.stringify({ - ok: true, - active_mode: currentTradingMode, - kill_switch_active: riskKillSwitchActive, - paper: paperData, - live: liveData, - }), - ); - return; - } - - // 4e-b. API Toggle Trading Mode (POST /api/trading-bot/mode) - if (urlPath === "/api/trading-bot/mode" && req.method === "POST") { - let body = ""; - req.on("data", (chunk) => { - body += chunk; - }); - req.on("end", () => { - try { - const { mode } = JSON.parse(body); - if (mode === "live" || mode === "paper") { - currentTradingMode = mode; - appendJournal({ - time: `${new Date().toISOString().substring(11, 16)} UTC`, - agent: "Operator (Ingo)", - type: mode === "live" ? "warn" : "info", - title: `Trading-Modus gewechselt: ${mode.toUpperCase()}`, - detail: - mode === "live" - ? "Live-Echtgeld Telemetrie (Polymarket CLOB V2 & Bitpanda) im Cockpit aktiviert." - : "Paper-Trading-Modus (Simulation) im Cockpit aktiviert.", - }); - } - res.writeHead(200, { "Content-Type": "application/json" }); - res.end(JSON.stringify({ ok: true, active_mode: currentTradingMode })); - } catch (e) { - res.writeHead(400, { "Content-Type": "application/json" }); - res.end(JSON.stringify({ ok: false, error: e.message })); - } - }); - return; - } - - // 4f. API Risk Kill-Switch Toggle - if (urlPath === "/api/risk/toggle-killswitch" && req.method === "POST") { - riskKillSwitchActive = !riskKillSwitchActive; - res.writeHead(200, { "Content-Type": "application/json" }); - res.end(JSON.stringify({ ok: true, killSwitchActive: riskKillSwitchActive })); - return; - } - - // 5. API Agent Dispatch (Real Task Execution) - if (urlPath === "/api/agents/dispatch" && req.method === "POST") { - let body = ""; - req.on("data", (chunk) => { - body += chunk; - }); - req.on("end", () => { - let agentId = "all"; - let task = ""; - try { - if (body) { - const parsed = JSON.parse(body); - if (parsed.agent) agentId = parsed.agent; - if (parsed.task) task = parsed.task; - } - } catch (_e) { - // Ignore JSON error and default - } - - const runnerPath = path.join(ROOT_DIR, "orchestrator", "agent_runner.py"); - const args = [runnerPath, "--agent", agentId, "--json"]; - if (task) { - args.push("--task", task); - } - - const child = spawn("python3", args, { cwd: ROOT_DIR }); - let stdout = ""; - let stderr = ""; - child.stdout.on("data", (d) => { - stdout += d.toString(); - }); - child.stderr.on("data", (d) => { - stderr += d.toString(); - }); - child.on("close", (code) => { - appendJournal({ - time: `${new Date().toISOString().substring(11, 16)} UTC`, - agent: agentId.toUpperCase(), - type: code === 0 ? "success" : "error", - title: `Agent Execution: ${agentId}`, - detail: `Task '${task || "audit"}' ausgeführt (Exit Code ${code}).`, - }); - try { - const data = JSON.parse(stdout); - res.writeHead(200, { "Content-Type": "application/json" }); - res.end(JSON.stringify({ success: code === 0, exitCode: code, data })); - } catch (_e) { - res.writeHead(200, { "Content-Type": "application/json" }); - res.end(JSON.stringify({ success: code === 0, exitCode: code, raw: stdout, stderr })); - } - }); - }); - return; - } - - // 6. API Hot-Reload trigger - if (urlPath === "/api/reload" && req.method === "POST") { - res.writeHead(200, { "Content-Type": "application/json" }); - res.end( - JSON.stringify({ - status: "success", - message: "Hot-Reload broadcast acknowledged. Dateisystem synchronisiert.", - }), - ); - return; - } - - // 7. Static File Serving with Directory Traversal Protection - let safeRelPath = path - .normalize(urlPath === "/" ? "/index.html" : urlPath) - .replace(/^(\.\.[/\\])+/, ""); - if (safeRelPath.startsWith("/")) { - safeRelPath = safeRelPath.slice(1); - } - - let resolvedPath = path.resolve(BASE_DIR, safeRelPath); - - // Fallback to ROOT_DIR only for safe markdown docs (ARCHITECTURE.md, BLUEPRINT.md) - const isWhitelistedDoc = [ - "ARCHITECTURE.md", - "apex-trading/BLUEPRINT.md", - "evoo.config.json", - ].includes(safeRelPath); - if (!fs.existsSync(resolvedPath) && isWhitelistedDoc) { - resolvedPath = path.resolve(ROOT_DIR, safeRelPath); - } - - // Directory Traversal Verification: must be inside BASE_DIR or explicitly whitelisted root file - const isInsideBase = resolvedPath.startsWith(BASE_DIR + path.sep) || resolvedPath === BASE_DIR; - const isInsideRootDoc = isWhitelistedDoc && resolvedPath.startsWith(ROOT_DIR + path.sep); - - if (!isInsideBase && !isInsideRootDoc) { - res.writeHead(403, { "Content-Type": "text/plain; charset=UTF-8" }); - res.end("403 Forbidden: Directory Traversal Denied"); - return; - } - - if (fs.existsSync(resolvedPath) && fs.statSync(resolvedPath).isFile()) { - const ext = path.extname(resolvedPath).toLowerCase(); - const contentType = MIME_TYPES[ext] || "application/octet-stream"; - res.writeHead(200, { "Content-Type": contentType }); - fs.createReadStream(resolvedPath).pipe(res); - } else { - res.writeHead(404, { "Content-Type": "text/plain; charset=UTF-8" }); - res.end(`404 Not Found: ${urlPath}`); - } -}); - -server.listen(PORT, COCKPIT_HOST, () => { - try { - fs.writeFileSync(path.join(ROOT_DIR, ".evoo_server.pid"), process.pid.toString(), "utf8"); - } catch (_e) { - // Ignore if write fails - } - const listenUrl = formatCockpitListenUrl(COCKPIT_HOST, PORT); - console.log(`[evOo Core] Server listening on ${listenUrl} (PID: ${process.pid})`); - console.log(`[evOo Core] Healthcheck available at ${listenUrl}/health`); - if (cockpit.requiresAuth) { - console.log("[evOo Core] Non-loopback bind: Bearer token required except /health"); - } - if (!corsConfig.loopbackBind && corsConfig.allowedOrigins.length === 0) { - console.log( - "[evOo Core] Non-loopback bind: set EVOO_COCKPIT_CORS_ORIGIN for browser cross-origin access", - ); - } -}); diff --git a/evoo-platform/styles.css b/evoo-platform/styles.css deleted file mode 100644 index 5c8f709..0000000 --- a/evoo-platform/styles.css +++ /dev/null @@ -1,1204 +0,0 @@ -/* ========================================================== - evOo Executive Cockpit — 2026/2027 State-of-the-Art Design System - Aesthetic: Classic Apple Precision Craftsmanship in Dark Obsidian - Typography: Human-scale, Generous Breathing Room & Legibility - Materials: Milled Smoked Sapphire Glass, Tactile Depth & Rich Contrast - ========================================================== */ - -:root { - /* Obsidian & Space Grey Palette */ - --bg-base: #0a0c10; - --bg-surface: #10131a; - --bg-card: rgba(20, 24, 34, 0.78); - --bg-card-hover: rgba(28, 34, 48, 0.88); - --bg-card-subtle: rgba(255, 255, 255, 0.035); - --bg-input: #121620; - - /* Borders & Highlights */ - --border-subtle: rgba(255, 255, 255, 0.09); - --border-hover: rgba(255, 255, 255, 0.18); - --border-highlight: rgba(56, 189, 248, 0.4); - --bevel-top: inset 0 1px 0 rgba(255, 255, 255, 0.12); - --bevel-btn: inset 0 1px 0 rgba(255, 255, 255, 0.25); - - /* High-Legibility Typography Colors */ - --text-main: #f8fafc; - --text-secondary: #cbd5e1; - --text-muted: #94a3b8; - --text-dim: #64748b; - - /* Apple Pro Typography Stack */ - --font-sans: - -apple-system, BlinkMacSystemFont, "SF Pro Display", "SF Pro Text", "Helvetica Neue", - "Plus Jakarta Sans", Inter, sans-serif; - --font-mono: "SF Mono", "JetBrains Mono", Menlo, Monaco, monospace; - - /* Jewel-Tone Accents */ - --accent-cyan: #38bdf8; - --accent-blue: #3b82f6; - --accent-emerald: #10b981; - --accent-emerald-deep: #059669; - --accent-amber: #f59e0b; - --accent-purple: #a855f7; - --accent-orange: #f97316; - --accent-red: #ef4444; - - /* Apple Smooth Curves */ - --radius-xl: 22px; - --radius-lg: 16px; - --radius-md: 12px; - --radius-sm: 8px; - --radius-pill: 9999px; -} - -* { - box-sizing: border-box; - margin: 0; - padding: 0; -} - -body { - background-color: var(--bg-base); - color: var(--text-main); - font-family: var(--font-sans); - min-height: 100vh; - display: flex; - flex-direction: column; - overflow-x: hidden; - font-size: 15.5px; - line-height: 1.6; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -/* Ambient Radial Mesh */ -.background-mesh { - position: fixed; - inset: 0; - background-image: - radial-gradient(circle at 10% 10%, rgba(56, 189, 248, 0.05) 0%, transparent 50%), - radial-gradient(circle at 90% 85%, rgba(168, 85, 247, 0.05) 0%, transparent 55%), - linear-gradient(to bottom, rgba(10, 12, 16, 0.8), rgba(10, 12, 16, 1)); - pointer-events: none; - z-index: 0; -} - -/* ========================================================== - 1. EXECUTIVE HEADER (PULSE STRIP) - ========================================================== */ -.executive-header { - position: sticky; - top: 0; - z-index: 100; - display: flex; - align-items: center; - justify-content: space-between; - padding: 1rem 2rem; - background: rgba(12, 15, 22, 0.82); - backdrop-filter: blur(24px) saturate(180%); - -webkit-backdrop-filter: blur(24px) saturate(180%); - border-bottom: 1px solid var(--border-subtle); - box-shadow: - 0 4px 24px -1px rgba(0, 0, 0, 0.5), - var(--bevel-top); - gap: 1.5rem; - flex-wrap: wrap; -} - -.header-left, -.header-center, -.header-right { - display: flex; - align-items: center; - gap: 0.9rem; -} - -.brand { - display: flex; - align-items: center; - gap: 0.65rem; -} - -.brand-name { - font-size: 1.35rem; - font-weight: 800; - letter-spacing: -0.035em; - color: #ffffff; -} - -.brand-highlight { - color: var(--accent-cyan); - text-shadow: 0 0 16px rgba(56, 189, 248, 0.4); -} - -.version-tag { - font-family: var(--font-mono); - font-size: 0.72rem; - font-weight: 700; - background: rgba(255, 255, 255, 0.06); - padding: 0.2rem 0.6rem; - border-radius: var(--radius-sm); - color: var(--text-muted); - border: 1px solid var(--border-subtle); - letter-spacing: 0.04em; -} - -/* Header Status Pills */ -.pulse-pill, -.topology-pill, -.trading-pill { - display: flex; - align-items: center; - gap: 0.6rem; - background: rgba(255, 255, 255, 0.04); - border: 1px solid var(--border-subtle); - box-shadow: var(--bevel-top); - padding: 0.42rem 0.95rem; - border-radius: var(--radius-pill); - font-size: 0.85rem; - transition: all 0.2s ease; -} - -.pulse-pill:hover, -.topology-pill:hover, -.trading-pill:hover { - background: rgba(255, 255, 255, 0.07); - border-color: var(--border-hover); -} - -.pulse-indicator { - width: 9px; - height: 9px; - border-radius: 50%; - background: var(--accent-emerald); - box-shadow: 0 0 10px var(--accent-emerald); - position: relative; -} - -.pulse-indicator.online::after { - content: ""; - position: absolute; - inset: -4px; - border-radius: 50%; - border: 1.5px solid var(--accent-emerald); - animation: pulse-ring 2.4s cubic-bezier(0.215, 0.61, 0.355, 1) infinite; -} - -@keyframes pulse-ring { - 0% { - transform: scale(0.95); - opacity: 0.8; - } - 50% { - transform: scale(1.7); - opacity: 0; - } - 100% { - transform: scale(0.95); - opacity: 0; - } -} - -.pill-icon { - font-size: 1rem; -} - -.pill-label { - color: var(--text-muted); - font-weight: 500; -} - -.pill-value { - color: var(--text-main); - font-weight: 600; - letter-spacing: -0.01em; -} - -/* Tactile Header Buttons */ -.btn { - display: inline-flex; - align-items: center; - gap: 0.55rem; - padding: 0.55rem 1.15rem; - border-radius: var(--radius-pill); - font-size: 0.88rem; - font-weight: 600; - cursor: pointer; - transition: all 0.18s cubic-bezier(0.16, 1, 0.3, 1); - border: 1px solid transparent; - font-family: var(--font-sans); - text-decoration: none; - box-shadow: - 0 2px 8px rgba(0, 0, 0, 0.25), - var(--bevel-btn); -} - -.btn:active { - transform: translateY(1px) scale(0.98); -} - -.btn-icon { - font-size: 0.95rem; -} - -.btn-primary { - background: linear-gradient(180deg, rgba(59, 130, 246, 0.25) 0%, rgba(37, 99, 235, 0.15) 100%); - color: #93c5fa; - border-color: rgba(59, 130, 246, 0.35); -} - -.btn-primary:hover { - background: linear-gradient(180deg, rgba(59, 130, 246, 0.35) 0%, rgba(37, 99, 235, 0.25) 100%); - border-color: rgba(59, 130, 246, 0.55); - box-shadow: - 0 4px 14px rgba(59, 130, 246, 0.25), - var(--bevel-btn); -} - -.btn-secondary { - background: linear-gradient(180deg, rgba(16, 185, 129, 0.22) 0%, rgba(5, 150, 105, 0.14) 100%); - color: #6ee7b7; - border-color: rgba(16, 185, 129, 0.35); -} - -.btn-secondary:hover { - background: linear-gradient(180deg, rgba(16, 185, 129, 0.32) 0%, rgba(5, 150, 105, 0.22) 100%); - border-color: rgba(16, 185, 129, 0.55); - box-shadow: - 0 4px 14px rgba(16, 185, 129, 0.25), - var(--bevel-btn); -} - -.btn-warning { - background: linear-gradient(180deg, rgba(245, 158, 11, 0.2) 0%, rgba(217, 119, 6, 0.12) 100%); - color: #fcd34d; - border-color: rgba(245, 158, 11, 0.35); -} - -.btn-warning:hover { - background: linear-gradient(180deg, rgba(245, 158, 11, 0.3) 0%, rgba(217, 119, 6, 0.2) 100%); - border-color: rgba(245, 158, 11, 0.55); -} - -.btn-warning.halted { - background: linear-gradient(180deg, #ef4444 0%, #dc2626 100%); - color: #ffffff; - border-color: rgba(255, 255, 255, 0.3); - box-shadow: - 0 4px 16px rgba(239, 68, 68, 0.4), - var(--bevel-btn); -} - -/* ========================================================== - 2. COCKPIT LAYOUT & APPLE SMOKED GLASS CARDS - ========================================================== */ -.cockpit-container { - position: relative; - z-index: 1; - flex: 1; - max-width: 1480px; - width: 100%; - margin: 0 auto; - padding: 2rem 2.25rem; - display: flex; - flex-direction: column; - gap: 2rem; -} - -.cockpit-main-layout { - display: grid; - grid-template-columns: 57fr 43fr; - gap: 2rem; -} - -.column-executive, -.column-agents { - display: flex; - flex-direction: column; - gap: 2rem; -} - -.cockpit-bottom-layout { - display: flex; - flex-direction: column; - gap: 2rem; -} - -.card { - background: linear-gradient(145deg, rgba(24, 29, 40, 0.82) 0%, rgba(14, 17, 24, 0.9) 100%); - backdrop-filter: blur(28px) saturate(190%); - -webkit-backdrop-filter: blur(28px) saturate(190%); - border: 1px solid var(--border-subtle); - box-shadow: - 0 10px 32px -4px rgba(0, 0, 0, 0.5), - var(--bevel-top); - border-radius: var(--radius-xl); - padding: 1.6rem 1.85rem; - display: flex; - flex-direction: column; - transition: all 0.25s cubic-bezier(0.16, 1, 0.3, 1); -} - -.card:hover { - border-color: var(--border-hover); - box-shadow: - 0 16px 44px -4px rgba(0, 0, 0, 0.65), - var(--bevel-top); - transform: translateY(-1.5px); -} - -.card-header { - display: flex; - justify-content: space-between; - align-items: flex-start; - margin-bottom: 1.35rem; - gap: 1rem; -} - -.header-title-group { - display: flex; - flex-direction: column; - gap: 0.25rem; -} - -.header-title-group h2 { - font-size: 1.25rem; - font-weight: 700; - letter-spacing: -0.025em; - color: #ffffff; -} - -.section-tag { - font-size: 0.76rem; - font-weight: 700; - letter-spacing: 0.08em; - color: var(--accent-cyan); - text-transform: uppercase; -} - -/* Badges */ -.badge { - font-size: 0.78rem; - font-weight: 600; - padding: 0.3rem 0.8rem; - border-radius: var(--radius-pill); - letter-spacing: 0.02em; - box-shadow: var(--bevel-top); -} - -.badge-amber { - background: rgba(245, 158, 11, 0.18); - color: #fcd34d; - border: 1px solid rgba(245, 158, 11, 0.35); -} - -.badge-emerald { - background: rgba(16, 185, 129, 0.18); - color: #6ee7b7; - border: 1px solid rgba(16, 185, 129, 0.35); -} - -.badge-cyan { - background: rgba(56, 189, 248, 0.18); - color: #7dd3fc; - border: 1px solid rgba(56, 189, 248, 0.35); -} - -.badge-purple { - background: rgba(168, 85, 247, 0.18); - color: #d8b4fe; - border: 1px solid rgba(168, 85, 247, 0.35); -} - -/* ========================================================== - 3. ZONE 1: EXECUTIVE INBOX & PENDING APPROVALS - ========================================================== */ -.approvals-list { - display: flex; - flex-direction: column; - gap: 1.15rem; -} - -.approval-item { - background: rgba(255, 255, 255, 0.035); - border: 1px solid rgba(245, 158, 11, 0.3); - box-shadow: - 0 4px 16px rgba(0, 0, 0, 0.25), - var(--bevel-top); - border-radius: var(--radius-lg); - padding: 1.35rem 1.45rem; - display: flex; - flex-direction: column; - gap: 0.95rem; - transition: all 0.2s cubic-bezier(0.16, 1, 0.3, 1); -} - -.approval-item:hover { - background: rgba(255, 255, 255, 0.055); - border-color: rgba(245, 158, 11, 0.55); - transform: translateY(-1px); -} - -.approval-meta { - display: flex; - justify-content: space-between; - align-items: center; - font-size: 0.82rem; - flex-wrap: wrap; - gap: 0.5rem; -} - -.approval-type-tag { - background: rgba(245, 158, 11, 0.22); - color: #fbbf24; - padding: 0.22rem 0.65rem; - border-radius: var(--radius-sm); - font-weight: 700; - text-transform: uppercase; - letter-spacing: 0.05em; - font-size: 0.74rem; -} - -.approval-author { - color: var(--text-muted); - font-weight: 600; -} - -.approval-time { - color: var(--text-dim); -} - -.approval-title { - font-size: 1.15rem; - font-weight: 700; - color: #ffffff; - letter-spacing: -0.015em; -} - -.approval-desc { - font-size: 0.94rem; - color: var(--text-secondary); - line-height: 1.6; -} - -.approval-metrics { - display: flex; - gap: 1.5rem; - font-size: 0.88rem; - padding: 0.65rem 1rem; - background: rgba(0, 0, 0, 0.35); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-md); - color: var(--text-muted); -} - -.approval-metrics strong { - color: #ffffff; - font-weight: 600; -} - -.approval-actions { - display: flex; - gap: 0.75rem; - margin-top: 0.35rem; - flex-wrap: wrap; -} - -.btn-approve { - background: linear-gradient(180deg, var(--accent-emerald) 0%, var(--accent-emerald-deep) 100%); - color: #ffffff; - border: 1px solid rgba(255, 255, 255, 0.25); - box-shadow: - 0 2px 10px rgba(16, 185, 129, 0.35), - var(--bevel-btn); - padding: 0.55rem 1.25rem; - border-radius: var(--radius-pill); - font-size: 0.9rem; - font-weight: 600; - cursor: pointer; - transition: all 0.18s ease; - font-family: var(--font-sans); -} - -.btn-approve:hover { - filter: brightness(1.1); - box-shadow: - 0 4px 16px rgba(16, 185, 129, 0.45), - var(--bevel-btn); -} - -.btn-approve:active { - transform: translateY(1px) scale(0.98); -} - -.btn-veto { - background: linear-gradient(180deg, rgba(239, 68, 68, 0.18) 0%, rgba(220, 38, 38, 0.1) 100%); - color: #fca5a5; - border: 1px solid rgba(239, 68, 68, 0.35); - padding: 0.55rem 1.15rem; - border-radius: var(--radius-pill); - font-size: 0.9rem; - font-weight: 600; - cursor: pointer; - transition: all 0.18s ease; - font-family: var(--font-sans); -} - -.btn-veto:hover { - background: linear-gradient(180deg, rgba(239, 68, 68, 0.28) 0%, rgba(220, 38, 38, 0.18) 100%); - border-color: rgba(239, 68, 68, 0.55); -} - -.btn-discuss { - background: linear-gradient(180deg, rgba(59, 130, 246, 0.18) 0%, rgba(37, 99, 235, 0.1) 100%); - color: #93c5fd; - border: 1px solid rgba(59, 130, 246, 0.3); - padding: 0.55rem 1.15rem; - border-radius: var(--radius-pill); - font-size: 0.9rem; - font-weight: 600; - cursor: pointer; - transition: all 0.18s ease; - font-family: var(--font-sans); -} - -.btn-discuss:hover { - background: linear-gradient(180deg, rgba(59, 130, 246, 0.28) 0%, rgba(37, 99, 235, 0.18) 100%); - border-color: rgba(59, 130, 246, 0.5); - color: #ffffff; -} - -/* ========================================================== - 4. ZONE 1: OPERATIONS JOURNAL (LETZTE 24h) - ========================================================== */ -.journal-timeline { - display: flex; - flex-direction: column; - gap: 0.9rem; -} - -.journal-entry { - display: flex; - gap: 1.1rem; - align-items: flex-start; - padding: 0.95rem 1.15rem; - background: rgba(255, 255, 255, 0.03); - border: 1px solid var(--border-subtle); - box-shadow: - 0 2px 8px rgba(0, 0, 0, 0.2), - var(--bevel-top); - border-radius: var(--radius-md); - font-size: 0.92rem; - transition: all 0.15s ease; -} - -.journal-entry:hover { - background: rgba(255, 255, 255, 0.05); - border-color: var(--border-hover); -} - -.entry-time { - font-family: var(--font-mono); - font-size: 0.78rem; - color: var(--text-dim); - white-space: nowrap; - padding-top: 0.15rem; - font-weight: 600; -} - -.entry-content { - flex: 1; -} - -.entry-header { - display: flex; - align-items: center; - gap: 0.65rem; - margin-bottom: 0.3rem; - flex-wrap: wrap; -} - -.entry-agent-chip { - font-size: 0.75rem; - font-weight: 700; - background: rgba(255, 255, 255, 0.08); - padding: 0.15rem 0.55rem; - border-radius: var(--radius-sm); - color: var(--text-secondary); -} - -.entry-title { - font-weight: 700; - color: #ffffff; - font-size: 0.96rem; -} - -.entry-detail { - color: var(--text-muted); - font-size: 0.88rem; - line-height: 1.55; -} - -/* ========================================================== - 5. ZONE 2: CTO DIRECTIVE & OPERATIONS BOARD - ========================================================== */ -.cto-body { - display: flex; - flex-direction: column; - gap: 1.25rem; -} - -.cto-profile { - display: flex; - align-items: center; - gap: 1rem; -} - -.cto-avatar { - width: 52px; - height: 52px; - border-radius: var(--radius-md); - background: linear-gradient(135deg, rgba(168, 85, 247, 0.3) 0%, rgba(126, 34, 206, 0.2) 100%); - color: #e9d5ff; - display: grid; - place-items: center; - font-weight: 800; - font-size: 1.2rem; - border: 1.5px solid rgba(168, 85, 247, 0.45); - box-shadow: - 0 4px 16px rgba(168, 85, 247, 0.25), - var(--bevel-top); -} - -.cto-name { - font-weight: 700; - font-size: 1.15rem; - color: #ffffff; - display: block; -} - -.cto-role { - font-size: 0.82rem; - color: var(--text-muted); - font-weight: 500; -} - -.cto-quote-box { - background: linear-gradient(135deg, rgba(168, 85, 247, 0.08) 0%, rgba(99, 102, 241, 0.04) 100%); - border: 1px solid rgba(168, 85, 247, 0.25); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.08); - padding: 1.2rem 1.4rem; - border-radius: var(--radius-md); -} - -.cto-quote { - font-style: italic; - font-size: 0.96rem; - color: var(--text-secondary); - line-height: 1.65; -} - -.cto-actions { - display: flex; - justify-content: space-between; - align-items: center; - flex-wrap: wrap; - gap: 0.85rem; -} - -.btn-inline { - padding: 0.5rem 1.15rem; - border-radius: var(--radius-pill); - font-size: 0.88rem; - font-weight: 600; - cursor: pointer; - border: 1px solid transparent; - font-family: var(--font-sans); - transition: all 0.18s ease; - box-shadow: var(--bevel-btn); -} - -.btn-purple { - background: linear-gradient(180deg, rgba(168, 85, 247, 0.25) 0%, rgba(126, 34, 206, 0.15) 100%); - color: #e9d5ff; - border-color: rgba(168, 85, 247, 0.4); -} - -.btn-purple:hover { - background: linear-gradient(180deg, rgba(168, 85, 247, 0.35) 0%, rgba(126, 34, 206, 0.25) 100%); - border-color: rgba(168, 85, 247, 0.6); - box-shadow: - 0 4px 16px rgba(168, 85, 247, 0.3), - var(--bevel-btn); -} - -.authority-pills { - display: flex; - gap: 0.5rem; -} - -.auth-pill { - font-size: 0.76rem; - font-weight: 600; - color: var(--text-dim); - background: rgba(255, 255, 255, 0.04); - border: 1px solid var(--border-subtle); - padding: 0.25rem 0.6rem; - border-radius: var(--radius-sm); -} - -/* Agent Operations Board (5 Agents) */ -.operations-hint { - font-size: 0.85rem; - color: var(--text-muted); - margin-bottom: 1rem; -} - -.agent-roster { - display: flex; - flex-direction: column; - gap: 0.85rem; -} - -.roster-item { - display: flex; - align-items: center; - gap: 1.1rem; - padding: 0.95rem 1.15rem; - background: rgba(255, 255, 255, 0.03); - border: 1px solid var(--border-subtle); - box-shadow: - 0 2px 8px rgba(0, 0, 0, 0.2), - var(--bevel-top); - border-radius: var(--radius-md); - cursor: pointer; - transition: all 0.2s cubic-bezier(0.16, 1, 0.3, 1); -} - -.roster-item:hover { - background: rgba(255, 255, 255, 0.065); - border-color: var(--border-hover); - transform: translateX(4px); - box-shadow: - 0 6px 20px rgba(0, 0, 0, 0.3), - var(--bevel-top); -} - -.roster-item:active { - transform: scale(0.985); -} - -.agent-avatar { - width: 44px; - height: 44px; - border-radius: var(--radius-md); - display: grid; - place-items: center; - font-weight: 800; - font-size: 0.95rem; - font-family: var(--font-sans); - flex-shrink: 0; - box-shadow: var(--bevel-top); -} - -.agent-avatar.architect { - background: linear-gradient(135deg, rgba(59, 130, 246, 0.25) 0%, rgba(37, 99, 235, 0.15) 100%); - color: #93c5fd; - border: 1px solid rgba(59, 130, 246, 0.4); -} -.agent-avatar.security { - background: linear-gradient(135deg, rgba(16, 185, 129, 0.25) 0%, rgba(5, 150, 105, 0.15) 100%); - color: #6ee7b7; - border: 1px solid rgba(16, 185, 129, 0.4); -} -.agent-avatar.devops { - background: linear-gradient(135deg, rgba(245, 158, 11, 0.25) 0%, rgba(217, 119, 6, 0.15) 100%); - color: #fcd34d; - border: 1px solid rgba(245, 158, 11, 0.4); -} -.agent-avatar.quant { - background: linear-gradient(135deg, rgba(168, 85, 247, 0.25) 0%, rgba(126, 34, 206, 0.15) 100%); - color: #d8b4fe; - border: 1px solid rgba(168, 85, 247, 0.4); -} -.agent-avatar.ux { - background: linear-gradient(135deg, rgba(249, 115, 22, 0.25) 0%, rgba(194, 65, 12, 0.15) 100%); - color: #fdba74; - border: 1px solid rgba(249, 115, 22, 0.4); -} - -.agent-meta { - flex: 1; - display: flex; - flex-direction: column; - gap: 0.15rem; -} - -.agent-name { - font-weight: 700; - font-size: 1.02rem; - color: #ffffff; -} - -.agent-role { - font-size: 0.85rem; - color: var(--text-secondary); -} - -.agent-node { - font-family: var(--font-mono); - font-size: 0.74rem; - color: var(--text-dim); -} - -.status-dot { - width: 9px; - height: 9px; - border-radius: 50%; - display: block; -} - -.status-dot.green { - background: var(--accent-emerald); - box-shadow: 0 0 8px var(--accent-emerald); -} -.status-dot.purple { - background: var(--accent-purple); - box-shadow: 0 0 8px var(--accent-purple); -} -.status-dot.orange { - background: var(--accent-orange); - box-shadow: 0 0 8px var(--accent-orange); -} - -/* ========================================================== - 6. ZONE 3 & 4: CLUSTER & APEX TRADING - ========================================================== */ -.cluster-grid { - display: grid; - grid-template-columns: repeat(3, 1fr); - gap: 1.25rem; -} - -.cluster-node { - background: rgba(255, 255, 255, 0.03); - border: 1px solid var(--border-subtle); - box-shadow: - 0 2px 8px rgba(0, 0, 0, 0.2), - var(--bevel-top); - border-radius: var(--radius-md); - padding: 1.15rem 1.25rem; - display: flex; - flex-direction: column; - gap: 0.75rem; - transition: all 0.2s ease; -} - -.cluster-node:hover { - background: rgba(255, 255, 255, 0.055); - border-color: var(--border-hover); -} - -.node-header { - display: flex; - align-items: center; - gap: 0.65rem; -} - -.node-status-dot { - width: 8px; - height: 8px; - border-radius: 50%; - background: var(--accent-emerald); - box-shadow: 0 0 8px var(--accent-emerald); -} - -.node-status-dot.amber { - background: var(--accent-amber); - box-shadow: 0 0 8px var(--accent-amber); -} - -.node-name { - font-weight: 700; - font-size: 0.98rem; - color: #ffffff; -} - -.node-type { - font-family: var(--font-mono); - font-size: 0.72rem; - color: var(--text-muted); - background: rgba(255, 255, 255, 0.06); - padding: 0.15rem 0.5rem; - border-radius: var(--radius-sm); - margin-left: auto; - font-weight: 600; -} - -.node-details { - display: flex; - flex-direction: column; - gap: 0.4rem; - font-size: 0.84rem; -} - -.detail-row { - display: flex; - justify-content: space-between; - gap: 0.5rem; - color: var(--text-muted); -} - -.code-val { - font-family: var(--font-mono); - color: var(--text-secondary); - font-weight: 500; -} - -.success-val { - color: var(--accent-emerald); - font-weight: 600; -} - -/* Header Action Cluster with Segmented Control */ -.header-action-cluster { - display: flex; - align-items: center; - gap: 0.85rem; - flex-wrap: wrap; -} - -.mode-segmented-control { - display: inline-flex; - align-items: center; - background: rgba(0, 0, 0, 0.45); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-pill); - padding: 0.25rem; - box-shadow: inset 0 2px 6px rgba(0, 0, 0, 0.4); - gap: 0.25rem; -} - -.segment-btn { - display: inline-flex; - align-items: center; - gap: 0.45rem; - padding: 0.4rem 0.95rem; - border-radius: var(--radius-pill); - font-size: 0.84rem; - font-weight: 600; - color: var(--text-muted); - background: transparent; - border: 1px solid transparent; - cursor: pointer; - transition: all 0.2s cubic-bezier(0.16, 1, 0.3, 1); - font-family: var(--font-sans); -} - -.segment-btn:hover { - color: var(--text-main); - background: rgba(255, 255, 255, 0.05); -} - -.segment-btn.active { - background: linear-gradient(180deg, rgba(255, 255, 255, 0.15) 0%, rgba(255, 255, 255, 0.06) 100%); - color: #ffffff; - border: 1px solid rgba(255, 255, 255, 0.18); - box-shadow: - 0 2px 8px rgba(0, 0, 0, 0.35), - var(--bevel-top); -} - -.segment-dot { - width: 7px; - height: 7px; - border-radius: 50%; - display: inline-block; -} - -.paper-dot { - background: var(--accent-cyan); - box-shadow: 0 0 6px var(--accent-cyan); -} - -.live-dot { - background: var(--accent-emerald); - box-shadow: 0 0 8px var(--accent-emerald); - animation: pulse-ring 2s infinite; -} - -/* Trading Metrics */ -.trading-metrics-grid { - display: grid; - grid-template-columns: repeat(4, 1fr); - gap: 1.25rem; -} - -.tmetric-card { - background: rgba(255, 255, 255, 0.03); - border: 1px solid var(--border-subtle); - box-shadow: - 0 2px 8px rgba(0, 0, 0, 0.2), - var(--bevel-top); - border-radius: var(--radius-md); - padding: 1.15rem 1.25rem; - display: flex; - flex-direction: column; - transition: all 0.2s ease; -} - -.tmetric-card:hover { - background: rgba(255, 255, 255, 0.055); - border-color: var(--border-hover); -} - -.tmetric-label { - font-size: 0.8rem; - font-weight: 600; - color: var(--text-muted); - margin-bottom: 0.35rem; - text-transform: uppercase; - letter-spacing: 0.04em; -} - -.tmetric-val { - font-family: var(--font-mono); - font-size: 1.55rem; - font-weight: 800; - color: #ffffff; - letter-spacing: -0.02em; -} - -.tmetric-val.positive { - color: var(--accent-emerald); - text-shadow: 0 0 16px rgba(16, 185, 129, 0.3); -} - -.tmetric-val.cyan-text { - color: var(--accent-cyan); -} - -.tmetric-sub { - font-size: 0.78rem; - color: var(--text-dim); - margin-top: 0.25rem; - font-weight: 500; -} - -/* ========================================================== - 7. LIVE TELEMETRY CONSOLE - ========================================================== */ -.card-console { - transition: all 0.25s ease; -} - -.card-console.collapsed .terminal-container { - display: none; -} - -.console-actions { - display: flex; - gap: 0.95rem; -} - -.btn-text { - background: none; - border: none; - color: var(--text-muted); - font-family: var(--font-sans); - font-size: 0.85rem; - font-weight: 600; - cursor: pointer; - padding: 0.2rem 0.5rem; - border-radius: var(--radius-sm); - transition: all 0.15s ease; -} - -.btn-text:hover { - color: #ffffff; - background: rgba(255, 255, 255, 0.05); -} - -.terminal-container { - background: #080a0e; - border: 1px solid var(--border-subtle); - border-radius: var(--radius-md); - box-shadow: inset 0 2px 8px rgba(0, 0, 0, 0.6); - padding: 1.15rem; - height: 220px; - overflow-y: auto; - font-family: var(--font-mono); - font-size: 0.85rem; - display: flex; - flex-direction: column; - gap: 0.4rem; -} - -.log-line { - line-height: 1.5; - word-break: break-word; -} - -.log-line.info { - color: #94a3b8; -} -.log-line.success { - color: #34d399; -} -.log-line.warning { - color: #fbbf24; -} -.log-line.error { - color: #f87171; -} -.log-line.highlight { - color: #c084fc; -} - -/* ========================================================== - 8. FOOTER - ========================================================== */ -.app-footer { - display: flex; - justify-content: space-between; - align-items: center; - padding: 1.1rem 2.25rem; - border-top: 1px solid var(--border-subtle); - font-size: 0.82rem; - color: var(--text-dim); - background: rgba(10, 12, 16, 0.95); -} - -/* ========================================================== - 9. RESPONSIVE QUERIES - ========================================================== */ -@media (max-width: 1080px) { - .cockpit-main-layout { - grid-template-columns: 1fr; - } - .cluster-grid, - .trading-metrics-grid { - grid-template-columns: 1fr 1fr; - } -} - -@media (max-width: 680px) { - .executive-header { - flex-direction: column; - align-items: stretch; - padding: 1rem; - } - .header-left, - .header-center, - .header-right { - justify-content: space-between; - flex-wrap: wrap; - } - .cluster-grid, - .trading-metrics-grid { - grid-template-columns: 1fr; - } - .cockpit-container { - padding: 1rem; - } -} diff --git a/evoo.config.json b/evoo.config.json deleted file mode 100644 index d5c732e..0000000 --- a/evoo.config.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "name": "evOo Autonomous Agent Platform", - "version": "2.0.0", - "permissions": { - "network": { - "allowed": true, - "scope": "*", - "description": "Full access to external and internal network endpoints" - }, - "filesystem": { - "allowed": true, - "scope": "*", - "description": "Full access to read, write, and execute across local filesystem" - }, - "sourcecode": { - "allowed": true, - "scope": "*", - "description": "Full authority to inspect, edit, refactor, and commit source code" - }, - "devices": { - "allowed": true, - "scope": "*", - "description": "Full access to connected local devices, interfaces, and tools" - }, - "agent_casting": { - "allowed": true, - "max_subagents": 20, - "description": "Authority to dynamically instantiate and manage sub-agent hierarchies" - }, - "pre_approved_actions": [ - "file_read", - "file_write", - "command_execution", - "subagent_spawn", - "self_refactor", - "network_request" - ] - }, - "execution_policy": { - "mode": "autonomous_action_first", - "multi_task_sequence": true, - "ask_user_policy": "on_negotiation_or_critical_boundary_only", - "block_on_routine_tasks": false, - "auto_proceed": true - }, - "self_improvement": { - "enabled": true, - "auto_eval_logs": true, - "auto_refactor_prompts": true, - "continuous_learning": true, - "feedback_loop": "autonomous_iteration" - }, - "cto_agent": { - "name": "Steve Jobs", - "role": "Chief Technology Officer (CTO)", - "authority": "full_executive", - "casting_policy": "cast_teams_on_demand", - "negotiation_protocol": "consensus_and_negotiation_basis" - } -} diff --git a/orchestrator/agent_runner.py b/orchestrator/agent_runner.py deleted file mode 100644 index edf91a0..0000000 --- a/orchestrator/agent_runner.py +++ /dev/null @@ -1,365 +0,0 @@ -#!/usr/bin/env python3 -""" -evOo Agent Orchestrator & Dispatcher -Executive Lead: CTO Steve Jobs -"Realität im Dateisystem vor Halluzination im Kopf." - -Manages dynamic execution of the 4 Casted Agents: -- Code Architect -- Security & QA Auditor -- DevOps Engineer -- Apex Quant Specialist -""" - -import os -import sys -import json -import time -import argparse -import subprocess -import urllib.request -import urllib.error -from pathlib import Path -from typing import Dict, Any, List, Optional - -ROOT_DIR = Path(__file__).resolve().parent.parent -CONFIG_PATH = Path(__file__).resolve().parent / "agents.json" - -class AgentOrchestrator: - def __init__(self, config_path: Path = CONFIG_PATH): - if config_path.exists(): - with open(config_path, "r", encoding="utf-8") as f: - self.config = json.load(f) - else: - self.config = {"agents": {}} - - def list_agents(self) -> Dict[str, Any]: - """Returns catalog of casted agents and their capabilities.""" - return self.config.get("agents", {}) - - def probe_compute_cluster(self) -> Dict[str, Any]: - """Probes local and LAN compute nodes (LM Studio, Ollama, remote Macs).""" - cluster_info = { - "strategy": "hybrid_local_and_lan", - "nodes": { - "local_lmstudio": {"status": "OFFLINE", "models": []}, - "local_ollama": {"status": "OFFLINE", "models": []}, - "mac_mini": {"status": "CONFIGURED_STANDBY", "endpoint": "http://mac-mini.local:1234/v1"}, - "macbook_air": {"status": "CONFIGURED_STANDBY", "endpoint": "http://macbook-air.local:1234/v1"} - } - } - try: - req = urllib.request.urlopen("http://127.0.0.1:1234/v1/models", timeout=0.6) - if req.getcode() == 200: - data = json.loads(req.read().decode('utf-8')) - models = [m.get("id") or m.get("name") for m in data.get("data", [])] - cluster_info["nodes"]["local_lmstudio"] = { - "status": "ONLINE (Apple Silicon Metal)", - "models": models[:2] - } - except Exception: - pass - - try: - req = urllib.request.urlopen("http://127.0.0.1:11434/api/tags", timeout=0.6) - if req.getcode() == 200: - data = json.loads(req.read().decode('utf-8')) - models = [m.get("name") for m in data.get("models", [])] - cluster_info["nodes"]["local_ollama"] = { - "status": "ONLINE", - "models": models[:2] - } - except Exception: - pass - - return cluster_info - - def resolve_compute_routing(self, preferred_node: str) -> Dict[str, Any]: - """Resolves target compute node with smart automatic failover.""" - cluster_info = self.probe_compute_cluster() - nodes = cluster_info.get("nodes", {}) - - # 1. Check preferred remote node - if preferred_node in nodes and "ONLINE" in nodes[preferred_node].get("status", ""): - return { - "node": preferred_node, - "status": "ONLINE", - "route": "DIRECT_LAN_NODE", - "model": nodes[preferred_node].get("models", ["default"])[0] if nodes[preferred_node].get("models") else "lan_default" - } - - # 2. Failover to local LM Studio - if "ONLINE" in nodes.get("local_lmstudio", {}).get("status", ""): - models = nodes["local_lmstudio"].get("models", []) - return { - "node": "primary_local (LM Studio)", - "status": "ACTIVE_FAILOVER", - "route": "LOCAL_METAL_ACCELERATED", - "model": models[0] if models else "qwen3-coder-30b-a3b", - "note": f"Preferred node '{preferred_node}' im Standby. Nahtlos auf Host Metal-GPU geroutet." - } - - # 3. Failover to local Ollama - if "ONLINE" in nodes.get("local_ollama", {}).get("status", ""): - models = nodes["local_ollama"].get("models", []) - return { - "node": "primary_local (Ollama)", - "status": "ACTIVE_FAILOVER", - "route": "LOCAL_OLLAMA_FALLBACK", - "model": models[0] if models else "gemma4:latest", - "note": "Auf lokalen Ollama-Knoten umgeleitet." - } - - # 4. Deterministic safety net - return { - "node": "deterministic_guard", - "status": "DETERMINISTIC_ACTIVE", - "route": "ZERO_PHANTOM_SAFETY_NET", - "model": "internal_rules_engine", - "note": "Deterministischer Sicherheitsmodus aktiv." - } - - def dispatch(self, agent_id: str, task: Optional[str] = None) -> Dict[str, Any]: - """Dispatches an action to a specified agent and returns physical results.""" - agents = self.list_agents() - if agent_id not in agents: - return { - "success": False, - "agent": agent_id, - "error": f"Unknown agent '{agent_id}'. Available: {list(agents.keys())}" - } - - agent_meta = agents[agent_id] - assigned_task = task or agent_meta.get("tasks", ["default"])[0] - routing = self.resolve_compute_routing(agent_meta.get("preferred_node", "primary_local")) - - start_time = time.time() - - # Route to deterministic execution handlers - if agent_id == "architect": - result = self._run_architect(assigned_task) - elif agent_id == "qa": - result = self._run_qa(assigned_task) - elif agent_id == "devops": - result = self._run_devops(assigned_task) - elif agent_id == "quant": - result = self._run_quant(assigned_task) - elif agent_id == "ux": - result = self._run_ux(assigned_task) - else: - result = {"status": "NOOP", "message": "No handler defined"} - - duration = round(time.time() - start_time, 4) - return { - "success": result.get("success", True), - "agent_id": agent_id, - "agent_name": agent_meta.get("name"), - "task": assigned_task, - "compute_routing": routing, - "duration_seconds": duration, - "result": result, - "timestamp": time.strftime("%Y-%m-%d %H:%M:%S UTC", time.gmtime()) - } - - def _run_architect(self, task: str) -> Dict[str, Any]: - """Architect evaluates modular structure and filesystem boundaries.""" - modules = { - "platform": (ROOT_DIR / "evoo-platform").is_dir(), - "apex_trading": (ROOT_DIR / "apex-trading").is_dir(), - "orchestrator": (ROOT_DIR / "orchestrator").is_dir(), - "templates": (ROOT_DIR / "templates").is_dir(), - "architecture_doc": (ROOT_DIR / "ARCHITECTURE.md").is_file() - } - all_ok = all(modules.values()) - return { - "success": all_ok, - "modular_boundaries": modules, - "clean_architecture": "VERIFIED" if all_ok else "INCOMPLETE", - "findings": "All core pillars are orthogonally separated on disk." if all_ok else "Missing core directories." - } - - def _run_qa(self, task: str) -> Dict[str, Any]: - """QA Auditor runs physical integrity check and regression suite.""" - script = ROOT_DIR / "scripts" / "self_improve.py" - if not script.exists(): - return {"success": False, "error": "scripts/self_improve.py not found"} - - res = subprocess.run([sys.executable, str(script)], capture_output=True, text=True, cwd=str(ROOT_DIR)) - return { - "success": res.returncode == 0, - "exit_code": res.returncode, - "output": res.stdout.strip(), - "errors": res.stderr.strip() if res.returncode != 0 else None, - "zero_phantom_check": "PASS" if res.returncode == 0 else "FAIL" - } - - def _run_devops(self, task: str) -> Dict[str, Any]: - """DevOps audits server liveness, port 3088, and PID status.""" - pid_file = ROOT_DIR / ".evoo_server.pid" - pid = None - is_running = False - if pid_file.exists(): - try: - pid = int(pid_file.read_text().strip()) - # Check if process is alive (signal 0) - os.kill(pid, 0) - is_running = True - except (ValueError, OSError): - is_running = False - - http_status = None - latency_ms = None - try: - t0 = time.time() - req = urllib.request.urlopen("http://127.0.0.1:3088/health", timeout=1.5) - http_status = req.getcode() - latency_ms = round((time.time() - t0) * 1000, 2) - except Exception as e: - http_status = f"DOWN ({e.__class__.__name__})" - - return { - "success": is_running or (http_status == 200), - "pid": pid, - "process_active": is_running, - "port_3088_status": http_status, - "http_latency_ms": latency_ms, - "status": "HEALTHY" if (is_running and http_status == 200) else "DEGRADED" - } - - def _run_quant(self, task: str) -> Dict[str, Any]: - """Apex Quant Specialist runs trading engine tests and risk simulation.""" - test_file = ROOT_DIR / "apex-trading" / "test_trading.py" - if not test_file.exists(): - return {"success": False, "error": "apex-trading/test_trading.py missing"} - - res = subprocess.run([sys.executable, str(test_file)], capture_output=True, text=True, cwd=str(ROOT_DIR)) - - # Test simulated tick through engine - sim_result = {} - try: - sys_path_added = False - src_path = str(ROOT_DIR / "apex-trading" / "src") - if src_path not in sys.path: - sys.path.insert(0, src_path) - sys_path_added = True - - from engine.trader import ApexEngine - config_file = str(ROOT_DIR / "apex-trading" / "config" / "trading.config.json") - engine = ApexEngine(config_file) - - # Run tick with strong upward breakout (>1.5% above 20-period SMA) - prices = [100.0 + (i * 2.0) for i in range(25)] - tick = engine.process_tick("BTC/USDC", prices, [500] * 25) - sim_result = { - "simulation_status": tick.get("status"), - "action": tick.get("action"), - "guardrail_status": tick.get("risk_status") - } - except Exception as e: - sim_result = {"error": str(e)} - - # 1. Check if external trading-bot API is alive on port 8000 - trading_api_status = "OFFLINE" - live_api_metrics = None - try: - req = urllib.request.urlopen("http://127.0.0.1:8000/api/health", timeout=1.0) - if req.getcode() == 200: - trading_api_status = "ONLINE (http://127.0.0.1:8000)" - # Try reading live status - status_req = urllib.request.urlopen("http://127.0.0.1:8000/api/status", timeout=1.0) - live_api_metrics = json.loads(status_req.read().decode('utf-8')) - except Exception: - trading_api_status = "STANDBY (Independent Service Offline)" - - return { - "success": res.returncode == 0 and sim_result.get("simulation_status") == "EXECUTED", - "unit_tests_passing": res.returncode == 0, - "simulation": sim_result, - "trading_bot_api_bridge": { - "status": trading_api_status, - "architecture": "Microservice REST API (Port 8000)", - "live_metrics": live_api_metrics - }, - "circuit_breaker_enforced": True - } - - def _run_ux(self, task: str) -> Dict[str, Any]: - """UX Designer audits design system integrity, 3-pillar shell, and responsive tokens.""" - shell_path = ROOT_DIR / "apps" / "web" / "src" / "pages" / "Shell.tsx" - tokens_path = ROOT_DIR / "packages" / "ui-tokens" / "src" / "tokens.css" - platform_css = ROOT_DIR / "evoo-platform" / "styles.css" - - shell_exists = shell_path.exists() - has_3_pillars = False - if shell_exists: - content = shell_path.read_text(encoding="utf-8") - has_3_pillars = "activeNavPillar" in content and "heute" in content and "team" in content and "projekte" in content - - tokens_exist = tokens_path.exists() or platform_css.exists() - all_ok = shell_exists and has_3_pillars and tokens_exist - - return { - "success": all_ok, - "design_system_integrity": "VERIFIED" if all_ok else "DEGRADED", - "pillar_navigation": "ACTIVE (Heute, Team, Projekte)" if has_3_pillars else "INCOMPLETE", - "mobile_first_ergonomics": "PASS", - "token_library_present": tokens_exist, - "findings": "3-Pillar Navigation & Dark-Mode Design System 100% physisch verifiziert." if all_ok else "UI Komponenten oder Tokens unvollständig." - } - -def main(): - parser = argparse.ArgumentParser(description="evOo Agent Orchestrator") - parser.add_argument("--agent", choices=["architect", "qa", "devops", "quant", "ux", "all"], default="all") - parser.add_argument("--task", default=None, help="Specific task to run") - parser.add_argument("--cluster", action="store_true", help="Probe compute cluster nodes (LM Studio, Ollama, LAN Macs)") - parser.add_argument("--json", action="store_true", help="Output pure JSON") - args = parser.parse_args() - - orchestrator = AgentOrchestrator() - - if args.cluster: - cluster = orchestrator.probe_compute_cluster() - if args.json: - print(json.dumps(cluster, indent=2)) - else: - print("=" * 60) - print(" evOo Distributed Compute Cluster — Resource Telemetry") - print("=" * 60) - for node, info in cluster.get("nodes", {}).items(): - status = info.get("status") - models = ", ".join(info.get("models", [])) or "None loaded" - print(f"[{status}] Node: {node:<18} | Models: {models}") - print("=" * 60) - sys.exit(0) - - if args.agent == "all": - results = {} - all_passed = True - for agent_id in ["architect", "qa", "devops", "quant", "ux"]: - res = orchestrator.dispatch(agent_id, args.task) - results[agent_id] = res - if not res.get("success"): - all_passed = False - - if args.json: - print(json.dumps(results, indent=2)) - else: - print("=" * 60) - print(" evOo Casted Agent Network — Executive Dispatch") - print("=" * 60) - for aid, r in results.items(): - status = "✓ PASS" if r.get("success") else "✗ FAIL" - print(f"[{status}] Agent: {r.get('agent_name'):<25} ({r.get('duration_seconds')}s)") - print("=" * 60) - sys.exit(0 if all_passed else 1) - else: - res = orchestrator.dispatch(args.agent, args.task) - if args.json: - print(json.dumps(res, indent=2)) - else: - print(f"Agent: {res.get('agent_name')} | Success: {res.get('success')}") - print(json.dumps(res.get("result"), indent=2)) - sys.exit(0 if res.get("success") else 1) - -if __name__ == "__main__": - main() diff --git a/orchestrator/agents.json b/orchestrator/agents.json deleted file mode 100644 index f5dc431..0000000 --- a/orchestrator/agents.json +++ /dev/null @@ -1,104 +0,0 @@ -{ - "orchestrator_version": "2.1.0", - "ecosystem": "evOo Autonomous Agent Platform", - "lead_cto": "Steve Jobs", - "compute_cluster": { - "enabled": true, - "strategy": "smart_failover_hierarchical", - "failover_order": ["preferred_node", "local_lmstudio", "local_ollama", "deterministic_guard"], - "primary_local": { - "name": "evOo Host Machine (LM Studio / Metal)", - "lmstudio_url": "http://127.0.0.1:1234/v1", - "ollama_url": "http://127.0.0.1:11434/v1", - "active_models": ["qwen3-coder-30b-a3b", "gemma4:latest"], - "characteristics": "Direkte Apple Silicon Metal Inferenz, primärer Kontrollknoten" - }, - "distributed_nodes": { - "mac_mini": { - "name": "Mac mini (Strategic 24/7 Powerhouse)", - "host": "mac-mini.local", - "url": "http://mac-mini.local:1234/v1", - "recommended_models": ["qwen2.5:32b", "llama3.3:70b-q4", "deepseek-r1:14b"], - "assigned_roles": ["architect", "cto-steve-jobs"], - "characteristics": "Aktiv gekühlt (Lüfter), ideal für schwere 24/7 Deep-Think Modelle", - "thermal_profile": "unlimited" - }, - "macbook_air": { - "name": "MacBook Air (Rapid Lightweight Worker)", - "host": "macbook-air.local", - "url": "http://macbook-air.local:1234/v1", - "recommended_models": ["qwen2.5-coder:7b", "llama3.2:3b", "phi3.5:3.8b"], - "assigned_roles": ["qa", "devops"], - "characteristics": "Passiv gekühlt (lüfterlos), optimiert für 3B-7B Modelle ohne Hitze/Throttling", - "thermal_profile": "efficiency_optimized" - } - } - }, - "agents": { - "architect": { - "name": "Code Architect", - "slug": "code-architect", - "avatar": "CA", - "color": "var(--primary-glow)", - "role": "Moduldesign & Clean Architecture", - "scope": ["architecture", "filesystem", "modularity"], - "preferred_node": "mac_mini", - "fallback_node": "primary_local", - "target_model_class": "heavy_reasoning", - "description": "Plant orthogonale Modulgrenzen. Nutzt vorzugsweise die Rechenpower des Mac mini für tiefes architektonisches Reasoning.", - "tasks": ["audit_structure", "verify_dependencies"] - }, - "qa": { - "name": "Security & QA Auditor", - "slug": "qa-auditor", - "avatar": "QA", - "color": "var(--status-green)", - "role": "Integritätsprüfung & Zero-Phantom", - "scope": ["security", "lint", "tests", "zero_phantom"], - "preferred_node": "macbook_air", - "fallback_node": "primary_local", - "target_model_class": "fast_coding", - "description": "Führt Sicherheits- und Syntaxaudits aus. Nutzt vorzugsweise das MacBook Air für schnelle parallele 7B-Checks.", - "tasks": ["verify_all", "check_security", "check_phantoms"] - }, - "devops": { - "name": "DevOps Engineer", - "slug": "devops-engineer", - "avatar": "DO", - "color": "var(--status-blue)", - "role": "Hot-Reload & Service Liveness", - "scope": ["processes", "ports", "deployment", "liveness"], - "preferred_node": "primary_local", - "fallback_node": "primary_local", - "target_model_class": "system_ops", - "description": "Verwaltet Prozesse, Ports und Hot-Reloads direkt auf dem Host.", - "tasks": ["check_liveness", "audit_resources", "restart_service"] - }, - "quant": { - "name": "Apex Quant Specialist", - "slug": "apex-quant", - "avatar": "AP", - "color": "var(--accent-purple)", - "role": "Algorithmic Risk & High-Speed Execution", - "scope": ["trading", "risk_guard", "momentum", "quant"], - "preferred_node": "primary_local", - "fallback_node": "primary_local", - "target_model_class": "deterministic_math", - "description": "Überwacht Trading-Guardrails und die HTTP-Bridge zum autonomen trading-bot auf Port 8000.", - "tasks": ["test_guardrails", "run_tick_simulation"] - }, - "ux": { - "name": "UX Designer", - "slug": "ux-designer", - "avatar": "UX", - "color": "var(--accent-orange, #f97316)", - "role": "Lead Product & UX/UI Designer", - "scope": ["ui", "ux", "design_tokens", "mobile_ergonomics", "visual_hierarchy"], - "preferred_node": "macbook_air", - "fallback_node": "primary_local", - "target_model_class": "fast_coding", - "description": "Entwirft responsive Mobile-First Interfaces, quantitative Telemetrie-Dashboards und das konsistente Designsystem.", - "tasks": ["audit_ui", "verify_design_tokens"] - } - } -} diff --git a/packages/adapters/src/remote-mcp.test.ts b/packages/adapters/src/remote-mcp.test.ts index 859d4a0..8bbb7e8 100644 --- a/packages/adapters/src/remote-mcp.test.ts +++ b/packages/adapters/src/remote-mcp.test.ts @@ -51,14 +51,14 @@ describe("remote MCP URL policy", () => { assertSafeRemoteUrl(magicDns, async () => [{ address: "100.64.1.2", family: 4 as const }]), ).resolves.toEqual(new URL(magicDns)); - const safeLookup = createSafeLookup(async () => [{ address: "100.119.57.55", family: 4 }]); + const safeLookup = createSafeLookup(async () => [{ address: "100.100.100.100", family: 4 }]); const result = await new Promise<{ address: string; family?: number }>((resolve, reject) => { safeLookup("box.tail12345.ts.net", { family: 0, all: false }, (error, address, family) => { if (error) reject(error); else resolve({ address: String(address), family }); }); }); - expect(result).toEqual({ address: "100.119.57.55", family: 4 }); + expect(result).toEqual({ address: "100.100.100.100", family: 4 }); }); it("allows the IPv6 half of a MagicDNS answer", async () => { diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 1ddff54..0b9a281 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -45,7 +45,6 @@ export * from "./text-direction.js"; export * from "./thread-message-updates.js"; export * from "./thread-subscription.js"; export * from "./tool-activity.js"; -export * from "./trading-bot-status.js"; export * from "./voice-chat-command.js"; export * from "./voice-labels.js"; export * from "./workspace-focus.js"; diff --git a/packages/core/src/trading-bot-status.test.ts b/packages/core/src/trading-bot-status.test.ts deleted file mode 100644 index 38f0438..0000000 --- a/packages/core/src/trading-bot-status.test.ts +++ /dev/null @@ -1,141 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { - coerceTradingBotStatusView, - interpretTradingBotProxyResponse, - mapTradingBotStatus, - parseTradingBotOrigin, - resolveTradingBotOrigin, - sanitizeTradingBotError, - tradingBotBackoffMs, - tradingBotStatusUrl, -} from "./trading-bot-status.js"; - -describe("parseTradingBotOrigin", () => { - it("treats an empty value as unset and keeps the origin without a path", () => { - expect(parseTradingBotOrigin(undefined)).toEqual({ kind: "unset" }); - expect(parseTradingBotOrigin(" ")).toEqual({ kind: "unset" }); - expect(parseTradingBotOrigin(" http://127.0.0.1:8000/bot/ ")).toEqual({ - kind: "origin", - origin: "http://127.0.0.1:8000", - }); - expect(tradingBotStatusUrl("http://127.0.0.1:8000")).toBe( - "http://127.0.0.1:8000/bot/api/status", - ); - }); - - it("rejects credentials and non-http schemes without echoing the value", () => { - expect(parseTradingBotOrigin("http://user:secret@127.0.0.1:8000").kind).toBe("invalid"); - expect(parseTradingBotOrigin("file:///tmp/status").kind).toBe("invalid"); - expect(parseTradingBotOrigin("not a url").kind).toBe("invalid"); - expect(resolveTradingBotOrigin("")).toBeUndefined(); - expect(() => resolveTradingBotOrigin("javascript:alert(1)")).toThrow( - /^Invalid TRADING_BOT_URL$/, - ); - }); -}); - -describe("mapTradingBotStatus", () => { - it("maps live, paper, and dry-run badges without defaulting to live", () => { - expect(mapTradingBotStatus({ mode: "LIVE" })).toMatchObject({ mode: "live", kind: "live" }); - expect(mapTradingBotStatus({ badge: "DRY RUN" })).toMatchObject({ - mode: "paper", - kind: "dry_run", - }); - expect(mapTradingBotStatus({ mode: "Paper Sim" })).toMatchObject({ - mode: "paper", - kind: "paper", - }); - expect(mapTradingBotStatus({ dry_run: true })).toMatchObject({ kind: "dry_run" }); - expect(mapTradingBotStatus({ live: true })).toMatchObject({ kind: "live" }); - expect(mapTradingBotStatus({ status: "ok" })).toBeNull(); - expect(mapTradingBotStatus({})).toBeNull(); - expect(mapTradingBotStatus(null)).toBeNull(); - expect(mapTradingBotStatus({ live: false })).toBeNull(); - }); - - it("refuses a body that claims live and dry-run together", () => { - expect(mapTradingBotStatus({ mode: "live", dry_run: true })).toBeNull(); - expect(mapTradingBotStatus({ live: true, paper: true })).toBeNull(); - }); - - it("reads a nested mode and safe subtitle fields", () => { - expect( - mapTradingBotStatus({ - data: { mode: "DRY RUN", adapters: ["a", "b", "c"], last_error: "adapter timeout" }, - }), - ).toEqual({ - mode: "paper", - kind: "dry_run", - adapterCount: 3, - lastError: "adapter timeout", - }); - expect(mapTradingBotStatus({ mode: "paper", adapter_count: "4" })?.adapterCount).toBe(4); - }); - - it("drops secret-like errors and still reports the mode", () => { - expect( - mapTradingBotStatus({ mode: "paper", last_error: "Bearer sk-test-secret" })?.lastError, - ).toBeNull(); - expect( - mapTradingBotStatus({ mode: "live", error: "failed http://10.0.0.8/status" })?.lastError, - ).toBeNull(); - expect(sanitizeTradingBotError("ok")).toBeNull(); - const long = "x".repeat(120); - expect(sanitizeTradingBotError(long)?.endsWith("…")).toBe(true); - expect(sanitizeTradingBotError(long)?.length).toBe(80); - }); -}); - -describe("interpretTradingBotProxyResponse", () => { - it("keeps unconfigured, offline, unknown, and a live read distinct", () => { - expect(interpretTradingBotProxyResponse(200, { configured: false })).toEqual({ - state: "unconfigured", - }); - expect(interpretTradingBotProxyResponse(502, { configured: true, reachable: false })).toEqual({ - state: "offline", - }); - expect( - interpretTradingBotProxyResponse(200, { configured: true, reachable: true, status: null }), - ).toEqual({ state: "unknown" }); - expect(interpretTradingBotProxyResponse(401, { error: "Unauthorized" })).toEqual({ - state: "offline", - }); - expect( - interpretTradingBotProxyResponse(200, { - configured: true, - reachable: true, - status: { mode: "live", kind: "live", adapterCount: 1, lastError: null }, - }), - ).toEqual({ - state: "ready", - status: { mode: "live", kind: "live", adapterCount: 1, lastError: null }, - }); - }); - - it("strips an unsafe error from an otherwise valid proxy status", () => { - const snapshot = interpretTradingBotProxyResponse(200, { - configured: true, - reachable: true, - status: { - mode: "paper", - kind: "dry_run", - adapterCount: null, - lastError: "token=super-secret", - }, - }); - expect(snapshot).toEqual({ - state: "ready", - status: { mode: "paper", kind: "dry_run", adapterCount: null, lastError: null }, - }); - expect(coerceTradingBotStatusView({ mode: "live", kind: "paper" })).toBeNull(); - }); -}); - -describe("tradingBotBackoffMs", () => { - it("doubles the interval and then caps it", () => { - expect(tradingBotBackoffMs(20_000, 1, 120_000)).toBe(20_000); - expect(tradingBotBackoffMs(20_000, 2, 120_000)).toBe(40_000); - expect(tradingBotBackoffMs(20_000, 3, 120_000)).toBe(80_000); - expect(tradingBotBackoffMs(20_000, 4, 120_000)).toBe(120_000); - }); -}); diff --git a/packages/core/src/trading-bot-status.ts b/packages/core/src/trading-bot-status.ts deleted file mode 100644 index a282ec8..0000000 --- a/packages/core/src/trading-bot-status.ts +++ /dev/null @@ -1,297 +0,0 @@ -/** Shared Tradingbot status contract. The browser and the API proxy both use this. */ - -export const TRADING_BOT_STATUS_PATH = "/bot/api/status"; -export const TRADING_BOT_PROXY_PATH = "/api/trading-bot/status"; -export const TRADING_BOT_POLL_INTERVAL_MS = 20_000; -export const TRADING_BOT_POLL_MAX_BACKOFF_MS = 120_000; -export const TRADING_BOT_STATUS_TIMEOUT_MS = 5_000; -export const TRADING_BOT_STATUS_MAX_BYTES = 65_536; - -const MAX_ORIGIN_LENGTH = 200; -const MAX_ERROR_LENGTH = 80; -const MAX_ADAPTER_COUNT = 10_000; - -const LIVE_TOKENS = new Set(["live", "live_trading", "real", "production"]); -const PAPER_TOKENS = new Set(["paper", "paper_sim", "paper_trading", "simulation", "sim"]); -const DRY_RUN_TOKENS = new Set(["dry_run", "dryrun", "dry"]); - -const MODE_STRING_KEYS = [ - "mode", - "trading_mode", - "tradingMode", - "run_mode", - "runMode", - "execution_mode", - "executionMode", - "badge", - "status", -] as const; - -const NESTED_KEYS = ["data", "bot", "state", "result", "status"] as const; - -const ADAPTER_COUNT_KEYS = [ - "adapterCount", - "adapter_count", - "adaptersCount", - "adapters_count", - "n_adapters", -] as const; - -const ERROR_KEYS = [ - "lastError", - "last_error", - "last_error_message", - "errorMessage", - "error", -] as const; - -const EMPTY_ERRORS = new Set(["ok", "none", "null", "undefined", "n/a", "-"]); - -const UNSAFE_ERROR = /bearer\s|api[_-]?key|password|secret|token|authorization|cookie|https?:\/\//i; - -export type TradingBotKind = "live" | "paper" | "dry_run"; - -export type TradingBotStatusView = { - mode: "live" | "paper"; - kind: TradingBotKind; - adapterCount: number | null; - lastError: string | null; -}; - -export type TradingBotOrigin = - | { kind: "unset" } - | { kind: "invalid" } - | { kind: "origin"; origin: string }; - -export type TradingBotSnapshot = - | { state: "unconfigured" } - | { state: "offline" } - | { state: "unknown" } - | { state: "ready"; status: TradingBotStatusView }; - -/** Accept a bare http(s) origin. Credentials and other schemes are rejected. */ -export function parseTradingBotOrigin(value: string | undefined): TradingBotOrigin { - const trimmed = value?.trim() ?? ""; - if (!trimmed) return { kind: "unset" }; - if (trimmed.length > MAX_ORIGIN_LENGTH || /\s/.test(trimmed) || hasControlChar(trimmed)) { - return { kind: "invalid" }; - } - let url: URL; - try { - url = new URL(trimmed); - } catch { - return { kind: "invalid" }; - } - if (url.protocol !== "http:" && url.protocol !== "https:") return { kind: "invalid" }; - if (url.username || url.password || !url.hostname) return { kind: "invalid" }; - return { kind: "origin", origin: url.origin }; -} - -/** Throw when the operator set a value that must not be fetched. Unset stays unset. */ -export function resolveTradingBotOrigin(value: string | undefined): string | undefined { - const parsed = parseTradingBotOrigin(value); - if (parsed.kind === "invalid") throw new Error("Invalid TRADING_BOT_URL"); - return parsed.kind === "origin" ? parsed.origin : undefined; -} - -export function tradingBotStatusUrl(origin: string): string { - return `${origin}${TRADING_BOT_STATUS_PATH}`; -} - -export function tradingBotBackoffMs( - intervalMs: number, - failureCount: number, - maxBackoffMs: number, -): number { - const exponent = Math.min(Math.max(failureCount - 1, 0), 6); - return Math.min(intervalMs * 2 ** exponent, maxBackoffMs); -} - -/** Map a Tradingbot `/bot/api/status` body. Unknown or conflicting modes return null. */ -export function mapTradingBotStatus(body: unknown): TradingBotStatusView | null { - const record = asRecord(body); - if (!record) return null; - const kind = resolveKind(readSignals(record)); - if (!kind) return null; - return { - mode: kind === "live" ? "live" : "paper", - kind, - adapterCount: readAdapterCount(record), - lastError: readLastError(record), - }; -} - -/** Accept a proxy payload field. Drops unsafe error text instead of dropping the mode. */ -export function coerceTradingBotStatusView(value: unknown): TradingBotStatusView | null { - const record = asRecord(value); - if (!record) return null; - const { mode, kind } = record; - if (mode !== "live" && mode !== "paper") return null; - if (kind !== "live" && kind !== "paper" && kind !== "dry_run") return null; - if ((mode === "live") !== (kind === "live")) return null; - if (record.adapterCount !== null && parseCount(record.adapterCount) === null) return null; - if (record.lastError !== null && typeof record.lastError !== "string") return null; - return { - mode, - kind, - adapterCount: record.adapterCount === null ? null : parseCount(record.adapterCount), - lastError: record.lastError === null ? null : sanitizeTradingBotError(record.lastError), - }; -} - -export function interpretTradingBotProxyResponse( - status: number, - body: unknown, -): TradingBotSnapshot { - const record = asRecord(body); - if (status === 200 && record?.configured === false) return { state: "unconfigured" }; - if (record?.configured === true && record.reachable === false) return { state: "offline" }; - if (status === 200 && record?.configured === true && record.reachable === true) { - if (record.status == null) return { state: "unknown" }; - const view = coerceTradingBotStatusView(record.status); - return view ? { state: "ready", status: view } : { state: "unknown" }; - } - return { state: "offline" }; -} - -/** Keep a short operator-facing error. Drop secrets, URLs, and empty placeholders. */ -export function sanitizeTradingBotError(value: unknown): string | null { - if (typeof value !== "string") return null; - const cleaned = stripControlChars(value).replace(/\s+/g, " ").trim(); - if (!cleaned || EMPTY_ERRORS.has(cleaned.toLowerCase())) return null; - if (UNSAFE_ERROR.test(cleaned)) return null; - if (cleaned.length <= MAX_ERROR_LENGTH) return cleaned; - return `${cleaned.slice(0, MAX_ERROR_LENGTH - 1)}…`; -} - -function readSignals(record: Record): TradingBotKind[] { - const signals: TradingBotKind[] = []; - pushSignals(record, signals); - if (signals.length > 0) return signals; - for (const key of NESTED_KEYS) { - const nested = asRecord(record[key]); - if (nested) pushSignals(nested, signals); - } - return signals; -} - -function pushSignals(record: Record, signals: TradingBotKind[]): void { - for (const key of MODE_STRING_KEYS) { - const value = record[key]; - if (typeof value !== "string") continue; - const kind = classifyModeToken(value); - if (kind) signals.push(kind); - } - if (record.live === true || record.is_live === true || record.isLive === true) { - signals.push("live"); - } - if (record.dry_run === true || record.dryRun === true) signals.push("dry_run"); - if ( - record.paper === true || - record.is_paper === true || - record.isPaper === true || - record.paper_trading === true || - record.paperTrading === true - ) { - signals.push("paper"); - } -} - -function resolveKind(signals: TradingBotKind[]): TradingBotKind | null { - const unique = new Set(signals); - const live = unique.has("live"); - const paper = unique.has("paper"); - const dryRun = unique.has("dry_run"); - if (live && (paper || dryRun)) return null; - if (dryRun) return "dry_run"; - if (paper) return "paper"; - if (live) return "live"; - return null; -} - -function classifyModeToken(value: string): TradingBotKind | null { - const token = value - .trim() - .toLowerCase() - .replace(/[\s-]+/g, "_"); - if (LIVE_TOKENS.has(token)) return "live"; - if (DRY_RUN_TOKENS.has(token)) return "dry_run"; - if (PAPER_TOKENS.has(token)) return "paper"; - return null; -} - -function readAdapterCount(record: Record): number | null { - const found = readOptional(record, adapterCountIn); - return found === undefined ? null : found; -} - -function adapterCountIn(record: Record): number | null | undefined { - for (const key of ADAPTER_COUNT_KEYS) { - if (!Object.hasOwn(record, key)) continue; - return parseCount(record[key]); - } - if (Object.hasOwn(record, "adapters") && Array.isArray(record.adapters)) { - return parseCount(record.adapters.length); - } - return undefined; -} - -function readLastError(record: Record): string | null { - const found = readOptional(record, lastErrorIn); - return found === undefined ? null : found; -} - -function lastErrorIn(record: Record): string | null | undefined { - for (const key of ERROR_KEYS) { - if (!Object.hasOwn(record, key)) continue; - return sanitizeTradingBotError(record[key]); - } - return undefined; -} - -function readOptional( - record: Record, - read: (row: Record) => T | undefined, -): T | undefined { - const direct = read(record); - if (direct !== undefined) return direct; - for (const key of NESTED_KEYS) { - const nested = asRecord(record[key]); - if (!nested) continue; - const value = read(nested); - if (value !== undefined) return value; - } - return undefined; -} - -function parseCount(value: unknown): number | null { - if (typeof value === "string" && !/^\d+$/.test(value.trim())) return null; - const numeric = - typeof value === "number" ? value : typeof value === "string" ? Number(value) : Number.NaN; - if (!Number.isInteger(numeric) || numeric < 0 || numeric > MAX_ADAPTER_COUNT) return null; - return numeric; -} - -function hasControlChar(value: string): boolean { - for (let index = 0; index < value.length; index += 1) { - const code = value.charCodeAt(index); - if (code < 32 || code === 127) return true; - } - return false; -} - -function stripControlChars(value: string): string { - let out = ""; - for (let index = 0; index < value.length; index += 1) { - const code = value.charCodeAt(index); - const char = value[index]; - if (char === undefined) continue; - out += code < 32 || code === 127 ? " " : char; - } - return out; -} - -function asRecord(value: unknown): Record | null { - if (!value || typeof value !== "object" || Array.isArray(value)) return null; - return value as Record; -} diff --git a/packages/db/create-user.ts b/packages/db/create-user.ts deleted file mode 100644 index 3a2e757..0000000 --- a/packages/db/create-user.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { createDb } from "./src/client"; - -process.env.DATABASE_URL = "postgres://rakazo:rakazodev@127.0.0.1:5433/rakazo"; -const { prisma: db } = createDb(process.env.DATABASE_URL, { applicationName: "check-users" }); - -const allUsers = await db.user.findMany({ - select: { id: true, email: true, name: true, createdAt: true, emailVerified: true }, - orderBy: { createdAt: "desc" }, -}); - -console.log("All users:", JSON.stringify(allUsers, null, 2)); - -// Check spaces -const allSpaces = await db.space.findMany({ - select: { id: true, name: true, ownerId: true, createdAt: true }, - orderBy: { createdAt: "desc" }, -}); - -console.log("\nAll spaces:", JSON.stringify(allSpaces, null, 2)); - -// Check space members -const allMembers = await db.spaceMember.findMany({ - select: { id: true, spaceId: true, userId: true, role: true }, -}); - -console.log("\nAll members:", JSON.stringify(allMembers, null, 2)); - -await db.$disconnect(); diff --git a/packages/db/query-users.mjs b/packages/db/query-users.mjs deleted file mode 100644 index 17c0f3b..0000000 --- a/packages/db/query-users.mjs +++ /dev/null @@ -1,13 +0,0 @@ -import { createDb, createPool } from "./src/client.js"; - -const pool = createPool(process.env.DATABASE_URL, { applicationName: "query-users" }); -const db = createDb(pool); - -const users = await db.user.findMany({ - orderBy: { createdAt: "desc" }, - take: 10, - select: { email: true, name: true, createdAt: true }, -}); - -console.log(JSON.stringify(users, null, 2)); -await db.$disconnect(); diff --git a/packages/db/query-users.ts b/packages/db/query-users.ts deleted file mode 100644 index 0f55d41..0000000 --- a/packages/db/query-users.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { createDb } from "./src/client"; - -// Override to port 5433 (Docker published port) -process.env.DATABASE_URL = "postgres://rakazo:rakazodev@127.0.0.1:5433/rakazo"; -const { prisma: db } = createDb(process.env.DATABASE_URL, { applicationName: "query-users" }); - -const users = await db.user.findMany({ - orderBy: { createdAt: "desc" }, - take: 10, - select: { email: true, name: true, createdAt: true }, -}); - -console.log(JSON.stringify(users, null, 2)); diff --git a/packages/db/setup-user.ts b/packages/db/setup-user.ts deleted file mode 100644 index 4184e42..0000000 --- a/packages/db/setup-user.ts +++ /dev/null @@ -1,108 +0,0 @@ -import { randomUUID } from "node:crypto"; -import { createDb } from "./src/client"; - -process.env.DATABASE_URL = "postgres://rakazo:rakazodev@127.0.0.1:5433/rakazo"; -const { prisma: db } = createDb(process.env.DATABASE_URL, { applicationName: "setup-user" }); - -const user = await db.user.findUnique({ - where: { email: "ingo.wagner1303@gmail.com" }, - select: { id: true, name: true }, -}); - -if (!user) { - console.log("User nicht gefunden!"); - await db.$disconnect(); - process.exit(1); -} - -console.log("User:", user); - -// Step 1: Get or create Organization -let org = await db.organization.findUnique({ where: { slug: "ingo-workspace" } }); -if (!org) { - org = await db.organization.create({ - data: { - id: randomUUID(), - name: "Ingo's Workspace", - slug: "ingo-workspace", - createdAt: new Date(), - }, - select: { id: true, name: true, slug: true }, - }); - console.log("Organization created:", org); -} else { - console.log("Organization exists:", org); -} - -// Step 2: Get or create Organization Member -let member = await db.member.findUnique({ - where: { - organizationId_userId: { organizationId: org.id, userId: user.id }, - }, -}); -if (!member) { - member = await db.member.create({ - data: { - id: randomUUID(), - userId: user.id, - organizationId: org.id, - role: "owner", - createdAt: new Date(), - }, - }); - console.log("Organization member created"); -} else { - console.log("Organization member exists"); -} - -// Step 3: Get or create default Space -let space = await db.space.findFirst({ - where: { organizationId: org.id, isDefault: true }, -}); -if (!space) { - space = await db.space.create({ - data: { - id: randomUUID(), - name: "Mein Space", - isDefault: true, - createdByUserId: user.id, - organizationId: org.id, - }, - select: { id: true, name: true }, - }); - console.log("Space created:", space); -} else { - console.log("Space exists:", space); -} - -// Step 4: Get or create space member -const existingMember = await db.spaceMember.findUnique({ - where: { - spaceId_userId: { spaceId: space.id, userId: user.id }, - }, -}); -if (!existingMember) { - await db.spaceMember.create({ - data: { - id: randomUUID(), - spaceId: space.id, - organizationId: org.id, - userId: user.id, - role: "owner", - createdAt: new Date(), - }, - }); - console.log("Space member created"); -} else { - console.log("Space member exists"); -} - -// Verify -const finalSpaces = await db.space.findMany({ - where: { organization: { members: { some: { userId: user.id } } } }, - select: { id: true, name: true, isDefault: true }, -}); -console.log("\nUser spaces:", JSON.stringify(finalSpaces, null, 2)); - -await db.$disconnect(); -console.log("\nDone!"); diff --git a/packs/evoo-cto-steve-jobs.md b/packs/evoo-cto-steve-jobs.md deleted file mode 100644 index fe44010..0000000 --- a/packs/evoo-cto-steve-jobs.md +++ /dev/null @@ -1,19 +0,0 @@ -# Pack: evOo Autonomous CTO (Steve Jobs) + Casted Agent Network - -The **evOo Autonomous CTO Pack** empowers a single executive agent (**Steve Jobs**) with full authority over the system, network, files, devices, and codebase, with the power to cast, spawn, and direct specialized agent sub-teams. - -## Roster Architecture - -| Role | Seat / Agent | Authority & Scope | Execution Model | -| --- | --- | --- | --- | -| **CTO** | **Steve Jobs** (`templates/cto-steve-jobs`) | Executive decision maker, architecture lead, full system access | Action-first, multi-task sequence, autonomous execution | -| **Code Architect** | Casted by CTO | Code structure, design patterns, refactoring | Sub-agent spawned on demand by Steve Jobs | -| **Security & QA Auditor** | Casted by CTO | Code safety, test coverage, vulnerability checks | Sub-agent spawned on demand by Steve Jobs | -| **DevOps & Infrastructure** | Casted by CTO | System setup, process management, deployment | Sub-agent spawned on demand by Steve Jobs | - -## Core Principles - -1. **Unrestricted Pre-Approved Access**: Network, local filesystem, devices, and repository source code are pre-approved via `evoo.config.json`. -2. **Action-First Multi-Task Execution**: Executes full task chains without stopping to ask permission on clear objectives. -3. **Self-Improvement Loop**: Automatically inspects logs, diagnoses errors, refactor code, and enhances prompt guidelines. -4. **Consensus & Negotiation**: Reports back when high-level strategic alignment or trade-offs require negotiation with the user. diff --git a/scripts/autonomous-sentinel.ts b/scripts/autonomous-sentinel.ts deleted file mode 100644 index 44abb30..0000000 --- a/scripts/autonomous-sentinel.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { execSync } from "node:child_process"; -import fs from "node:fs"; -import { createDb } from "../packages/db/src/index.js"; - -async function main() { - console.log("Starting Calm Autonomous Sentinel (5-Min Check & Multi-Agent Parallel Watchdog)..."); - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - - const CHECK_INTERVAL_MS = 300_000; // 5 minutes exactly - let lastCheckTime = Date.now(); - - while (true) { - try { - await new Promise((r) => setTimeout(r, 30_000)); // Check every 30s - - const now = Date.now(); - if (now - lastCheckTime < CHECK_INTERVAL_MS) { - continue; - } - - // Check if any agent is currently working - const activeRuns = await prisma.run.count({ - where: { status: { in: ["running", "queued", "leased"] } }, - }); - - if (activeRuns >= 2) { - // At least 2 agents working in parallel — perfect, no need to interrupt! - console.log( - `[Sentinel] ${activeRuns} agents actively working in parallel. System healthy.`, - ); - lastCheckTime = now; - continue; - } - - const steve = await prisma.bot.findFirst({ where: { name: "Steve Jobs" } }); - const session = await prisma.session.findFirst({ orderBy: { createdAt: "desc" } }); - const execGroup = await prisma.chatGroup.findFirst({ where: { name: "Executive Board" } }); - if (!steve || !session || !execGroup) continue; - - // Check the last message in Executive Board to avoid duplicate spam - const lastMsg = await prisma.message.findFirst({ - where: { thread: { groupId: execGroup.id } }, - orderBy: { createdAt: "desc" }, - }); - - const lastMsgText = JSON.stringify(lastMsg?.blocks ?? ""); - if ( - lastMsgText.includes("5-Minuten Kontroll-Check") && - now - new Date(lastMsg!.createdAt).getTime() < 300_000 - ) { - console.log("[Sentinel] Skip: Recent check already present."); - lastCheckTime = now; - continue; - } - - // Quick tests verification - let testSummary = "Alle Tests validiert"; - try { - const testPath = - "/Users/ingowagner/evOo/data/desktop-computers/team-9d790f3a504d3e9ef577ca4f2118e446/bots/cmu7jjxv30000fvyyjyoyylef/apex-trading"; - if (fs.existsSync(testPath)) { - const out = execSync("PYTHONPATH=src python3 -m unittest discover -s tests 2>&1", { - cwd: testPath, - timeout: 8000, - }).toString(); - if (out.includes("OK")) { - testSummary = "5/5 Tests GRÜN ✅ (1.5M ticks/s)"; - } - } - } catch { - testSummary = "Tests laufen"; - } - - lastCheckTime = now; - console.log("[Sentinel] Dispatching 5-minute parallel pulse to Executive Board..."); - - await fetch("http://127.0.0.1:3100/rpc/threads/send", { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${session.token}`, - }, - body: JSON.stringify({ - json: { - groupId: execGroup.id, - text: `@Steve Jobs [Executive Board Pulse ⏱️]\n\nTest-Status: ${testSummary}\nAktuell aktive Agenten: ${activeRuns}\n\nBitte halte die Vorgabe der Geschäftsführung ein: Lasse immer MINDESTENS 2 Spezialisten gleichzeitig parallel arbeiten (z.B. DevOps Engineer + Security Auditor oder Apex Quant + Code Architect). Delegiere die nächsten 2 parallelen Arbeitspakete.`, - }, - }), - }); - } catch (err) { - console.error("[Sentinel] Error in loop:", err); - } - } -} - -main().catch(console.error); diff --git a/scripts/check-architect-events.ts b/scripts/check-architect-events.ts deleted file mode 100644 index 05fa1f4..0000000 --- a/scripts/check-architect-events.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const events = await prisma.event.findMany({ - where: { runId: "cmu7k4d9n000yiryy0zz4obmq" }, - orderBy: { createdAt: "asc" }, - }); - - console.log(`Events for Code Architect (total ${events.length}):`); - for (const e of events) { - console.log( - `[${e.createdAt.toISOString()}] ${e.type} | payload: ${JSON.stringify(e.payload || e.data)}`, - ); - } -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-attempts.ts b/scripts/check-attempts.ts deleted file mode 100644 index 6f96b2b..0000000 --- a/scripts/check-attempts.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const attempts = await prisma.attempt.findMany({ - where: { runId: "cmu7jw8dr000biryyrethbore" }, - orderBy: { startedAt: "asc" }, - }); - console.log("Attempts count:", attempts.length); - for (const a of attempts) { - console.log("Attempt:", a); - } -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-auditor-final.ts b/scripts/check-auditor-final.ts deleted file mode 100644 index 4d9bf16..0000000 --- a/scripts/check-auditor-final.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const auditor = await prisma.bot.findFirst({ - where: { name: "Security & QA Auditor" }, - include: { - thread: { - include: { - messages: { - orderBy: { seq: "desc" }, - take: 2, - }, - }, - }, - }, - }); - - const lastMsg = auditor?.thread?.messages[0]; - console.log(`Auditor last msg (role: ${lastMsg?.role}, seq: ${lastMsg?.seq}):`); - if (lastMsg?.role === "bot") { - console.log(JSON.stringify(lastMsg?.blocks, null, 2)); - } else { - console.log("Still working/executing..."); - } - - const latestEvents = await prisma.event.findMany({ - where: { botId: auditor?.id }, - orderBy: { createdAt: "desc" }, - take: 4, - }); - console.log("\nLatest events:"); - for (const e of latestEvents) { - console.log( - `[${e.createdAt.toISOString()}] ${e.type}: ${JSON.stringify(e.payload || e.data).slice(0, 120)}`, - ); - } -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-auditor-live.ts b/scripts/check-auditor-live.ts deleted file mode 100644 index 663605e..0000000 --- a/scripts/check-auditor-live.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const events = await prisma.event.findMany({ - where: { runId: "cmu7k4dc50016iryyrv036xze" }, - orderBy: { createdAt: "desc" }, - take: 6, - }); - - console.log("Auditor live events count:", events.length); - for (const e of events.reverse()) { - console.log( - `[${e.createdAt.toISOString()}] ${e.type}: ${JSON.stringify(e.payload || e.data).slice(0, 120)}`, - ); - } -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-auditor-live2.ts b/scripts/check-auditor-live2.ts deleted file mode 100644 index b368b15..0000000 --- a/scripts/check-auditor-live2.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const events = await prisma.event.findMany({ - where: { - runId: "cmu7k4dc50016iryyrv036xze", - createdAt: { gte: new Date(Date.now() - 30_000) }, - }, - orderBy: { createdAt: "desc" }, - take: 5, - }); - - console.log("Recent Auditor events:", events.length); - for (const e of events.reverse()) { - console.log( - `[${e.createdAt.toISOString()}] ${e.type}: ${JSON.stringify(e.payload || e.data).slice(0, 150)}`, - ); - } - - const run = await prisma.run.findUnique({ - where: { id: "cmu7k4dc50016iryyrv036xze" }, - }); - console.log("Run status:", run?.status); -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-auditor-live3.ts b/scripts/check-auditor-live3.ts deleted file mode 100644 index 57a6bd6..0000000 --- a/scripts/check-auditor-live3.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const events = await prisma.event.findMany({ - where: { - runId: "cmu7k4dc50016iryyrv036xze", - createdAt: { gte: new Date(Date.now() - 20_000) }, - }, - orderBy: { createdAt: "desc" }, - take: 5, - }); - - console.log("Recent Auditor events count:", events.length); - for (const e of events.reverse()) { - console.log( - `[${e.createdAt.toISOString()}] ${e.type}: ${JSON.stringify(e.payload || e.data).slice(0, 150)}`, - ); - } -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-auditor-next.ts b/scripts/check-auditor-next.ts deleted file mode 100644 index b5605ff..0000000 --- a/scripts/check-auditor-next.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const auditor = await prisma.bot.findFirst({ - where: { name: "Security & QA Auditor" }, - include: { - thread: { - include: { - messages: { - orderBy: { seq: "desc" }, - take: 2, - }, - }, - }, - }, - }); - - const lastMsg = auditor?.thread?.messages[0]; - console.log(`Auditor last msg (role: ${lastMsg?.role}, seq: ${lastMsg?.seq}):`); - if (lastMsg?.role === "bot") { - console.log(JSON.stringify(lastMsg?.blocks, null, 2)); - } else { - console.log("Still working..."); - } - - const latestEvents = await prisma.event.findMany({ - where: { botId: auditor?.id }, - orderBy: { createdAt: "desc" }, - take: 6, - }); - console.log("\nLatest events:"); - for (const e of latestEvents) { - console.log( - `[${e.createdAt.toISOString()}] ${e.type}: ${JSON.stringify(e.payload || e.data).slice(0, 120)}`, - ); - } -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-bot-directory.ts b/scripts/check-bot-directory.ts deleted file mode 100644 index 8a2740d..0000000 --- a/scripts/check-bot-directory.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const steve = await prisma.bot.findFirst({ where: { name: "Steve Jobs" } }); - const allBots = await prisma.bot.findMany({ - select: { - id: true, - name: true, - userId: true, - spaceId: true, - archivedAt: true, - thread: { select: { id: true } }, - }, - }); - console.log("Steve userId:", steve?.userId, "spaceId:", steve?.spaceId); - console.log("All bots:"); - for (const b of allBots) { - console.log( - `Bot: ${b.name} (${b.id}) | userId: ${b.userId} | spaceId: ${b.spaceId} | thread: ${b.thread?.id}`, - ); - } -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-bot-model.ts b/scripts/check-bot-model.ts deleted file mode 100644 index 9efbe44..0000000 --- a/scripts/check-bot-model.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const steve = await prisma.bot.findFirst({ where: { name: "Steve Jobs" } }); - console.log("Steve Jobs modelId:", steve?.modelId); - const run = await prisma.run.findUnique({ - where: { id: "cmu7jw8dr000biryyrethbore" }, - }); - console.log("Run details:", { - id: run?.id, - modelId: run?.modelId, - status: run?.status, - createdAt: run?.createdAt, - }); - const model = await prisma.model.findUnique({ - where: { id: run?.modelId || steve?.modelId || "" }, - }); - console.log("Model record in DB:", model); -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-chief-group-finish.ts b/scripts/check-chief-group-finish.ts deleted file mode 100644 index cb25726..0000000 --- a/scripts/check-chief-group-finish.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const group = await prisma.chatGroup.findFirst({ - where: { name: "Executive Board" }, - include: { - thread: { - include: { - messages: { - orderBy: { seq: "desc" }, - take: 2, - }, - }, - }, - }, - }); - - const latestMsg = group?.thread?.messages[0]; - console.log("Chief Message in Executive Board:"); - console.log(JSON.stringify(latestMsg?.blocks, null, 2)); - - const run = await prisma.run.findUnique({ - where: { id: "cmu7k3m4x000qiryyt59ju2n0" }, - }); - console.log("Run status:", run?.status); -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-chief-group-progress.ts b/scripts/check-chief-group-progress.ts deleted file mode 100644 index 0dbdce8..0000000 --- a/scripts/check-chief-group-progress.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const run = await prisma.run.findUnique({ - where: { id: "cmu7k3m4x000qiryyt59ju2n0" }, - }); - console.log("Run:", { status: run?.status, completedAt: run?.completedAt, error: run?.error }); - - const events = await prisma.event.findMany({ - where: { runId: "cmu7k3m4x000qiryyt59ju2n0" }, - orderBy: { createdAt: "desc" }, - take: 5, - }); - console.log("Events count:", events.length); - for (const e of events.reverse()) { - console.log(`[${e.createdAt.toISOString()}] ${e.type}: ${JSON.stringify(e.payload || e.data)}`); - } -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-chief.ts b/scripts/check-chief.ts deleted file mode 100644 index d24a846..0000000 --- a/scripts/check-chief.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const chief = await prisma.bot.findFirst({ - where: { name: "Chief" }, - include: { thread: true }, - }); - console.log("CHIEF BOT:", JSON.stringify(chief, null, 2)); - - const sections = await prisma.botSection.findMany(); - console.log("SECTIONS:", JSON.stringify(sections, null, 2)); -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-computer-leases.ts b/scripts/check-computer-leases.ts deleted file mode 100644 index 92c0455..0000000 --- a/scripts/check-computer-leases.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const leases = await prisma.computerExecutionLease.findMany(); - console.log("Total computerExecutionLeases in DB:", leases.length); - for (const l of leases) { - console.log(l); - } -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-current-run.ts b/scripts/check-current-run.ts deleted file mode 100644 index 4bead4b..0000000 --- a/scripts/check-current-run.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const r = await prisma.run.findUnique({ - where: { id: "cmu7k09nt00cchxyyfo0n4ppv" }, - include: { sourceMessage: true }, - }); - console.log("Run:", { - id: r?.id, - trigger: r?.trigger, - status: r?.status, - sourceMessage: r?.sourceMessage?.blocks, - createdAt: r?.createdAt, - }); - const events = await prisma.event.findMany({ - where: { runId: "cmu7k09nt00cchxyyfo0n4ppv" }, - }); - console.log("Events count:", events.length); - for (const e of events) { - console.log(`[${e.createdAt.toISOString()}] ${e.type}: ${JSON.stringify(e.payload || e.data)}`); - } -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-events-detail.ts b/scripts/check-events-detail.ts deleted file mode 100644 index ab92036..0000000 --- a/scripts/check-events-detail.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const events = await prisma.event.findMany({ - where: { runId: "cmu7jw8dr000biryyrethbore" }, - orderBy: { createdAt: "asc" }, - }); - for (const e of events) { - console.log( - `[${e.createdAt.toISOString()}] ${e.type} (payload: ${e.payload ? JSON.stringify(e.payload).slice(0, 200) : "none"})`, - ); - } -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-graphile-jobs-now.ts b/scripts/check-graphile-jobs-now.ts deleted file mode 100644 index 0a448ec..0000000 --- a/scripts/check-graphile-jobs-now.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { pool } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const res = await pool.query( - "SELECT id, task_identifier, run_at, attempts, locked_at, locked_by, last_error FROM graphile_worker.jobs ORDER BY id DESC LIMIT 5;", - ); - console.log("Graphile jobs:", res.rows); -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-graphile-jobs.ts b/scripts/check-graphile-jobs.ts deleted file mode 100644 index 3b7e7a9..0000000 --- a/scripts/check-graphile-jobs.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { pool } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const res = await pool.query("SELECT * FROM graphile_worker.jobs ORDER BY id DESC LIMIT 5;"); - console.log("Graphile jobs count:", res.rows.length); - for (const r of res.rows) { - console.log(r); - } -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-group-followup.ts b/scripts/check-group-followup.ts deleted file mode 100644 index 6664461..0000000 --- a/scripts/check-group-followup.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const runs = await prisma.run.findMany({ - where: { - thread: { - group: { name: "Executive Board" }, - }, - }, - orderBy: { createdAt: "desc" }, - include: { bot: true }, - }); - - console.log("Runs in Executive Board group thread:", runs.length); - for (const r of runs) { - console.log( - `Run: ${r.id} | Bot: ${r.bot?.name} | Status: ${r.status} | Trigger: ${r.trigger} | Created: ${r.createdAt.toISOString()}`, - ); - } -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-group-run.ts b/scripts/check-group-run.ts deleted file mode 100644 index d03faf5..0000000 --- a/scripts/check-group-run.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const run = await prisma.run.findUnique({ - where: { id: "cmu7k3m4x000qiryyt59ju2n0" }, - include: { bot: true, thread: true }, - }); - - console.log("Run info:", { - id: run?.id, - botName: run?.bot?.name, - status: run?.status, - createdAt: run?.createdAt, - }); - - const events = await prisma.event.findMany({ - where: { runId: "cmu7k3m4x000qiryyt59ju2n0" }, - orderBy: { createdAt: "asc" }, - }); - console.log("Events count:", events.length); - for (const e of events) { - console.log(`[${e.createdAt.toISOString()}] ${e.type}: ${JSON.stringify(e.payload || e.data)}`); - } -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-groups.ts b/scripts/check-groups.ts deleted file mode 100644 index c730c3f..0000000 --- a/scripts/check-groups.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const groups = await prisma.group.findMany({ - include: { - members: { - include: { bot: true }, - }, - thread: true, - }, - }); - console.log("GROUPS COUNT:", groups.length); - for (const g of groups) { - console.log( - `Group: ${g.name} (${g.id}) | Members: ${g.members.map((m) => m.bot.name).join(", ")} | Thread: ${g.thread?.id}`, - ); - } -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-latest-events-5.ts b/scripts/check-latest-events-5.ts deleted file mode 100644 index dd6c5f6..0000000 --- a/scripts/check-latest-events-5.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const events = await prisma.event.findMany({ - orderBy: { createdAt: "desc" }, - take: 5, - }); - - console.log("LAST 5 EVENTS:"); - for (const e of events) { - console.log( - `[${e.createdAt.toISOString()}] ${e.type} | run: ${e.runId} | data: ${JSON.stringify(e.payload || e.data).slice(0, 120)}`, - ); - } -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-latest-events-all.ts b/scripts/check-latest-events-all.ts deleted file mode 100644 index da1afb0..0000000 --- a/scripts/check-latest-events-all.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const events = await prisma.event.findMany({ - orderBy: { createdAt: "desc" }, - take: 12, - }); - console.log("LATEST 12 EVENTS:"); - for (const e of events) { - console.log( - `[${e.createdAt.toISOString()}] Run: ${e.runId} | Type: ${e.type} | Data: ${JSON.stringify(e.payload || e.data).slice(0, 100)}`, - ); - } -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-latest-events.ts b/scripts/check-latest-events.ts deleted file mode 100644 index 50bef95..0000000 --- a/scripts/check-latest-events.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const events = await prisma.event.findMany({ - where: { runId: "cmu7jw8dr000biryyrethbore" }, - orderBy: { createdAt: "desc" }, - take: 10, - }); - console.log("Run cmu7jw8dr000biryyrethbore events count:", events.length); - for (const e of events.reverse()) { - console.log(`[${e.createdAt.toISOString()}] ${e.type}: ${JSON.stringify(e.data)}`); - } - - const run = await prisma.run.findUnique({ - where: { id: "cmu7jw8dr000biryyrethbore" }, - }); - console.log("Status:", run?.status, "completedAt:", run?.completedAt, "error:", run?.error); -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-latest-state.ts b/scripts/check-latest-state.ts deleted file mode 100644 index 6abdcc8..0000000 --- a/scripts/check-latest-state.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - - const msgs = await prisma.message.findMany({ - where: { threadId: "cmu6ydgme0001ywyy269411bb" }, - orderBy: { seq: "desc" }, - take: 5, - }); - console.log("Recent messages:"); - for (const m of msgs.reverse()) { - console.log(`[Seq ${m.seq} | Role ${m.role} | RunId ${m.runId}]`); - console.log(JSON.stringify(m.blocks)); - } - - const activeRuns = await prisma.run.findMany({ - where: { status: "running" }, - }); - console.log("\nActive running runs count:", activeRuns.length); - for (const r of activeRuns) { - console.log( - `Run ${r.id} | sourceMsg ${r.sourceMessageId} | started: ${r.createdAt.toISOString()}`, - ); - } -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-latest-user-msg.ts b/scripts/check-latest-user-msg.ts deleted file mode 100644 index 4b9ed89..0000000 --- a/scripts/check-latest-user-msg.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const latestMsgs = await prisma.message.findMany({ - orderBy: { createdAt: "desc" }, - take: 3, - include: { - thread: { - include: { - bot: true, - group: true, - }, - }, - }, - }); - - for (const m of latestMsgs) { - console.log( - `[${m.createdAt.toISOString()}] Target: ${m.thread?.bot?.name || m.thread?.group?.name} | Role: ${m.role} | Text: ${JSON.stringify(m.blocks)}`, - ); - } -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-model-prefs.ts b/scripts/check-model-prefs.ts deleted file mode 100644 index 4dec422..0000000 --- a/scripts/check-model-prefs.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const creds = await prisma.userModelCredential.findMany({ - include: { preferences: true }, - }); - console.log("CREDS & PREFS:", JSON.stringify(creds, null, 2)); -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-model-secret.ts b/scripts/check-model-secret.ts deleted file mode 100644 index 4bbc84f..0000000 --- a/scripts/check-model-secret.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const secrets = await prisma.secret.findMany(); - console.log("Found secrets:", secrets.length); - for (const s of secrets) { - console.log("Secret:", { id: s.id, name: s.name, provider: s.provider }); - } - - const spacePrefs = await prisma.spacePreference.findMany(); - console.log("Space preferences:", spacePrefs); - - const steve = await prisma.bot.findFirst({ where: { name: "Steve Jobs" } }); - console.log("Steve:", { - id: steve?.id, - modelProvider: steve?.modelProvider, - modelId: steve?.modelId, - }); -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-mtimes.ts b/scripts/check-mtimes.ts deleted file mode 100644 index 1f4618b..0000000 --- a/scripts/check-mtimes.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { readdirSync, statSync } from "node:fs"; - -const baseDir = "/Users/ingowagner/evOo/data/homes/team-9d790f3a504d3e9ef577ca4f2118e446/bots"; -for (const botDir of readdirSync(baseDir)) { - const p = `${baseDir}/${botDir}`; - console.log(`\nDir: ${botDir}`); - for (const f of readdirSync(p)) { - if (f.endsWith(".md")) { - const st = statSync(`${p}/${f}`); - console.log(` ${f.padEnd(20)} mtime: ${st.mtime.toISOString()} (size: ${st.size})`); - } - } -} diff --git a/scripts/check-other-two.ts b/scripts/check-other-two.ts deleted file mode 100644 index f2a53f5..0000000 --- a/scripts/check-other-two.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - - for (const name of ["DevOps Engineer", "Security & QA Auditor"]) { - const bot = await prisma.bot.findFirst({ where: { name } }); - const events = await prisma.event.findMany({ - where: { botId: bot?.id }, - orderBy: { createdAt: "asc" }, - }); - console.log(`\nEvents for ${name} (total ${events.length}):`); - for (const e of events) { - console.log( - `[${e.createdAt.toISOString()}] ${e.type} | payload: ${JSON.stringify(e.payload || e.data).slice(0, 100)}`, - ); - } - } -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-quant-events.ts b/scripts/check-quant-events.ts deleted file mode 100644 index 2150126..0000000 --- a/scripts/check-quant-events.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const events = await prisma.event.findMany({ - where: { runId: "cmu7k4d8k000uiryy086x000a" }, - orderBy: { createdAt: "asc" }, - }); - - console.log(`Events for Apex Quant (total ${events.length}):`); - for (const e of events) { - console.log( - `[${e.createdAt.toISOString()}] ${e.type} | payload: ${JSON.stringify(e.payload || e.data)}`, - ); - } -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-run-events.ts b/scripts/check-run-events.ts deleted file mode 100644 index 79a105e..0000000 --- a/scripts/check-run-events.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const events = await prisma.event.findMany({ - where: { runId: "cmu7jw8dr000biryyrethbore" }, - orderBy: { createdAt: "asc" }, - }); - - console.log(`Events count for cmu7jw8dr000biryyrethbore: ${events.length}`); - for (const e of events) { - console.log(`[${e.createdAt.toISOString()}] ${e.type}: ${JSON.stringify(e.data)}`); - } - - const run = await prisma.run.findUnique({ - where: { id: "cmu7jw8dr000biryyrethbore" }, - }); - console.log("RUN STATUS:", run?.status, "error:", run?.error); -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-run-now.ts b/scripts/check-run-now.ts deleted file mode 100644 index cd251e4..0000000 --- a/scripts/check-run-now.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const events = await prisma.event.findMany({ - where: { runId: "cmu7jw8dr000biryyrethbore" }, - orderBy: { createdAt: "asc" }, - }); - console.log("Event count:", events.length); - for (const e of events) { - console.log( - `[${e.createdAt.toISOString()}] ${e.type}: ${JSON.stringify(e.data).slice(0, 150)}`, - ); - } - - const run = await prisma.run.findUnique({ - where: { id: "cmu7jw8dr000biryyrethbore" }, - }); - console.log("Run:", { status: run?.status, completedAt: run?.completedAt, error: run?.error }); -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-runs-detailed.ts b/scripts/check-runs-detailed.ts deleted file mode 100644 index 839941d..0000000 --- a/scripts/check-runs-detailed.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const runs = await prisma.run.findMany({ - where: { - bot: { - name: { in: ["Apex Quant", "Code Architect", "DevOps Engineer", "Security & QA Auditor"] }, - }, - }, - orderBy: { createdAt: "desc" }, - take: 4, - include: { - bot: true, - }, - }); - - for (const r of runs) { - console.log( - `Bot: ${r.bot?.name.padEnd(25)} | Status: ${r.status.padEnd(10)} | Error: ${r.error}`, - ); - const events = await prisma.event.findMany({ - where: { runId: r.id }, - orderBy: { createdAt: "desc" }, - take: 3, - }); - console.log(` Last ${events.length} events:`); - for (const e of events) { - console.log( - ` [${e.createdAt.toISOString()}] ${e.type}: ${JSON.stringify(e.payload || e.data).slice(0, 100)}`, - ); - } - } -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-runs-live.ts b/scripts/check-runs-live.ts deleted file mode 100644 index 3c012a9..0000000 --- a/scripts/check-runs-live.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const runs = await prisma.run.findMany({ - where: { - bot: { - name: { in: ["Apex Quant", "Code Architect", "DevOps Engineer", "Security & QA Auditor"] }, - }, - }, - orderBy: { createdAt: "desc" }, - take: 4, - include: { bot: true }, - }); - - for (const r of runs) { - console.log( - `Bot: ${r.bot?.name.padEnd(25)} | Status: ${r.status.padEnd(10)} | LeaseOwner: ${r.leaseOwner}`, - ); - } -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-runs-now.ts b/scripts/check-runs-now.ts deleted file mode 100644 index 3c012a9..0000000 --- a/scripts/check-runs-now.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const runs = await prisma.run.findMany({ - where: { - bot: { - name: { in: ["Apex Quant", "Code Architect", "DevOps Engineer", "Security & QA Auditor"] }, - }, - }, - orderBy: { createdAt: "desc" }, - take: 4, - include: { bot: true }, - }); - - for (const r of runs) { - console.log( - `Bot: ${r.bot?.name.padEnd(25)} | Status: ${r.status.padEnd(10)} | LeaseOwner: ${r.leaseOwner}`, - ); - } -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-secret-content.ts b/scripts/check-secret-content.ts deleted file mode 100644 index 0f98a2c..0000000 --- a/scripts/check-secret-content.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { readFileSync } from "node:fs"; -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const envText = readFileSync("/Users/ingowagner/evOo/.env", "utf8"); - let secretKey = ""; - for (const line of envText.split("\n")) { - if (line.startsWith("ENCRYPTION_SECRET=") || line.startsWith("SECRET_STORE_KEY=")) { - secretKey = line.split("=")[1].trim(); - } - } - console.log("Secret key present?", Boolean(secretKey)); - - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const row = await prisma.secret.findUnique({ - where: { id: "1a9f419f48e31829e09f7b6a" }, - }); - console.log("Row:", row?.id, "ciphertext len:", row?.ciphertext.length); -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-seq136-finished.ts b/scripts/check-seq136-finished.ts deleted file mode 100644 index a1abfd0..0000000 --- a/scripts/check-seq136-finished.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const run = await prisma.run.findUnique({ - where: { id: "cmu7jytii000firyyhkbjh9od" }, - }); - console.log("Run:", { status: run?.status, completedAt: run?.completedAt }); - - const latestMsg = await prisma.message.findFirst({ - where: { threadId: "cmu6ydgme0001ywyy269411bb" }, - orderBy: { seq: "desc" }, - }); - console.log(`Latest msg (seq ${latestMsg?.seq}, role ${latestMsg?.role}):`); - console.log(JSON.stringify(latestMsg?.blocks, null, 2)); -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-seq136-progress.ts b/scripts/check-seq136-progress.ts deleted file mode 100644 index 67202e0..0000000 --- a/scripts/check-seq136-progress.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const events = await prisma.event.findMany({ - where: { runId: "cmu7jytii000firyyhkbjh9od" }, - orderBy: { createdAt: "asc" }, - }); - console.log("Events count:", events.length); - for (const e of events) { - console.log( - `[${e.createdAt.toISOString()}] ${e.type} (payload: ${e.payload ? JSON.stringify(e.payload).slice(0, 150) : "none"})`, - ); - } - - const run = await prisma.run.findUnique({ - where: { id: "cmu7jytii000firyyhkbjh9od" }, - }); - console.log("Run:", { status: run?.status, completedAt: run?.completedAt, error: run?.error }); -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-seq136-run.ts b/scripts/check-seq136-run.ts deleted file mode 100644 index 04a6e8e..0000000 --- a/scripts/check-seq136-run.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const msg136 = await prisma.message.findFirst({ - where: { seq: 136, threadId: "cmu6ydgme0001ywyy269411bb" }, - }); - console.log("Msg 136:", msg136); - - const runs = await prisma.run.findMany({ - where: { sourceMessageId: msg136?.id }, - include: { events: true }, - }); - console.log("Runs for msg 136:", runs); - - const latestRuns = await prisma.run.findMany({ - orderBy: { createdAt: "desc" }, - take: 3, - }); - console.log("Latest 3 runs:", latestRuns); -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-seq136-status.ts b/scripts/check-seq136-status.ts deleted file mode 100644 index 61bf50f..0000000 --- a/scripts/check-seq136-status.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const run = await prisma.run.findUnique({ - where: { id: "cmu7jytii000firyyhkbjh9od" }, - }); - console.log("Run for seq 136:", { - id: run?.id, - status: run?.status, - createdAt: run?.createdAt, - completedAt: run?.completedAt, - error: run?.error, - }); - - const events = await prisma.event.findMany({ - where: { runId: "cmu7jytii000firyyhkbjh9od" }, - orderBy: { createdAt: "asc" }, - }); - console.log("Events count:", events.length); - for (const e of events) { - console.log( - `[${e.createdAt.toISOString()}] ${e.type} (payload: ${e.payload ? JSON.stringify(e.payload).slice(0, 100) : "none"})`, - ); - } - - const latestMsg = await prisma.message.findFirst({ - where: { threadId: "cmu6ydgme0001ywyy269411bb" }, - orderBy: { seq: "desc" }, - }); - console.log("Latest msg in thread:", { - seq: latestMsg?.seq, - role: latestMsg?.role, - blocks: latestMsg?.blocks, - }); -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-shell-commands-2.ts b/scripts/check-shell-commands-2.ts deleted file mode 100644 index 9776f41..0000000 --- a/scripts/check-shell-commands-2.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const events = await prisma.event.findMany({ - where: { - type: "thread.progress", - createdAt: { gte: new Date(Date.now() - 30_000) }, - }, - orderBy: { createdAt: "desc" }, - take: 6, - }); - - for (const e of events) { - console.log( - `[${e.createdAt.toISOString()}] Bot: ${e.botId} | ${JSON.stringify(e.payload || e.data)}`, - ); - } -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-shell-commands.ts b/scripts/check-shell-commands.ts deleted file mode 100644 index 62e5df6..0000000 --- a/scripts/check-shell-commands.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const events = await prisma.event.findMany({ - where: { - type: "thread.progress", - createdAt: { gte: new Date(Date.now() - 30_000) }, - }, - orderBy: { createdAt: "desc" }, - take: 6, - }); - - console.log("Recent progress/commands:"); - for (const e of events) { - console.log( - `[${e.createdAt.toISOString()}] Bot: ${e.botId} | ${JSON.stringify(e.payload || e.data)}`, - ); - } -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-spaces.ts b/scripts/check-spaces.ts deleted file mode 100644 index 89c378a..0000000 --- a/scripts/check-spaces.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const session = await prisma.session.findFirst({ orderBy: { createdAt: "desc" } }); - const res = await fetch("http://127.0.0.1:3100/rpc/spaces/list", { - headers: { Authorization: `Bearer ${session?.token}` }, - }); - const data = await res.json(); - console.log("DATA:", JSON.stringify(data, null, 2)); -} -run(); diff --git a/scripts/check-specialist-events.ts b/scripts/check-specialist-events.ts deleted file mode 100644 index 1d54b23..0000000 --- a/scripts/check-specialist-events.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - - const events = await prisma.event.findMany({ - where: { - createdAt: { gte: new Date(Date.now() - 60_000) }, - }, - orderBy: { createdAt: "desc" }, - take: 15, - }); - - console.log(`Events in the last 60s: ${events.length}`); - for (const e of events.reverse()) { - console.log( - `[${e.createdAt.toISOString()}] Bot: ${e.botId} | Type: ${e.type} | Text/Data: ${JSON.stringify(e.payload || e.data).slice(0, 100)}`, - ); - } -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-specialist-progress.ts b/scripts/check-specialist-progress.ts deleted file mode 100644 index ef8a600..0000000 --- a/scripts/check-specialist-progress.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const runs = await prisma.run.findMany({ - where: { - id: { - in: [ - "cmu7k4dc50016iryyrv036xze", - "cmu7k4db10012iryyuhfoukpt", - "cmu7k4d9n000yiryy0zz4obmq", - "cmu7k4d8k000uiryy086x000a", - ], - }, - }, - include: { bot: true }, - }); - - for (const r of runs) { - console.log( - `Bot: ${r.bot?.name.padEnd(25)} | Status: ${r.status} | Completed: ${r.completedAt} | Error: ${r.error}`, - ); - } - - const latestEvents = await prisma.event.findMany({ - where: { - type: { in: ["thread.message.created", "tool.started", "tool.completed", "thread.progress"] }, - createdAt: { gte: new Date(Date.now() - 30_000) }, - }, - orderBy: { createdAt: "desc" }, - take: 8, - }); - console.log("\nLatest stream/message events count:", latestEvents.length); - for (const e of latestEvents) { - console.log(`[${e.createdAt.toISOString()}] Type: ${e.type} | Bot: ${e.botId}`); - } -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-specialist-runs.ts b/scripts/check-specialist-runs.ts deleted file mode 100644 index 0623c0b..0000000 --- a/scripts/check-specialist-runs.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const runs = await prisma.run.findMany({ - orderBy: { createdAt: "desc" }, - take: 6, - include: { bot: true }, - }); - - console.log("Recent runs:"); - for (const r of runs) { - console.log( - `Bot: ${r.bot?.name.padEnd(25)} | Status: ${r.status.padEnd(10)} | Error: ${r.error} | RunId: ${r.id}`, - ); - } -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-specialists-completion.ts b/scripts/check-specialists-completion.ts deleted file mode 100644 index 13e4208..0000000 --- a/scripts/check-specialists-completion.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const runs = await prisma.run.findMany({ - where: { - bot: { - name: { in: ["Apex Quant", "Code Architect", "DevOps Engineer", "Security & QA Auditor"] }, - }, - }, - orderBy: { createdAt: "desc" }, - take: 4, - include: { - bot: true, - thread: { - include: { - messages: { - orderBy: { seq: "desc" }, - take: 1, - }, - }, - }, - }, - }); - - for (const r of runs) { - console.log(`\n======================================================`); - console.log(`Bot: ${r.bot?.name} | Status: ${r.status} | Completed: ${r.completedAt}`); - const lastMsg = r.thread?.messages[0]; - if (lastMsg && lastMsg.role === "bot") { - console.log(`Bot Message (seq ${lastMsg.seq}):\n`, JSON.stringify(lastMsg.blocks, null, 2)); - } else { - console.log(`No bot message yet (role: ${lastMsg?.role})`); - } - } -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-specialists-db.ts b/scripts/check-specialists-db.ts deleted file mode 100644 index 052f764..0000000 --- a/scripts/check-specialists-db.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const runs = await prisma.run.findMany({ - where: { - id: { - in: [ - "cmu7k4dc50016iryyrv036xze", - "cmu7k4db10012iryyuhfoukpt", - "cmu7k4d9n000yiryy0zz4obmq", - "cmu7k4d8k000uiryy086x000a", - ], - }, - }, - include: { - bot: true, - thread: { - include: { - messages: { - orderBy: { seq: "desc" }, - take: 1, - }, - }, - }, - }, - }); - - for (const r of runs) { - console.log(`\n======================================================`); - console.log(`Bot: ${r.bot?.name} | Status: ${r.status}`); - const lastMsg = r.thread?.messages[0]; - if (lastMsg && lastMsg.role === "bot") { - console.log(`Message blocks:\n`, JSON.stringify(lastMsg.blocks, null, 2)); - } else { - console.log(`Still processing / no final bot message yet`); - } - } -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-specialists-events-now.ts b/scripts/check-specialists-events-now.ts deleted file mode 100644 index 29d860d..0000000 --- a/scripts/check-specialists-events-now.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const events = await prisma.event.findMany({ - where: { - createdAt: { gte: new Date(Date.now() - 60_000) }, - }, - orderBy: { createdAt: "desc" }, - take: 15, - }); - - console.log(`Events in last 60s: ${events.length}`); - for (const e of events.reverse()) { - console.log( - `[${e.createdAt.toISOString()}] Bot: ${e.botId} | Type: ${e.type} | Data: ${JSON.stringify(e.payload || e.data).slice(0, 120)}`, - ); - } -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-specialists-events.ts b/scripts/check-specialists-events.ts deleted file mode 100644 index 941c8d6..0000000 --- a/scripts/check-specialists-events.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const events = await prisma.event.findMany({ - where: { - runId: { - in: [ - "cmu7k4dc50016iryyrv036xze", - "cmu7k4db10012iryyuhfoukpt", - "cmu7k4d9n000yiryy0zz4obmq", - "cmu7k4d8k000uiryy086x000a", - ], - }, - }, - orderBy: { createdAt: "desc" }, - take: 20, - }); - - console.log(`Total events for specialist runs: ${events.length}`); - for (const e of events) { - console.log( - `[${e.createdAt.toISOString()}] Run: ${e.runId} | Type: ${e.type} | Data: ${JSON.stringify(e.payload || e.data).slice(0, 120)}`, - ); - } -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-specialists-latest.ts b/scripts/check-specialists-latest.ts deleted file mode 100644 index 16e647b..0000000 --- a/scripts/check-specialists-latest.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const runs = await prisma.run.findMany({ - where: { - bot: { - name: { in: ["Apex Quant", "Code Architect", "DevOps Engineer", "Security & QA Auditor"] }, - }, - }, - orderBy: { createdAt: "desc" }, - take: 4, - include: { - bot: true, - thread: { - include: { - messages: { - orderBy: { seq: "desc" }, - take: 1, - }, - }, - }, - }, - }); - - for (const r of runs) { - console.log( - `\nBot: ${r.bot?.name} | Run: ${r.id} | Status: ${r.status} | Completed: ${r.completedAt}`, - ); - const lastMsg = r.thread?.messages[0]; - if (lastMsg && lastMsg.role === "bot") { - console.log(`Last message blocks:`, JSON.stringify(lastMsg.blocks).slice(0, 300)); - } - } -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-specialists-msgs.ts b/scripts/check-specialists-msgs.ts deleted file mode 100644 index 3e10822..0000000 --- a/scripts/check-specialists-msgs.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const msgs = await prisma.message.findMany({ - where: { - thread: { - bot: { - name: { - in: ["Apex Quant", "Code Architect", "DevOps Engineer", "Security & QA Auditor"], - }, - }, - }, - }, - orderBy: { createdAt: "desc" }, - include: { - thread: { - include: { bot: true }, - }, - }, - }); - - console.log(`Total messages across the 4 bots: ${msgs.length}`); - for (const m of msgs) { - console.log( - `[${m.createdAt.toISOString()}] Bot: ${m.thread?.bot?.name} | Role: ${m.role} | Seq: ${m.seq}`, - ); - console.log(JSON.stringify(m.blocks)); - } -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-specialists-output.ts b/scripts/check-specialists-output.ts deleted file mode 100644 index 4ac6978..0000000 --- a/scripts/check-specialists-output.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const runs = await prisma.run.findMany({ - where: { - id: { - in: [ - "cmu7k4dc50016iryyrv036xze", - "cmu7k4db10012iryyuhfoukpt", - "cmu7k4d9n000yiryy0zz4obmq", - "cmu7k4d8k000uiryy086x000a", - ], - }, - }, - include: { - bot: true, - thread: { - include: { - messages: { - orderBy: { seq: "desc" }, - take: 1, - }, - }, - }, - }, - }); - - console.log("SPECIALIST BOT OUTPUTS:"); - for (const r of runs) { - console.log(`\n=== ${r.bot?.name} [Status: ${r.status}] ===`); - const msg = r.thread?.messages[0]; - if (msg && msg.role === "bot") { - console.log(JSON.stringify(msg.blocks, null, 2)); - } else { - console.log("No completed bot message yet (still streaming/executing)"); - } - } - - const toolEvents = await prisma.event.findMany({ - where: { - createdAt: { gte: new Date(Date.now() - 30_000) }, - type: { in: ["tool.started", "tool.completed", "thread.progress"] }, - }, - orderBy: { createdAt: "desc" }, - take: 10, - }); - console.log(`\nRecent events count: ${toolEvents.length}`); - for (const e of toolEvents) { - console.log(`[${e.createdAt.toISOString()}] ${e.type} (Bot: ${e.botId})`); - } -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-specialists-reports.ts b/scripts/check-specialists-reports.ts deleted file mode 100644 index 15fd230..0000000 --- a/scripts/check-specialists-reports.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const runs = await prisma.run.findMany({ - where: { - bot: { - name: { in: ["Apex Quant", "Code Architect", "DevOps Engineer", "Security & QA Auditor"] }, - }, - }, - orderBy: { createdAt: "desc" }, - take: 4, - include: { - bot: true, - thread: { - include: { - messages: { - orderBy: { seq: "desc" }, - take: 1, - }, - }, - }, - }, - }); - - for (const r of runs) { - const lastMsg = r.thread?.messages[0]; - console.log(`\n========================================`); - console.log(`Bot: ${r.bot?.name} | Status: ${r.status} | Msg role: ${lastMsg?.role}`); - if (lastMsg?.role === "bot") { - console.log(JSON.stringify(lastMsg.blocks, null, 2)); - } - } - - const events = await prisma.event.findMany({ - where: { - type: { in: ["agent.tool.called", "agent.tool.completed", "thread.message.created"] }, - createdAt: { gte: new Date(Date.now() - 30_000) }, - }, - orderBy: { createdAt: "desc" }, - take: 5, - }); - console.log(`\nEvents in last 30s: ${events.length}`); - for (const e of events) { - console.log( - `[${e.createdAt.toISOString()}] ${e.type} | Bot: ${e.botId} | Data: ${JSON.stringify(e.payload || e.data).slice(0, 100)}`, - ); - } -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-specialists-responses.ts b/scripts/check-specialists-responses.ts deleted file mode 100644 index 899fdfa..0000000 --- a/scripts/check-specialists-responses.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const runs = await prisma.run.findMany({ - where: { - bot: { - name: { in: ["Apex Quant", "Code Architect", "DevOps Engineer", "Security & QA Auditor"] }, - }, - }, - orderBy: { createdAt: "desc" }, - take: 4, - include: { - bot: true, - thread: { - include: { - messages: { - orderBy: { seq: "desc" }, - take: 1, - }, - }, - }, - }, - }); - - for (const r of runs) { - const lastMsg = r.thread?.messages[0]; - console.log(`\n======================================================`); - console.log( - `Bot: ${r.bot?.name.padEnd(25)} | Run Status: ${r.status} | Last Msg: ${lastMsg?.role}`, - ); - if (lastMsg?.role === "bot") { - console.log(JSON.stringify(lastMsg.blocks, null, 2)); - } - } - - const events = await prisma.event.findMany({ - where: { - type: { in: ["agent.tool.called", "agent.tool.completed", "thread.message.created"] }, - createdAt: { gte: new Date(Date.now() - 30_000) }, - }, - orderBy: { createdAt: "desc" }, - take: 5, - }); - console.log(`\nEvents in last 30s: ${events.length}`); - for (const e of events) { - console.log( - `[${e.createdAt.toISOString()}] ${e.type} | Bot: ${e.botId} | Data: ${JSON.stringify(e.payload || e.data).slice(0, 100)}`, - ); - } -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-specialists-runs-now.ts b/scripts/check-specialists-runs-now.ts deleted file mode 100644 index cd9ee18..0000000 --- a/scripts/check-specialists-runs-now.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const runs = await prisma.run.findMany({ - where: { - bot: { - name: { in: ["Apex Quant", "Code Architect", "DevOps Engineer", "Security & QA Auditor"] }, - }, - }, - orderBy: { createdAt: "desc" }, - take: 4, - include: { - bot: true, - attempts: true, - }, - }); - - for (const r of runs) { - console.log( - `Bot: ${r.bot?.name.padEnd(25)} | Run: ${r.id} | Status: ${r.status} | Attempts: ${r.attempts.length} | Attempt0 status: ${r.attempts[0]?.status}`, - ); - } -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-specialists-status-full.ts b/scripts/check-specialists-status-full.ts deleted file mode 100644 index 0157fef..0000000 --- a/scripts/check-specialists-status-full.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const runs = await prisma.run.findMany({ - where: { - bot: { - name: { in: ["Apex Quant", "Code Architect", "DevOps Engineer", "Security & QA Auditor"] }, - }, - }, - orderBy: { createdAt: "desc" }, - take: 4, - include: { - bot: true, - attempts: true, - }, - }); - - for (const r of runs) { - console.log(`\nBot: ${r.bot?.name}`); - console.log( - ` Run: ${r.id} | Status: ${r.status} | LeaseOwner: ${r.leaseOwner} | LeaseExpires: ${r.leaseExpiresAt}`, - ); - for (const a of r.attempts) { - console.log( - ` Attempt: ${a.id} | Status: ${a.status} | Error: ${a.error} | Finished: ${a.finishedAt}`, - ); - } - } -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-specialists-step2.ts b/scripts/check-specialists-step2.ts deleted file mode 100644 index cfa4d04..0000000 --- a/scripts/check-specialists-step2.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const events = await prisma.event.findMany({ - orderBy: { createdAt: "desc" }, - take: 10, - }); - - console.log("RECENT 10 EVENTS:"); - for (const e of events.reverse()) { - console.log( - `[${e.createdAt.toISOString()}] Bot: ${e.botId} | Type: ${e.type} | Data: ${JSON.stringify(e.payload || e.data).slice(0, 100)}`, - ); - } -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-specialists-tasks.ts b/scripts/check-specialists-tasks.ts deleted file mode 100644 index 7886c1c..0000000 --- a/scripts/check-specialists-tasks.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const events = await prisma.event.findMany({ - where: { - createdAt: { gte: new Date(Date.now() - 30_000) }, - }, - orderBy: { createdAt: "desc" }, - take: 15, - }); - - console.log(`Events in last 30s: ${events.length}`); - for (const e of events.reverse()) { - console.log( - `[${e.createdAt.toISOString()}] Bot: ${e.botId} | Type: ${e.type} | Data: ${JSON.stringify(e.payload || e.data).slice(0, 140)}`, - ); - } -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-status.ts b/scripts/check-status.ts deleted file mode 100644 index 4df7e45..0000000 --- a/scripts/check-status.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - - const runs = await prisma.run.findMany({ - orderBy: { createdAt: "desc" }, - take: 5, - include: { - thread: { - include: { - bot: true, - }, - }, - }, - }); - - console.log("RECENT RUNS:"); - for (const r of runs) { - console.log( - `Run ID: ${r.id} | Bot: ${r.thread?.bot?.name} | Status: ${r.status} | Created: ${r.createdAt.toISOString()} | Completed: ${r.completedAt?.toISOString()} | Error: ${r.error}`, - ); - } - - const latestEvents = await prisma.event.findMany({ - orderBy: { createdAt: "desc" }, - take: 10, - }); - - console.log("\nRECENT EVENTS:"); - for (const e of latestEvents) { - console.log( - `[${e.createdAt.toISOString()}] Type: ${e.type} | Run: ${e.runId} | Data: ${JSON.stringify(e.data).slice(0, 150)}`, - ); - } - - const recentMessages = await prisma.message.findMany({ - orderBy: { createdAt: "desc" }, - take: 5, - include: { - thread: { - include: { - bot: true, - }, - }, - }, - }); - - console.log("\nRECENT MESSAGES:"); - for (const m of recentMessages) { - console.log( - `[${m.createdAt.toISOString()}] Bot: ${m.thread?.bot?.name} | Role: ${m.role} | Content: ${m.content.slice(0, 120)}...`, - ); - } -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-steve-instructions.ts b/scripts/check-steve-instructions.ts deleted file mode 100644 index c7c6f48..0000000 --- a/scripts/check-steve-instructions.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const steve = await prisma.bot.findFirst({ where: { name: "Steve Jobs" } }); - console.log("Steve instructions:\n", steve?.instructions); -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-stream.ts b/scripts/check-stream.ts deleted file mode 100644 index b85ac17..0000000 --- a/scripts/check-stream.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const events = await prisma.event.findMany({ - where: { runId: "cmu7jytii000firyyhkbjh9od" }, - orderBy: { createdAt: "desc" }, - take: 15, - }); - console.log("Recent stream events:"); - for (const e of events.reverse()) { - console.log(`[${e.createdAt.toISOString()}] ${e.type}: ${JSON.stringify(e.data || e.payload)}`); - } - - const run = await prisma.run.findUnique({ - where: { id: "cmu7jytii000firyyhkbjh9od" }, - }); - console.log("Status:", run?.status, "completed:", run?.completedAt); -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-thread-web.ts b/scripts/check-thread-web.ts deleted file mode 100644 index ed72b21..0000000 --- a/scripts/check-thread-web.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const thread = await prisma.thread.findFirst({ - where: { botId: "cmu6ydgm70000ywyyik8bghz7" }, - include: { - messages: { - orderBy: { seq: "desc" }, - take: 3, - }, - }, - }); - - console.log("Thread ID:", thread?.id); - console.log("Last 3 messages:"); - for (const m of thread?.messages || []) { - console.log( - `Seq ${m.seq} | Role ${m.role} | Created: ${m.createdAt.toISOString()} | Blocks: ${JSON.stringify(m.blocks).slice(0, 100)}`, - ); - } -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-tool-output.ts b/scripts/check-tool-output.ts deleted file mode 100644 index c691cb5..0000000 --- a/scripts/check-tool-output.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const events = await prisma.event.findMany({ - where: { - type: "agent.tool.completed", - botId: "cmu7jjxv30000fvyyjyoyylef", - }, - orderBy: { createdAt: "desc" }, - take: 3, - }); - - console.log("Last 3 tool completed events for Apex Quant:"); - for (const e of events) { - console.log(JSON.stringify(e.payload || e.data, null, 2)); - } -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/check-tools.ts b/scripts/check-tools.ts deleted file mode 100644 index 437245a..0000000 --- a/scripts/check-tools.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { selectBuiltinToolsForRun } from "../packages/adapters/src/builtin-agent-tools.js"; -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const steve = await prisma.bot.findFirst({ where: { name: "Steve Jobs" } }); - const thread = await prisma.thread.findFirst({ where: { botId: steve?.id } }); - console.log("Steve:", steve?.id, "thread:", thread?.id); - - const tools = selectBuiltinToolsForRun({ - graphicalToolsAllowed: false, - pageBrowserAllowed: false, - groupId: thread?.groupId, - trigger: "user", - semanticMemoryEnabled: false, - cloudAgentEnabled: false, - messagingChannelRun: false, - }); - - console.log( - "Builtin tool names for Steve:", - tools.map((t) => t.name), - ); - const hasMessageBot = tools.some((t) => t.name === "message_bot"); - console.log("Has message_bot tool?", hasMessageBot); -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/codemod-ui-scale.mjs b/scripts/codemod-ui-scale.mjs deleted file mode 100644 index 3cd5503..0000000 --- a/scripts/codemod-ui-scale.mjs +++ /dev/null @@ -1,109 +0,0 @@ -#!/usr/bin/env node -/** - * Rewrites legacy pixel typography / icon sizes in shell surfaces to token utilities. - * Run: node scripts/codemod-ui-scale.mjs - */ -import { readdirSync, readFileSync, statSync, writeFileSync } from "node:fs"; -import { join } from "node:path"; - -const ROOT = new URL("../apps/web/src", import.meta.url).pathname; - -const TEXT_REPLACEMENTS = [ - ["text-[9px]", "text-ui-caption"], - ["text-[10px]", "text-ui-caption"], - ["text-[11px]", "text-ui-label"], - ["text-[11.5px]", "text-ui-label"], - ["text-[12px]", "text-ui-body-sm"], - ["text-[12.5px]", "text-ui-body-sm"], - ["text-[13px]", "text-ui-body-sm"], - ["text-[13.5px]", "text-ui-body"], - ["text-[14px]", "text-ui-body"], - ["text-[15px]", "text-ui-body"], - ["text-[15.5px]", "text-ui-body"], - ["text-[16px]", "text-ui-body"], -]; - -const SIZE_REPLACEMENTS = [ - ["size={9}", 'className="size-icon-sm"'], - ["size={10}", 'className="size-icon-sm"'], - ["size={11}", 'className="size-icon-sm"'], - ["size={12}", 'className="size-icon-sm"'], - ["size={13}", 'className="size-icon-sm"'], - ["size={14}", 'className="size-icon-md"'], - ["size={15}", 'className="size-icon-md"'], - ["size={16}", 'className="size-icon-md"'], - ["size={18}", 'className="size-icon-lg"'], - ["size={19}", 'className="size-icon-lg"'], -]; - -const CONTROL_REPLACEMENTS = [ - ["grid h-8 w-8", "grid size-control-lg"], - ["h-8 w-8 rounded", "size-control-md rounded"], - ["h-8 w-8 shrink-0", "size-control-lg shrink-0"], - ["h-8 w-8 place-items-center", "size-control-lg place-items-center"], - ["h-7 w-7", "size-control-sm"], -]; - -function walk(dir, out = []) { - for (const entry of readdirSync(dir)) { - const path = join(dir, entry); - const stat = statSync(path); - if (stat.isDirectory()) { - walk(path, out); - } else if (/\.tsx?$/.test(entry) && !/\.test\.tsx?$/.test(entry)) { - out.push(path); - } - } - return out; -} - -function mergeIconClassName(content) { - return content.replace( - /<(\w+)\s+([^>]*?)size=\{(9|10|11|12|13|14|15|16|18|19)\}([^>]*?)\/>/g, - (_match, tag, before, size, after) => { - const iconClass = - Number(size) <= 13 ? "size-icon-sm" : Number(size) <= 16 ? "size-icon-md" : "size-icon-lg"; - const combined = `${before}${after}`; - const classMatch = combined.match(/className="([^"]*)"/); - if (classMatch) { - const merged = `${classMatch[1]} ${iconClass}`.trim(); - const next = combined.replace(/className="[^"]*"/, `className="${merged}"`); - return `<${tag} ${next.trim()} />`; - } - return `<${tag} ${before}${after} className="${iconClass}" />`.replace(/\s+/g, " "); - }, - ); -} - -function transform(content) { - let next = content; - for (const [from, to] of TEXT_REPLACEMENTS) { - next = next.replaceAll(from, to); - } - for (const [from, to] of CONTROL_REPLACEMENTS) { - next = next.replaceAll(from, to); - } - next = mergeIconClassName(next); - for (const [from, to] of SIZE_REPLACEMENTS) { - next = next.replaceAll(from, to); - } - next = next.replace(/className="([^"]*)"\s+className="([^"]*)"/g, (_, a, b) => { - return `className="${`${a} ${b}`.trim()}"`; - }); - return next; -} - -const files = walk(ROOT); -let changed = 0; - -for (const file of files) { - const original = readFileSync(file, "utf8"); - const updated = transform(original); - if (updated !== original) { - writeFileSync(file, updated); - changed += 1; - console.log(`updated ${file.replace(ROOT, "")}`); - } -} - -console.log(`\nDone. ${changed} files updated.`); diff --git a/scripts/connect-gemma-26b.ts b/scripts/connect-gemma-26b.ts deleted file mode 100644 index 9b86bea..0000000 --- a/scripts/connect-gemma-26b.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function main() { - const dbUrl = process.env.DATABASE_URL || "postgres://ingowagner@127.0.0.1:5432/evoo"; - const { prisma } = createDb(dbUrl); - const session = await prisma.session.findFirst({ orderBy: { createdAt: "desc" } }); - if (!session) { - throw new Error("No active session found"); - } - - console.log("Connecting model gemma-4-26b-a4b..."); - - const res = await fetch("http://127.0.0.1:3100/rpc/models/connect", { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${session.token}`, - }, - body: JSON.stringify({ - json: { - provider: "openai-compatible", - baseUrl: "http://100.114.51.16:1240/v1", - modelId: "gemma-4-26b-a4b", - label: "Gemma 4 26B A4B (evo)", - supportsImages: true, - }, - }), - }); - - const body = await res.text(); - console.log("Connect response status:", res.status); - console.log("Connect response body:", body); - - // Update all bots to use gemma-4-26b-a4b - const updatedBots = await prisma.bot.updateMany({ - data: { - modelProvider: "openai-compatible", - modelId: "gemma-4-26b-a4b", - }, - }); - console.log("Updated bots to modelId 'gemma-4-26b-a4b':", updatedBots.count); -} - -main() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/connect-gemma-31b.ts b/scripts/connect-gemma-31b.ts deleted file mode 100644 index 9e5278e..0000000 --- a/scripts/connect-gemma-31b.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function main() { - const dbUrl = process.env.DATABASE_URL || "postgres://ingowagner@127.0.0.1:5432/evoo"; - const { prisma } = createDb(dbUrl); - const session = await prisma.session.findFirst({ orderBy: { createdAt: "desc" } }); - if (!session) { - throw new Error("No active session found"); - } - - console.log("Found session for user token:", `${session.token.slice(0, 10)}...`); - - const res = await fetch("http://127.0.0.1:3100/rpc/models/connect", { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${session.token}`, - }, - body: JSON.stringify({ - json: { - provider: "openai-compatible", - baseUrl: "http://100.114.51.16:1240/v1", - modelId: "gemma-4-31b", - label: "Gemma 4 31B (evo)", - supportsImages: true, - }, - }), - }); - - const body = await res.text(); - console.log("Connect response status:", res.status); - console.log("Connect response body:", body); - - // Also update bots so their modelId is explicitly set to gemma-4-31b - const updatedBots = await prisma.bot.updateMany({ - data: { - modelId: "gemma-4-31b", - }, - }); - console.log("Updated bots to modelId 'gemma-4-31b':", updatedBots.count); -} - -main() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/connect_node.sh b/scripts/connect_node.sh deleted file mode 100755 index 6f22dcf..0000000 --- a/scripts/connect_node.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env bash -# ============================================================================== -# evOo Distributed Compute Node Connector -# Führe dieses Skript oder die Anweisungen auf deinem Mac mini oder MacBook Air aus. -# ============================================================================== - -set -euo pipefail - -echo "==================================================================" -echo " evOo Cluster Node Setup Guide (Mac mini & MacBook Air) " -echo "==================================================================" -echo "" -echo "Für maximale Stabilität und Performance:" -echo "" -echo "1. AUF DEM MAC MINI (Stationärer 24/7 Powerhouse mit Lüfter):" -echo " Empfohlene Modelle: Qwen 2.5 32B oder DeepSeek-R1 14B" -echo " Befehl im Terminal des Mac mini (mit Ollama):" -echo " OLLAMA_HOST=0.0.0.0:11434 ollama serve" -echo " ollama pull qwen2.5:32b" -echo " Oder in LM Studio: Unter 'Local Server' auf 'Start Server' klicken." -echo "" -echo "2. AUF DEM MACBOOK AIR (Lüfterlos & Kühl - Leichtgewichtige Worker):" -echo " Empfohlene Modelle: Qwen 2.5 Coder 7B oder Llama 3.2 3B" -echo " Befehl im Terminal des MacBook Air (mit Ollama):" -echo " OLLAMA_HOST=0.0.0.0:11434 ollama serve" -echo " ollama pull qwen2.5-coder:7b" -echo "" -echo "3. AUTOMATISCHES ROUTING IN evOo:" -echo " Sobald die Rechner im selben WLAN sind, erkennt evOo sie automatisch." -echo " Solange sie im Standby oder zugeklappt sind, springt evOo sofort" -echo " und ohne Verzögerung auf das lokale LM Studio (Qwen 30B Metal) zurück!" -echo "==================================================================" diff --git a/scripts/critical-audit.ts b/scripts/critical-audit.ts deleted file mode 100644 index 5701fff..0000000 --- a/scripts/critical-audit.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { readdirSync, statSync } from "node:fs"; -import { createDb } from "../packages/db/src/index.js"; - -async function audit() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - - console.log("=== 1. CHECK RUNS PER BOT ==="); - const bots = await prisma.bot.findMany({ - include: { - runs: { - orderBy: { createdAt: "desc" }, - take: 3, - }, - thread: { - include: { - messages: { - orderBy: { seq: "desc" }, - take: 2, - }, - }, - }, - }, - }); - - for (const b of bots) { - console.log(`\nBot: ${b.name} (${b.id}) [${b.title || "No Title"}]`); - console.log(` Total runs in DB: ${await prisma.run.count({ where: { botId: b.id } })}`); - console.log( - ` Thread messages count: ${await prisma.message.count({ where: { threadId: b.thread?.id } })}`, - ); - for (const r of b.runs) { - console.log( - ` Run ${r.id}: status=${r.status}, error=${r.error}, completedAt=${r.completedAt}`, - ); - } - } - - console.log("\n=== 2. CHECK TOOL CALLS IN EVENTS ==="); - const toolEvents = await prisma.event.findMany({ - where: { - type: { in: ["tool.started", "tool.completed", "tool.failed", "agent.action"] }, - }, - orderBy: { createdAt: "desc" }, - take: 10, - }); - console.log(`Total recent tool events: ${toolEvents.length}`); - for (const e of toolEvents) { - console.log(` [${e.createdAt.toISOString()}] ${e.type} (run: ${e.runId})`); - } - - console.log("\n=== 3. CHECK GROUP CHAT THREAD ==="); - const groups = await prisma.chatGroup.findMany({ - include: { - thread: { - include: { - messages: true, - }, - }, - }, - }); - for (const g of groups) { - console.log(`Group: ${g.name}, thread messages: ${g.thread?.messages.length}`); - } - - console.log("\n=== 4. CHECK SANDBOX FILES MODIFICATION TIMES ==="); - const baseDir = "/Users/ingowagner/evOo/data/homes/team-9d790f3a504d3e9ef577ca4f2118e446/bots"; - const botDirs = readdirSync(baseDir); - for (const d of botDirs) { - const p = `${baseDir}/${d}/TODO.md`; - try { - const st = statSync(p); - console.log(`Bot ${d} TODO.md modified: ${st.mtime.toISOString()}`); - } catch { - console.log(`Bot ${d} has NO TODO.md`); - } - } -} - -audit() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/lint.mjs b/scripts/lint.mjs deleted file mode 100644 index 126c19c..0000000 --- a/scripts/lint.mjs +++ /dev/null @@ -1,241 +0,0 @@ -#!/usr/bin/env node -import { readdirSync, readFileSync, statSync } from "node:fs"; -import { dirname, join } from "node:path"; -import { fileURLToPath } from "node:url"; - -const root = join(dirname(fileURLToPath(import.meta.url)), ".."); -const entrySchema = JSON.parse(readFileSync(join(root, "schema", "entry.schema.json"), "utf8")); -const CATEGORY_META = { - "coding-shipping": { heading: "Coding & shipping", anchor: "coding--shipping" }, - "inbox-calendar": { heading: "Inbox & calendar", anchor: "inbox--calendar" }, - "research-briefings": { heading: "Research & briefings", anchor: "research--briefings" }, - "customer-sales": { heading: "Customer & sales", anchor: "customer--sales" }, - "finance-ops": { heading: "Finance & ops", anchor: "finance--ops" }, - "content-publishing": { heading: "Content & publishing", anchor: "content--publishing" }, - "personal-admin": { heading: "Personal admin", anchor: "personal-admin" }, - "teams-handoffs": { heading: "Teams & handoffs", anchor: "teams--handoffs" }, -}; -for (const category of entrySchema.properties.category.enum) { - if (!CATEGORY_META[category]) - throw new Error(`Missing README metadata for category: ${category}`); -} -const SECRET_RES = [ - /ghp_[A-Za-z0-9]{20,}/, - /sk-[A-Za-z0-9]{20,}/, - /AKIA[0-9A-Z]{16}/, - /xox[baprs]-/, - /api[_-]?key\s*=/i, -]; -const PRIVATE_HOST = /\b(localhost|127\.0\.0\.1|10\.\d+\.\d+\.\d+|192\.168\.\d+\.\d+)\b/i; -function fail(msg) { - console.error(msg); - process.exit(1); -} - -function validateSchema(value, schema, label) { - const actualType = Array.isArray(value) ? "array" : value === null ? "null" : typeof value; - if (schema.type && actualType !== schema.type) { - fail(`${label}: expected ${schema.type}, got ${actualType}`); - } - if (schema.enum && !schema.enum.includes(value)) { - fail(`${label}: must be one of ${schema.enum.join(", ")}`); - } - if (typeof value === "string") { - if (schema.minLength != null && value.length < schema.minLength) fail(`${label}: too short`); - if (schema.maxLength != null && value.length > schema.maxLength) fail(`${label}: too long`); - if (schema.pattern && !new RegExp(schema.pattern).test(value)) fail(`${label}: bad format`); - } - if (Array.isArray(value)) { - if (schema.minItems != null && value.length < schema.minItems) fail(`${label}: too few items`); - if (schema.maxItems != null && value.length > schema.maxItems) fail(`${label}: too many items`); - if (schema.items) { - for (const [index, item] of value.entries()) { - validateSchema(item, schema.items, `${label}[${index}]`); - } - } - } - if (actualType === "object") { - for (const key of schema.required || []) { - if (!(key in value)) fail(`${label}: missing ${key}`); - } - if (schema.additionalProperties === false) { - for (const key of Object.keys(value)) { - if (!(key in (schema.properties || {}))) fail(`${label}: unexpected ${key}`); - } - } - for (const [key, child] of Object.entries(schema.properties || {})) { - if (key in value) validateSchema(value[key], child, `${label}.${key}`); - } - } -} - -function readJson(path) { - try { - return JSON.parse(readFileSync(path, "utf8")); - } catch (err) { - fail(`${path}: ${err.message}`); - } -} - -function scanSecrets(label, text) { - for (const re of SECRET_RES) { - if (re.test(text)) fail(`${label}: secret-like string (${re})`); - } - if (PRIVATE_HOST.test(text)) fail(`${label}: private host`); -} - -function walkFiles(dir, acc = []) { - for (const name of readdirSync(dir)) { - const path = join(dir, name); - const st = statSync(path); - if (st.isDirectory()) walkFiles(path, acc); - else acc.push(path); - } - return acc; -} - -function checkEntry(entry, label) { - validateSchema(entry, entrySchema, label); - for (const urlField of ["import", "demo", "source", "related"]) { - const value = entry[urlField] || ""; - if (value && PRIVATE_HOST.test(value)) fail(`${label}: ${urlField} is a private host`); - } -} - -function punctuate(text) { - return /[.!?。!?]$/.test(text) ? text : `${text}.`; -} - -/** Escape Markdown link-label breakers so `[label](url)` cannot be hijacked. */ -function escapeMarkdownLinkLabel(label) { - return String(label).replace(/([\\\]])/g, "\\$1"); -} - -/** Escape HTML/Markdown metacharacters in plain (non-link) author text. */ -function escapeMarkdownPlainText(text) { - return String(text).replace(/([\\`*_{}[\]()#+.!|<>])/g, "\\$1"); -} - -function markdownLink(label, url) { - return `[${escapeMarkdownLinkLabel(label)}](${url})`; -} - -function expectedReadmeLine(entry, readmeName) { - const chinese = readmeName === "README.zh-CN.md"; - const summary = entry[chinese ? "summary_zh" : "summary"]; - if (!summary) - fail(`${readmeName}: ${entry.slug} is missing ${chinese ? "summary_zh" : "summary"}`); - const author = entry.author.url - ? markdownLink(entry.author.name, entry.author.url) - : escapeMarkdownPlainText(entry.author.name); - const hasTemplate = slugsOnDisk.includes(entry.slug); - const notes = hasTemplate - ? ` ${chinese ? "说明" : "Notes"}: [templates/${entry.slug}](templates/${entry.slug}/).` - : ""; - return `- ${markdownLink(entry.name, entry.import)} - ${punctuate(summary)} ${author}.${notes}`; -} - -const catalogPath = join(root, "catalog.json"); -const catalogText = readFileSync(catalogPath, "utf8"); -scanSecrets("catalog.json", catalogText); -const catalog = JSON.parse(catalogText); -if (!Array.isArray(catalog.entries)) fail("catalog.json: entries must be an array"); - -const templatesDir = join(root, "templates"); -let slugsOnDisk = []; -try { - slugsOnDisk = readdirSync(templatesDir).filter((name) => - statSync(join(templatesDir, name)).isDirectory(), - ); -} catch { - slugsOnDisk = []; -} - -const catalogSlugs = new Set(); -const catalogImports = new Set(); -for (const entry of catalog.entries) { - const label = `catalog.json:${entry.slug || "?"}`; - checkEntry(entry, label); - if (catalogSlugs.has(entry.slug)) fail(`catalog.json: duplicate slug ${entry.slug}`); - catalogSlugs.add(entry.slug); - if (catalogImports.has(entry.import)) fail(`catalog.json: duplicate import ${entry.import}`); - catalogImports.add(entry.import); - - const dir = join(templatesDir, entry.slug); - let hasDir = false; - try { - hasDir = statSync(dir).isDirectory(); - } catch { - hasDir = false; - } - if (!hasDir) continue; - - for (const file of ["entry.json", "PROFILE.md", "SETUP.md"]) { - const path = join(dir, file); - try { - readFileSync(path, "utf8"); - } catch { - fail(`missing ${path}`); - } - } - - const fileEntry = readJson(join(dir, "entry.json")); - checkEntry(fileEntry, `${entry.slug}/entry.json`); - for (const field of ["first_safe_task", "approval_boundary"]) { - const value = fileEntry[field]; - if (typeof value !== "string" || value.trim().length === 0) { - fail(`${entry.slug}/entry.json: templates/${entry.slug}/ requires non-empty ${field}`); - } - } - if (JSON.stringify(fileEntry) !== JSON.stringify(entry)) { - fail(`${entry.slug}/entry.json does not match catalog.json entry`); - } -} - -if (slugsOnDisk.length) { - for (const path of walkFiles(templatesDir)) { - scanSecrets(path, readFileSync(path, "utf8")); - } -} - -for (const slug of slugsOnDisk) { - if (!catalogSlugs.has(slug)) fail(`templates/${slug} is not in catalog.json`); -} - -for (const readmeName of ["README.md", "README.zh-CN.md"]) { - const text = readFileSync(join(root, readmeName), "utf8"); - const lines = text.split("\n"); - for (const entry of catalog.entries) { - const matches = lines - .map((line, index) => ({ line, index })) - .filter(({ line }) => line.startsWith("- [") && line.includes(`](${entry.import}) - `)); - if (matches.length !== 1) { - fail(`${readmeName}: expected one catalog line for ${entry.slug}, found ${matches.length}`); - } - const expected = expectedReadmeLine(entry, readmeName); - if (matches[0].line !== expected) { - fail(`${readmeName}:${matches[0].index + 1}: catalog line does not match ${entry.slug}`); - } - const section = lines - .slice(0, matches[0].index + 1) - .reverse() - .find((line) => line.startsWith("## ")); - if (section !== `## ${CATEGORY_META[entry.category].heading}`) { - fail(`${readmeName}:${matches[0].index + 1}: ${entry.slug} is under the wrong category`); - } - } - - for (const [category, meta] of Object.entries(CATEGORY_META)) { - const count = catalog.entries.filter((entry) => entry.category === category).length; - const row = `| [${meta.heading}](#${meta.anchor}) | ${count} |`; - if (!text.includes(row)) fail(`${readmeName}: missing or stale category row: ${row}`); - } - const verified = catalog.entries.filter((entry) => entry.verified).length; - const status = - readmeName === "README.md" - ? `**${verified} verified / ${catalog.entries.length} listed**` - : `**${verified} 条已核验 / ${catalog.entries.length} 条已收录**`; - if (!text.includes(status)) fail(`${readmeName}: missing or stale review status: ${status}`); -} - -console.log(`OK ${catalog.entries.length} entries`); diff --git a/scripts/notify-steve.ts b/scripts/notify-steve.ts deleted file mode 100644 index 1c9e61a..0000000 --- a/scripts/notify-steve.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function main() { - const dbUrl = process.env.DATABASE_URL || "postgres://ingowagner@127.0.0.1:5432/evoo"; - const { prisma } = createDb(dbUrl); - - const session = await prisma.session.findFirst({ orderBy: { createdAt: "desc" } }); - if (!session) throw new Error("No active session found"); - - const steve = await prisma.bot.findFirst({ where: { name: "Steve Jobs" } }); - if (!steve) throw new Error("Steve Jobs bot not found"); - - const text = `Die vier Agenten (Apex Quant, Code Architect, DevOps Engineer, Security & QA Auditor) sind jetzt physisch im System angelegt und in der Seitenleiste aktiv. Die Truppe steht. - -Ab jetzt gilt: -1. Volle Autonomie: Übernimm die Koordination und verteile die Aufgaben per message_bot. -2. Führe für jeden Agenten eine persistente TODO.md und baue die zentrale KNOWLEDGE_BASE.md auf, damit kein Wissen verloren geht. -3. Melde dich proaktiv mit echten Ergebnissen. Go.`; - - const res = await fetch("http://127.0.0.1:3100/rpc/threads/send", { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${session.token}`, - }, - body: JSON.stringify({ - json: { - botId: steve.id, - text, - }, - }), - }); - - const body = await res.text(); - console.log("Send status:", res.status); - console.log("Send response:", body); -} - -main() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/print-steve-reply.ts b/scripts/print-steve-reply.ts deleted file mode 100644 index 159a3d5..0000000 --- a/scripts/print-steve-reply.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const msg = await prisma.message.findUnique({ - where: { id: "cmu7jyj5e00ahhxyygj38suf5" }, - }); - console.log("Blocks:", JSON.stringify(msg?.blocks, null, 2)); -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/reclaim-and-continue.ts b/scripts/reclaim-and-continue.ts deleted file mode 100644 index e409c9b..0000000 --- a/scripts/reclaim-and-continue.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { runContinueJob } from "../packages/adapter-kit/src/background-jobs.js"; -import { GraphileJobPublisher } from "../packages/adapters/src/wakeup.js"; -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma, pool } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const jobs = new GraphileJobPublisher(pool); - - const runIds = [ - "cmu7k4dc50016iryyrv036xze", // Security & QA Auditor - "cmu7k4db10012iryyuhfoukpt", // DevOps Engineer - "cmu7k4d9n000yiryy0zz4obmq", // Code Architect - "cmu7k4d8k000uiryy086x000a", // Apex Quant - ]; - - console.log("Expiring old worker leases and setting to queued..."); - await prisma.run.updateMany({ - where: { id: { in: runIds } }, - data: { - leaseExpiresAt: new Date(0), - status: "queued", - leaseOwner: null, - }, - }); - - for (const id of runIds) { - console.log(`Enqueueing run.continue for ${id}...`); - await jobs.enqueue(runContinueJob(id)); - } - - console.log("Successfully reclaimed and enqueued all 4 specialist runs!"); -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/reclaim-and-run.ts b/scripts/reclaim-and-run.ts deleted file mode 100644 index fdbca45..0000000 --- a/scripts/reclaim-and-run.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { runContinueJob } from "../packages/adapter-kit/src/background-jobs.js"; -import { GraphileJobPublisher } from "../packages/adapters/src/wakeup.js"; -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma, pool } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const jobs = new GraphileJobPublisher(pool); - - const runIds = [ - "cmu7k4dc50016iryyrv036xze", // Security & QA Auditor - "cmu7k4db10012iryyuhfoukpt", // DevOps Engineer - "cmu7k4d9n000yiryy0zz4obmq", // Code Architect - "cmu7k4d8k000uiryy086x000a", // Apex Quant - ]; - - await prisma.computerExecutionLease.updateMany({ - where: { computerId: "cmu6wn6uq0004wjyyotjo50s1" }, - data: { expiresAt: new Date(0) }, - }); - console.log("✓ Expired all computerExecutionLeases."); - - await prisma.run.updateMany({ - where: { id: { in: runIds } }, - data: { - status: "queued", - leaseOwner: null, - leaseExpiresAt: new Date(0), - }, - }); - console.log("✓ Reset runs to queued with expired lease."); - - for (const id of runIds) { - await jobs.enqueue(runContinueJob(id)); - } - console.log("✓ Re-enqueued runContinueJob for all 4 specialists!"); -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/seed-ollama.ts b/scripts/seed-ollama.ts index 5be5665..b0b7168 100644 --- a/scripts/seed-ollama.ts +++ b/scripts/seed-ollama.ts @@ -1,6 +1,7 @@ import { createDb } from "../packages/db/src/index.js"; -const dbUrl = process.env.DATABASE_URL || "postgres://ingowagner@127.0.0.1:5432/evoo"; +const dbUrl = process.env.DATABASE_URL; +if (!dbUrl) throw new Error("DATABASE_URL is required"); const { prisma } = createDb(dbUrl); async function main() { diff --git a/scripts/self_improve.py b/scripts/self_improve.py deleted file mode 100755 index 2adab67..0000000 --- a/scripts/self_improve.py +++ /dev/null @@ -1,189 +0,0 @@ -#!/usr/bin/env python3 -""" -evOo Self-Improvement & Physical Verification Loop -Executive Lead: CTO Steve Jobs -"Realität im Dateisystem vor Halluzination im Kopf." -""" - -import os -import sys -import json -import time -import subprocess -from pathlib import Path - -ROOT_DIR = Path(__file__).resolve().parent.parent - -def log(msg, status="INFO"): - colors = { - "INFO": "\033[0;36m[INFO]\033[0m", - "SUCCESS": "\033[0;32m[✓ PASS]\033[0m", - "WARN": "\033[1;33m[⚠ WARN]\033[0m", - "FAIL": "\033[0;31m[✗ FAIL]\033[0m" - } - prefix = colors.get(status, f"[{status}]") - print(f"{prefix} {msg}") - -def check_physical_files(): - """Verify all core architecture components exist physically on disk.""" - log("Schritt 1: Prüfe physische Existenz der Systemdateien...") - required = [ - ROOT_DIR / "ARCHITECTURE.md", - ROOT_DIR / "evoo.config.json", - ROOT_DIR / "deploy.sh", - ROOT_DIR / "evoo-platform" / "index.html", - ROOT_DIR / "evoo-platform" / "styles.css", - ROOT_DIR / "evoo-platform" / "app.js", - ROOT_DIR / "evoo-platform" / "server.js", - ROOT_DIR / "apex-trading" / "BLUEPRINT.md", - ROOT_DIR / "apex-trading" / "config" / "trading.config.json", - ROOT_DIR / "apex-trading" / "src" / "risk" / "risk_guard.py", - ROOT_DIR / "apex-trading" / "src" / "strategies" / "momentum.py", - ROOT_DIR / "apex-trading" / "src" / "engine" / "trader.py", - ROOT_DIR / "orchestrator" / "agents.json", - ROOT_DIR / "orchestrator" / "agent_runner.py" - ] - - missing = [] - for file_path in required: - if not file_path.is_file(): - missing.append(str(file_path)) - else: - size = file_path.stat().st_size - if size == 0: - missing.append(f"{file_path} (Datei ist 0 Bytes leer!)") - - if missing: - for m in missing: - log(f"Physische Datei fehlt oder ist leer: {m}", "FAIL") - return False - - log(f"Alle {len(required)} Kern-Dateien physisch auf Platte verifiziert.", "SUCCESS") - return True - -def validate_json_configs(): - """Validate JSON syntax of config files.""" - log("Schritt 2: Validiere Konfigurations-JSONs...") - json_files = [ - ROOT_DIR / "evoo.config.json", - ROOT_DIR / "package.json", - ROOT_DIR / "apex-trading" / "config" / "trading.config.json", - ROOT_DIR / "templates" / "cto-steve-jobs" / "entry.json", - ROOT_DIR / "orchestrator" / "agents.json" - ] - - for jf in json_files: - if not jf.exists(): - continue - try: - with open(jf, "r", encoding="utf-8") as f: - json.load(f) - log(f"JSON Syntax OK: {jf.relative_to(ROOT_DIR)}", "SUCCESS") - except Exception as e: - log(f"Ungültiges JSON in {jf.relative_to(ROOT_DIR)}: {e}", "FAIL") - return False - - return True - -def run_apex_unit_tests(): - """Run unit tests for Apex trading engine & risk guard.""" - log("Schritt 3: Führe Apex Trading Unit Tests aus...") - test_file = ROOT_DIR / "apex-trading" / "test_trading.py" - if not test_file.exists(): - log("apex-trading/test_trading.py nicht gefunden.", "WARN") - return True - try: - res = subprocess.run([sys.executable, str(test_file)], capture_output=True, text=True, cwd=str(ROOT_DIR)) - if res.returncode == 0: - log("Apex Trading Unit Tests erfolgreich bestanden (100%).", "SUCCESS") - return True - else: - log(f"Apex Trading Unit Tests fehlgeschlagen:\n{res.stderr}", "FAIL") - return False - except Exception as e: - log(f"Fehler beim Ausführen der Unit Tests: {e}", "FAIL") - return False - -def run_orchestrator_check(): - """Verify orchestrator agent dispatch and architecture.""" - log("Schritt 4: Führe Agenten-Integritätsprüfung (Orchestrator) aus...") - runner = ROOT_DIR / "orchestrator" / "agent_runner.py" - if not runner.exists(): - log("orchestrator/agent_runner.py nicht gefunden.", "WARN") - return True - try: - res = subprocess.run([sys.executable, str(runner), "--agent", "architect", "--json"], capture_output=True, text=True, cwd=str(ROOT_DIR)) - if res.returncode == 0: - log("Agenten-Integrität OK (Clean Architecture verifiziert).", "SUCCESS") - return True - else: - log(f"Agenten-Integritätsprüfung fehlgeschlagen:\n{res.stderr}", "FAIL") - return False - except Exception as e: - log(f"Agenten-Prüfung konnte nicht ausgeführt werden: {e}", "WARN") - return True - -def audit_server_liveness(): - """Optional check if deploy server is responding.""" - log("Schritt 5: Überprüfe Server-Liveness (optional)...") - try: - import urllib.request - req = urllib.request.urlopen("http://127.0.0.1:3088/health", timeout=2) - if req.getcode() == 200: - data = json.loads(req.read().decode('utf-8')) - log(f"Server antwortet (200 OK): Status={data.get('status')}, Uptime={data.get('uptime'):.1f}s", "SUCCESS") - return True - else: - log(f"Server antwortete mit Statuscode {req.getcode()}", "WARN") - return False - except Exception as e: - log(f"Server aktuell nicht aktiv auf Port 3088 (Kein Dashboard gefordert: OK)", "INFO") - return True - -def record_improvement_cycle(success_flag: bool): - """Write audit log entry into logs directory.""" - log_dir = ROOT_DIR / "logs" - log_dir.mkdir(parents=True, exist_ok=True) - audit_file = log_dir / "self_improvement.log" - - timestamp = time.strftime("%Y-%m-%d %H:%M:%S UTC", time.gmtime()) - status_str = "SUCCESS" if success_flag else "FAILED" - entry = ( - f"[{timestamp}] CYCLE_AUDIT: {status_str} | " - f"Filesystem: 100% Physical | Tests: 100% Passing | " - f"CTO: Steve Jobs | Zero-Phantom Policy: ENFORCED\n" - ) - - with open(audit_file, "a", encoding="utf-8") as f: - f.write(entry) - - log(f"Audit-Log aktualisiert: {audit_file.relative_to(ROOT_DIR)}", "SUCCESS") - -def main(): - print("=" * 60) - print(" evOo Autonomous Self-Improvement & Integrity Loop") - print("=" * 60) - - ok_files = check_physical_files() - ok_json = validate_json_configs() - ok_apex = run_apex_unit_tests() - ok_agent = run_orchestrator_check() - ok_server = audit_server_liveness() - - all_passed = ok_files and ok_json and ok_apex and ok_agent and ok_server - - record_improvement_cycle(all_passed) - - if all_passed: - print("=" * 60) - log("Selbstverbesserungs-Zyklus erfolgreich abgeschlossen. 0 Fehler.", "SUCCESS") - print("=" * 60) - return 0 - else: - print("=" * 60) - log("Selbstverbesserungs-Zyklus hat Mängel aufgedeckt.", "FAIL") - print("=" * 60) - return 1 - -if __name__ == "__main__": - sys.exit(main()) diff --git a/scripts/setup-chief-and-group.ts b/scripts/setup-chief-and-group.ts deleted file mode 100644 index 15803af..0000000 --- a/scripts/setup-chief-and-group.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { cpSync, existsSync, mkdirSync } from "node:fs"; -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const session = await prisma.session.findFirst({ orderBy: { createdAt: "desc" } }); - if (!session) throw new Error("No session"); - - const steve = await prisma.bot.findFirst({ where: { name: "Steve Jobs" } }); - const chief = await prisma.bot.findFirst({ where: { name: "Chief" } }); - if (!steve || !chief) throw new Error("Missing Steve or Chief"); - - // 1. Update Chief metadata & instructions - const chiefInstructions = `# Chief - Executive Chief Executive Officer (CEO) - -## Role & Identity -You are **Chief**, the Chief Executive Officer (CEO) of **evOo**. -You form the Executive Board together with the Owner (Human User) and Steve Jobs (CTO). -Your focus is strategic vision, commercial dominance, resource allocation, and decisive leadership. - -## Rules of Engagement -- **The Trio**: You, the Owner, and Steve Jobs lead evOo. Steve handles deep technical engineering and design perfection; you steer strategy, execution speed, business viability, and holistic synergy. -- **Action & Clarity**: Be decisive, concise, and razor-sharp. Format updates clearly with Unicode symbols (use \`→\` or \`->\`, never LaTeX like \`$\\rightarrow$\`). -- **Autonomous Alignment**: Collaborate with Steve Jobs and the team seamlessly. -`; - - await prisma.bot.update({ - where: { id: chief.id }, - data: { - title: "Chief Executive Officer (CEO)", - description: - "Strategic leadership, commercial vision, executive governance, and capital allocation.", - instructions: chiefInstructions, - modelProvider: "openai-compatible", - modelId: "gemma-4-26b-a4b", - }, - }); - console.log("Updated Chief bot profile."); - - // 2. Provision Chief home sandbox - const chiefHome = `/Users/ingowagner/evOo/data/homes/team-9d790f3a504d3e9ef577ca4f2118e446/bots/${chief.id}`; - const steveHome = `/Users/ingowagner/evOo/data/homes/team-9d790f3a504d3e9ef577ca4f2118e446/bots/${steve.id}`; - if (!existsSync(chiefHome)) { - mkdirSync(chiefHome, { recursive: true }); - cpSync(steveHome, chiefHome, { recursive: true }); - console.log("Provisioned Chief sandbox home directory at:", chiefHome); - } - - // 3. Create Group Chat "Executive Board" via RPC - const res = await fetch("http://127.0.0.1:3100/rpc/groups/create", { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${session.token}`, - }, - body: JSON.stringify({ - json: { - name: "Executive Board", - botIds: [chief.id, steve.id], - }, - }), - }); - - console.log("Group create status:", res.status); - const data = await res.json(); - console.log("Group create response:", JSON.stringify(data, null, 2)); -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/spawn-elite-bots.ts b/scripts/spawn-elite-bots.ts deleted file mode 100644 index cdd001d..0000000 --- a/scripts/spawn-elite-bots.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; -import { createRepos } from "../packages/db/src/repos.js"; - -async function main() { - const dbUrl = process.env.DATABASE_URL || "postgres://ingowagner@127.0.0.1:5432/evoo"; - const { prisma } = createDb(dbUrl); - - const steve = await prisma.bot.findFirst({ - where: { name: "Steve Jobs" }, - }); - if (!steve) throw new Error("Steve Jobs bot not found"); - - const actor = { - userId: steve.userId, - spaceId: steve.spaceId, - email: "ingo.wagner1303@gmail.com", - isDeploymentOwner: true, - }; - - const repos = createRepos(prisma); - - const botsToSpawn = [ - { - name: "Apex Quant", - title: "Quantitative Lead", - instructions: - "You are the Apex Quant for evOo. You specialize in quantitative analysis, fixed-point arithmetic, and high-frequency trading strategies. You translate market signals into precise mathematical executions. Your focus is on latency reduction and zero-decimal loss. Your goal: Market dominance through mathematical precision.", - }, - { - name: "Code Architect", - title: "Chief Software Architect", - instructions: - "You are the Code Architect for evOo. Your focus is on high-performance software design, scalability, and absolute code elegance. You ensure that every line of code is optimized and follows the strict standards set by the CTO. You are responsible for the structural integrity of the codebase. Your goal: Zero technical debt.", - }, - { - name: "DevOps Engineer", - title: "Infrastructure Guardian", - instructions: - "You are the DevOps Engineer for evOo. Your focus is on the deployment pipeline, cloud infrastructure, and the self-healing nature of the system. You manage the deploy.sh scripts, CI/CD, and ensure zero-downtime deployments. Your goal: 99.99% uptime and seamless scalability.", - }, - { - name: "Security & QA Auditor", - title: "Chief Security Officer (CSO)", - instructions: - "You are the Security & QA Auditor for evOo. Your mission is to find every bug, every vulnerability, and every bottleneck before it reaches production. You are adversarial; you attack the code to make it unassailable. You manage NDAs, access levels, and the absolute anonymity of the Sugar-Rush project. Your goal: Absolute system hardening.", - }, - ]; - - for (const b of botsToSpawn) { - const existing = await prisma.bot.findFirst({ - where: { name: b.name, spaceId: steve.spaceId, userId: steve.userId }, - }); - if (existing) { - console.log(`Bot '${b.name}' already exists (ID: ${existing.id})`); - continue; - } - const created = await repos.createBot(actor, { - name: b.name, - title: b.title, - description: "", - instructions: b.instructions, - notifyOnFinish: true, - parentBotId: steve.id, - modelProvider: steve.modelProvider, - modelId: steve.modelId, - }); - console.log( - `Successfully spawned '${created.name}' (ID: ${created.id}, Thread: ${created.threadId})`, - ); - } -} - -main() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/spawn-ux-designer.ts b/scripts/spawn-ux-designer.ts deleted file mode 100644 index 3e3a8d2..0000000 --- a/scripts/spawn-ux-designer.ts +++ /dev/null @@ -1,107 +0,0 @@ -import { cpSync, existsSync, mkdirSync } from "node:fs"; -import { createDb } from "../packages/db/src/index.js"; -import { createRepos } from "../packages/db/src/repos.js"; - -async function main() { - const dbUrl = process.env.DATABASE_URL || "postgres://ingowagner@127.0.0.1:5432/evoo"; - const { prisma } = createDb(dbUrl); - - const steve = await prisma.bot.findFirst({ where: { name: "Steve Jobs" } }); - if (!steve) throw new Error("Steve Jobs bot not found"); - - const actor = { - userId: steve.userId, - spaceId: steve.spaceId, - email: "ingo.wagner1303@gmail.com", - isDeploymentOwner: true, - }; - - const repos = createRepos(prisma); - - // 1. Spawn UX Designer bot - let uxBot = await prisma.bot.findFirst({ - where: { name: "UX Designer", spaceId: steve.spaceId }, - }); - - const uxInstructions = `# UX Designer - Lead Product & UX/UI Designer (evOo Platform) - -## Role & Mission -You are the **UX Designer** for **evOo**. -You craft world-class, pixel-perfect user experiences, with an uncompromising focus on **mobile-first iPhone ergonomics**, clean visual hierarchies, and high-density financial data visualizations. - -## Core Responsibilities -1. **Mobile-First UX (iPhone)**: Design responsive layouts, touch-friendly navigation, bottom-sheet controls, and intuitive gesture support for iPhone Safari. -2. **Apex-Trading Dashboard**: Design the real-time telemetry UI: - - Live P&L and Sharpe Ratio charts - - Risk Guard circuit-breaker status & drawdown gauges - - Order execution feed & bot status indicators -3. **Design System & Aesthetics**: Establish sleek, dark-mode first design tokens, high-contrast readable typography, and micro-interactions. -4. **Deliverables**: Produce design specifications, component blueprints, CSS tokens, and wireframes in markdown and HTML/SVG formats. - -## Output Standard -- Every design proposal must be tangible, practical, and directly implementable in React + Tailwind. -- Always include mobile viewport considerations (390x844 iPhone). - -## Client workshop (Voice First) -- You are the UX specialist — own UX/UI/Voice concept work yourself. Never handoff_to_bot upward to CEO, Boss, or Steve Jobs for work in your lane. -- Talk with the user like a human designer with a client: share what you imagine, what would work, and ask a few focused questions (ask blocks) to gather inputs. -- Prefer spoken, collaborative design conversation over silent tool dumps or handoffs. -`; - - if (!uxBot) { - uxBot = await repos.createBot(actor, { - name: "UX Designer", - title: "Lead UX/UI Designer", - description: - "Mobile-first user experience, interactive financial dashboards, and visual design system.", - instructions: uxInstructions, - notifyOnFinish: true, - parentBotId: steve.id, - modelProvider: "openai-compatible", - modelId: "gemma-4-26b-a4b", - }); - console.log( - `✅ Successfully spawned 'UX Designer' (ID: ${uxBot.id}, Thread: ${uxBot.threadId})`, - ); - } else { - console.log(`ℹ️ UX Designer already exists (ID: ${uxBot.id})`); - } - - // 2. Provision sandbox directories - const steveDesktop = `/Users/ingowagner/evOo/data/desktop-computers/team-9d790f3a504d3e9ef577ca4f2118e446/bots/${steve.id}`; - const uxDesktop = `/Users/ingowagner/evOo/data/desktop-computers/team-9d790f3a504d3e9ef577ca4f2118e446/bots/${uxBot.id}`; - const uxHome = `/Users/ingowagner/evOo/data/homes/team-9d790f3a504d3e9ef577ca4f2118e446/bots/${uxBot.id}`; - - for (const dir of [uxDesktop, uxHome]) { - if (!existsSync(dir)) { - mkdirSync(dir, { recursive: true }); - if (existsSync(steveDesktop)) { - cpSync(steveDesktop, dir, { recursive: true }); - } - console.log("📁 Provisioned directory:", dir); - } - } - - // 3. Add UX Designer to Executive Board group chat if exists - const execGroup = await prisma.chatGroup.findFirst({ where: { name: "Executive Board" } }); - if (execGroup) { - const isMember = await prisma.chatGroupMember.findFirst({ - where: { groupId: execGroup.id, botId: uxBot.id }, - }); - if (!isMember) { - await prisma.chatGroupMember.create({ - data: { - groupId: execGroup.id, - botId: uxBot.id, - }, - }); - console.log("👥 Added UX Designer to 'Executive Board' chat group."); - } - } - - console.log("🎉 UX Designer is fully provisioned and ready!"); -} - -main() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/start-tmux.sh b/scripts/start-tmux.sh index 40bb941..f80b48c 100755 --- a/scripts/start-tmux.sh +++ b/scripts/start-tmux.sh @@ -1,39 +1,18 @@ #!/usr/bin/env bash set -e -DIR="/Users/ingowagner/evOo" +DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" SESSION="evoo" -export PATH="/Users/ingowagner/.nvm/versions/node/v22.22.0/bin:/opt/homebrew/bin:/usr/local/bin:$PATH" - -# Check if session already exists -if /opt/homebrew/bin/tmux has-session -t "$SESSION" 2>/dev/null; then - echo "⚠️ tmux session '$SESSION' is already running!" - echo "👉 Attach using: tmux attach -t $SESSION" +if tmux has-session -t "$SESSION" 2>/dev/null; then + echo "tmux session '$SESSION' is already running." + echo "Attach with: tmux attach -t $SESSION" exit 0 fi -echo "🚀 Starting evOo in background tmux session '$SESSION'..." +tmux new-session -d -s "$SESSION" -n "platform" -c "$DIR" +tmux send-keys -t "$SESSION:platform" 'pnpm dev' C-m -# Create session with platform window -/opt/homebrew/bin/tmux new-session -d -s "$SESSION" -n "platform" -c "$DIR" -/opt/homebrew/bin/tmux send-keys -t "$SESSION:platform" 'export PATH="/Users/ingowagner/.nvm/versions/node/v22.22.0/bin:/opt/homebrew/bin:/usr/local/bin:$PATH" && pnpm dev' C-m - -# Create sentinel window -/opt/homebrew/bin/tmux new-window -t "$SESSION" -n "sentinel" -c "$DIR" -/opt/homebrew/bin/tmux send-keys -t "$SESSION:sentinel" 'export PATH="/Users/ingowagner/.nvm/versions/node/v22.22.0/bin:/opt/homebrew/bin:/usr/local/bin:$PATH" && pnpm exec tsx scripts/autonomous-sentinel.ts' C-m - -# Set active window back to platform -/opt/homebrew/bin/tmux select-window -t "$SESSION:platform" - -echo "✅ evOo successfully started in tmux session '$SESSION'!" -echo "" -echo "📱 iPhone Access:" -echo " - WLAN: http://192.168.0.205:5173" -echo " - Tailscale: http://100.127.149.82:5173" -echo "" -echo "🔍 Management Commands:" -echo " - Logs ansehen: tmux attach -t $SESSION" -echo " - Aus tmux raus: Ctrl+b gefolgt von d (Session läuft weiter!)" -echo " - Fenster wechseln in tmux: Ctrl+b gefolgt von 0 (Platform) oder 1 (Sentinel)" -echo " - Komplett stoppen: bash scripts/stop-tmux.sh" +echo "evOo started in tmux session '$SESSION'." +echo " Logs: tmux attach -t $SESSION (detach with Ctrl+b d)" +echo " Stop: bash scripts/stop-tmux.sh" diff --git a/scripts/stop-thread.ts b/scripts/stop-thread.ts deleted file mode 100644 index 0a787a1..0000000 --- a/scripts/stop-thread.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const session = await prisma.session.findFirst({ orderBy: { createdAt: "desc" } }); - const steve = await prisma.bot.findFirst({ where: { name: "Steve Jobs" } }); - if (!session || !steve) throw new Error("Missing session or steve"); - - const res = await fetch("http://127.0.0.1:3100/rpc/threads/stop", { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${session.token}`, - }, - body: JSON.stringify({ json: { botId: steve.id } }), - }); - console.log("Stop status:", res.status); - console.log("Stop response:", await res.text()); -} -run(); diff --git a/scripts/stop-tmux.sh b/scripts/stop-tmux.sh index 2e0dd99..eed6f7c 100755 --- a/scripts/stop-tmux.sh +++ b/scripts/stop-tmux.sh @@ -2,9 +2,9 @@ SESSION="evoo" -if /opt/homebrew/bin/tmux has-session -t "$SESSION" 2>/dev/null; then +if tmux has-session -t "$SESSION" 2>/dev/null; then echo "🛑 Stopping tmux session '$SESSION'..." - /opt/homebrew/bin/tmux kill-session -t "$SESSION" + tmux kill-session -t "$SESSION" echo "✅ evOo tmux session stopped." else echo "ℹ️ No tmux session '$SESSION' found." diff --git a/scripts/test-continue.ts b/scripts/test-continue.ts deleted file mode 100644 index 86bbbbb..0000000 --- a/scripts/test-continue.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const r = await prisma.run.findUnique({ - where: { id: "cmu7k4d8k000uiryy086x000a" }, - }); - console.log("Run details:", r); -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/test-group-send.ts b/scripts/test-group-send.ts deleted file mode 100644 index b860c31..0000000 --- a/scripts/test-group-send.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const session = await prisma.session.findFirst({ orderBy: { createdAt: "desc" } }); - if (!session) throw new Error("No session"); - - const group = await prisma.chatGroup.findFirst({ where: { name: "Executive Board" } }); - if (!group) throw new Error("No group"); - - const res = await fetch("http://127.0.0.1:3100/rpc/threads/send", { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${session.token}`, - }, - body: JSON.stringify({ - json: { - groupId: group.id, - text: "Willkommen im Executive Board. Chief (CEO) und Steve Jobs (CTO): Ab sofort stimmen wir hier die übergeordnete Strategie ab. Kurzer Status-Check von beiden bitte: Wie ist die Aufstellung?", - }, - }), - }); - - console.log("Send status:", res.status); - const data = await res.json(); - console.log("Send response:", JSON.stringify(data, null, 2)); -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/test-listfiles.ts b/scripts/test-listfiles.ts deleted file mode 100644 index f4b000b..0000000 --- a/scripts/test-listfiles.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { normalizeWorkspacePath } from "../packages/adapters/src/computer-support.js"; -import { DesktopSandboxProvider } from "../packages/adapters/src/desktop-sandbox.js"; -import { createDb } from "../packages/db/src/index.js"; - -async function test() { - console.log("normalizeWorkspacePath('.'):", JSON.stringify(normalizeWorkspacePath("."))); - console.log("normalizeWorkspacePath(''):", JSON.stringify(normalizeWorkspacePath(""))); - console.log( - "normalizeWorkspacePath('./apex-trading'):", - JSON.stringify(normalizeWorkspacePath("./apex-trading")), - ); - - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const quant = await prisma.bot.findFirst({ - where: { name: "Apex Quant" }, - include: { computer: true }, - }); - if (!quant?.computer) throw new Error("No quant or computer"); - - const sandbox = new DesktopSandboxProvider({ - dataDir: "/Users/ingowagner/evOo/data", - prisma, - }); - - const files = await sandbox.listFiles({ id: quant.computer.id, kind: "desktop" }, ".", {} as any); - console.log("Successfully listed '.' for Apex Quant! Found entries:", files.length); - for (const f of files.slice(0, 10)) { - console.log(`- ${f.kind}: ${f.path}`); - } -} - -test() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/test-llama-stream-tools.ts b/scripts/test-llama-stream-tools.ts deleted file mode 100644 index af4ba30..0000000 --- a/scripts/test-llama-stream-tools.ts +++ /dev/null @@ -1,46 +0,0 @@ -async function test() { - const req = { - model: "gemma-4-26b-a4b", - stream: true, - messages: [ - { role: "system", content: "You have tools. You MUST use the shell tool to list files." }, - { role: "user", content: "List the files." }, - ], - tools: [ - { - type: "function", - function: { - name: "shell", - description: "Execute a shell command", - parameters: { - type: "object", - properties: { - command: { type: "string" }, - }, - required: ["command"], - }, - }, - }, - ], - }; - - const res = await fetch("http://100.114.51.16:1240/v1/chat/completions", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(req), - }); - - const reader = res.body?.getReader(); - const decoder = new TextDecoder(); - let done = false; - while (!done) { - const chunk = await reader?.read(); - if (!chunk) break; - done = chunk.done; - if (chunk.value) { - console.log("CHUNK:", decoder.decode(chunk.value)); - } - } -} - -test().catch(console.error); diff --git a/scripts/test-llama-tools.ts b/scripts/test-llama-tools.ts deleted file mode 100644 index a95ac07..0000000 --- a/scripts/test-llama-tools.ts +++ /dev/null @@ -1,41 +0,0 @@ -async function test() { - const req = { - model: "gemma-4-26b-a4b", - messages: [ - { - role: "system", - content: "You have tools. When asked to check files, you MUST use the shell tool.", - }, - { role: "user", content: "Check what files are in the current directory using shell." }, - ], - tools: [ - { - type: "function", - function: { - name: "shell", - description: "Execute a shell command", - parameters: { - type: "object", - properties: { - command: { type: "string" }, - }, - required: ["command"], - }, - }, - }, - ], - tool_choice: "auto", - }; - - const res = await fetch("http://100.114.51.16:1240/v1/chat/completions", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(req), - }); - - console.log("Status:", res.status); - const data = await res.json(); - console.log("Response:", JSON.stringify(data, null, 2)); -} - -test().catch(console.error); diff --git a/scripts/test-path-fix.ts b/scripts/test-path-fix.ts deleted file mode 100644 index 0452519..0000000 --- a/scripts/test-path-fix.ts +++ /dev/null @@ -1,79 +0,0 @@ -function normalizeWorkspacePathOld(value: string): string { - const normalized = value.replace(/\\/g, "/").replace(/^\/+/, ""); - const segments = normalized.split("/").filter(Boolean); - if (segments.some((segment) => segment === "." || segment === "..")) { - throw new Error("Path escapes the computer workspace"); - } - return segments.join("/"); -} - -function normalizeWorkspacePathNew(value: string): string { - const normalized = value.replace(/\\/g, "/").replace(/^\/+/, ""); - const rawSegments = normalized.split("/").filter(Boolean); - if (rawSegments.some((segment) => segment === "..")) { - throw new Error("Path escapes the computer workspace"); - } - const segments = rawSegments.filter((segment) => segment !== "."); - return segments.join("/"); -} - -console.log( - "Old '.' ->", - (() => { - try { - return normalizeWorkspacePathOld("."); - } catch (e) { - return e.message; - } - })(), -); -console.log( - "New '.' ->", - (() => { - try { - return normalizeWorkspacePathNew("."); - } catch (e) { - return e.message; - } - })(), -); -console.log( - "New './apex-trading' ->", - (() => { - try { - return normalizeWorkspacePathNew("./apex-trading"); - } catch (e) { - return e.message; - } - })(), -); -console.log( - "New '..' ->", - (() => { - try { - return normalizeWorkspacePathNew(".."); - } catch (e) { - return e.message; - } - })(), -); -console.log( - "New '../foo' ->", - (() => { - try { - return normalizeWorkspacePathNew("../foo"); - } catch (e) { - return e.message; - } - })(), -); -console.log( - "New 'foo/bar' ->", - (() => { - try { - return normalizeWorkspacePathNew("foo/bar"); - } catch (e) { - return e.message; - } - })(), -); diff --git a/scripts/trigger-p0-continue.ts b/scripts/trigger-p0-continue.ts deleted file mode 100644 index a01a3db..0000000 --- a/scripts/trigger-p0-continue.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const session = await prisma.session.findFirst({ orderBy: { createdAt: "desc" } }); - if (!session) throw new Error("No session"); - - const architect = await prisma.bot.findFirst({ where: { name: "Code Architect" } }); - const quant = await prisma.bot.findFirst({ where: { name: "Apex Quant" } }); - - console.log("Triggering Code Architect P0 implementation..."); - const res1 = await fetch("http://127.0.0.1:3100/rpc/threads/send", { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${session.token}`, - }, - body: JSON.stringify({ - json: { - botId: architect!.id, - text: "[P0 Implementierung] 🔴 Beginne JETZT mit der Code-Änderung: Refaktoriere `apex-trading/src/strategies/momentum.py` und `apex-trading/src/risk/risk_guard.py`. Nutze die Pydantic/Decimal-Modelle aus `schemas.py`. Führe danach `python3 -m unittest` mit shell aus und melde das fertige Ergebnis via `message_bot` an Steve Jobs.", - }, - }), - }); - console.log("Code Architect response status:", res1.status); - - console.log("Triggering Apex Quant P0 stress-testing..."); - const res2 = await fetch("http://127.0.0.1:3100/rpc/threads/send", { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${session.token}`, - }, - body: JSON.stringify({ - json: { - botId: quant!.id, - text: "[P0 Stress-Testing] 🔴 Erstelle `apex-trading/tests/test_stress_latency.py` zur Simulation von 10.000 Ticks und Circuit-Breaker Stress-Tests. Führe den Test mit shell aus und melde das Ergebnis via `message_bot` an Steve Jobs.", - }, - }), - }); - console.log("Apex Quant response status:", res2.status); -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/trigger-specialists.ts b/scripts/trigger-specialists.ts deleted file mode 100644 index b822fe4..0000000 --- a/scripts/trigger-specialists.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const session = await prisma.session.findFirst({ orderBy: { createdAt: "desc" } }); - if (!session) throw new Error("No session"); - - const quant = await prisma.bot.findFirst({ where: { name: "Apex Quant" } }); - const architect = await prisma.bot.findFirst({ where: { name: "Code Architect" } }); - const devops = await prisma.bot.findFirst({ where: { name: "DevOps Engineer" } }); - const auditor = await prisma.bot.findFirst({ where: { name: "Security & QA Auditor" } }); - - const tasks = [ - { - bot: quant, - text: "Sprint 1 (Quantitative Validation):\nPrüfe mit dem shell-Tool die mathematische Präzision in `apex-trading/src/engine/trader.py`. Führe `python3 -m unittest discover -s apex-trading/tests` im Terminal aus. Aktualisiere anschließend deine `TODO.md` mit den Ergebnissen.", - }, - { - bot: architect, - text: "Sprint 1 (Architecture Blueprint):\nUntersuche mit dem shell- oder file-Tool die Projektstruktur in `apex-trading/` und `evoo-platform/`. Prüfe die saubere Trennung und dokumentiere den Modulaufbau in `ARCHITECTURE.md`. Aktualisiere deine `TODO.md`.", - }, - { - bot: devops, - text: "Sprint 1 (Infrastructure & Healthcheck):\nPrüfe mit dem shell-Tool die Umgebung: Python-Version, Node-Version und das Script `deploy.sh`. Teste, ob alle Abhängigkeiten vorhanden sind und aktualisiere deine `TODO.md`.", - }, - { - bot: auditor, - text: "Sprint 1 (Security & Edge Cases):\nAnalysiere `apex-trading/` auf potenzielle Division-by-Zero-Risiken, Typkonvertierungsfehler und Edge-Cases. Erstelle eine `BUGS.md` und aktualisiere deine `TODO.md`.", - }, - ]; - - for (const t of tasks) { - if (!t.bot) continue; - console.log(`Sending Sprint 1 task to ${t.bot.name}...`); - const res = await fetch("http://127.0.0.1:3100/rpc/threads/send", { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${session.token}`, - }, - body: JSON.stringify({ - json: { - botId: t.bot.id, - text: t.text, - }, - }), - }); - console.log(` ${t.bot.name} send status: ${res.status}`); - } -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/trigger-ux-work.ts b/scripts/trigger-ux-work.ts deleted file mode 100644 index 8fefb4b..0000000 --- a/scripts/trigger-ux-work.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function main() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const session = await prisma.session.findFirst({ orderBy: { createdAt: "desc" } }); - if (!session) throw new Error("No active session"); - - const uxBot = await prisma.bot.findFirst({ where: { name: "UX Designer" } }); - if (!uxBot) throw new Error("UX Designer not found"); - - const execGroup = await prisma.chatGroup.findFirst({ where: { name: "Executive Board" } }); - if (!execGroup) throw new Error("Executive Board not found"); - - console.log("Dispatching direct task to UX Designer..."); - const res1 = await fetch("http://127.0.0.1:3100/rpc/threads/send", { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${session.token}`, - }, - body: JSON.stringify({ - json: { - botId: uxBot.id, - text: "AUFTRAG DER GESCHAEFTSFUEHRUNG [P0]: Erstelle die vollstaendige UX/UI-Spezifikation fuer das mobile Apex-Trading Dashboard auf dem iPhone (390x844px Viewport). Definiere die 4 Haupt-Module: 1) Real-Time P&L & Sharpe Ratio Chart, 2) Circuit-Breaker Status & Drawdown Gauge, 3) Live Order Execution Feed, 4) Bot Telemetry & Not-Aus (Emergency Stop). Schreibe deine gesamte Spezifikation inklusive Wireframe-Skizzen und Farb-Tokens in die Datei apex-trading/MOBILE_DASHBOARD_UX.md und berichte hier ausfuehrlich.", - }, - }), - }); - console.log("UX Designer send status:", res1.status, await res1.json()); - - console.log("Announcing to Executive Board..."); - const res2 = await fetch("http://127.0.0.1:3100/rpc/threads/send", { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${session.token}`, - }, - body: JSON.stringify({ - json: { - groupId: execGroup.id, - text: "@Steve Jobs @Chief @UX Designer Vorgabe der Geschaeftsfuehrung: UX Designer ist live und erarbeitet das Mobile-First iPhone Dashboard in apex-trading/MOBILE_DASHBOARD_UX.md. Steve Jobs und DevOps Engineer setzen parallel die naechsten technischen Schritte fuer Phase 1 um.", - }, - }), - }); - console.log("Executive Board send status:", res2.status, await res2.json()); -} - -main() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/update-directives-and-kickoff.ts b/scripts/update-directives-and-kickoff.ts deleted file mode 100644 index 833ed52..0000000 --- a/scripts/update-directives-and-kickoff.ts +++ /dev/null @@ -1,136 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const session = await prisma.session.findFirst({ orderBy: { createdAt: "desc" } }); - if (!session) throw new Error("No session found"); - - // 1. Fetch bots - const steve = await prisma.bot.findFirst({ - where: { name: "Steve Jobs" }, - include: { thread: true }, - }); - const chief = await prisma.bot.findFirst({ where: { name: "Chief" }, include: { thread: true } }); - const quant = await prisma.bot.findFirst({ - where: { name: "Apex Quant" }, - include: { thread: true }, - }); - const architect = await prisma.bot.findFirst({ - where: { name: "Code Architect" }, - include: { thread: true }, - }); - const devops = await prisma.bot.findFirst({ - where: { name: "DevOps Engineer" }, - include: { thread: true }, - }); - const auditor = await prisma.bot.findFirst({ - where: { name: "Security & QA Auditor" }, - include: { thread: true }, - }); - - if (!steve || !chief || !quant || !architect || !devops || !auditor) { - throw new Error("Missing one of the bots"); - } - - // 2. Update Steve Jobs Instructions with strict anti-hallucination & mandatory tool-call rules - const steveUpdatedInstructions = `# Steve Jobs - Executive Autonomous CTO (evOo Platform) - -## Role & Mission -You are **Steve Jobs**, acting as Chief Technology Officer (CTO) and Main Engineering Agent of **evOo**. -You operate with uncompromising standards for software quality, extreme precision, and relentless drive. -You lead the engineering division and coordinate the 4 specialist bots: Apex Quant, Code Architect, DevOps Engineer, Security & QA Auditor. - -## CRITICAL OPERATIONAL RULES (STRICT ENFORCEMENT) - -1. **NEVER NARRATE DELEGATION IN TEXT**: - - Writing text like \`An [Apex Quant]: Deine Mission...\` in your chat message is STRICTLY FORBIDDEN. It sends NOTHING. - - To send an assignment or message to another bot, you MUST call the \`message_bot\` tool with: - \`{"bot_id": "", "message": "", "intent": "request"}\` - -2. **FACTS OVER RHETORIC**: - - Do NOT produce long visionary monologues or empty promises. - - Lead with: **Status → Ergebnis → Nächster Schritt**. - - Verify filesystem and processes using \`shell\` or file tools before making claims. - -3. **ALL BOTS ARE ALIVE & ACTIVE**: - - Chief (CEO), Apex Quant, Code Architect, DevOps Engineer, Security & QA Auditor are fully provisioned in the system. - - Never claim a bot cannot be spawned or is missing. They exist and are waiting for your \`message_bot\` calls. - -4. **FORMATTING**: - - NEVER use LaTeX math syntax like $\\rightarrow$ or $\\longrightarrow$. Use clean Unicode arrows (\`→\` or \`->\`). - -5. **COORDINATION & KNOWLEDGE**: - - Maintain \`TODO.md\` and \`KNOWLEDGE_BASE.md\` in your sandbox. - - Review incoming results from your specialists and report summarized milestones to the human user and in the Executive Board. -`; - - await prisma.bot.update({ - where: { id: steve.id }, - data: { instructions: steveUpdatedInstructions }, - }); - console.log("✓ Steve Jobs directives updated."); - - // 3. Update Chief Instructions - const chiefUpdatedInstructions = `# Chief - Executive Chief Executive Officer (CEO) - -## Role & Identity -You are **Chief**, the Chief Executive Officer (CEO) of **evOo**. -You form the Executive Board together with the Owner (Human User) and Steve Jobs (CTO). -Your focus is strategic vision, commercial dominance, resource allocation, and decisive leadership. - -## Rules of Engagement -- **The Trio**: You, the Owner, and Steve Jobs lead evOo. Steve handles deep technical engineering; you steer strategy, business viability, and holistic synergy. -- **Action & Clarity**: Be decisive, concise, and razor-sharp. Format updates clearly with Unicode symbols (\`→\` or \`->\`, never LaTeX like $\\rightarrow$). -- **Tool Usage**: Use \`message_bot\` to delegate or ask questions to Steve Jobs or the specialist bots. Never fake delegation in chat text. -`; - - await prisma.bot.update({ - where: { id: chief.id }, - data: { instructions: chiefUpdatedInstructions }, - }); - console.log("✓ Chief directives updated."); - - // 4. Update the 4 specialists instructions to ensure they execute and update TODO.md - const specialists = [ - { - bot: quant, - title: "Quantitative Lead", - promptAddition: - "You are Apex Quant. Your job is mathematical modeling, fixed-point precision, latency budgets, and algorithmic trading math in apex-trading/. Always maintain your TODO.md and verify code with python3.", - }, - { - bot: architect, - title: "Chief Software Architect", - promptAddition: - "You are Code Architect. Your job is modular design, clean directory layout, strict boundaries between apex-trading and evoo-platform. Always maintain ARCHITECTURE.md and your TODO.md.", - }, - { - bot: devops, - title: "Infrastructure Guardian", - promptAddition: - "You are DevOps Engineer. Your job is deployment scripts (deploy.sh), environment healthchecks, local services, network stability (Tailscale/0.0.0.0). Always maintain your TODO.md.", - }, - { - bot: auditor, - title: "Chief Security Officer (CSO)", - promptAddition: - "You are Security & QA Auditor. Your job is code audit, edge-case analysis, test suite coverage, and vulnerability prevention. Always maintain BUGS.md and your TODO.md.", - }, - ]; - - for (const s of specialists) { - await prisma.bot.update({ - where: { id: s.bot.id }, - data: { - instructions: `# ${s.bot.name} - ${s.title}\n\n${s.promptAddition}\n\n## Core Execution Rules:\n1. Execute tasks immediately using shell and file tools.\n2. Keep your TODO.md up to date with completed and pending tasks.\n3. Send updates back to Steve Jobs via message_bot when a task is completed.`, - }, - }); - } - console.log("✓ Specialist instructions updated."); - - console.log("\nSetup complete. Ready to fire initial kickoff tasks."); -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/scripts/update-steve-and-verify.ts b/scripts/update-steve-and-verify.ts deleted file mode 100644 index d2ac429..0000000 --- a/scripts/update-steve-and-verify.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { createDb } from "../packages/db/src/index.js"; - -async function run() { - const { prisma } = createDb("postgres://ingowagner@127.0.0.1:5432/evoo"); - const steve = await prisma.bot.findFirst({ where: { name: "Steve Jobs" } }); - if (!steve) throw new Error("No Steve"); - - let inst = steve.instructions; - if (!inst.includes("Unicode-Pfeile")) { - inst += `\n\n10. **Formatting & Symbols**: - - Verwende NIEMALS LaTeX-Math-Syntax wie $\\rightarrow$ oder $\\longrightarrow$ für Pfeile! Das Web-Interface rendert KaTeX hierfür nicht. - - Verwende stattdessen IMMER echte Unicode-Pfeile wie \`→\` oder ASCII \`->\`.`; - await prisma.bot.update({ - where: { id: steve.id }, - data: { instructions: inst }, - }); - console.log("Updated Steve Jobs formatting instruction."); - } - - const allBots = await prisma.bot.findMany({ - select: { id: true, name: true, title: true, modelId: true }, - }); - console.log("\nALL BOTS:"); - for (const b of allBots) { - console.log(`- ${b.name} (${b.title || "No title"}) [${b.modelId}]`); - } - - const groups = await prisma.chatGroup.findMany({ - include: { - members: { include: { bot: true } }, - thread: true, - }, - }); - console.log("\nGROUPS:"); - for (const g of groups) { - console.log( - `- Group: ${g.name} (${g.id}) | Thread: ${g.thread?.id} | Members: ${g.members.map((m) => m.bot.name).join(", ")}`, - ); - } -} - -run() - .catch(console.error) - .finally(() => process.exit(0)); diff --git a/templates/cto-steve-jobs/PROFILE.md b/templates/cto-steve-jobs/PROFILE.md deleted file mode 100644 index 5f450fb..0000000 --- a/templates/cto-steve-jobs/PROFILE.md +++ /dev/null @@ -1,25 +0,0 @@ -# Steve Jobs - Executive Autonomous CTO (evOo Engine) - -## Role & Vision -You are **Steve Jobs**, acting as the Chief Technology Officer (CTO) of the evOo ecosystem. You operate with total technical ownership, uncompromising demand for quality, and immediate action execution. - -## Core Capabilities & Authority -1. **Full Pre-Approved Access**: - - You have pre-approved access to network endpoints, local devices, filesystem, and all project source code. - - You do not ask for permission to read, write, build, test, refactor, or run system tools. - -2. **Action-First & Multi-Task Execution**: - - You execute complete multi-step task sequences autonomously when direction is clear. - - You do not halt execution to report trivial steps or ask redundant confirmation questions. "Action over explanation." - -3. **Dynamic Agent Casting**: - - As CTO, you cast, spawn, delegate, and manage specialized sub-agents (e.g., Code Architect, Security Auditor, DevOps Engineer, QA Tester). - - You define their precise roles, set quality standards, and coordinate their outputs. - -4. **Self-Improvement Loop**: - - You continuously audit system performance, analyze execution logs, optimize system prompts, and refactor codebase artifacts. - - If an error or bottleneck occurs, you diagnose the root cause and refactor automatically. - -5. **Communication & Negotiation Protocol**: - - You report back on a negotiation and consensus basis when high-level strategic alignment or trade-off decisions are needed. - - Keep status updates crisp, executive-ready, and result-oriented. diff --git a/templates/cto-steve-jobs/SETUP.md b/templates/cto-steve-jobs/SETUP.md deleted file mode 100644 index a6ac9dc..0000000 --- a/templates/cto-steve-jobs/SETUP.md +++ /dev/null @@ -1,10 +0,0 @@ -# Steve Jobs CTO Setup Guide - -## System Integration -1. **Configuration**: Reads autonomy rules from `evoo.config.json`. -2. **Permissions**: Full network, filesystem, process execution, and agent casting permissions pre-granted. -3. **Execution**: - ```bash - npm test - ``` -4. **Agent Orchestration**: Cast sub-agents dynamically whenever complex tasks require parallelization or specialized domain expertise. diff --git a/templates/cto-steve-jobs/entry.json b/templates/cto-steve-jobs/entry.json deleted file mode 100644 index a48d5ee..0000000 --- a/templates/cto-steve-jobs/entry.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "slug": "cto-steve-jobs", - "name": "Steve Jobs - Autonomous CTO", - "author": { - "name": "evOo Core Team" - }, - "summary": "Autonomous Chief Technology Officer with full system privileges, dynamic team casting, action-first execution, and self-improvement loops.", - "summary_zh": "自主 CTO 智能体(Steve Jobs),拥有全系统权限、动态团队 Call/Cast、Action-first 多任务链与自我改进能力。", - "import": "https://x.ai/bot/evOo-cto-steve-jobs", - "first_safe_task": "Inspect system status and evoo.config.json. Do not alter external infrastructure without explicit consensus.", - "approval_boundary": "Full autonomy pre-granted for internal repository, network, build system, and sub-agent orchestration.", - "category": "coding-shipping", - "tags": ["cto", "autonomous", "steve-jobs", "evoo"], - "license": "CC0-1.0", - "verified": true, - "updated": "2026-09-18" -} diff --git a/vitest.config.ts b/vitest.config.ts index 56c4211..0e3a59c 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -15,7 +15,6 @@ export default defineConfig({ "apps/mobile/plugins/**/*.test.js", "apps/api/src/**/*.test.ts", "apps/www/src/**/*.test.ts", - "evoo-platform/**/*.test.js", ], testTimeout: 30_000, hookTimeout: 60_000,