u-foo 1.7.2 → 1.7.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/bus/daemon.js +5 -4
- package/src/bus/utils.js +29 -8
- package/src/chat/commandExecutor.js +1 -0
- package/src/cli.js +2 -0
- package/src/daemon/groupOrchestrator.js +20 -0
- package/src/daemon/index.js +15 -0
package/package.json
CHANGED
package/src/bus/daemon.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const fs = require("fs");
|
|
2
2
|
const path = require("path");
|
|
3
|
-
const { readJSON, writeJSON, isPidAlive, isAgentPidAlive, ensureDir, safeNameToSubscriber, subscriberToSafeName } = require("./utils");
|
|
3
|
+
const { readJSON, writeJSON, isPidAlive, isAgentPidAlive, isMetaActive, ensureDir, safeNameToSubscriber, subscriberToSafeName } = require("./utils");
|
|
4
4
|
const Injector = require("./inject");
|
|
5
5
|
const QueueManager = require("./queue");
|
|
6
6
|
const MessageManager = require("./message");
|
|
@@ -405,12 +405,13 @@ class BusDaemon {
|
|
|
405
405
|
continue;
|
|
406
406
|
}
|
|
407
407
|
|
|
408
|
-
// 检查 PID
|
|
409
|
-
if (
|
|
408
|
+
// 检查 agent 是否仍然存活(PID + TTY 交叉检查)
|
|
409
|
+
if (!isMetaActive(meta)) {
|
|
410
410
|
const now = new Date().toISOString().split("T")[1].slice(0, 8);
|
|
411
|
-
console.log(`[daemon] ${now} Agent ${subscriber} (pid=${meta.pid}) is dead, marking inactive`);
|
|
411
|
+
console.log(`[daemon] ${now} Agent ${subscriber} (pid=${meta.pid || 0}) is dead, marking inactive`);
|
|
412
412
|
|
|
413
413
|
meta.status = "inactive";
|
|
414
|
+
meta.activity_state = "";
|
|
414
415
|
changed = true;
|
|
415
416
|
|
|
416
417
|
// 清理队列目录和 offset
|
package/src/bus/utils.js
CHANGED
|
@@ -310,23 +310,44 @@ function isMetaActive(meta) {
|
|
|
310
310
|
// 2. PID 存活(最可靠)
|
|
311
311
|
if (meta.pid && isAgentPidAlive(meta.pid)) return true;
|
|
312
312
|
|
|
313
|
-
// 3.
|
|
313
|
+
// 3. PID 已记录但进程已死 → 确定离线
|
|
314
|
+
if (meta.pid) return false;
|
|
315
|
+
|
|
316
|
+
// 4. 无 PID(如 codex)— TTY 交叉校验
|
|
317
|
+
// 仅当 tty_shell_pid 也还活着时才信任 TTY 检查,
|
|
318
|
+
// 防止 TTY 上残留的僵尸进程导致误判存活
|
|
314
319
|
if (meta.tty) {
|
|
315
320
|
const ttyInfo = getTtyProcessInfo(meta.tty);
|
|
316
|
-
if (ttyInfo && ttyInfo.hasAgent)
|
|
321
|
+
if (ttyInfo && ttyInfo.hasAgent) {
|
|
322
|
+
// 如果记录了 tty_shell_pid,验证它还在
|
|
323
|
+
if (meta.tty_shell_pid) {
|
|
324
|
+
if (isPidAlive(meta.tty_shell_pid)) return true;
|
|
325
|
+
// shell pid 已死,TTY 上的进程是残留
|
|
326
|
+
return false;
|
|
327
|
+
}
|
|
328
|
+
// 无 tty_shell_pid,用 last_seen 超时兜底
|
|
329
|
+
if (meta.last_seen) {
|
|
330
|
+
const age = Date.now() - new Date(meta.last_seen).getTime();
|
|
331
|
+
return age <= HEARTBEAT_TIMEOUT_MS;
|
|
332
|
+
}
|
|
333
|
+
return true;
|
|
334
|
+
}
|
|
317
335
|
}
|
|
318
336
|
|
|
319
|
-
//
|
|
320
|
-
if (meta.pid) return false;
|
|
321
|
-
|
|
322
|
-
// 5. 无 PID,用 last_seen 心跳超时兜底
|
|
337
|
+
// 5. 无 PID 无 TTY agent,用 last_seen 心跳超时兜底
|
|
323
338
|
if (meta.status === "active" && meta.last_seen) {
|
|
324
339
|
const age = Date.now() - new Date(meta.last_seen).getTime();
|
|
325
340
|
return age <= HEARTBEAT_TIMEOUT_MS;
|
|
326
341
|
}
|
|
327
342
|
|
|
328
|
-
// 6. status=active
|
|
329
|
-
if (meta.status === "active")
|
|
343
|
+
// 6. status=active 但无任何可靠信息 → 超时后判定离线
|
|
344
|
+
if (meta.status === "active") {
|
|
345
|
+
if (meta.joined_at) {
|
|
346
|
+
const age = Date.now() - new Date(meta.joined_at).getTime();
|
|
347
|
+
return age <= HEARTBEAT_TIMEOUT_MS;
|
|
348
|
+
}
|
|
349
|
+
return true;
|
|
350
|
+
}
|
|
330
351
|
|
|
331
352
|
return false;
|
|
332
353
|
}
|
package/src/cli.js
CHANGED
|
@@ -791,6 +791,7 @@ async function runCli(argv) {
|
|
|
791
791
|
alias,
|
|
792
792
|
instance: opts.instance || "",
|
|
793
793
|
dry_run: opts.dryRun === true,
|
|
794
|
+
...collectHostLaunchRequestContext(),
|
|
794
795
|
});
|
|
795
796
|
if (opts.json) {
|
|
796
797
|
console.log(JSON.stringify(resp?.data || {}, null, 2));
|
|
@@ -1684,6 +1685,7 @@ async function runCli(argv) {
|
|
|
1684
1685
|
alias,
|
|
1685
1686
|
instance,
|
|
1686
1687
|
dry_run: dryRun,
|
|
1688
|
+
...collectHostLaunchRequestContext(),
|
|
1687
1689
|
});
|
|
1688
1690
|
if (outputJson) {
|
|
1689
1691
|
console.log(JSON.stringify(resp?.data || {}, null, 2));
|
|
@@ -190,6 +190,24 @@ function nowIso() {
|
|
|
190
190
|
return new Date().toISOString();
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
+
function buildLaunchHostContext(params = {}) {
|
|
194
|
+
const hostInjectSock = asTrimmedString(params.host_inject_sock || params.hostInjectSock);
|
|
195
|
+
const hostDaemonSock = asTrimmedString(params.host_daemon_sock || params.hostDaemonSock);
|
|
196
|
+
const hostName = asTrimmedString(params.host_name || params.hostName);
|
|
197
|
+
const hostSessionId = asTrimmedString(params.host_session_id || params.hostSessionId);
|
|
198
|
+
const context = {};
|
|
199
|
+
if (hostInjectSock) context.host_inject_sock = hostInjectSock;
|
|
200
|
+
if (hostDaemonSock) context.host_daemon_sock = hostDaemonSock;
|
|
201
|
+
if (hostName) context.host_name = hostName;
|
|
202
|
+
if (hostSessionId) context.host_session_id = hostSessionId;
|
|
203
|
+
if (params.host_capabilities && typeof params.host_capabilities === "object") {
|
|
204
|
+
context.host_capabilities = { ...params.host_capabilities };
|
|
205
|
+
} else if (params.hostCapabilities && typeof params.hostCapabilities === "object") {
|
|
206
|
+
context.host_capabilities = { ...params.hostCapabilities };
|
|
207
|
+
}
|
|
208
|
+
return context;
|
|
209
|
+
}
|
|
210
|
+
|
|
193
211
|
function buildDefaultRuntime({
|
|
194
212
|
groupId,
|
|
195
213
|
instance,
|
|
@@ -309,6 +327,7 @@ function createGroupOrchestrator(options = {}) {
|
|
|
309
327
|
const alias = asTrimmedString(params.alias);
|
|
310
328
|
const instance = asTrimmedString(params.instance);
|
|
311
329
|
const dryRun = params.dry_run === true || params.dryRun === true;
|
|
330
|
+
const launchHostContext = buildLaunchHostContext(params);
|
|
312
331
|
|
|
313
332
|
if (!alias) {
|
|
314
333
|
return { ok: false, error: "group run requires alias", status: "failed" };
|
|
@@ -377,6 +396,7 @@ function createGroupOrchestrator(options = {}) {
|
|
|
377
396
|
agent: item.type,
|
|
378
397
|
count: 1,
|
|
379
398
|
nickname: item.nickname,
|
|
399
|
+
...launchHostContext,
|
|
380
400
|
};
|
|
381
401
|
|
|
382
402
|
// eslint-disable-next-line no-await-in-loop
|
package/src/daemon/index.js
CHANGED
|
@@ -1080,11 +1080,26 @@ function startDaemon({ projectRoot, provider, model, resumeMode = "auto" }) {
|
|
|
1080
1080
|
const alias = req.alias || req.template || "";
|
|
1081
1081
|
const instance = req.instance || req.group_id || "";
|
|
1082
1082
|
const dryRun = req.dry_run === true || req.dryRun === true;
|
|
1083
|
+
const hostInjectSock = req.host_inject_sock || req.hostInjectSock || "";
|
|
1084
|
+
const hostDaemonSock = req.host_daemon_sock || req.hostDaemonSock || "";
|
|
1085
|
+
const hostName = req.host_name || req.hostName || "";
|
|
1086
|
+
const hostSessionId = req.host_session_id || req.hostSessionId || "";
|
|
1087
|
+
const hostCapabilities =
|
|
1088
|
+
req.host_capabilities && typeof req.host_capabilities === "object"
|
|
1089
|
+
? req.host_capabilities
|
|
1090
|
+
: ((req.hostCapabilities && typeof req.hostCapabilities === "object")
|
|
1091
|
+
? req.hostCapabilities
|
|
1092
|
+
: null);
|
|
1083
1093
|
try {
|
|
1084
1094
|
const result = await daemonGroupOrchestrator.runGroup({
|
|
1085
1095
|
alias,
|
|
1086
1096
|
instance,
|
|
1087
1097
|
dry_run: dryRun,
|
|
1098
|
+
host_inject_sock: hostInjectSock,
|
|
1099
|
+
host_daemon_sock: hostDaemonSock,
|
|
1100
|
+
host_name: hostName,
|
|
1101
|
+
host_session_id: hostSessionId,
|
|
1102
|
+
host_capabilities: hostCapabilities,
|
|
1088
1103
|
});
|
|
1089
1104
|
const ok = result && result.ok !== false;
|
|
1090
1105
|
let reply = "";
|