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 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
This commit is contained in:
parent
8449dec6c2
commit
b81f7d18f6
@ -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.
|
||||
11
.env.example
11
.env.example
@ -210,17 +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 <token> 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.
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@ -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/
|
||||
|
||||
@ -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: `<T extends { name: string }>` — 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`.
|
||||
@ -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.
|
||||
@ -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
|
||||
@ -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)
|
||||
@ -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`
|
||||
|
||||
2
VOICE.md
2
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.
|
||||
|
||||
|
||||
@ -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
|
||||
```
|
||||
@ -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.
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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.")
|
||||
@ -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}")
|
||||
@ -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
|
||||
@ -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()
|
||||
@ -22,8 +22,7 @@
|
||||
"!**/data",
|
||||
"!**/*.astro",
|
||||
"!**/apps/www",
|
||||
"!**/.claude-handoff*",
|
||||
"!**/screenshot*.cjs"
|
||||
"!**/.claude-handoff*"
|
||||
]
|
||||
},
|
||||
"formatter": {
|
||||
|
||||
138
deploy.sh
138
deploy.sh
@ -1,138 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# ==============================================================================
|
||||
# evOo Platform — Hot-Reload & Physical Deployment Orchestrator
|
||||
# Executive Lead: CTO Steve Jobs
|
||||
# "Realität im Dateisystem vor Halluzination im Kopf."
|
||||
# ==============================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PORT="${PORT:-3088}"
|
||||
PID_FILE="${SCRIPT_DIR}/.evoo_server.pid"
|
||||
LOG_FILE="${SCRIPT_DIR}/deploy.log"
|
||||
|
||||
# Colors
|
||||
GREEN='\033[0;32m'
|
||||
CYAN='\033[0;36m'
|
||||
YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
log() {
|
||||
echo -e "${CYAN}[evOo Deploy]${NC} $1"
|
||||
}
|
||||
|
||||
success() {
|
||||
echo -e "${GREEN}[✓ SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
warn() {
|
||||
echo -e "${YELLOW}[⚠ WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
fail() {
|
||||
echo -e "${RED}[✗ FAILED]${NC} $1" >&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}"
|
||||
@ -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).
|
||||
|
||||
@ -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` |
|
||||
|
||||
@ -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 =
|
||||
'<div class="text-dim p-3">Keine ausstehenden Entscheidungen. System läuft autonom.</div>';
|
||||
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 = `
|
||||
<div class="approval-meta">
|
||||
<span class="approval-type-tag">${item.type}</span>
|
||||
<span class="approval-author">${item.author}</span>
|
||||
<span class="approval-time">${item.timestamp}</span>
|
||||
${isDecided ? `<span class="badge ${statusClass}">${item.status.toUpperCase()}</span>` : ""}
|
||||
</div>
|
||||
<div class="approval-title">${item.title}</div>
|
||||
<div class="approval-desc">${item.details}</div>
|
||||
<div class="approval-metrics">
|
||||
<span>Conviction: <strong>${item.conviction}%</strong></span>
|
||||
<span>R:R: <strong>${item.riskReward}</strong></span>
|
||||
<span>Umfang: <strong>${item.size}</strong></span>
|
||||
</div>
|
||||
${
|
||||
!isDecided
|
||||
? `
|
||||
<div class="approval-actions">
|
||||
<button class="btn-approve" data-id="${item.id}" data-action="approve">✓ Genehmigen</button>
|
||||
<button class="btn-veto" data-id="${item.id}" data-action="veto">✗ Veto / Ablehnen</button>
|
||||
<button class="btn-discuss" data-author="${item.author}">💬 Im Chat besprechen</button>
|
||||
</div>
|
||||
`
|
||||
: ""
|
||||
}
|
||||
`;
|
||||
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 = `
|
||||
<div class="entry-time">${ev.time}</div>
|
||||
<div class="entry-content">
|
||||
<div class="entry-header">
|
||||
<span class="entry-agent-chip">${ev.agent}</span>
|
||||
<span class="entry-title">${ev.title}</span>
|
||||
</div>
|
||||
<div class="entry-detail">${ev.detail}</div>
|
||||
</div>
|
||||
`;
|
||||
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" }, "*");
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
@ -1,347 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>evOo Mission Control | Executive Operations Cockpit</title>
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<div class="background-mesh"></div>
|
||||
|
||||
<!-- Executive Pulse Strip (Header) -->
|
||||
<header class="executive-header">
|
||||
<div class="header-left">
|
||||
<div class="brand">
|
||||
<span class="brand-name">ev<span class="brand-highlight">Oo</span></span>
|
||||
<span class="version-tag">v2.0-EXEC</span>
|
||||
</div>
|
||||
<div class="pulse-pill" id="system-health-pill">
|
||||
<span class="pulse-indicator online"></span>
|
||||
<span class="pill-label">System:</span>
|
||||
<span class="pill-value" id="system-health-val">HEALTHY (Port 3088)</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="header-center">
|
||||
<div class="topology-pill" title="Hardware-Cluster & Lokale Modelle">
|
||||
<span class="pill-icon">🖥️</span>
|
||||
<span class="pill-label">Cluster:</span>
|
||||
<span class="pill-value" id="cluster-summary-val">Host (Metal GPU 30B) · Mini · Air</span>
|
||||
</div>
|
||||
<div class="trading-pill" title="Trading-Bot & Apex Risk Subsystem">
|
||||
<span class="pill-icon">📈</span>
|
||||
<span class="pill-label">Trading:</span>
|
||||
<span class="pill-value" id="trading-summary-val">ARMED (Max DD 0.21% / 1.5%)</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="header-right">
|
||||
<button type="button" id="btn-self-improve" class="btn btn-secondary" title="Führt den vollständigen Verifizierungs- & Test-Loop aus">
|
||||
<span class="btn-icon">⚡</span>
|
||||
<span>Selbstoptimierung</span>
|
||||
</button>
|
||||
<button type="button" id="btn-killswitch" class="btn btn-warning" title="Notfall-Stopp aller autonomen Trading-Aktionen">
|
||||
<span class="btn-icon">🛡️</span>
|
||||
<span id="killswitch-text">Risk Guard: Aktiv</span>
|
||||
</button>
|
||||
<button type="button" id="btn-hot-reload" class="btn btn-primary" title="Server-Synchronisation und Hot-Reload">
|
||||
<span class="btn-icon">↺</span>
|
||||
<span>Hot-Reload</span>
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Main Cockpit Grid -->
|
||||
<main class="cockpit-container">
|
||||
<div class="cockpit-main-layout">
|
||||
|
||||
<!-- ZONE 1: Executive Action & Overnight Journal (Left Column, ~58%) -->
|
||||
<div class="column-executive">
|
||||
|
||||
<!-- PENDING APPROVALS CARD -->
|
||||
<section class="card card-approvals">
|
||||
<div class="card-header">
|
||||
<div class="header-title-group">
|
||||
<span class="section-tag">EXECUTIVE INBOX</span>
|
||||
<h2>Offene Entscheidungen & Freigaben</h2>
|
||||
</div>
|
||||
<span class="badge badge-amber" id="approvals-badge">2 Ausstehend</span>
|
||||
</div>
|
||||
<div class="approvals-list" id="approvals-container">
|
||||
<!-- Rendered dynamically by app.js -->
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- WAS GESCHAH IN DEN LETZTEN 24h? (Overnight Briefing) -->
|
||||
<section class="card card-journal">
|
||||
<div class="card-header">
|
||||
<div class="header-title-group">
|
||||
<span class="section-tag">OVERNIGHT BRIEFING</span>
|
||||
<h2>Operations Journal (Letzte 24h)</h2>
|
||||
</div>
|
||||
<span class="badge badge-emerald" id="journal-badge">100% Physisch</span>
|
||||
</div>
|
||||
<div class="journal-timeline" id="journal-container">
|
||||
<!-- Rendered dynamically by app.js -->
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- ZONE 2: Executive Board & Casted Agent Network (Right Column, ~42%) -->
|
||||
<div class="column-agents">
|
||||
|
||||
<!-- CTO DIRECTIVE CARD -->
|
||||
<section class="card card-cto">
|
||||
<div class="card-header">
|
||||
<div class="header-title-group">
|
||||
<span class="section-tag">EXECUTIVE COMMAND</span>
|
||||
<h2>CTO Steve Jobs</h2>
|
||||
</div>
|
||||
<span class="badge badge-purple">Vollmacht: Unbeschränkt</span>
|
||||
</div>
|
||||
<div class="cto-body">
|
||||
<div class="cto-profile">
|
||||
<div class="cto-avatar">SJ</div>
|
||||
<div class="cto-details">
|
||||
<span class="cto-name">Steve Jobs</span>
|
||||
<span class="cto-role">Chief Technology Officer · Modus: Action-First</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cto-quote-box">
|
||||
<p class="cto-quote">
|
||||
„Realität im Dateisystem vor Halluzination im Kopf. Das 3-Säulen-System steht. Alle 5 Spezialisten arbeiten mit deterministischen Schnittstellen und echten Tests.“
|
||||
</p>
|
||||
</div>
|
||||
<div class="cto-actions">
|
||||
<button type="button" id="btn-open-steve-chat" class="btn-inline btn-purple">
|
||||
<span>💬 Chat mit Steve öffnen</span>
|
||||
</button>
|
||||
<div class="authority-pills">
|
||||
<span class="auth-pill">✓ Filesystem Write</span>
|
||||
<span class="auth-pill">✓ Multi-Node Orchestration</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- OPERATIONS BOARD (5 CASTED AGENTS) -->
|
||||
<section class="card card-operations-board">
|
||||
<div class="card-header">
|
||||
<div class="header-title-group">
|
||||
<span class="section-tag">OPERATIONS BOARD</span>
|
||||
<h2>Agenten-Flotte</h2>
|
||||
</div>
|
||||
<span class="badge badge-cyan" id="agent-count">5 Casts aktiv</span>
|
||||
</div>
|
||||
<p class="operations-hint">Klicke auf einen Spezialisten, um sofort ein physisches System-Audit auszulösen:</p>
|
||||
|
||||
<div class="agent-roster">
|
||||
<!-- 1. Code Architect -->
|
||||
<div class="roster-item interactive-agent" data-agent="architect" title="Klicken für Architecture- & Modul-Audit">
|
||||
<div class="agent-avatar architect">CA</div>
|
||||
<div class="agent-meta">
|
||||
<span class="agent-name">Code Architect</span>
|
||||
<span class="agent-role">Moduldesign & Clean Architecture</span>
|
||||
<span class="agent-node">Node: Mac mini (Strategic Reasoning)</span>
|
||||
</div>
|
||||
<div class="agent-status">
|
||||
<span class="status-dot green" id="dot-architect"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 2. Security & QA Auditor -->
|
||||
<div class="roster-item interactive-agent" data-agent="qa" title="Klicken für Zero-Phantom- & Test-Audit">
|
||||
<div class="agent-avatar security">QA</div>
|
||||
<div class="agent-meta">
|
||||
<span class="agent-name">Security & QA Auditor</span>
|
||||
<span class="agent-role">Integritätsprüfung & Zero-Phantom</span>
|
||||
<span class="agent-node">Node: MacBook Air (Fast Coding)</span>
|
||||
</div>
|
||||
<div class="agent-status">
|
||||
<span class="status-dot green" id="dot-qa"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 3. DevOps Engineer -->
|
||||
<div class="roster-item interactive-agent" data-agent="devops" title="Klicken für Liveness- & Port-Audit">
|
||||
<div class="agent-avatar devops">DO</div>
|
||||
<div class="agent-meta">
|
||||
<span class="agent-name">DevOps Engineer</span>
|
||||
<span class="agent-role">Hot-Reload & Service Liveness</span>
|
||||
<span class="agent-node">Node: Host Mac (Daemons & Ports)</span>
|
||||
</div>
|
||||
<div class="agent-status">
|
||||
<span class="status-dot green" id="dot-devops"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 4. Apex Quant Specialist -->
|
||||
<div class="roster-item interactive-agent" data-agent="quant" title="Klicken für Apex Risk & Trading-Bot-Bridge Audit">
|
||||
<div class="agent-avatar quant">AP</div>
|
||||
<div class="agent-meta">
|
||||
<span class="agent-name">Apex Quant Specialist</span>
|
||||
<span class="agent-role">Algorithmic Risk & Port 8000 Bridge</span>
|
||||
<span class="agent-node">Node: Host Mac (Deterministic Math)</span>
|
||||
</div>
|
||||
<div class="agent-status">
|
||||
<span class="status-dot purple" id="dot-quant"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 5. UX Designer -->
|
||||
<div class="roster-item interactive-agent" data-agent="ux" title="Klicken für UI/UX & Design-System-Audit">
|
||||
<div class="agent-avatar ux">UX</div>
|
||||
<div class="agent-meta">
|
||||
<span class="agent-name">UX Designer</span>
|
||||
<span class="agent-role">Lead Product & UX/UI Designer</span>
|
||||
<span class="agent-node">Node: MacBook Air / Host (3-Pillar UI)</span>
|
||||
</div>
|
||||
<div class="agent-status">
|
||||
<span class="status-dot orange" id="dot-ux"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ZONE 3 & 4: Distributed Cluster, Apex Trading & Telemetry Console (Bottom Full Width) -->
|
||||
<div class="cockpit-bottom-layout">
|
||||
|
||||
<!-- CLUSTER TOPOLOGY CARD -->
|
||||
<section class="card card-cluster">
|
||||
<div class="card-header">
|
||||
<div class="header-title-group">
|
||||
<span class="section-tag">COMPUTE TOPOLOGY</span>
|
||||
<h2>Distributed Mac Cluster</h2>
|
||||
</div>
|
||||
<span class="badge badge-emerald" id="cluster-badge">Metal GPU Aktiv</span>
|
||||
</div>
|
||||
<div class="cluster-grid">
|
||||
<div class="cluster-node online">
|
||||
<div class="node-header">
|
||||
<span class="node-status-dot"></span>
|
||||
<span class="node-name">Host Mac (Primär)</span>
|
||||
<span class="node-type">Metal GPU</span>
|
||||
</div>
|
||||
<div class="node-details">
|
||||
<div class="detail-row"><span>LM Studio:</span><span class="code-val">qwen3-coder-30b-a3b (Metal)</span></div>
|
||||
<div class="detail-row"><span>Ollama:</span><span class="code-val">gemma4:latest (Port 11434)</span></div>
|
||||
<div class="detail-row"><span>Zustand:</span><span class="success-val">Aktiv · Keine Drosselung</span></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="cluster-node standby">
|
||||
<div class="node-header">
|
||||
<span class="node-status-dot amber"></span>
|
||||
<span class="node-name">Mac mini</span>
|
||||
<span class="node-type">24/7 Powerhouse</span>
|
||||
</div>
|
||||
<div class="node-details">
|
||||
<div class="detail-row"><span>Profil:</span><span>Aktiv gekühlt (Lüfter)</span></div>
|
||||
<div class="detail-row"><span>Empfohlen:</span><span class="code-val">qwen2.5:32b / deepseek-r1</span></div>
|
||||
<div class="detail-row"><span>Routing:</span><span class="text-dim">Failover zu Host Metal GPU</span></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="cluster-node standby">
|
||||
<div class="node-header">
|
||||
<span class="node-status-dot amber"></span>
|
||||
<span class="node-name">MacBook Air</span>
|
||||
<span class="node-type">Mobile Worker</span>
|
||||
</div>
|
||||
<div class="node-details">
|
||||
<div class="detail-row"><span>Profil:</span><span>Lüfterlos (Thermisch geschützt)</span></div>
|
||||
<div class="detail-row"><span>Empfohlen:</span><span class="code-val">qwen2.5-coder:7b / 3b</span></div>
|
||||
<div class="detail-row"><span>Routing:</span><span class="text-dim">Failover zu Host Metal GPU</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- APEX QUANT TRADING ENGINE SUBSYSTEM -->
|
||||
<section class="card card-apex">
|
||||
<div class="card-header">
|
||||
<div class="header-title-group">
|
||||
<span class="section-tag">SUBSYSTEM APEX</span>
|
||||
<h2>Apex Quantitative Trading Engine</h2>
|
||||
</div>
|
||||
<div class="header-action-cluster">
|
||||
<div class="mode-segmented-control" role="tablist">
|
||||
<button type="button" id="tab-mode-paper" class="segment-btn active" data-mode="paper" title="Simulierter Test-Modus mit $1,000 Paper Equity">
|
||||
<span class="segment-dot paper-dot"></span>
|
||||
<span>Paper-Modus</span>
|
||||
</button>
|
||||
<button type="button" id="tab-mode-live" class="segment-btn" data-mode="live" title="Echtgeld-Modus: Polymarket Deposit Wallet, Bitpanda & Fiat-Reserven">
|
||||
<span class="segment-dot live-dot"></span>
|
||||
<span>🟢 Live-Echtgeld</span>
|
||||
</button>
|
||||
</div>
|
||||
<span class="badge badge-cyan" id="trading-badge">REST API: Port 8001</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="trading-metrics-grid">
|
||||
<div class="tmetric-card">
|
||||
<span class="tmetric-label" id="card1-label">Equity & Portfolio</span>
|
||||
<span class="tmetric-val positive" id="card1-val">$1,000.00</span>
|
||||
<span class="tmetric-sub" id="card1-sub">€921.66 · Paper Trading (Safe)</span>
|
||||
</div>
|
||||
<div class="tmetric-card">
|
||||
<span class="tmetric-label" id="card2-label">Max Drawdown</span>
|
||||
<span class="tmetric-val" id="card2-val">0.00%</span>
|
||||
<span class="tmetric-sub" id="card2-sub">Cap: 1.50% (Hard Stop)</span>
|
||||
</div>
|
||||
<div class="tmetric-card">
|
||||
<span class="tmetric-label" id="card3-label">Aktives Markt-Regime</span>
|
||||
<span class="tmetric-val cyan-text" id="card3-val">Donchian / ADX</span>
|
||||
<span class="tmetric-sub" id="card3-sub">Trend-Following + Mean-Reversion</span>
|
||||
</div>
|
||||
<div class="tmetric-card">
|
||||
<span class="tmetric-label" id="card4-label">Circuit Breaker Status</span>
|
||||
<span class="tmetric-val success-val" id="card4-val">ARMED</span>
|
||||
<span class="tmetric-sub" id="card4-sub">Fixed Fractional (1-2%)</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- LIVE TELEMETRY & LOG CONSOLE (COLLAPSIBLE) -->
|
||||
<section class="card card-console">
|
||||
<div class="card-header">
|
||||
<div class="header-title-group">
|
||||
<span class="section-tag">LIVE TELEMETRY STREAM</span>
|
||||
<h2>Self-Improvement & System Event Log</h2>
|
||||
</div>
|
||||
<div class="console-actions">
|
||||
<button type="button" id="btn-toggle-console" class="btn-text">Minimieren</button>
|
||||
<button type="button" id="btn-clear-console" class="btn-text">Leeren</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="terminal-container" id="terminal-logs">
|
||||
<div class="log-line info">[SYSTEM] evOo Executive Cockpit initialized. Version 2.0.0.</div>
|
||||
<div class="log-line success">[CLUSTER] Host Metal GPU (30B Qwen) aktiv. Port 1234 bereit.</div>
|
||||
<div class="log-line success">[VERIFY] 3-Säulen-Navigation (Heute/Team/Projekte) in Chat-App verankert.</div>
|
||||
<div class="log-line success">[OPERATIONS] 5 Casted Agents (Architect, QA, DevOps, Quant, UX) aktiv.</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer class="app-footer">
|
||||
<div class="footer-left">
|
||||
<span>evOo Autonomous Platform • Physical Reality Guaranteed • Executive Command Center</span>
|
||||
</div>
|
||||
<div class="footer-right">
|
||||
<span id="timestamp-clock">--:--:-- UTC</span>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script src="app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@ -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}`;
|
||||
}
|
||||
@ -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();
|
||||
});
|
||||
});
|
||||
@ -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",
|
||||
);
|
||||
}
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
@ -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"
|
||||
}
|
||||
}
|
||||
@ -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()
|
||||
@ -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"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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.
|
||||
@ -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);
|
||||
@ -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));
|
||||
@ -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));
|
||||
@ -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));
|
||||
@ -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));
|
||||
@ -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));
|
||||
@ -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));
|
||||
@ -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));
|
||||
@ -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));
|
||||
@ -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));
|
||||
@ -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));
|
||||
@ -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));
|
||||
@ -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));
|
||||
@ -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));
|
||||
@ -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));
|
||||
@ -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));
|
||||
@ -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));
|
||||
@ -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));
|
||||
@ -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));
|
||||
@ -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));
|
||||
@ -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));
|
||||
@ -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));
|
||||
@ -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));
|
||||
@ -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));
|
||||
@ -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));
|
||||
@ -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));
|
||||
@ -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));
|
||||
@ -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));
|
||||
@ -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})`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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));
|
||||
@ -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));
|
||||
@ -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));
|
||||
@ -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));
|
||||
@ -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));
|
||||
@ -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));
|
||||
@ -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));
|
||||
@ -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));
|
||||
@ -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));
|
||||
@ -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));
|
||||
@ -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));
|
||||
@ -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));
|
||||
@ -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));
|
||||
@ -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));
|
||||
@ -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();
|
||||
@ -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));
|
||||
@ -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));
|
||||
@ -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));
|
||||
@ -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));
|
||||
@ -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));
|
||||
@ -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));
|
||||
@ -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));
|
||||
@ -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));
|
||||
@ -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));
|
||||
@ -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));
|
||||
@ -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));
|
||||
@ -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));
|
||||
@ -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));
|
||||
@ -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));
|
||||
@ -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));
|
||||
@ -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));
|
||||
@ -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));
|
||||
@ -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));
|
||||
@ -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));
|
||||
@ -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));
|
||||
@ -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));
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user