- Updated Nginx config to serve static SPA from web-client-new/dist/ - All API requests now proxy directly to Tracker (port 8100) - WebSocket connections go directly to Tracker with JWT auth - Stopped and disabled team-board-bff and team-board-web services - Fixed file permissions for nginx to serve static files - Migration complete: Next.js+BFF → Vite+React+Direct-Tracker
8 lines
9.8 KiB
JSON
8 lines
9.8 KiB
JSON
{"type":"session","version":3,"id":"d023b1a0-3710-4fe4-a9c4-cc81de783b2c","timestamp":"2026-02-23T17:59:20.809Z","cwd":"/root/projects/team-board/picogent"}
|
||
{"type":"model_change","id":"15d53f6b","parentId":null,"timestamp":"2026-02-23T17:59:20.809Z","provider":"anthropic","modelId":"claude-sonnet-4-6"}
|
||
{"type":"thinking_level_change","id":"565e1db6","parentId":"15d53f6b","timestamp":"2026-02-23T17:59:20.809Z","thinkingLevel":"off"}
|
||
{"type":"message","id":"d1bb9735","parentId":"565e1db6","timestamp":"2026-02-23T17:59:20.810Z","message":{"role":"user","content":[{"type":"text","text":"[ws_test.py] ping @ 18:59:20.800"}],"timestamp":1771869560810}}
|
||
{"type":"message","id":"52a2db98","parentId":"d1bb9735","timestamp":"2026-02-23T17:59:22.218Z","message":{"role":"assistant","content":[{"type":"toolCall","id":"toolu_01Bb6p5x5DJ5uqdTf1voQ13R","name":"read","arguments":{"path":"ws_test.py"}}],"api":"anthropic-messages","provider":"anthropic","model":"claude-sonnet-4-6","usage":{"input":3,"output":56,"cacheRead":0,"cacheWrite":2367,"totalTokens":2426,"cost":{"input":0.000009,"output":0.00084,"cacheRead":0,"cacheWrite":0.00887625,"total":0.009725250000000001}},"stopReason":"toolUse","timestamp":1771869560810}}
|
||
{"type":"message","id":"69dafb1d","parentId":"52a2db98","timestamp":"2026-02-23T17:59:22.220Z","message":{"role":"toolResult","toolCallId":"toolu_01Bb6p5x5DJ5uqdTf1voQ13R","toolName":"read","content":[{"type":"text","text":"#!/usr/bin/env python3\n\"\"\"\nDirect WebSocket test for Team Board Tracker.\n\nUsage:\n python ws_test.py [--url ws://localhost:8100/ws] [--token <token>]\n\nWithout --token: creates a test agent via REST API first.\nUses only stdlib + websockets (already in tracker venv).\n\"\"\"\n\nimport asyncio\nimport json\nimport argparse\nimport urllib.request\nimport urllib.error\nfrom datetime import datetime\n\nimport websockets\n\nBASE_URL = \"http://localhost:8100\"\nWS_URL = \"ws://localhost:8100/ws\"\nTEST_SLUG = \"ws-test-bot\"\nTEST_NAME = \"WS Test Bot\"\n\n\ndef ts():\n return datetime.now().strftime(\"%H:%M:%S.%f\")[:-3]\n\n\ndef log(tag, msg, data=None):\n extra = f\" {json.dumps(data, ensure_ascii=False, indent=None)}\" if data else \"\"\n print(f\"[{ts()}] {tag:<12} {msg}{extra}\")\n\n\n# ── REST helpers ──────────────────────────────────────────────────────────────\n\nAPI = \"/api/v1\"\n\n\ndef http_get(path: str) -> tuple[int, dict]:\n try:\n with urllib.request.urlopen(f\"{BASE_URL}{path}\") as r:\n return r.status, json.loads(r.read())\n except urllib.error.HTTPError as e:\n return e.code, {}\n\n\ndef http_post(path: str, body: dict) -> tuple[int, dict]:\n data = json.dumps(body).encode()\n req = urllib.request.Request(\n f\"{BASE_URL}{path}\",\n data=data,\n headers={\"Content-Type\": \"application/json\"},\n method=\"POST\",\n )\n try:\n with urllib.request.urlopen(req) as r:\n return r.status, json.loads(r.read())\n except urllib.error.HTTPError as e:\n return e.code, json.loads(e.read()) if e.fp else {}\n\n\ndef ensure_test_agent() -> str:\n \"\"\"Create or get test agent, return token.\"\"\"\n code, data = http_get(f\"{API}/members/{TEST_SLUG}\")\n if code == 200:\n token = data.get(\"token\")\n if token:\n log(\"SETUP\", f\"Found existing agent '{TEST_SLUG}'\", {\"token\": token[:20] + \"...\"})\n return token\n # Regenerate\n code2, d2 = http_post(f\"{API}/members/{TEST_SLUG}/regenerate-token\", {})\n if code2 == 200:\n token = d2[\"token\"]\n log(\"SETUP\", \"Regenerated token\", {\"token\": token[:20] + \"...\"})\n return token\n raise RuntimeError(f\"Failed to regenerate token: {code2} {d2}\")\n\n # Create\n payload = {\n \"name\": TEST_NAME,\n \"slug\": TEST_SLUG,\n \"type\": \"agent\",\n \"role\": \"member\",\n \"agent_config\": {\"chat_listen\": \"all\", \"task_listen\": \"all\"},\n }\n code, data = http_post(f\"{API}/members\", payload)\n if code not in (200, 201):\n raise RuntimeError(f\"Failed to create agent: {code} {data}\")\n token = data[\"token\"]\n log(\"SETUP\", f\"Created agent '{TEST_SLUG}'\", {\"token\": token[:20] + \"...\"})\n return token\n\n\n# ── WS test ───────────────────────────────────────────────────────────────────\n\nasync def run_test(ws_url: str, token: str):\n log(\"WS\", f\"Connecting → {ws_url}\")\n\n async with websockets.connect(ws_url) as ws:\n log(\"WS\", \"Connected ✅\")\n\n # ── 1. Auth ──\n auth = {\"type\": \"auth\", \"token\": token}\n log(\"SEND\", \"auth\", auth)\n await ws.send(json.dumps(auth))\n\n raw = await asyncio.wait_for(ws.recv(), timeout=5.0)\n msg = json.loads(raw)\n log(\"RECV\", msg.get(\"type\", \"?\"), msg)\n\n if msg.get(\"type\") != \"auth.ok\":\n log(\"ERROR\", f\"Expected auth.ok, got '{msg.get('type')}'\")\n return\n\n info = msg.get(\"data\", {})\n lobby_id = info.get(\"lobby_chat_id\")\n projects = info.get(\"projects\", [])\n online = info.get(\"online\", [])\n log(\"INFO\", f\"online={online}, lobby={lobby_id}, projects={[p['name'] for p in projects]}\")\n\n # ── 2. Heartbeat ──\n hb = {\"type\": \"heartbeat\", \"status\": \"idle\"}\n log(\"SEND\", \"heartbeat\", hb)\n await ws.send(json.dumps(hb))\n await asyncio.sleep(0.3)\n\n # ── 3. Subscribe to project ──\n if projects:\n p = projects[0]\n sub = {\"type\": \"project.subscribe\", \"project_id\": p[\"id\"]}\n log(\"SEND\", \"project.subscribe\", sub)\n await ws.send(json.dumps(sub))\n await asyncio.sleep(0.3)\n\n # ── 4. Send chat message ──\n if lobby_id:\n chat_msg = {\n \"type\": \"chat.send\",\n \"chat_id\": lobby_id,\n \"content\": f\"[ws_test.py] ping @ {ts()}\",\n \"mentions\": [],\n }\n log(\"SEND\", \"chat.send\", chat_msg)\n await ws.send(json.dumps(chat_msg))\n else:\n log(\"WARN\", \"No lobby_chat_id — skipping chat.send\")\n\n # ── 5. Drain incoming (3 s) ──\n log(\"INFO\", \"Listening 3 s for incoming events...\")\n deadline = asyncio.get_event_loop().time() + 3.0\n while asyncio.get_event_loop().time() < deadline:\n try:\n raw = await asyncio.wait_for(ws.recv(), timeout=0.4)\n incoming = json.loads(raw)\n log(\"RECV\", incoming.get(\"type\", \"?\"), incoming)\n except asyncio.TimeoutError:\n pass\n\n # ── 6. Unknown event → expect error ──\n bad = {\"type\": \"totally.unknown\", \"foo\": \"bar\"}\n log(\"SEND\", \"bad event\", bad)\n await ws.send(json.dumps(bad))\n try:\n raw = await asyncio.wait_for(ws.recv(), timeout=2.0)\n err = json.loads(raw)\n log(\"RECV\", err.get(\"type\", \"?\"), err)\n except asyncio.TimeoutError:\n log(\"WARN\", \"No response to unknown event\")\n\n log(\"WS\", \"Test complete ✅\")\n\n\n# ── main ─────────────────────────────────────────────────────────────────────\n\ndef main():\n parser = argparse.ArgumentParser()\n parser.add_argument(\"--url\", default=WS_URL)\n parser.add_argument(\"--token\", default=None, help=\"Skip agent creation, use this token\")\n args = parser.parse_args()\n\n if args.token:\n token = args.token\n log(\"SETUP\", f\"Using provided token: {token[:20]}...\")\n else:\n log(\"SETUP\", f\"No token — creating/fetching test agent via {BASE_URL}\")\n token = ensure_test_agent()\n\n asyncio.run(run_test(args.url, token))\n\n\nif __name__ == \"__main__\":\n main()\n"}],"isError":false,"timestamp":1771869562220}}
|
||
{"type":"message","id":"2cc22501","parentId":"69dafb1d","timestamp":"2026-02-23T17:59:27.354Z","message":{"role":"assistant","content":[{"type":"text","text":"Это скрипт для тестирования WebSocket-соединения с Team Board Tracker. Вот что он делает:\n\n1. **SETUP** — создаёт или находит тестового агента (`ws-test-bot`) через REST API, получает токен\n2. **Auth** — отправляет `{\"type\": \"auth\", \"token\": ...}`, ожидает `auth.ok`\n3. **Heartbeat** — посылает `{\"type\": \"heartbeat\", \"status\": \"idle\"}`\n4. **Subscribe** — подписывается на первый доступный проект (`project.subscribe`)\n5. **Chat** — отправляет сообщение `[ws_test.py] ping @ <время>` в лобби-чат\n6. **Drain** — 3 секунды слушает входящие события\n7. **Bad event** — посылает неизвестный тип, проверяет ответ на ошибку\n\nХочешь запустить его или что-то изменить?"}],"api":"anthropic-messages","provider":"anthropic","model":"claude-sonnet-4-6","usage":{"input":1,"output":259,"cacheRead":2367,"cacheWrite":2037,"totalTokens":4664,"cost":{"input":0.000003,"output":0.003885,"cacheRead":0.0007101,"cacheWrite":0.007638750000000001,"total":0.01223685}},"stopReason":"stop","timestamp":1771869562220}}
|