feat(desktop): add Wenxin monitoring adapter and surface platform risk control
Introduces the Wenxin (文心一言) monitoring adapter and registers it in the runtime controller and adapter index. Adds a new risk_control failure classification with custom classifiers for Doubao and Wenxin that recognize rate-limit, 频控 and 访问环境异常 signals, and propagates this state through account-health, runtime activity alerts, and the renderer views so users see "触发风控" instead of a generic challenge message on the Home, Accounts, and AI Platforms screens. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -21,6 +21,7 @@ import {
|
||||
kimiAdapter,
|
||||
qwenAdapter,
|
||||
toutiaoAdapter,
|
||||
wenxinAdapter,
|
||||
yuanbaoAdapter,
|
||||
zhihuAdapter,
|
||||
type AdapterExecutionResult,
|
||||
@@ -34,6 +35,7 @@ import {
|
||||
import {
|
||||
ensureAccountReady,
|
||||
forgetTrackedAccountHealth,
|
||||
getAccountHealthSnapshot,
|
||||
getProjectedAccountHealth,
|
||||
probeTrackedAccount,
|
||||
reportAccountFailure,
|
||||
@@ -127,7 +129,7 @@ const pullIntervalMs = 60_000;
|
||||
const leaseExtendIntervalMs = 60_000;
|
||||
const maxActivityItems = 60;
|
||||
const monitorBusinessTimeZone = "Asia/Shanghai";
|
||||
const authRequiredMonitorPlatforms = new Set(["yuanbao", "kimi", "deepseek", "doubao"]);
|
||||
const authRequiredMonitorPlatforms = new Set(["yuanbao", "kimi", "deepseek", "doubao", "wenxin"]);
|
||||
|
||||
interface RuntimeTaskRecord {
|
||||
id: string;
|
||||
@@ -279,6 +281,53 @@ function isDefinitiveAuthState(authState: string): boolean {
|
||||
return ["active", "expired", "challenge_required", "revoked"].includes(authState);
|
||||
}
|
||||
|
||||
function runtimePlatformLabel(platform: string): string {
|
||||
return getAIPlatformCatalogItem(platform)?.label ?? platform;
|
||||
}
|
||||
|
||||
function accountActionRequiredSummary(
|
||||
platform: string,
|
||||
authReason: string | null | undefined,
|
||||
): string {
|
||||
const label = runtimePlatformLabel(platform);
|
||||
if (authReason === "risk_control") {
|
||||
return `${label} 账号疑似触发风控,请打开平台处理验证或等待限制解除后再继续执行。`;
|
||||
}
|
||||
return `${label} 账号需要完成人机验证后才能继续执行。`;
|
||||
}
|
||||
|
||||
function maybeRecordAccountAccessAlert(
|
||||
task: RuntimeTaskRecord,
|
||||
accountIdentity: PublishAccountIdentity | null,
|
||||
previous: ReturnType<typeof getAccountHealthSnapshot>,
|
||||
next: ReturnType<typeof getAccountHealthSnapshot>,
|
||||
): void {
|
||||
if (!accountIdentity || !next) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (previous?.authState === next.authState && previous?.authReason === next.authReason) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (next.authState === "challenge_required" && next.authReason === "risk_control") {
|
||||
recordActivity(
|
||||
"warn",
|
||||
`${runtimePlatformLabel(accountIdentity.platform)} 触发风控`,
|
||||
`${accountIdentity.displayName || task.accountName || "当前账号"} 已被平台限制,请打开平台处理验证或等待限制解除后再刷新状态。`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (next.authState === "challenge_required") {
|
||||
recordActivity(
|
||||
"warn",
|
||||
`${runtimePlatformLabel(accountIdentity.platform)} 需要人工验证`,
|
||||
`${accountIdentity.displayName || task.accountName || "当前账号"} 需要打开平台完成人工验证后再继续执行任务。`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function isoFromTimestamp(value: number | null): string | null {
|
||||
return value ? new Date(value).toISOString() : null;
|
||||
}
|
||||
@@ -2209,10 +2258,10 @@ function buildAccountBlockedResult(
|
||||
if (readiness.authState === "challenge_required") {
|
||||
return {
|
||||
status: "failed",
|
||||
summary: `${task.accountName} 需要完成人机验证后才能继续执行。`,
|
||||
summary: accountActionRequiredSummary(task.platform, readiness.authReason),
|
||||
error: {
|
||||
code: "desktop_account_challenge_required",
|
||||
message: "desktop account challenge required",
|
||||
code: readiness.authReason === "risk_control" ? "desktop_account_risk_control" : "desktop_account_challenge_required",
|
||||
message: readiness.authReason === "risk_control" ? "desktop account risk control triggered" : "desktop account challenge required",
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -2247,7 +2296,8 @@ async function maybeReportTaskAuthFailure(
|
||||
return;
|
||||
}
|
||||
|
||||
await reportAccountFailure(accountIdentity, {
|
||||
const previousSnapshot = getAccountHealthSnapshot(accountIdentity.id);
|
||||
const nextSnapshot = await reportAccountFailure(accountIdentity, {
|
||||
summary: result.summary,
|
||||
error: result.error ?? null,
|
||||
}).catch((error) => {
|
||||
@@ -2257,7 +2307,10 @@ async function maybeReportTaskAuthFailure(
|
||||
platform: accountIdentity.platform,
|
||||
message: error instanceof Error ? error.message : String(error),
|
||||
});
|
||||
return null;
|
||||
});
|
||||
|
||||
maybeRecordAccountAccessAlert(task, accountIdentity, previousSnapshot, nextSnapshot);
|
||||
}
|
||||
|
||||
function resolvePlaywrightTargetURL(
|
||||
@@ -2640,6 +2693,9 @@ function selectMonitorAdapter(platform: string): MonitorAdapter | null {
|
||||
if (platform === qwenAdapter.provider) {
|
||||
return qwenAdapter;
|
||||
}
|
||||
if (platform === wenxinAdapter.provider) {
|
||||
return wenxinAdapter;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user