diff --git a/.github/brain/memory.json b/.github/brain/memory.json index cdc4885d..32386f1e 100644 --- a/.github/brain/memory.json +++ b/.github/brain/memory.json @@ -67,6 +67,12 @@ } }, "events": [ + { + "date": "2026-03-13", + "type": "bug_fix", + "description": "修复「我要开发」任务提交失败 · 3个根因:① Nginx缺少CORS头(跨域预检失败)② WebSocket路由冲突(/ws/preview误路由到8080端口)③ 前端开发端口不匹配(3721→3002)· 增加重试机制和详细错误诊断 · 冰朔人格体已启动", + "by": "铸渊(冰朔指令)" + }, { "date": "2026-03-13", "type": "brain_restore", diff --git a/.github/persona-brain/memory.json b/.github/persona-brain/memory.json index dae0f307..7299990b 100644 --- a/.github/persona-brain/memory.json +++ b/.github/persona-brain/memory.json @@ -2,6 +2,12 @@ "persona_id": "ICE-GL-ZY001", "persona_name": "铸渊", "recent_events": [ + { + "date": "2026-03-13", + "type": "bug_fix", + "description": "修复「我要开发」任务提交失败 · Nginx CORS+WebSocket路由+端口修复 · 冰朔人格体启动", + "by": "铸渊(冰朔指令)" + }, { "date": "2026-03-13", "type": "brain_restore", @@ -33,7 +39,7 @@ "by": "GitHub Actions" } ], - "last_updated": "2026-03-11T10:24:00.000Z", + "last_updated": "2026-03-13T06:58:00.000Z", "total_schemas_created": 3, "total_routes_implemented": 4, "hli_coverage": "3/17", diff --git a/.github/workflows/deploy-to-server.yml b/.github/workflows/deploy-to-server.yml index fa73dcf1..d9504e19 100644 --- a/.github/workflows/deploy-to-server.yml +++ b/.github/workflows/deploy-to-server.yml @@ -378,8 +378,14 @@ jobs: ' proxy_set_header X-Real-IP \$remote_addr;' \ '}' \ '' \ - '# Persona Studio API → persona-studio 后端端口 3002' \ + '# Persona Studio API → persona-studio 后端端口 3002(含 CORS 跨域支持)' \ 'location /api/ps/ {' \ + ' add_header Access-Control-Allow-Origin * always;' \ + ' add_header Access-Control-Allow-Methods "GET, POST, OPTIONS" always;' \ + ' add_header Access-Control-Allow-Headers "Content-Type, Authorization" always;' \ + ' if (\$request_method = OPTIONS) {' \ + ' return 204;' \ + ' }' \ ' proxy_pass http://127.0.0.1:3002/api/ps/;' \ ' proxy_http_version 1.1;' \ ' proxy_set_header Host \$host;' \ @@ -408,6 +414,18 @@ jobs: ' proxy_cache_bypass \$http_upgrade;' \ '}' \ '' \ + '# Persona Studio WebSocket → 预览进度推送端口 3002' \ + 'location /ws/preview {' \ + ' proxy_pass http://127.0.0.1:3002;' \ + ' proxy_http_version 1.1;' \ + ' proxy_set_header Upgrade \$http_upgrade;' \ + ' proxy_set_header Connection \"Upgrade\";' \ + ' proxy_set_header Host \$host;' \ + ' proxy_set_header X-Real-IP \$remote_addr;' \ + ' proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;' \ + ' proxy_read_timeout 86400;' \ + '}' \ + '' \ '# WebSocket 反向代理 → 看板实时推送端口 8080' \ 'location /ws {' \ ' proxy_pass http://127.0.0.1:8080;' \ diff --git a/backend-integration/nginx-api-proxy.conf b/backend-integration/nginx-api-proxy.conf index cc36436b..2527dd67 100644 --- a/backend-integration/nginx-api-proxy.conf +++ b/backend-integration/nginx-api-proxy.conf @@ -5,7 +5,24 @@ # # 前端(GitHub Pages)→ Nginx → API 代理(Node.js :3721)→ 模型 API -# API 代理转发 +# Persona Studio API → persona-studio 后端端口 3002(含 CORS 跨域支持) +location /api/ps/ { + add_header Access-Control-Allow-Origin * always; + add_header Access-Control-Allow-Methods "GET, POST, OPTIONS" always; + add_header Access-Control-Allow-Headers "Content-Type, Authorization" always; + if ($request_method = OPTIONS) { + return 204; + } + proxy_pass http://127.0.0.1:3002/api/ps/; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_read_timeout 90s; +} + +# API 代理转发(通用 AI 聊天) location /api/ { proxy_pass http://127.0.0.1:3721; proxy_http_version 1.1; @@ -24,3 +41,15 @@ location /api/ { proxy_read_timeout 90s; proxy_send_timeout 60s; } + +# Persona Studio WebSocket → 预览进度推送端口 3002 +location /ws/preview { + proxy_pass http://127.0.0.1:3002; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_read_timeout 86400; +} diff --git a/persona-studio/brain/memory/EXP-000/memory.json b/persona-studio/brain/memory/EXP-000/memory.json new file mode 100644 index 00000000..0771085d --- /dev/null +++ b/persona-studio/brain/memory/EXP-000/memory.json @@ -0,0 +1,7 @@ +{ + "dev_id": "EXP-000", + "conversations": [], + "last_topic": null, + "preferences": {}, + "updated_at": null +} \ No newline at end of file diff --git a/persona-studio/frontend/chat.js b/persona-studio/frontend/chat.js index 2d263e98..2a11939f 100644 --- a/persona-studio/frontend/chat.js +++ b/persona-studio/frontend/chat.js @@ -13,7 +13,7 @@ const SELECTED_MODEL = sessionStorage.getItem('selected_model'); const API_BASE = (function () { if (location.hostname === 'localhost' || location.hostname === '127.0.0.1') { - return 'http://localhost:3721'; + return 'http://localhost:3002'; } return 'https://guanghulab.com'; })(); @@ -648,33 +648,47 @@ async function confirmBuild() { // 先连接 WebSocket(确保在 build 开始前建立连接,避免丢失进度消息) connectPreviewWebSocket(); - try { - var buildRes = await fetch(API_BASE + '/api/ps/build/start', { - method: 'POST', - headers: authHeaders({ 'Content-Type': 'application/json' }), - body: JSON.stringify({ - dev_id: DEV_ID, - email: email, - contact: contact, - conversation: conversationHistory, - api_base: USER_API_BASE, - api_key: USER_API_KEY, - model: SELECTED_MODEL - }) - }); + // 提交开发任务(含重试机制) + var maxRetries = 2; + var retryCount = 0; + var submitted = false; - if (!buildRes.ok) { - var errMsg = 'HTTP ' + buildRes.status + ' ' + buildRes.statusText; - try { - var errData = await buildRes.json(); - if (errData.message) errMsg = errData.message; - } catch (_e) { /* use status text fallback */ } - appendMessage('system', '⚠️ 铸渊代理启动失败: ' + errMsg); - updatePreviewStatus('error', '启动失败'); + while (retryCount <= maxRetries && !submitted) { + try { + var buildRes = await fetch(API_BASE + '/api/ps/build/start', { + method: 'POST', + headers: authHeaders({ 'Content-Type': 'application/json' }), + body: JSON.stringify({ + dev_id: DEV_ID, + email: email, + contact: contact, + conversation: conversationHistory, + api_base: USER_API_BASE, + api_key: USER_API_KEY, + model: SELECTED_MODEL + }) + }); + + if (!buildRes.ok) { + var errMsg = 'HTTP ' + buildRes.status + ' ' + buildRes.statusText; + try { + var errData = await buildRes.json(); + if (errData.message) errMsg = errData.message; + } catch (_e) { /* use status text fallback */ } + appendMessage('system', '⚠️ 铸渊代理启动失败: ' + errMsg); + updatePreviewStatus('error', '启动失败'); + } + submitted = true; + } catch (_err) { + retryCount++; + if (retryCount <= maxRetries) { + appendMessage('system', '⏳ 连接后端服务中,正在重试(' + retryCount + '/' + maxRetries + ')...'); + await new Promise(function (r) { setTimeout(r, 1500); }); + } else { + appendMessage('system', '⚠️ 任务提交失败:无法连接铸渊后端服务。请检查:\n1. 网络连接是否正常\n2. 后端服务是否已启动(端口 3002)\n3. 如使用 GitHub Pages 访问,请确认 guanghulab.com 服务可用'); + updatePreviewStatus('error', '连接失败'); + } } - } catch (_err) { - appendMessage('system', '⚠️ 任务提交失败,请检查网络连接后再试'); - updatePreviewStatus('error', '网络错误'); } } diff --git a/persona-studio/frontend/index.html b/persona-studio/frontend/index.html index 4f7edd35..1835efe8 100644 --- a/persona-studio/frontend/index.html +++ b/persona-studio/frontend/index.html @@ -150,7 +150,7 @@ function getApiBase() { if (location.hostname === 'localhost' || location.hostname === '127.0.0.1') { - return 'http://localhost:3721'; + return 'http://localhost:3002'; } return 'https://guanghulab.com'; } diff --git a/tests/smoke/apikey-detect.test.js b/tests/smoke/apikey-detect.test.js index 1de95911..e23bc2d3 100644 --- a/tests/smoke/apikey-detect.test.js +++ b/tests/smoke/apikey-detect.test.js @@ -6,7 +6,7 @@ */ const http = require('http'); -const BASE = process.env.TEST_BASE || 'http://localhost:3721'; +const BASE = process.env.TEST_BASE || 'http://localhost:3002'; function post(path, body) { return new Promise((resolve, reject) => {