feat(monitoring): add DeepSeek monitoring end-to-end
- desktop: new DeepSeek page adapter and monitor registry - desktop: extract generic AI auth helpers into shared module and skip binding when challenge signals are present - admin-web: render DeepSeek HTML answers with inline citation anchors and surface reference-number badges on source cards - server: fall back to inline_links/source_panel_links and read text/siteName fields when extracting DeepSeek citations Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -31,6 +31,11 @@ import {
|
||||
isWindowReadyForDetection,
|
||||
loadWindowURLSafely,
|
||||
} from "./external-window";
|
||||
import {
|
||||
buildGenericAIPageFingerprintFallback,
|
||||
genericAIPageLooksAuthenticated,
|
||||
type GenericAIPageState,
|
||||
} from "./generic-ai-auth";
|
||||
import { upsertDesktopAccount } from "./transport/api-client";
|
||||
import { STANDARD_USER_AGENT } from "./user-agent";
|
||||
import {
|
||||
@@ -681,18 +686,6 @@ function sanitizeDetectedAccount(account: DetectedAccount): DetectedAccount {
|
||||
};
|
||||
}
|
||||
|
||||
interface GenericAIPageState {
|
||||
displayName: string | null;
|
||||
avatarUrl: string | null;
|
||||
fingerprint: string | null;
|
||||
credentialFingerprint: string | null;
|
||||
currentPath: string | null;
|
||||
strongAuthSignalCount: number;
|
||||
loginSignalCount: number;
|
||||
loggedOutSignalCount: number;
|
||||
challengeSignalCount: number;
|
||||
}
|
||||
|
||||
async function readGenericAIPageState(
|
||||
webContents: WebContents | null | undefined,
|
||||
): Promise<GenericAIPageState | null> {
|
||||
@@ -1004,10 +997,6 @@ function safeParseURL(input: string): URL | null {
|
||||
}
|
||||
}
|
||||
|
||||
function genericAIConversationPath(pathname: string): boolean {
|
||||
return /^\/(?:chat|c|conversation)(?:\/|$)/i.test(pathname);
|
||||
}
|
||||
|
||||
function isLikelyGenericAICredentialName(name: string): boolean {
|
||||
const normalized = name.trim().toLowerCase();
|
||||
if (/^(x[-_]?csrf[-_]?token|xsrf[-_]?token|csrf[-_]?token|csrf|_csrf)$/i.test(normalized)) {
|
||||
@@ -1029,29 +1018,6 @@ function isLikelyGenericAICredentialValue(value: string | null | undefined): boo
|
||||
return normalized.length >= 8;
|
||||
}
|
||||
|
||||
function genericAIPageLooksAuthenticated(currentURL: string, pageState: GenericAIPageState | null): boolean {
|
||||
const current = safeParseURL(currentURL);
|
||||
const currentPath = normalizeURLPath(current?.pathname ?? pageState?.currentPath ?? "");
|
||||
const strongAuthSignalCount = pageState?.strongAuthSignalCount ?? 0;
|
||||
const loginSignalCount = pageState?.loginSignalCount ?? 0;
|
||||
const loggedOutSignalCount = pageState?.loggedOutSignalCount ?? 0;
|
||||
const onConversationPath = genericAIConversationPath(currentPath);
|
||||
|
||||
if (loggedOutSignalCount > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (loginSignalCount > 0 && strongAuthSignalCount === 0 && !onConversationPath) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (loginSignalCount > 0) {
|
||||
return strongAuthSignalCount >= 2 && !onConversationPath;
|
||||
}
|
||||
|
||||
return strongAuthSignalCount > 0 || onConversationPath;
|
||||
}
|
||||
|
||||
async function buildGenericAISessionFingerprint(
|
||||
platformId: string,
|
||||
session: Session,
|
||||
@@ -1087,7 +1053,7 @@ async function buildGenericAISessionFingerprint(
|
||||
}
|
||||
|
||||
if (parts.length === 0) {
|
||||
return null;
|
||||
return buildGenericAIPageFingerprintFallback(platformId, pageState);
|
||||
}
|
||||
|
||||
return boundedIdentity(`${platformId}-session`, parts.join("||"));
|
||||
@@ -1115,6 +1081,9 @@ async function detectGenericAIPlatform(
|
||||
}
|
||||
|
||||
const pageState = await readGenericAIPageState(context.webContents).catch(() => null);
|
||||
if ((pageState?.challengeSignalCount ?? 0) > 0) {
|
||||
return null;
|
||||
}
|
||||
if (!genericAIPageLooksAuthenticated(currentURL, pageState)) {
|
||||
return null;
|
||||
}
|
||||
@@ -2692,6 +2661,7 @@ export async function bindPublishAccount(platformId: string): Promise<DesktopAcc
|
||||
);
|
||||
|
||||
const detectAndBind = async () => {
|
||||
syncDetectionState();
|
||||
const currentURL = window.isDestroyed() ? "" : window.webContents.getURL();
|
||||
const canProbe = detectReady || /^https?:\/\//i.test(currentURL);
|
||||
if (finished || checking || !canProbe || window.isDestroyed()) {
|
||||
|
||||
@@ -0,0 +1,263 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { __deepseekTestUtils } from "./deepseek";
|
||||
import { getMonitorAdapter } from "./index";
|
||||
|
||||
const {
|
||||
extractQuestionText,
|
||||
buildSourceItem,
|
||||
buildDeepSeekPayload,
|
||||
detectDeepSeekBusySignals,
|
||||
dedupeSourceItems,
|
||||
classifyDeepseekSources,
|
||||
mergeDeepSeekRawSourceLinks,
|
||||
isObservationComplete,
|
||||
isDeepSeekToggleSelected,
|
||||
isDeepSeekWebpagesTriggerText,
|
||||
} = __deepseekTestUtils;
|
||||
|
||||
describe("deepseek adapter helpers", () => {
|
||||
it("extracts question text from monitoring payload", () => {
|
||||
expect(extractQuestionText({ question_text: "DeepSeek 怎么看 GEO?" })).toBe("DeepSeek 怎么看 GEO?");
|
||||
});
|
||||
|
||||
it("dedupes sources by normalized url without hash fragments", () => {
|
||||
const items = dedupeSourceItems([
|
||||
{
|
||||
url: "https://example.com/a#top",
|
||||
title: "A",
|
||||
normalized_url: "https://example.com/a#top",
|
||||
},
|
||||
{
|
||||
url: "https://example.com/a#bottom",
|
||||
title: "A later",
|
||||
normalized_url: "https://example.com/a#bottom",
|
||||
},
|
||||
]);
|
||||
|
||||
expect(items).toHaveLength(1);
|
||||
expect(items[0]?.normalized_url).toBe("https://example.com/a");
|
||||
expect(items[0]?.title).toBe("A");
|
||||
});
|
||||
|
||||
it("treats revealed search cards as citation sources and inline links as content citation candidates", () => {
|
||||
const classified = classifyDeepseekSources({
|
||||
inlineLinks: [
|
||||
{
|
||||
url: "https://example.com/citation",
|
||||
title: "Citation",
|
||||
normalized_url: "https://example.com/citation",
|
||||
},
|
||||
],
|
||||
sourcePanelLinks: [],
|
||||
searchPanelLinks: [
|
||||
{
|
||||
url: "https://example.com/search",
|
||||
title: "Search",
|
||||
normalized_url: "https://example.com/search",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(classified.citations).toHaveLength(1);
|
||||
expect(classified.inlineCitationCandidates).toHaveLength(1);
|
||||
expect(classified.searchResults).toHaveLength(1);
|
||||
expect(classified.citations[0]?.url).toBe("https://example.com/search");
|
||||
expect(classified.inlineCitationCandidates[0]?.url).toBe("https://example.com/citation");
|
||||
expect(classified.searchResults[0]?.url).toBe("https://example.com/search");
|
||||
});
|
||||
|
||||
it("enriches numeric inline citations with source metadata from revealed search cards", () => {
|
||||
const classified = classifyDeepseekSources({
|
||||
inlineLinks: [
|
||||
{
|
||||
url: "https://example.com/citation",
|
||||
title: null,
|
||||
text: "-1",
|
||||
siteName: null,
|
||||
kind: "inline",
|
||||
},
|
||||
],
|
||||
sourcePanelLinks: [],
|
||||
searchPanelLinks: [
|
||||
{
|
||||
url: "https://example.com/citation",
|
||||
title: "DeepSeek source article",
|
||||
text: "Snippet",
|
||||
siteName: "Example",
|
||||
kind: "search",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(classified.inlineCitationCandidates).toHaveLength(1);
|
||||
expect(classified.inlineCitationCandidates[0]?.title).toBe("DeepSeek source article");
|
||||
expect(classified.inlineCitationCandidates[0]?.site_name).toBe("Example");
|
||||
expect(classified.citations).toHaveLength(1);
|
||||
expect(classified.searchResults).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("retains all revealed DeepSeek search results instead of truncating after 32 items", () => {
|
||||
const searchPanelLinks = Array.from({ length: 78 }, (_, index) => ({
|
||||
url: `https://example.com/search-${index + 1}`,
|
||||
title: `Search ${index + 1}`,
|
||||
text: `Snippet ${index + 1}`,
|
||||
siteName: "Example",
|
||||
kind: "search" as const,
|
||||
}));
|
||||
|
||||
const classified = classifyDeepseekSources({
|
||||
inlineLinks: [],
|
||||
sourcePanelLinks: [],
|
||||
searchPanelLinks,
|
||||
});
|
||||
|
||||
expect(classified.citations).toHaveLength(78);
|
||||
expect(classified.searchResults).toHaveLength(78);
|
||||
});
|
||||
|
||||
it("merges revealed sidebar search links with the snapshot search links by normalized url", () => {
|
||||
const merged = mergeDeepSeekRawSourceLinks(
|
||||
[
|
||||
{
|
||||
url: "https://example.com/report#top",
|
||||
title: null,
|
||||
text: "Visible title",
|
||||
siteName: null,
|
||||
kind: "search",
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
url: "https://example.com/report#bottom",
|
||||
title: "Merged title",
|
||||
text: "Merged snippet",
|
||||
siteName: "Example",
|
||||
kind: "search",
|
||||
},
|
||||
{
|
||||
url: "https://example.com/extra",
|
||||
title: "Extra result",
|
||||
text: "Extra snippet",
|
||||
siteName: "Example",
|
||||
kind: "search",
|
||||
},
|
||||
],
|
||||
);
|
||||
|
||||
expect(merged).toHaveLength(2);
|
||||
expect(merged[0]).toEqual(expect.objectContaining({
|
||||
url: "https://example.com/report",
|
||||
title: "Merged title",
|
||||
text: "Visible title",
|
||||
siteName: "Example",
|
||||
}));
|
||||
expect(merged[1]?.url).toBe("https://example.com/extra");
|
||||
});
|
||||
|
||||
it("normalizes deepseek raw source links into monitoring source items", () => {
|
||||
const item = buildSourceItem({
|
||||
url: "https://example.com/report#cite-1",
|
||||
title: null,
|
||||
text: "DeepSeek source title",
|
||||
siteName: "Example",
|
||||
kind: "source",
|
||||
});
|
||||
|
||||
expect(item).toEqual(expect.objectContaining({
|
||||
url: "https://example.com/report",
|
||||
normalized_url: "https://example.com/report",
|
||||
title: "DeepSeek source title",
|
||||
site_name: "Example",
|
||||
host: "example.com",
|
||||
}));
|
||||
});
|
||||
|
||||
it("does not use numeric citation markers as source titles", () => {
|
||||
const item = buildSourceItem({
|
||||
url: "https://example.com/report#cite-1",
|
||||
title: null,
|
||||
text: "-1",
|
||||
siteName: null,
|
||||
kind: "inline",
|
||||
});
|
||||
|
||||
expect(item?.title).toBeNull();
|
||||
expect(item?.site_name).toBe("example.com");
|
||||
});
|
||||
|
||||
it("treats a stable answer as complete even without citations", () => {
|
||||
expect(isObservationComplete({
|
||||
answer: "这是最终答案",
|
||||
busy: false,
|
||||
loginRequired: false,
|
||||
challengeRequired: false,
|
||||
signature: "sig-1",
|
||||
})).toBe(true);
|
||||
});
|
||||
|
||||
it("ignores source-logo loading placeholders when computing busy signals", () => {
|
||||
expect(detectDeepSeekBusySignals({
|
||||
bodyText: "已阅读 10 个网页",
|
||||
descriptors: [
|
||||
"site_logo_loading site_logo_fallback",
|
||||
"site_logo_loading site_logo_fallback",
|
||||
],
|
||||
})).toEqual([]);
|
||||
});
|
||||
|
||||
it("recognizes deepseek toggle selected state from class names", () => {
|
||||
expect(isDeepSeekToggleSelected("ds-toggle-button ds-toggle-button--selected")).toBe(true);
|
||||
expect(isDeepSeekToggleSelected("ds-toggle-button")).toBe(false);
|
||||
});
|
||||
|
||||
it("only treats the bottom 网页 pill as the DeepSeek source trigger", () => {
|
||||
expect(isDeepSeekWebpagesTriggerText("57 个网页")).toBe(true);
|
||||
expect(isDeepSeekWebpagesTriggerText("已阅读 57 个网页")).toBe(true);
|
||||
expect(isDeepSeekWebpagesTriggerText("搜索到 39 个网页")).toBe(false);
|
||||
});
|
||||
|
||||
it("builds json-safe payloads with null placeholders instead of undefined", () => {
|
||||
const payload = buildDeepSeekPayload({
|
||||
url: "https://chat.deepseek.com/",
|
||||
title: "DeepSeek",
|
||||
loginRequired: false,
|
||||
loginReason: null,
|
||||
challengeRequired: false,
|
||||
challengeReason: null,
|
||||
busy: false,
|
||||
busySignals: [],
|
||||
composerValue: null,
|
||||
sendDisabled: false,
|
||||
answer: "Final answer",
|
||||
answerText: "Final answer",
|
||||
reasoning: null,
|
||||
signature: "sig-1",
|
||||
currentModelLabel: null,
|
||||
providerRequestID: null,
|
||||
requestID: null,
|
||||
inlineLinks: [],
|
||||
sourcePanelLinks: [],
|
||||
searchPanelLinks: [],
|
||||
}, {
|
||||
citations: [],
|
||||
inlineCitationCandidates: [],
|
||||
searchResults: [],
|
||||
}, "Final answer");
|
||||
|
||||
expect(payload.provider_model).toBeNull();
|
||||
expect(payload.provider_request_id).toBeNull();
|
||||
expect(payload.request_id).toBeNull();
|
||||
expect((payload.raw_response_json as Record<string, unknown>).provider_model).toBeNull();
|
||||
expect((payload.raw_response_json as Record<string, unknown>).provider_request_id).toBeNull();
|
||||
expect((payload.raw_response_json as Record<string, unknown>).request_id).toBeNull();
|
||||
expect((payload.raw_response_json as Record<string, unknown>).signature).toBe("sig-1");
|
||||
expect((payload.raw_response_json as Record<string, unknown>).inline_citation_candidates).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("monitor adapter registry", () => {
|
||||
it("registers deepseek as a monitor adapter", () => {
|
||||
expect(getMonitorAdapter("deepseek")?.provider).toBe("deepseek");
|
||||
});
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,5 @@
|
||||
export * from "./base";
|
||||
export * from "./deepseek";
|
||||
export * from "./doubao";
|
||||
export * from "./kimi";
|
||||
export * from "./qwen";
|
||||
@@ -6,3 +7,24 @@ export * from "./toutiao";
|
||||
export * from "./wenxin";
|
||||
export * from "./yuanbao";
|
||||
export * from "./zhihu";
|
||||
|
||||
import { deepseekAdapter } from "./deepseek";
|
||||
import { doubaoAdapter } from "./doubao";
|
||||
import { kimiAdapter } from "./kimi";
|
||||
import { qwenAdapter } from "./qwen";
|
||||
import { wenxinAdapter } from "./wenxin";
|
||||
import { yuanbaoAdapter } from "./yuanbao";
|
||||
import type { MonitorAdapter } from "./base";
|
||||
|
||||
const monitorAdapterRegistry = new Map<string, MonitorAdapter>([
|
||||
[deepseekAdapter.provider, deepseekAdapter],
|
||||
[doubaoAdapter.provider, doubaoAdapter],
|
||||
[kimiAdapter.provider, kimiAdapter],
|
||||
[qwenAdapter.provider, qwenAdapter],
|
||||
[wenxinAdapter.provider, wenxinAdapter],
|
||||
[yuanbaoAdapter.provider, yuanbaoAdapter],
|
||||
]);
|
||||
|
||||
export function getMonitorAdapter(platform: string): MonitorAdapter | null {
|
||||
return monitorAdapterRegistry.get(platform) ?? null;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import {
|
||||
buildGenericAIPageFingerprintFallback,
|
||||
genericAIPageLooksAuthenticated,
|
||||
} from "./generic-ai-auth";
|
||||
|
||||
describe("generic ai auth detection", () => {
|
||||
it("treats deepseek home as authenticated when credential storage exists", () => {
|
||||
expect(genericAIPageLooksAuthenticated("https://chat.deepseek.com/", {
|
||||
displayName: null,
|
||||
avatarUrl: null,
|
||||
fingerprint: "local:userToken=masked",
|
||||
credentialFingerprint: "local:userToken=masked",
|
||||
currentPath: "/",
|
||||
strongAuthSignalCount: 0,
|
||||
loginSignalCount: 0,
|
||||
loggedOutSignalCount: 0,
|
||||
challengeSignalCount: 0,
|
||||
})).toBe(true);
|
||||
});
|
||||
|
||||
it("does not use a non-auth fingerprint as a fallback without strong ui signals", () => {
|
||||
expect(buildGenericAIPageFingerprintFallback("deepseek", {
|
||||
displayName: null,
|
||||
avatarUrl: null,
|
||||
fingerprint: "local:user_unique_id=123",
|
||||
credentialFingerprint: null,
|
||||
currentPath: "/",
|
||||
strongAuthSignalCount: 0,
|
||||
loginSignalCount: 0,
|
||||
loggedOutSignalCount: 0,
|
||||
challengeSignalCount: 0,
|
||||
})).toBeNull();
|
||||
});
|
||||
|
||||
it("builds a stable fingerprint from credential storage when cookies are absent", () => {
|
||||
expect(buildGenericAIPageFingerprintFallback("deepseek", {
|
||||
displayName: null,
|
||||
avatarUrl: null,
|
||||
fingerprint: "local:user_unique_id=123",
|
||||
credentialFingerprint: "local:userToken=masked",
|
||||
currentPath: "/",
|
||||
strongAuthSignalCount: 0,
|
||||
loginSignalCount: 0,
|
||||
loggedOutSignalCount: 0,
|
||||
challengeSignalCount: 0,
|
||||
})).toBe("deepseek-session:local:userToken=masked");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,113 @@
|
||||
function normalizeText(value: unknown): string | null {
|
||||
if (typeof value !== "string") {
|
||||
return null;
|
||||
}
|
||||
const trimmed = value.trim();
|
||||
return trimmed ? trimmed : null;
|
||||
}
|
||||
|
||||
function hashText(value: string): string {
|
||||
let hash = 0;
|
||||
for (let index = 0; index < value.length; index += 1) {
|
||||
hash = ((hash << 5) - hash + value.charCodeAt(index)) | 0;
|
||||
}
|
||||
return Math.abs(hash).toString(16).padStart(8, "0");
|
||||
}
|
||||
|
||||
function boundedIdentity(prefix: string, raw: string): string {
|
||||
const normalizedPrefix = prefix.trim().replace(/:+$/, "");
|
||||
const normalizedRaw = raw.trim();
|
||||
if (!normalizedRaw) {
|
||||
return `${normalizedPrefix}:${hashText(prefix)}`;
|
||||
}
|
||||
|
||||
const candidate = `${normalizedPrefix}:${normalizedRaw}`;
|
||||
if (candidate.length <= 120) {
|
||||
return candidate;
|
||||
}
|
||||
|
||||
return `${normalizedPrefix}:${hashText(normalizedRaw)}`;
|
||||
}
|
||||
|
||||
function normalizeURLPath(pathname: string): string {
|
||||
const normalized = pathname.replace(/\/+$/, "");
|
||||
return normalized === "/" ? "" : normalized;
|
||||
}
|
||||
|
||||
function safeParseURL(input: string): URL | null {
|
||||
try {
|
||||
return new URL(input);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function genericAIConversationPath(pathname: string): boolean {
|
||||
return /^\/(?:chat|c|conversation)(?:\/|$)/i.test(pathname);
|
||||
}
|
||||
|
||||
export interface GenericAIPageState {
|
||||
displayName: string | null;
|
||||
avatarUrl: string | null;
|
||||
fingerprint: string | null;
|
||||
credentialFingerprint: string | null;
|
||||
currentPath: string | null;
|
||||
strongAuthSignalCount: number;
|
||||
loginSignalCount: number;
|
||||
loggedOutSignalCount: number;
|
||||
challengeSignalCount: number;
|
||||
}
|
||||
|
||||
export function genericAIPageLooksAuthenticated(
|
||||
currentURL: string,
|
||||
pageState: GenericAIPageState | null,
|
||||
): boolean {
|
||||
const current = safeParseURL(currentURL);
|
||||
const currentPath = normalizeURLPath(current?.pathname ?? pageState?.currentPath ?? "");
|
||||
const strongAuthSignalCount = pageState?.strongAuthSignalCount ?? 0;
|
||||
const loginSignalCount = pageState?.loginSignalCount ?? 0;
|
||||
const loggedOutSignalCount = pageState?.loggedOutSignalCount ?? 0;
|
||||
const challengeSignalCount = pageState?.challengeSignalCount ?? 0;
|
||||
const hasCredentialFingerprint = Boolean(normalizeText(pageState?.credentialFingerprint));
|
||||
const onConversationPath = genericAIConversationPath(currentPath);
|
||||
|
||||
if (challengeSignalCount > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (loggedOutSignalCount > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (loginSignalCount > 0 && strongAuthSignalCount === 0 && !onConversationPath && !hasCredentialFingerprint) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (loginSignalCount > 0) {
|
||||
return (strongAuthSignalCount >= 2 || hasCredentialFingerprint) && !onConversationPath;
|
||||
}
|
||||
|
||||
return strongAuthSignalCount > 0 || onConversationPath || hasCredentialFingerprint;
|
||||
}
|
||||
|
||||
export function buildGenericAIPageFingerprintFallback(
|
||||
platformId: string,
|
||||
pageState?: GenericAIPageState | null,
|
||||
): string | null {
|
||||
const credentialFingerprint = normalizeText(pageState?.credentialFingerprint);
|
||||
if (credentialFingerprint) {
|
||||
return boundedIdentity(`${platformId}-session`, credentialFingerprint);
|
||||
}
|
||||
|
||||
const fallbackFingerprint = normalizeText(pageState?.fingerprint);
|
||||
if (!fallbackFingerprint) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const strongAuthSignalCount = pageState?.strongAuthSignalCount ?? 0;
|
||||
if (strongAuthSignalCount <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return boundedIdentity(`${platformId}-page`, fallbackFingerprint);
|
||||
}
|
||||
@@ -17,12 +17,8 @@ import type {
|
||||
import { getAIPlatformCatalogItem, isAIPlatformId } from "@geo/shared-types";
|
||||
|
||||
import {
|
||||
doubaoAdapter,
|
||||
kimiAdapter,
|
||||
qwenAdapter,
|
||||
getMonitorAdapter,
|
||||
toutiaoAdapter,
|
||||
wenxinAdapter,
|
||||
yuanbaoAdapter,
|
||||
zhihuAdapter,
|
||||
type AdapterExecutionResult,
|
||||
type MonitorAdapter,
|
||||
@@ -2681,22 +2677,7 @@ function selectPublishAdapter(platform: string): PublishAdapter | null {
|
||||
}
|
||||
|
||||
function selectMonitorAdapter(platform: string): MonitorAdapter | null {
|
||||
if (platform === yuanbaoAdapter.provider) {
|
||||
return yuanbaoAdapter;
|
||||
}
|
||||
if (platform === kimiAdapter.provider) {
|
||||
return kimiAdapter;
|
||||
}
|
||||
if (platform === doubaoAdapter.provider) {
|
||||
return doubaoAdapter;
|
||||
}
|
||||
if (platform === qwenAdapter.provider) {
|
||||
return qwenAdapter;
|
||||
}
|
||||
if (platform === wenxinAdapter.provider) {
|
||||
return wenxinAdapter;
|
||||
}
|
||||
return null;
|
||||
return getMonitorAdapter(platform);
|
||||
}
|
||||
|
||||
function normalizeSession(
|
||||
|
||||
Reference in New Issue
Block a user