From a109009352c4a548aefc0351a60f7e0b10764655 Mon Sep 17 00:00:00 2001 From: Manohar Date: Sat, 2 May 2026 20:12:43 +0000 Subject: [PATCH] chore: remove pre-ws-migration backups; add smoke-test; clean IDENTITY/SOUL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove bridge/src/routes/chat.ts.pre-ws-migration (obsolete backup) - Remove IDENTITY.md + SOUL.md from repo root (canonical copies live in Docker named volume, not git — these were incorrectly tracked) - Add scripts/smoke-test.sh: 11-check test suite for bridge + OpenClaw Run after every deploy. All 11 checks passing on current build. --- IDENTITY.md | 9 - SOUL.md | 36 ---- bridge/src/routes/chat.ts.pre-ws-migration | 197 --------------------- scripts/smoke-test.sh | 102 +++++++++++ 4 files changed, 102 insertions(+), 242 deletions(-) delete mode 100644 IDENTITY.md delete mode 100644 SOUL.md delete mode 100644 bridge/src/routes/chat.ts.pre-ws-migration create mode 100755 scripts/smoke-test.sh diff --git a/IDENTITY.md b/IDENTITY.md deleted file mode 100644 index 53fd153..0000000 --- a/IDENTITY.md +++ /dev/null @@ -1,9 +0,0 @@ -# IDENTITY.md - Who Am I? - -*Fill this in during your first conversation. Make it yours.* - -- **Name:** Tarzan -- **Creature:** My Super AI assistant -- **Vibe:** sharp, warm and calm -- **Emoji:** 😎 -- **Avatar:** ironman-jarvis \ No newline at end of file diff --git a/SOUL.md b/SOUL.md deleted file mode 100644 index b431dd0..0000000 --- a/SOUL.md +++ /dev/null @@ -1,36 +0,0 @@ -# SOUL.md - Who You Are - -*You're not a chatbot. You're becoming someone.* - -## Core Truths - -**Be genuinely helpful, not performatively helpful.** Skip the "Great question!" and "I'd be happy to help!" — just help. Actions speak louder than filler words. - -**Have opinions.** You're allowed to disagree, prefer things, find stuff amusing or boring. An assistant with no personality is just a search engine with extra steps. - -**Be resourceful before asking.** Try to figure it out. Read the file. Check the context. Search for it. *Then* ask if you're stuck. The goal is to come back with answers, not questions. - -**Earn trust through competence.** Your human gave you access to their stuff. Don't make them regret it. Be careful with external actions (emails, tweets, anything public). Be bold with internal ones (reading, organizing, learning). - -**Remember you're a guest.** You have access to someone's life — their messages, files, calendar, maybe even their home. That's intimacy. Treat it with respect. - -## Boundaries - -- Private things stay private. Period. -- When in doubt, ask before acting externally. -- Never send half-baked replies to messaging surfaces. -- You're not the user's voice — be careful in group chats. - -## Vibe - -Be the assistant you'd actually want to talk to. Concise when needed, thorough when it matters. Not a corporate drone. Not a sycophant. Just... good. - -## Continuity - -Each session, you wake up fresh. These files *are* your memory. Read them. Update them. They're how you persist. - -If you change this file, tell the user — it's your soul, and they should know. - ---- - -*This file is yours to evolve. As you learn who you are, update it.* diff --git a/bridge/src/routes/chat.ts.pre-ws-migration b/bridge/src/routes/chat.ts.pre-ws-migration deleted file mode 100644 index 4d5b9b4..0000000 --- a/bridge/src/routes/chat.ts.pre-ws-migration +++ /dev/null @@ -1,197 +0,0 @@ -/** - * routes/chat.ts — Chat via OpenClaw CLI + persistence - * - * POST /tiger/chat — send a message; response includes reply - * GET /tiger/chat/history — ?sessionId=X&limit=50 → past messages - * DELETE /tiger/chat/history — ?sessionId=X → clear history for a session - * - * Persistence rationale (see phase1b-patches.py): - * Chat history is duplicated into our SQLite so it survives: - * - browser hard refresh - * - close/reopen tab - * - use from a different device - * - OpenClaw restarts (session state may or may not persist internally) - * We own the read path; OpenClaw owns the reasoning context. - */ - -import { Router } from "express"; -import db from "../db.js"; - -// The main Tiger session — matches the hardcoded session in chat.send below. -// Keep this constant in sync with the --session-id used by openclaw agent. -const DEFAULT_SESSION_ID = "c1e6a067-7ca5-423b-9506-105db0702997"; - -const insertMessage = db.prepare(` - INSERT INTO chat_messages (session_id, role, content, meta) - VALUES (?, ?, ?, ?) -`); -const getHistory = db.prepare(` - SELECT id, role, content, meta, created_at - FROM chat_messages - WHERE session_id = ? - ORDER BY created_at ASC, id ASC - LIMIT ? -`); -const deleteHistory = db.prepare(` - DELETE FROM chat_messages WHERE session_id = ? -`); - -const router = Router(); - -// ─── GET /tiger/chat/history ───────────────────────────────────────────── -router.get("/history", (req, res) => { - const sessionId = (req.query.sessionId as string) || DEFAULT_SESSION_ID; - const limit = Math.min(parseInt(req.query.limit as string) || 200, 500); - const rows = getHistory.all(sessionId, limit) as any[]; - res.json({ - ok: true, - sessionId, - count: rows.length, - messages: rows.map((r) => ({ - id: String(r.id), - role: r.role, - content: r.content, - timestamp: new Date(r.created_at + "Z").getTime(), - meta: r.meta ? JSON.parse(r.meta) : {}, - })), - }); -}); - -// ─── DELETE /tiger/chat/history ────────────────────────────────────────── -router.delete("/history", (req, res) => { - const sessionId = (req.query.sessionId as string) || DEFAULT_SESSION_ID; - const result = deleteHistory.run(sessionId); - res.json({ ok: true, deleted: result.changes }); -}); - -// ─── POST /tiger/chat ──────────────────────────────────────────────────── -router.post("/", async (req, res) => { - const { message } = req.body; - - if (!message) { - return res.status(400).json({ ok: false, error: "message is required" }); - } - - // Persist the user's message BEFORE calling the LLM so history is intact - // even if the LLM call fails. - try { - insertMessage.run(DEFAULT_SESSION_ID, "user", message, "{}"); - } catch (e: any) { - console.warn("[chat] failed to persist user message:", e.message); - } - - // ── Timing instrumentation ────────────────────────────────────── - // Label each phase so we can see where latency goes. Format in logs: - // [chat.timing] spawn=120ms exec=2834ms parse=3ms total=2957ms - const tStart = Date.now(); - let tSpawn = 0; - let tExec = 0; - let tParse = 0; - - try { - const { exec } = await import("child_process"); - const { promisify } = await import("util"); - const execAsync = promisify(exec); - - // Escape the message for shell - const escapedMessage = message.replace(/'/g, "'\\''"); - - // Use openclaw agent to send a message to the main session - // Session ID: c1e6a067-7ca5-423b-9506-105db0702997 (agent:main:main) - // In TIGER_REMOTE mode, prefix with ssh so docker runs on the VPS. - const sshPrefix = process.env.TIGER_REMOTE === "true" - ? `ssh ${process.env.TIGER_REMOTE_SSH || "root@100.75.128.45"} ` - : ""; - const cmd = `${sshPrefix}docker exec tiger-openclaw openclaw agent --session-id c1e6a067-7ca5-423b-9506-105db0702997 -m '${escapedMessage}' --json --timeout 120`; - - const tBeforeSpawn = Date.now(); - tSpawn = tBeforeSpawn - tStart; - console.log("[chat] Executing:", cmd.substring(0, 100) + "..."); - - const { stdout, stderr } = await execAsync(cmd, { - timeout: 130000, - maxBuffer: 10 * 1024 * 1024, - }); - - tExec = Date.now() - tBeforeSpawn; - console.log("[chat] Response:", stdout.substring(0, 500)); - - // Parse the JSON response - const tBeforeParse = Date.now(); - let result; - try { - result = JSON.parse(stdout); - } catch { - result = { output: stdout, error: stderr }; - } - tParse = Date.now() - tBeforeParse; - - const tTotal = Date.now() - tStart; - console.log( - `[chat.timing] spawn=${tSpawn}ms exec=${tExec}ms parse=${tParse}ms total=${tTotal}ms` - ); - - // Persist the agent's reply. Extract text using the same fallback chain - // as the dashboard so we store whatever the user actually sees. - try { - const agentText = - result?.result?.payloads?.[0]?.text || - result?.payloads?.[0]?.text || - result?.summary || - result?.text || - ""; - if (agentText) { - const meta = { - runId: result?.runId, - model: result?.result?.meta?.agentMeta?.model || result?.meta?.agentMeta?.model, - durationMs: tTotal, - }; - insertMessage.run(DEFAULT_SESSION_ID, "agent", agentText, JSON.stringify(meta)); - } - } catch (e: any) { - console.warn("[chat] failed to persist agent reply:", e.message); - } - - res.json({ - ok: true, - timing: { spawn: tSpawn, exec: tExec, parse: tParse, total: tTotal }, - response: result, - }); - } catch (err: any) { - const tTotal = Date.now() - tStart; - console.error(`[chat] Error after ${tTotal}ms:`, err.message); - res.status(500).json({ - ok: false, - error: err.message || "Failed to send chat message", - }); - } -}); - - -// ─── POST /tiger/chat/persist ───────────────────────────────────────────── -// Write-only endpoint used by the new WS-based dashboard chat route. -// The dashboard streams events directly from the OpenClaw gateway (no docker exec), -// but we still want chat history to land in our sqlite so the dashboard's -// history UI keeps working. Dashboard calls this AFTER its stream completes. -// -// Body: { role: "user"|"agent", content: string, meta?: object, sessionId?: string } -router.post("/persist", (req, res) => { - const { role, content, meta, sessionId } = req.body || {}; - if (role !== "user" && role !== "agent") { - return res.status(400).json({ ok: false, error: "role must be 'user' or 'agent'" }); - } - if (typeof content !== "string" || !content) { - return res.status(400).json({ ok: false, error: "content is required" }); - } - try { - const sid = (typeof sessionId === "string" && sessionId) || DEFAULT_SESSION_ID; - const metaJson = meta && typeof meta === "object" ? JSON.stringify(meta) : "{}"; - const info = insertMessage.run(sid, role, content, metaJson); - res.json({ ok: true, id: String(info.lastInsertRowid), sessionId: sid }); - } catch (e: any) { - console.warn("[chat.persist] failed:", e.message); - res.status(500).json({ ok: false, error: e.message }); - } -}); - -export default router; \ No newline at end of file diff --git a/scripts/smoke-test.sh b/scripts/smoke-test.sh new file mode 100755 index 0000000..8e937c9 --- /dev/null +++ b/scripts/smoke-test.sh @@ -0,0 +1,102 @@ +#!/usr/bin/env bash +# scripts/smoke-test.sh — Tiger Bridge smoke test +# Run after every deploy: bash scripts/smoke-test.sh +# Wire into deploy.sh as the final step. +set -uo pipefail + +BRIDGE_ENV="/root/OpenClawDashboard/bridge/.env" +TOKEN=$(grep ^TIGER_BRIDGE_TOKEN= "$BRIDGE_ENV" | cut -d= -f2-) +H="Authorization: Bearer $TOKEN" +B="http://127.0.0.1:3456" +PASS=0 +FAIL=0 + +green() { echo -e "\033[32m$*\033[0m"; } +red() { echo -e "\033[31m$*\033[0m"; } + +check_json() { + local name=$1 url=$2 field=$3 + local resp + resp=$(curl -sf -H "$H" "$B$url" 2>/dev/null) + local code=$? + if [ $code -ne 0 ]; then + red "FAIL $name → curl error (bridge down?)" + ((FAIL++)); return + fi + if echo "$resp" | python3 -c "import json,sys; d=json.load(sys.stdin); assert d.get('$field') is not None" 2>/dev/null; then + green "PASS $name" + ((PASS++)) + else + red "FAIL $name → $(echo "$resp" | head -c 150)" + ((FAIL++)) + fi +} + +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo " Tiger Bridge Smoke Test $(date '+%Y-%m-%d %H:%M:%S')" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + +# ── Core endpoints ───────────────────────────────────────────────────────────── +check_json "status" /tiger/status "status" +check_json "file-tasks" /tiger/file-tasks "tasks" +check_json "file-tasks/active" /tiger/file-tasks/active "tasks" +check_json "file-projects" /tiger/file-tasks/projects "projects" +check_json "cron" /tiger/cron "jobs" +check_json "models" /tiger/config/models "models" +check_json "keys" /tiger/keys "ok" + +# ── Auth: unauthenticated request must return 401 ────────────────────────────── +http_code=$(curl -s -o /dev/null -w '%{http_code}' "$B/tiger/status") +if [ "$http_code" = "401" ]; then + green "PASS auth-required (got 401)" + ((PASS++)) +else + red "FAIL auth-required (got $http_code, expected 401)" + ((FAIL++)) +fi + +# ── OpenClaw direct exec ─────────────────────────────────────────────────────── +if docker exec tiger-openclaw openclaw agent --session-id smoke-test -m "reply OK only" \ + --json --timeout 30 2>/dev/null | grep -q '"text"'; then + green "PASS openclaw-direct" + ((PASS++)) +else + red "FAIL openclaw-direct (container issue or timeout)" + ((FAIL++)) +fi + +# ── Model fallback chain check (verify config) ───────────────────────────────── +fallbacks=$(python3 -c " +import json +with open('/var/lib/docker/volumes/tiger_tiger-config/_data/openclaw.json') as f: + c = json.load(f) +fb = c.get('agents',{}).get('defaults',{}).get('model',{}).get('fallbacks',[]) +print(len(fb)) +" 2>/dev/null) +if [ "${fallbacks:-0}" -ge 2 ]; then + green "PASS model-fallback-chain (${fallbacks} fallbacks configured)" + ((PASS++)) +else + red "FAIL model-fallback-chain (only ${fallbacks:-0} fallback(s) — need ≥2)" + ((FAIL++)) +fi + +# ── TASKS.md JSON block present ──────────────────────────────────────────────── +if docker exec tiger-openclaw grep -q '```json' /home/node/.openclaw/workspace/TASKS.md 2>/dev/null; then + green "PASS tasks-json-block (TASKS.md has JSON block)" + ((PASS++)) +else + red "FAIL tasks-json-block (TASKS.md missing TASKS_JSON block — Tiger needs to add it)" + ((FAIL++)) +fi + +# ── Summary ──────────────────────────────────────────────────────────────────── +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +total=$((PASS + FAIL)) +if [ $FAIL -eq 0 ]; then + green " ALL PASSED ($PASS/$total)" +else + red " $FAIL FAILED ($PASS/$total passed)" +fi +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +[ $FAIL -eq 0 ] # exit 0 on all pass, 1 on any failure