]> acesimba.cloud Git - codebuddy-web.git/commitdiff
feat: 拆分后台状态为运行中/等待中
authorCodebuddy <codebuddy@localhost>
Thu, 13 Aug 2026 23:57:02 +0000 (07:57 +0800)
committerCodebuddy <codebuddy@localhost>
Thu, 13 Aug 2026 23:57:02 +0000 (07:57 +0800)
- 后端 drain 记录 last_output_ts,/api/tasks 按 IDLE_THRESHOLD(2.5s) 区分
  background(后台运行中) 与新增 background_idle(后台等待中)
- 前端新增 tc-waiting 样式与 background_idle 渲染(⏸ 后台等待中)
- 解决关窗后交互式会话仍显示'后台运行中'的误导问题

backend/app.py
frontend/index.html

index e290f990f353bdc4ffc840cc4e5f5edaac85a062..3314f997a5cdd2fb996935a7db4fb5296fc43f20 100644 (file)
@@ -312,6 +312,7 @@ async def _drain(entry: dict) -> None:
             text = data.decode("latin-1", errors="replace")
         entry["buffer"].append(text)
         entry["buflen"] += len(text)
+        entry["last_output_ts"] = time.time()
         _trim_buffer(entry)
         ws = entry.get("ws")
         if ws is not None:
@@ -411,6 +412,7 @@ def _launch_process(task: dict, sid: str, model: str, cwd: str, codebuddy_path:
         "name": task["name"], "cwd": cwd, "model": model,
         "started_at": time.time(), "buffer": [], "buflen": 0,
         "ws": None, "done": False, "exit_code": None,
+        "last_output_ts": time.time(),
         "send_lock": asyncio.Lock(),
     }
     RUNNING[task["id"]] = entry
@@ -479,6 +481,10 @@ async def api_models(request: Request):
     })
 
 
+# 进程活着但无 WS 时,距上次 PTY 输出超过该秒数即视为“后台等待中”(已输出完、等下一条指令)
+IDLE_THRESHOLD = 2.5
+
+
 @app.get("/api/tasks")
 async def api_tasks(request: Request):
     if not _auth_ok(request):
@@ -494,7 +500,11 @@ async def api_tasks(request: Request):
         elif t["id"] in RUNNING:
             e = RUNNING[t["id"]]
             if e["proc"].poll() is None:
-                tt["live_status"] = "background"
+                last_ts = e.get("last_output_ts") or 0
+                if (time.time() - last_ts) < IDLE_THRESHOLD:
+                    tt["live_status"] = "background"
+                else:
+                    tt["live_status"] = "background_idle"
             else:
                 # 后台跑完了但当时没人在看 → 提示用户回来确认
                 tt["live_status"] = "done_detached"
index e84124f8f47e6fa5fc259ac97b12d377c0ed74ae..9e6f0c580f6567e966e1963e6c69fbd4ce6c05e2 100644 (file)
@@ -132,6 +132,8 @@ body { position: fixed; top: 0; left: 0; right: 0; bottom: 0; height: 100%; over
 .task-card.tc-connected .tc-status { color: #ffe66d; }
 .task-card.tc-background { border-color: #4ecca3; }
 .task-card.tc-background .tc-status { color: #4ecca3; }
+.task-card.tc-waiting { border-color: #74c0fc; }
+.task-card.tc-waiting .tc-status { color: #74c0fc; }
 .task-card.tc-paused .tc-status { color: #ffd866; }
 .task-card.tc-idle .tc-status { color: #777; }
 .task-card.tc-done { border-color: #ffa94d; }
@@ -378,6 +380,7 @@ body { position: fixed; top: 0; left: 0; right: 0; bottom: 0; height: 100%; over
       let statusLine, statusClass;
       if (ls === 'connected') { statusLine = '● 前台已登录'; statusClass = 'tc-connected'; }
       else if (ls === 'background') { statusLine = '⚙ 后台运行中'; statusClass = 'tc-background'; }
+      else if (ls === 'background_idle') { statusLine = '⏸ 后台等待中'; statusClass = 'tc-waiting'; }
       else if (ls === 'done_detached') { statusLine = '⏳ 后台任务待确认' + (t.exit_code != null ? '(退出码 ' + t.exit_code + ')' : ''); statusClass = 'tc-done'; }
       else if (isPaused) { statusLine = '⏸ 已暂停'; statusClass = 'tc-paused'; }
       else { statusLine = '○ 空闲'; statusClass = 'tc-idle'; }