feat: integrate 5 feature branches + daemon/job 命令层级化 + 跨平台后台引擎 + TypeScript 错误修复

Squashed merge of:
1. fix/mcp-tsc-errors — 修复上游 MCP 重构后的 tsc 错误和测试失败
2. feat/pipe-mute-disconnect — Pipe IPC 逻辑断开、/lang 命令、mute 状态机
3. feat/stub-recovery-all — 实现全部 stub 恢复 (task 001-012)
4. feat/kairos-activation — KAIROS 激活解除阻塞 + 工具实现
5. codex/openclaw-autonomy-pr — 自治权限系统、运行记录、managed flows

Additional:
6. daemon/job 命令层级化重构 (subcommand 架构)
7. 跨平台后台引擎抽象 (detached/tmux engines)
8. 修复 src/ 中 43 个预存在的 TypeScript 类型错误
9. 修复 langfuse isolated test mock 完整性
10. 修复 CodeRabbit 审查的 Critical/Major/Minor 问题
11. remote-control-server logger 抽象 (测试 stderr 静默化)
12. /simplify 审查修复 (代码复用、质量、效率)
This commit is contained in:
unraid
2026-04-14 14:19:00 +08:00
parent dad3ad2b8d
commit 637c9081f6
123 changed files with 13536 additions and 1887 deletions

View File

@@ -1,3 +1,5 @@
import { log, error as logError } from "../logger";
export interface SessionEvent {
id: string;
sessionId: string;
@@ -33,12 +35,12 @@ export class EventBus {
createdAt: Date.now(),
};
this.events.push(full);
console.log(`[RC-DEBUG] bus publish: sessionId=${event.sessionId} type=${event.type} dir=${event.direction} seq=${full.seqNum} subscribers=${this.subscribers.size}`);
log(`[RC-DEBUG] bus publish: sessionId=${event.sessionId} type=${event.type} dir=${event.direction} seq=${full.seqNum} subscribers=${this.subscribers.size}`);
for (const cb of this.subscribers) {
try {
cb(full);
} catch (err) {
console.error(`[RC-DEBUG] bus subscriber error:`, err);
logError(`[RC-DEBUG] bus subscriber error:`, err);
}
}
return full;

View File

@@ -1,3 +1,4 @@
import { log, error as logError } from "../logger";
import type { Context } from "hono";
import type { SessionEvent } from "./event-bus";
import { getEventBus } from "./event-bus";
@@ -76,7 +77,7 @@ export function createSSEStream(c: Context, sessionId: string, fromSeqNum = 0) {
seqNum: event.seqNum,
});
try {
console.log(`[RC-DEBUG] SSE -> web: sessionId=${sessionId} type=${event.type} dir=${event.direction} seq=${event.seqNum}`);
log(`[RC-DEBUG] SSE -> web: sessionId=${sessionId} type=${event.type} dir=${event.direction} seq=${event.seqNum}`);
controller.enqueue(encoder.encode(`id: ${event.seqNum}\nevent: message\ndata: ${data}\n\n`));
} catch {
unsub();

View File

@@ -2,6 +2,7 @@ import type { WSContext } from "hono/ws";
import { getEventBus } from "./event-bus";
import type { SessionEvent } from "./event-bus";
import { publishSessionEvent } from "../services/transport";
import { log, error as logError } from "../logger";
// Per-connection cleanup, keyed by sessionId (only one WS per session)
interface CleanupEntry {
@@ -96,13 +97,13 @@ function toSDKMessage(event: SessionEvent): string {
/** Called from onOpen — subscribes to event bus, forwards outbound events to bridge WS */
export function handleWebSocketOpen(ws: WSContext, sessionId: string) {
const openTime = Date.now();
console.log(`[RC-DEBUG] [WS] Open session=${sessionId}`);
log(`[RC-DEBUG] [WS] Open session=${sessionId}`);
activeConnections.add(ws);
// If there's an existing connection for this session, clean it up first
const existing = cleanupBySession.get(sessionId);
if (existing) {
console.log(`[WS] Replacing existing connection for session=${sessionId}`);
log(`[WS] Replacing existing connection for session=${sessionId}`);
existing.unsub();
clearInterval(existing.keepalive);
activeConnections.delete(existing.ws);
@@ -114,7 +115,7 @@ export function handleWebSocketOpen(ws: WSContext, sessionId: string) {
// the full conversation history — assistant replies are inbound events.
const missed = bus.getEventsSince(0);
if (missed.length > 0) {
console.log(`[WS] Replaying ${missed.length} missed event(s)`);
log(`[WS] Replaying ${missed.length} missed event(s)`);
for (const event of missed) {
if (ws.readyState !== 1) break;
try {
@@ -130,10 +131,10 @@ export function handleWebSocketOpen(ws: WSContext, sessionId: string) {
if (event.direction !== "outbound") return;
try {
const sdkMsg = toSDKMessage(event);
console.log(`[RC-DEBUG] [WS] -> bridge (outbound): type=${event.type} len=${sdkMsg.length} msg=${sdkMsg.slice(0, 300)}`);
log(`[RC-DEBUG] [WS] -> bridge (outbound): type=${event.type} len=${sdkMsg.length} msg=${sdkMsg.slice(0, 300)}`);
ws.send(sdkMsg);
} catch (err) {
console.error("[RC-DEBUG] [WS] send error:", err);
logError("[RC-DEBUG] [WS] send error:", err);
}
});
@@ -161,7 +162,7 @@ export function handleWebSocketMessage(ws: WSContext, sessionId: string, data: s
try {
ingestBridgeMessage(sessionId, JSON.parse(line));
} catch (err) {
console.error("[WS] parse error:", err);
logError("[WS] parse error:", err);
}
}
}
@@ -173,7 +174,7 @@ export function handleWebSocketClose(ws: WSContext, sessionId: string, code?: nu
const entry = cleanupBySession.get(sessionId);
const duration = entry ? Math.round((Date.now() - entry.openTime) / 1000) : -1;
console.log(`[WS] Close session=${sessionId} code=${code ?? "none"} reason=${reason || "(none)"} duration=${duration}s`);
log(`[WS] Close session=${sessionId} code=${code ?? "none"} reason=${reason || "(none)"} duration=${duration}s`);
if (entry) {
entry.unsub();
@@ -215,7 +216,7 @@ export function ingestBridgeMessage(sessionId: string, msg: Record<string, unkno
const eventType = deriveEventType(msg);
console.log(`[RC-DEBUG] [WS] <- bridge (inbound): sessionId=${sessionId} type=${eventType}${msg.uuid ? ` uuid=${msg.uuid}` : ""} msg=${JSON.stringify(msg).slice(0, 300)}`);
log(`[RC-DEBUG] [WS] <- bridge (inbound): sessionId=${sessionId} type=${eventType}${msg.uuid ? ` uuid=${msg.uuid}` : ""} msg=${JSON.stringify(msg).slice(0, 300)}`);
let payload: unknown;
@@ -255,7 +256,7 @@ export function closeAllConnections(): void {
const count = activeConnections.size;
if (count === 0) return;
console.log(`[WS] Gracefully closing ${count} active connection(s)...`);
log(`[WS] Gracefully closing ${count} active connection(s)...`);
for (const [sessionId, entry] of cleanupBySession) {
try {
entry.unsub();
@@ -269,5 +270,5 @@ export function closeAllConnections(): void {
}
cleanupBySession.clear();
activeConnections.clear();
console.log("[WS] All connections closed");
log("[WS] All connections closed");
}