fix: restore master agent relay guidance
This commit is contained in:
@@ -57,6 +57,22 @@ function emptyDraft(): AccountDraft {
|
||||
};
|
||||
}
|
||||
|
||||
function buildMasterNodeLoginGuide(account: {
|
||||
nodeLabel?: string;
|
||||
nodeId?: string;
|
||||
statusLabel?: string;
|
||||
}) {
|
||||
const nodeLabel = account.nodeLabel?.trim() || account.nodeId?.trim() || "绑定设备";
|
||||
return [
|
||||
`主 GPT 不在手机里直接登录。`,
|
||||
`请到绑定设备 ${nodeLabel} 上打开 Codex / ChatGPT Plus 会话完成登录。`,
|
||||
`登录完成后,回到这里点“测试连接”确认 relay 和主 Agent 已接通。`,
|
||||
account.statusLabel ? `当前状态:${account.statusLabel}` : null,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join("\n");
|
||||
}
|
||||
|
||||
function draftFromAccount(account: AiAccountSummary): AccountDraft {
|
||||
return {
|
||||
label: account.label,
|
||||
@@ -142,6 +158,7 @@ export function AiAccountsClient({
|
||||
const [newDraft, setNewDraft] = useState<AccountDraft>(emptyDraft());
|
||||
const [busyKey, setBusyKey] = useState<string | null>(null);
|
||||
const [message, setMessage] = useState("");
|
||||
const [guideAccountId, setGuideAccountId] = useState<string | null>(null);
|
||||
|
||||
const accountDrafts = useMemo(
|
||||
() =>
|
||||
@@ -437,6 +454,19 @@ export function AiAccountsClient({
|
||||
</div>
|
||||
|
||||
<div className="mt-4 flex flex-wrap gap-2">
|
||||
{account.provider === "master_codex_node" ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() =>
|
||||
setGuideAccountId((current) =>
|
||||
current === account.accountId ? null : account.accountId,
|
||||
)
|
||||
}
|
||||
className="rounded-full border border-[#D9D9D9] px-3 py-2 text-[12px] font-semibold text-[#57606A]"
|
||||
>
|
||||
{guideAccountId === account.accountId ? "收起登录指引" : "登录指引"}
|
||||
</button>
|
||||
) : null}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => void validateAccount(account.accountId)}
|
||||
@@ -472,6 +502,12 @@ export function AiAccountsClient({
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
{account.provider === "master_codex_node" && guideAccountId === account.accountId ? (
|
||||
<div className="mt-3 rounded-2xl bg-[#F7F8FA] px-3 py-3 text-[12px] leading-6 text-[#57606A] whitespace-pre-line">
|
||||
{buildMasterNodeLoginGuide(account)}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -753,13 +753,55 @@ export async function validateAiAccountConnection(accountId: string) {
|
||||
}
|
||||
|
||||
if (account.provider === "master_codex_node") {
|
||||
const state = await readState();
|
||||
const nodeId = account.nodeId?.trim() || state.user.boundDeviceId || "";
|
||||
const boundDevice = state.devices.find((device) => device.id === nodeId);
|
||||
const boundNodeLabel =
|
||||
account.nodeLabel?.trim() ||
|
||||
boundDevice?.name ||
|
||||
state.user.boundCodexNodeLabel ||
|
||||
state.user.boundDeviceId ||
|
||||
"绑定设备";
|
||||
|
||||
if (!nodeId) {
|
||||
await updateAiAccountHealth({
|
||||
accountId: account.accountId,
|
||||
status: "needs_login",
|
||||
lastError: "MASTER_CODEX_NODE_NOT_CONFIGURED",
|
||||
lastValidatedAt: new Date().toISOString(),
|
||||
});
|
||||
return {
|
||||
ok: false as const,
|
||||
status: "needs_login" as const,
|
||||
message: `主 GPT 不在手机里直接登录。请先在绑定设备(例如 ${boundNodeLabel})上的 Codex / ChatGPT Plus 会话里登录,并填写正确的节点 ID,再回来校验连接。`,
|
||||
};
|
||||
}
|
||||
|
||||
if (!boundDevice || boundDevice.status !== "online") {
|
||||
await updateAiAccountHealth({
|
||||
accountId: account.accountId,
|
||||
status: "degraded",
|
||||
lastError: !boundDevice ? "MASTER_CODEX_NODE_DEVICE_NOT_FOUND" : "MASTER_CODEX_NODE_DEVICE_OFFLINE",
|
||||
lastValidatedAt: new Date().toISOString(),
|
||||
});
|
||||
return {
|
||||
ok: false as const,
|
||||
status: "degraded" as const,
|
||||
message: `主 GPT 不在手机里直接登录。当前绑定设备 ${boundNodeLabel}${boundDevice ? " 不在线" : " 未找到"},主 Agent 暂时无法通过该节点对话。请先在这台设备上登录 Codex / ChatGPT Plus,并确保 local-agent 在线。`,
|
||||
};
|
||||
}
|
||||
|
||||
await updateAiAccountHealth({
|
||||
accountId: account.accountId,
|
||||
status: "ready",
|
||||
lastError: undefined,
|
||||
lastValidatedAt: new Date().toISOString(),
|
||||
lastUsedAt: boundDevice.lastSeenAt || new Date().toISOString(),
|
||||
});
|
||||
return {
|
||||
ok: Boolean(account.nodeId?.trim()) as boolean,
|
||||
status: account.nodeId?.trim() ? "ready" : "needs_login",
|
||||
message:
|
||||
account.nodeId?.trim()
|
||||
? "Master Codex Node 已配置。主 Agent 会通过 local-agent relay 把任务转交给该节点上的 Codex。"
|
||||
: "请先填写 Master Codex Node 的节点 ID,再让 local-agent 认领主 Agent 任务。",
|
||||
ok: true as const,
|
||||
status: "ready" as const,
|
||||
message: `主 GPT 不在手机里直接登录。当前已通过绑定设备 ${boundNodeLabel} 接好 Master Codex Node,主 Agent 会把任务转交给这台设备上的 Codex / ChatGPT Plus 会话。`,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -811,6 +853,27 @@ export async function replyToMasterAgentUserMessage(params: {
|
||||
if (runtime.account.provider === "master_codex_node") {
|
||||
const state = await readState();
|
||||
const deviceId = runtime.account.nodeId || state.user.boundDeviceId || "mac-studio";
|
||||
const boundDevice = state.devices.find((device) => device.id === deviceId);
|
||||
const boundNodeLabel =
|
||||
runtime.account.nodeLabel?.trim() ||
|
||||
boundDevice?.name ||
|
||||
state.user.boundCodexNodeLabel ||
|
||||
deviceId;
|
||||
|
||||
if (!boundDevice || boundDevice.status !== "online") {
|
||||
await updateAiAccountHealth({
|
||||
accountId: runtime.account.accountId,
|
||||
status: "degraded",
|
||||
lastError: !boundDevice ? "MASTER_CODEX_NODE_DEVICE_NOT_FOUND" : "MASTER_CODEX_NODE_DEVICE_OFFLINE",
|
||||
lastValidatedAt: new Date().toISOString(),
|
||||
});
|
||||
await appendMasterAgentSystemReply(
|
||||
`主 GPT 不在手机里直接登录。当前绑定设备 ${boundNodeLabel}${boundDevice ? " 不在线" : " 未找到"},主 Agent 暂时无法通过这台设备对话。请先在该设备上登录 Codex / ChatGPT Plus,并确保 local-agent 在线后再重试。`,
|
||||
`主 Agent · ${runtime.summary.roleLabel}`,
|
||||
);
|
||||
return { ok: false as const, reason: "MASTER_NODE_OFFLINE" };
|
||||
}
|
||||
|
||||
const task = await queueMasterAgentTask({
|
||||
requestMessageId: params.requestMessageId ?? "master-agent-manual",
|
||||
requestText: params.requestText,
|
||||
@@ -845,7 +908,7 @@ export async function replyToMasterAgentUserMessage(params: {
|
||||
|
||||
await appendMasterAgentSystemReply(
|
||||
[
|
||||
`当前主控身份是 ${runtime.summary.roleLabel},任务已经转交到 ${runtime.account.nodeLabel ?? deviceId} 的 Master Codex Node。`,
|
||||
`当前主控身份是 ${runtime.summary.roleLabel},任务已经转交到 ${boundNodeLabel} 的 Master Codex Node。`,
|
||||
"如果本机 Codex 节点在线,回复会在稍后自动回写到当前会话。",
|
||||
].join(""),
|
||||
`主 Agent · ${runtime.summary.roleLabel}`,
|
||||
|
||||
Reference in New Issue
Block a user