u-foo 2.3.6 → 2.3.7
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/subscriber.js +24 -3
- package/src/daemon/ops.js +61 -19
package/package.json
CHANGED
package/src/bus/subscriber.js
CHANGED
|
@@ -94,6 +94,10 @@ function isInternalLaunchMode(meta) {
|
|
|
94
94
|
return mode === "internal" || mode === "internal-pty";
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
+
function hasProviderSession(meta) {
|
|
98
|
+
return typeof meta?.provider_session_id === "string" && meta.provider_session_id.trim() !== "";
|
|
99
|
+
}
|
|
100
|
+
|
|
97
101
|
/**
|
|
98
102
|
* 订阅者管理
|
|
99
103
|
*/
|
|
@@ -412,9 +416,26 @@ class SubscriberManager {
|
|
|
412
416
|
if (!this.busData.agents) return;
|
|
413
417
|
|
|
414
418
|
for (const [id, meta] of Object.entries(this.busData.agents)) {
|
|
415
|
-
if (isInternalLaunchMode(meta)
|
|
416
|
-
|
|
417
|
-
|
|
419
|
+
if (isInternalLaunchMode(meta)) {
|
|
420
|
+
const recoverable = hasProviderSession(meta);
|
|
421
|
+
if (meta.status === "inactive") {
|
|
422
|
+
if (!recoverable) {
|
|
423
|
+
delete this.busData.agents[id];
|
|
424
|
+
this.cleanupSubscriberArtifacts(id);
|
|
425
|
+
}
|
|
426
|
+
continue;
|
|
427
|
+
}
|
|
428
|
+
if (!isMetaActive(meta)) {
|
|
429
|
+
if (recoverable) {
|
|
430
|
+
meta.status = "inactive";
|
|
431
|
+
meta.activity_state = "";
|
|
432
|
+
meta.last_seen = getTimestamp();
|
|
433
|
+
this.cleanupSubscriberArtifacts(id);
|
|
434
|
+
} else {
|
|
435
|
+
delete this.busData.agents[id];
|
|
436
|
+
this.cleanupSubscriberArtifacts(id);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
418
439
|
continue;
|
|
419
440
|
}
|
|
420
441
|
if (meta.status === "active" && !isMetaActive(meta)) {
|
package/src/daemon/ops.js
CHANGED
|
@@ -594,7 +594,16 @@ async function spawnManagedHostAgent(
|
|
|
594
594
|
return { child: null, subscriberId: resultSubscriberId, subscriberIds: [resultSubscriberId].filter(Boolean), sessionId, injectSock };
|
|
595
595
|
}
|
|
596
596
|
|
|
597
|
-
async function spawnInternalAgent(
|
|
597
|
+
async function spawnInternalAgent(
|
|
598
|
+
projectRoot,
|
|
599
|
+
agent,
|
|
600
|
+
count = 1,
|
|
601
|
+
nickname = "",
|
|
602
|
+
processManager = null,
|
|
603
|
+
extraEnv = {},
|
|
604
|
+
extraArgs = [],
|
|
605
|
+
options = {}
|
|
606
|
+
) {
|
|
598
607
|
const runner = resolveUfooRunnerPath();
|
|
599
608
|
const logDir = getUfooPaths(projectRoot).runDir;
|
|
600
609
|
fs.mkdirSync(logDir, { recursive: true });
|
|
@@ -627,10 +636,15 @@ async function spawnInternalAgent(projectRoot, agent, count = 1, nickname = "",
|
|
|
627
636
|
// Daemon 预先在 bus 中注册
|
|
628
637
|
bus.loadBusData();
|
|
629
638
|
process.env.UFOO_PARENT_PID = String(originalPid);
|
|
639
|
+
const replaceAgentId = typeof options.replaceAgentId === "string" ? options.replaceAgentId.trim() : "";
|
|
640
|
+
if (replaceAgentId && bus.busData.agents && bus.busData.agents[replaceAgentId]) {
|
|
641
|
+
delete bus.busData.agents[replaceAgentId];
|
|
642
|
+
}
|
|
630
643
|
|
|
631
644
|
const requestedNickname = nickname
|
|
632
645
|
? (count > 1 ? `${nickname}-${i + 1}` : nickname)
|
|
633
646
|
: "";
|
|
647
|
+
const providerSessionId = typeof options.providerSessionId === "string" ? options.providerSessionId.trim() : "";
|
|
634
648
|
const usePty = process.env.UFOO_INTERNAL_PTY !== "0";
|
|
635
649
|
const launchMode = usePty ? "internal-pty" : "internal";
|
|
636
650
|
|
|
@@ -638,12 +652,14 @@ async function spawnInternalAgent(projectRoot, agent, count = 1, nickname = "",
|
|
|
638
652
|
const joinResult = await bus.subscriberManager.join(sessionId, agentType, requestedNickname, {
|
|
639
653
|
launchMode,
|
|
640
654
|
parentPid: originalPid,
|
|
655
|
+
providerSessionId,
|
|
641
656
|
});
|
|
642
657
|
const finalNickname = joinResult.nickname || requestedNickname || "";
|
|
643
658
|
bus.saveBusData();
|
|
644
659
|
|
|
645
660
|
const runnerCmd = usePty ? "agent-pty-runner" : "agent-runner";
|
|
646
|
-
const
|
|
661
|
+
const args = Array.isArray(extraArgs) ? extraArgs : [];
|
|
662
|
+
const child = spawn(process.execPath, [runner, runnerCmd, agent, ...args], {
|
|
647
663
|
// 关键改动:不使用 detached,daemon 作为父进程
|
|
648
664
|
detached: false,
|
|
649
665
|
stdio: ["ignore", errLog, errLog],
|
|
@@ -657,6 +673,7 @@ async function spawnInternalAgent(projectRoot, agent, count = 1, nickname = "",
|
|
|
657
673
|
UFOO_NICKNAME: finalNickname,
|
|
658
674
|
UFOO_LAUNCH_MODE: usePty ? "internal-pty" : "internal",
|
|
659
675
|
UFOO_PARENT_PID: String(originalPid),
|
|
676
|
+
...(providerSessionId ? { UFOO_PROVIDER_SESSION_ID: providerSessionId } : {}),
|
|
660
677
|
},
|
|
661
678
|
});
|
|
662
679
|
|
|
@@ -878,7 +895,8 @@ async function launchAgent(projectRoot, agent, count = 1, nickname = "", process
|
|
|
878
895
|
count,
|
|
879
896
|
nickname,
|
|
880
897
|
processManager,
|
|
881
|
-
launchEnvObject
|
|
898
|
+
launchEnvObject,
|
|
899
|
+
extraArgs
|
|
882
900
|
);
|
|
883
901
|
return { mode: "internal", launchScope, subscriberIds: result.subscriberIds };
|
|
884
902
|
}
|
|
@@ -1072,11 +1090,6 @@ function collectRecoverableAgents(projectRoot, target = "") {
|
|
|
1072
1090
|
continue;
|
|
1073
1091
|
}
|
|
1074
1092
|
|
|
1075
|
-
if (mode === "internal") {
|
|
1076
|
-
skipped.push({ id, reason: "internal mode not supported for resume" });
|
|
1077
|
-
continue;
|
|
1078
|
-
}
|
|
1079
|
-
|
|
1080
1093
|
recoverableEntries.push({ id, meta, agent });
|
|
1081
1094
|
}
|
|
1082
1095
|
|
|
@@ -1113,19 +1126,48 @@ async function resumeAgents(projectRoot, target = "", processManager = null) {
|
|
|
1113
1126
|
for (const item of recoverableEntries) {
|
|
1114
1127
|
const nickname = resolveDisplayNickname(projectRoot, item.meta);
|
|
1115
1128
|
const sessionId = item.meta.provider_session_id;
|
|
1116
|
-
const
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1129
|
+
const args = buildResumeArgs(item.agent, sessionId);
|
|
1130
|
+
let reused = false;
|
|
1131
|
+
let resumedId = item.id;
|
|
1132
|
+
if (mode === "internal") {
|
|
1133
|
+
// Internal agents have no terminal/pane to reattach. Start a fresh
|
|
1134
|
+
// daemon-managed runner and replace the old recoverable registration.
|
|
1135
|
+
// The provider session is still reused via the normal provider args.
|
|
1136
|
+
// eslint-disable-next-line no-await-in-loop
|
|
1137
|
+
const launchResult = await spawnInternalAgent(
|
|
1138
|
+
projectRoot,
|
|
1139
|
+
item.agent,
|
|
1140
|
+
1,
|
|
1141
|
+
nickname,
|
|
1142
|
+
processManager,
|
|
1143
|
+
{ UFOO_SKIP_SESSION_PROBE: "1" },
|
|
1144
|
+
args,
|
|
1145
|
+
{ replaceAgentId: item.id, providerSessionId: sessionId }
|
|
1146
|
+
);
|
|
1147
|
+
resumedId = launchResult.subscriberIds && launchResult.subscriberIds[0]
|
|
1148
|
+
? launchResult.subscriberIds[0]
|
|
1149
|
+
: item.id;
|
|
1150
|
+
} else {
|
|
1151
|
+
reused = await tryReuseTerminal(projectRoot, item.id, item.meta, item.agent, sessionId);
|
|
1152
|
+
if (!reused) {
|
|
1153
|
+
const envPrefix = "UFOO_SKIP_SESSION_PROBE=1";
|
|
1154
|
+
if (mode === "tmux") {
|
|
1155
|
+
// eslint-disable-next-line no-await-in-loop
|
|
1156
|
+
await spawnTmuxWindow(projectRoot, item.agent, nickname, args, envPrefix);
|
|
1157
|
+
} else {
|
|
1158
|
+
// eslint-disable-next-line no-await-in-loop
|
|
1159
|
+
await spawnManagedTerminalAgent(projectRoot, item.agent, nickname, processManager, args, envPrefix);
|
|
1160
|
+
}
|
|
1126
1161
|
}
|
|
1127
1162
|
}
|
|
1128
|
-
resumed.push({
|
|
1163
|
+
resumed.push({
|
|
1164
|
+
id: resumedId,
|
|
1165
|
+
previous_id: resumedId === item.id ? undefined : item.id,
|
|
1166
|
+
nickname,
|
|
1167
|
+
agent: item.agent,
|
|
1168
|
+
sessionId,
|
|
1169
|
+
reused,
|
|
1170
|
+
});
|
|
1129
1171
|
}
|
|
1130
1172
|
|
|
1131
1173
|
return { ok: true, resumed, skipped };
|