u-foo 1.8.4 → 1.8.5
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/index.js +12 -6
- package/src/bus/message.js +16 -0
- package/src/bus/store.js +72 -25
package/package.json
CHANGED
package/src/bus/index.js
CHANGED
|
@@ -285,13 +285,19 @@ class EventBus {
|
|
|
285
285
|
|
|
286
286
|
// 如果 publisher 不在 agents 列表中,自动注册它(懒加载模式)
|
|
287
287
|
if (publisher !== "unknown" && this.busData.agents && !this.busData.agents[publisher]) {
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
288
|
+
let subscriber;
|
|
289
|
+
if (publisher === "ufoo-agent") {
|
|
290
|
+
// Keep the reserved controller on its fixed identity even after metadata loss.
|
|
291
|
+
subscriber = (await this.subscriberManager.join("ufoo-agent", "ufoo-agent", "ufoo-agent")).subscriber;
|
|
292
|
+
} else {
|
|
293
|
+
// 解析 agent 信息
|
|
294
|
+
const parts = publisher.split(":");
|
|
295
|
+
const agentType = parts[0] || "unknown-agent";
|
|
296
|
+
const sessionId = parts[1] || require("./utils").generateInstanceId();
|
|
292
297
|
|
|
293
|
-
|
|
294
|
-
|
|
298
|
+
// 自动加入总线(静默模式,不输出日志)
|
|
299
|
+
subscriber = (await this.subscriberManager.join(sessionId, agentType, null)).subscriber;
|
|
300
|
+
}
|
|
295
301
|
this.saveBusData();
|
|
296
302
|
publisher = subscriber; // 使用规范化的 subscriber ID
|
|
297
303
|
}
|
package/src/bus/message.js
CHANGED
|
@@ -178,6 +178,7 @@ class MessageManager {
|
|
|
178
178
|
resolveTarget(target) {
|
|
179
179
|
const nicknameManager = new NicknameManager(this.busData);
|
|
180
180
|
const normalizedTarget = normalizeAgentTypeAlias(target);
|
|
181
|
+
const controllerTarget = target === "ufoo-agent";
|
|
181
182
|
|
|
182
183
|
// 0. Exact subscriber ID match (allows ids without ":" e.g. "ufoo-agent")
|
|
183
184
|
const subscribers = this.busData.agents || {};
|
|
@@ -185,6 +186,21 @@ class MessageManager {
|
|
|
185
186
|
return [target];
|
|
186
187
|
}
|
|
187
188
|
|
|
189
|
+
// Reserved controller ID should never fan out to legacy typed aliases.
|
|
190
|
+
if (controllerTarget) {
|
|
191
|
+
try {
|
|
192
|
+
const queueDir = this.queueManager && this.queueManager.getQueueDir
|
|
193
|
+
? this.queueManager.getQueueDir(target)
|
|
194
|
+
: path.join(this.busDir, "queues", target);
|
|
195
|
+
if (queueDir && fs.existsSync(queueDir)) {
|
|
196
|
+
return [target];
|
|
197
|
+
}
|
|
198
|
+
} catch {
|
|
199
|
+
// ignore queue lookup errors
|
|
200
|
+
}
|
|
201
|
+
return [];
|
|
202
|
+
}
|
|
203
|
+
|
|
188
204
|
// 1. 尝试作为订阅者 ID
|
|
189
205
|
if (target.includes(":")) {
|
|
190
206
|
return [target];
|
package/src/bus/store.js
CHANGED
|
@@ -47,6 +47,75 @@ function allocateRecoveredNickname(agentType, used) {
|
|
|
47
47
|
return nick;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
function recoverQueueEntry(data, subscriber, queueDir, usedNicknames, now) {
|
|
51
|
+
if (!subscriber || data.agents[subscriber]) return false;
|
|
52
|
+
|
|
53
|
+
if (subscriber === "ufoo-agent") {
|
|
54
|
+
const tty = readQueueTty(queueDir);
|
|
55
|
+
const ttyInfo = tty ? getTtyProcessInfo(tty) : null;
|
|
56
|
+
data.agents[subscriber] = {
|
|
57
|
+
agent_type: "ufoo-agent",
|
|
58
|
+
nickname: "ufoo-agent",
|
|
59
|
+
status: "active",
|
|
60
|
+
joined_at: now,
|
|
61
|
+
last_seen: now,
|
|
62
|
+
pid: 0,
|
|
63
|
+
tty,
|
|
64
|
+
tty_shell_pid: ttyInfo && ttyInfo.shellPid ? ttyInfo.shellPid : 0,
|
|
65
|
+
tmux_pane: "",
|
|
66
|
+
launch_mode: "",
|
|
67
|
+
};
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const parts = subscriber.split(":");
|
|
72
|
+
if (parts.length !== 2) return false;
|
|
73
|
+
const [agentType, sessionId] = parts;
|
|
74
|
+
if (!agentType || !sessionId) return false;
|
|
75
|
+
if (!isRecoverableSessionId(sessionId)) return false;
|
|
76
|
+
|
|
77
|
+
const tty = readQueueTty(queueDir);
|
|
78
|
+
const ttyInfo = tty ? getTtyProcessInfo(tty) : null;
|
|
79
|
+
const activeByTty = Boolean(ttyInfo && ttyInfo.alive && ttyInfo.hasAgent);
|
|
80
|
+
const nickname = activeByTty ? allocateRecoveredNickname(agentType, usedNicknames) : "";
|
|
81
|
+
|
|
82
|
+
data.agents[subscriber] = {
|
|
83
|
+
agent_type: agentType,
|
|
84
|
+
nickname,
|
|
85
|
+
status: activeByTty ? "active" : "inactive",
|
|
86
|
+
joined_at: now,
|
|
87
|
+
last_seen: now,
|
|
88
|
+
pid: 0,
|
|
89
|
+
tty,
|
|
90
|
+
tty_shell_pid: ttyInfo && ttyInfo.shellPid ? ttyInfo.shellPid : 0,
|
|
91
|
+
tmux_pane: "",
|
|
92
|
+
launch_mode: "",
|
|
93
|
+
};
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function reconcileReservedControllerAliases(data, now) {
|
|
98
|
+
if (!data.agents || !data.agents["ufoo-agent"]) return false;
|
|
99
|
+
|
|
100
|
+
let changed = false;
|
|
101
|
+
for (const [id, meta] of Object.entries(data.agents)) {
|
|
102
|
+
if (!id.startsWith("ufoo-agent:")) continue;
|
|
103
|
+
if (!meta || meta.status !== "active") continue;
|
|
104
|
+
if (String(meta.agent_type || "").trim() !== "ufoo-agent") continue;
|
|
105
|
+
const hasRuntimeBinding = Boolean(
|
|
106
|
+
meta.tty
|
|
107
|
+
|| meta.tmux_pane
|
|
108
|
+
|| meta.host_inject_sock
|
|
109
|
+
|| meta.host_daemon_sock
|
|
110
|
+
);
|
|
111
|
+
if (hasRuntimeBinding) continue;
|
|
112
|
+
meta.status = "inactive";
|
|
113
|
+
meta.last_seen = now;
|
|
114
|
+
changed = true;
|
|
115
|
+
}
|
|
116
|
+
return changed;
|
|
117
|
+
}
|
|
118
|
+
|
|
50
119
|
class BusStore {
|
|
51
120
|
constructor(projectRoot) {
|
|
52
121
|
this.projectRoot = projectRoot;
|
|
@@ -89,33 +158,11 @@ class BusStore {
|
|
|
89
158
|
if (!stat.isDirectory()) continue;
|
|
90
159
|
|
|
91
160
|
const subscriber = safeNameToSubscriber(entry);
|
|
92
|
-
|
|
93
|
-
if (parts.length !== 2) continue;
|
|
94
|
-
const [agentType, sessionId] = parts;
|
|
95
|
-
if (!agentType || !sessionId) continue;
|
|
96
|
-
if (!isRecoverableSessionId(sessionId)) continue;
|
|
97
|
-
if (data.agents[subscriber]) continue;
|
|
98
|
-
|
|
99
|
-
const tty = readQueueTty(queueDir);
|
|
100
|
-
const ttyInfo = tty ? getTtyProcessInfo(tty) : null;
|
|
101
|
-
const activeByTty = Boolean(ttyInfo && ttyInfo.alive && ttyInfo.hasAgent);
|
|
102
|
-
const nickname = activeByTty ? allocateRecoveredNickname(agentType, usedNicknames) : "";
|
|
103
|
-
|
|
104
|
-
data.agents[subscriber] = {
|
|
105
|
-
agent_type: agentType,
|
|
106
|
-
nickname,
|
|
107
|
-
status: activeByTty ? "active" : "inactive",
|
|
108
|
-
joined_at: now,
|
|
109
|
-
last_seen: now,
|
|
110
|
-
pid: 0,
|
|
111
|
-
tty,
|
|
112
|
-
tty_shell_pid: ttyInfo && ttyInfo.shellPid ? ttyInfo.shellPid : 0,
|
|
113
|
-
tmux_pane: "",
|
|
114
|
-
launch_mode: "",
|
|
115
|
-
};
|
|
116
|
-
recovered = true;
|
|
161
|
+
recovered = recoverQueueEntry(data, subscriber, queueDir, usedNicknames, now) || recovered;
|
|
117
162
|
}
|
|
118
163
|
|
|
164
|
+
recovered = reconcileReservedControllerAliases(data, now) || recovered;
|
|
165
|
+
|
|
119
166
|
if (recovered) {
|
|
120
167
|
saveAgentsData(this.agentsFile, data);
|
|
121
168
|
}
|