feat: Implement Qwen adapter and enhance AI platform detection
- Added a new Qwen adapter to facilitate monitoring through Playwright, leveraging internal text/chat managers. - Updated account detection logic to recognize Qwen sessions based on persisted cookies, improving binding reliability. - Relaxed authentication requirements for monitor tasks, allowing anonymous execution for certain AI platforms. - Enhanced the generic AI platform detection to include Qwen-specific logic, ensuring accurate session identification. - Modified the monitoring dashboard to include runtime state indicating if the current user's desktop client is online. - Updated various files including `account-binder.ts`, `runtime-controller.ts`, and `monitoring_service.go` to support new features and improvements.
This commit is contained in:
@@ -1009,6 +1009,10 @@ function genericAIConversationPath(pathname: string): boolean {
|
||||
}
|
||||
|
||||
function isLikelyGenericAICredentialName(name: string): boolean {
|
||||
const normalized = name.trim().toLowerCase();
|
||||
if (/^(x[-_]?csrf[-_]?token|xsrf[-_]?token|csrf[-_]?token|csrf|_csrf)$/i.test(normalized)) {
|
||||
return false;
|
||||
}
|
||||
return /(token|session|auth|login|passport|ticket|jwt|refresh|access)/i.test(name);
|
||||
}
|
||||
|
||||
@@ -1126,6 +1130,103 @@ async function detectGenericAIPlatform(
|
||||
});
|
||||
}
|
||||
|
||||
async function detectQwenFromSession(
|
||||
session: Session,
|
||||
hints: {
|
||||
displayName?: string | null;
|
||||
avatarUrl?: string | null;
|
||||
} = {},
|
||||
): Promise<DetectedAccount | null> {
|
||||
const cookies = await session.cookies.get({ url: "https://www.qianwen.com/" }).catch(() => []);
|
||||
if (!cookies.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const cookieMap = new Map<string, string>();
|
||||
for (const cookie of cookies) {
|
||||
if (!cookieMap.has(cookie.name)) {
|
||||
cookieMap.set(cookie.name, cookie.value);
|
||||
}
|
||||
}
|
||||
|
||||
const ticketHash = normalizeText(cookieMap.get("tongyi_sso_ticket_hash"));
|
||||
const ticket = normalizeText(cookieMap.get("tongyi_sso_ticket"));
|
||||
const platformUid = ticketHash ?? ticket;
|
||||
if (!platformUid) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return sanitizeDetectedAccount({
|
||||
platformUid,
|
||||
displayName: hints.displayName ?? "通义千问账号",
|
||||
avatarUrl: hints.avatarUrl ?? null,
|
||||
});
|
||||
}
|
||||
|
||||
async function detectQwenPlatform(
|
||||
platformId: string,
|
||||
label: string,
|
||||
context: DetectContext,
|
||||
): Promise<DetectedAccount | null> {
|
||||
const genericDetected = await detectGenericAIPlatform(platformId, label, context);
|
||||
if (genericDetected) {
|
||||
return genericDetected;
|
||||
}
|
||||
|
||||
if (!context.webContents || context.webContents.isDestroyed()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const currentURL = context.webContents.getURL();
|
||||
const platformMeta = aiPlatformCatalog.find((item) => item.id === platformId) ?? null;
|
||||
if (!platformMeta) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (currentURL && looksLikeLoginRedirect(currentURL, platformMeta.loginUrl)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const pageState = await readGenericAIPageState(context.webContents).catch(() => null);
|
||||
if ((pageState?.challengeSignalCount ?? 0) > 0) {
|
||||
return null;
|
||||
}
|
||||
if ((pageState?.loggedOutSignalCount ?? 0) > 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const fromSession = await detectQwenFromSession(context.session, {
|
||||
displayName: pageState?.displayName ?? `${label} 会话`,
|
||||
avatarUrl: pageState?.avatarUrl ?? null,
|
||||
});
|
||||
if (fromSession) {
|
||||
return fromSession;
|
||||
}
|
||||
|
||||
const fingerprint = await buildGenericAISessionFingerprint(platformId, context.session, pageState);
|
||||
if (!fingerprint) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return sanitizeDetectedAccount({
|
||||
platformUid: fingerprint,
|
||||
displayName: pageState?.displayName ?? `${label} 会话`,
|
||||
avatarUrl: pageState?.avatarUrl ?? null,
|
||||
});
|
||||
}
|
||||
|
||||
async function detectAIPlatformAccount(
|
||||
platformId: string,
|
||||
label: string,
|
||||
context: DetectContext,
|
||||
): Promise<DetectedAccount | null> {
|
||||
if (platformId === "qwen") {
|
||||
return await detectQwenPlatform(platformId, label, context);
|
||||
}
|
||||
|
||||
return await detectGenericAIPlatform(platformId, label, context);
|
||||
}
|
||||
|
||||
async function detectToutiao({ session, webContents }: DetectContext): Promise<DetectedAccount | null> {
|
||||
const pageResponse = await pageFetchJson<ToutiaoMediaInfoResponse>(
|
||||
webContents,
|
||||
@@ -1537,7 +1638,7 @@ export async function probePublishAccountSession(
|
||||
};
|
||||
}
|
||||
|
||||
const detected = await detectGenericAIPlatform(account.platform, definition.label, {
|
||||
const detected = await detectAIPlatformAccount(account.platform, definition.label, {
|
||||
session: handle.session,
|
||||
webContents: window.webContents,
|
||||
});
|
||||
@@ -2329,7 +2430,7 @@ const aiBindingDefinitions: Record<string, PublishPlatformBindingDefinition> = O
|
||||
label: platform.label,
|
||||
loginUrl: platform.loginUrl,
|
||||
consoleUrl: platform.consoleUrl,
|
||||
detect: (context: DetectContext) => detectGenericAIPlatform(platform.id, platform.label, context),
|
||||
detect: (context: DetectContext) => detectAIPlatformAccount(platform.id, platform.label, context),
|
||||
},
|
||||
]),
|
||||
) as Record<string, PublishPlatformBindingDefinition>;
|
||||
|
||||
Reference in New Issue
Block a user