@chainlesschain/personal-data-hub 0.1.0 → 0.2.1
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/__tests__/adapters/ai-chat-cookie-capture-spec.test.js +211 -0
- package/__tests__/adapters/ai-chat-health-checker.test.js +262 -0
- package/__tests__/adapters/ai-chat-history.test.js +396 -0
- package/__tests__/adapters/ai-chat-http-client.test.js +242 -0
- package/__tests__/adapters/ai-chat-vendors.test.js +874 -0
- package/__tests__/adapters/alipay-bill-adapter.test.js +538 -0
- package/__tests__/adapters/email-adapter.test.js +138 -1
- package/__tests__/adapters/email-classifier.test.js +347 -0
- package/__tests__/adapters/email-pdf-extractor.test.js +529 -0
- package/__tests__/adapters/email-retry-progress.test.js +294 -0
- package/__tests__/adapters/email-templates.test.js +699 -0
- package/__tests__/adapters/social-toutiao-kuaishou-scaffold.test.js +269 -0
- package/__tests__/adapters/system-data-adapter.test.js +440 -0
- package/__tests__/adapters/system-data-android-ingest.test.js +144 -0
- package/__tests__/adapters/system-data-android.test.js +387 -0
- package/__tests__/adapters/system-data-disclosure.test.js +153 -0
- package/__tests__/adapters/wechat-bootstrap.test.js +240 -0
- package/__tests__/adapters/wechat-env-probe.test.js +162 -0
- package/__tests__/adapters/wechat-frida-agent.test.js +191 -0
- package/__tests__/adapters/wechat-frida-integration.test.js +149 -0
- package/__tests__/adapters/wechat-frida-key-provider.test.js +188 -0
- package/__tests__/adapters/wechat-md5-key-provider.test.js +101 -0
- package/__tests__/analysis-skills.test.js +556 -0
- package/__tests__/analysis.test.js +329 -1
- package/__tests__/e2e/ai-chat-cross-source-journey.test.js +213 -0
- package/__tests__/e2e/full-user-journey.test.js +188 -0
- package/__tests__/entity-resolver-ingest-hook.test.js +177 -0
- package/__tests__/entity-resolver-stages.test.js +411 -0
- package/__tests__/entity-resolver-vault.test.js +246 -0
- package/__tests__/entity-resolver.test.js +526 -0
- package/__tests__/fixtures/entity-resolver-200-mock.json +96 -0
- package/__tests__/integration/ai-chat-history-registry.test.js +228 -0
- package/__tests__/integration/aichat-wizard-end-to-end.test.js +282 -0
- package/__tests__/integration/cross-adapter-pipelines.test.js +396 -0
- package/__tests__/integration/wechat-bootstrap-end-to-end.test.js +390 -0
- package/__tests__/longtail-adapters.test.js +217 -0
- package/__tests__/mobile-extractor.test.js +288 -0
- package/__tests__/registry.test.js +4 -2
- package/__tests__/shopping-adapters.test.js +296 -0
- package/__tests__/sidecar-contacts-cross-validate.test.js +163 -0
- package/__tests__/sidecar-supervisor.test.js +120 -0
- package/__tests__/social-adapters.test.js +206 -0
- package/__tests__/travel-adapters.test.js +325 -0
- package/__tests__/vault.test.js +3 -3
- package/__tests__/wechat-adapter.test.js +476 -0
- package/__tests__/whatsapp-adapter.test.js +135 -0
- package/lib/adapter-spec.js +12 -0
- package/lib/adapters/_python-sidecar-base.js +207 -0
- package/lib/adapters/ai-chat-history/ai-chat-adapter.js +374 -0
- package/lib/adapters/ai-chat-history/cookie-auth.js +109 -0
- package/lib/adapters/ai-chat-history/cookie-capture-spec.js +331 -0
- package/lib/adapters/ai-chat-history/health-checker.js +210 -0
- package/lib/adapters/ai-chat-history/http-client.js +211 -0
- package/lib/adapters/ai-chat-history/index.js +28 -0
- package/lib/adapters/ai-chat-history/schema-map.js +258 -0
- package/lib/adapters/ai-chat-history/vendor-spec.js +86 -0
- package/lib/adapters/ai-chat-history/vendors/coze.js +179 -0
- package/lib/adapters/ai-chat-history/vendors/deepseek.js +199 -0
- package/lib/adapters/ai-chat-history/vendors/doubao.js +255 -0
- package/lib/adapters/ai-chat-history/vendors/dreamina.js +174 -0
- package/lib/adapters/ai-chat-history/vendors/hunyuan.js +176 -0
- package/lib/adapters/ai-chat-history/vendors/kimi.js +182 -0
- package/lib/adapters/ai-chat-history/vendors/qianfan.js +160 -0
- package/lib/adapters/ai-chat-history/vendors/tongyi.js +193 -0
- package/lib/adapters/ai-chat-history/vendors/zhipu.js +202 -0
- package/lib/adapters/ai-chat-history/wizard-controller.js +473 -0
- package/lib/adapters/alipay-bill/alipay-bill-adapter.js +311 -0
- package/lib/adapters/alipay-bill/counterparty.js +129 -0
- package/lib/adapters/alipay-bill/csv-parser.js +217 -0
- package/lib/adapters/alipay-bill/index.js +41 -0
- package/lib/adapters/alipay-bill/zip-decryptor.js +111 -0
- package/lib/adapters/email-imap/classifier.js +495 -0
- package/lib/adapters/email-imap/email-adapter.js +419 -8
- package/lib/adapters/email-imap/index.js +42 -0
- package/lib/adapters/email-imap/pdf-extractor.js +192 -0
- package/lib/adapters/email-imap/templates/bill.js +232 -0
- package/lib/adapters/email-imap/templates/government.js +120 -0
- package/lib/adapters/email-imap/templates/index.js +78 -0
- package/lib/adapters/email-imap/templates/order.js +186 -0
- package/lib/adapters/email-imap/templates/other.js +114 -0
- package/lib/adapters/email-imap/templates/register.js +113 -0
- package/lib/adapters/email-imap/templates/travel.js +157 -0
- package/lib/adapters/email-imap/templates/utils.js +275 -0
- package/lib/adapters/email-imap/transactions.js +234 -0
- package/lib/adapters/messaging-qq/index.js +158 -0
- package/lib/adapters/messaging-telegram/index.js +142 -0
- package/lib/adapters/messaging-whatsapp/index.js +189 -0
- package/lib/adapters/shopping-base/index.js +208 -0
- package/lib/adapters/shopping-jd/index.js +150 -0
- package/lib/adapters/shopping-meituan/index.js +154 -0
- package/lib/adapters/shopping-taobao/index.js +176 -0
- package/lib/adapters/social-bilibili/index.js +171 -0
- package/lib/adapters/social-douyin/index.js +116 -0
- package/lib/adapters/social-kuaishou/index.js +237 -0
- package/lib/adapters/social-toutiao/index.js +236 -0
- package/lib/adapters/social-weibo/index.js +164 -0
- package/lib/adapters/social-xiaohongshu/index.js +96 -0
- package/lib/adapters/system-data/disclosure.js +166 -0
- package/lib/adapters/system-data/index.js +34 -0
- package/lib/adapters/system-data/system-data-adapter.js +344 -0
- package/lib/adapters/system-data-android/adapter.js +348 -0
- package/lib/adapters/system-data-android/index.js +76 -0
- package/lib/adapters/travel-12306/index.js +151 -0
- package/lib/adapters/travel-amap/index.js +164 -0
- package/lib/adapters/travel-baidu-map/index.js +162 -0
- package/lib/adapters/travel-base/index.js +240 -0
- package/lib/adapters/travel-ctrip/index.js +151 -0
- package/lib/adapters/wechat/bootstrap.js +146 -0
- package/lib/adapters/wechat/content-parser.js +326 -0
- package/lib/adapters/wechat/db-reader.js +209 -0
- package/lib/adapters/wechat/env-probe.js +218 -0
- package/lib/adapters/wechat/frida-agent/loader.js +67 -0
- package/lib/adapters/wechat/frida-agent/wechat-key-hook.js +126 -0
- package/lib/adapters/wechat/index.js +37 -0
- package/lib/adapters/wechat/key-extractor.js +158 -0
- package/lib/adapters/wechat/key-providers/frida-key-provider.js +244 -0
- package/lib/adapters/wechat/key-providers/index.js +22 -0
- package/lib/adapters/wechat/key-providers/key-provider-base.js +44 -0
- package/lib/adapters/wechat/key-providers/md5-key-provider.js +81 -0
- package/lib/adapters/wechat/normalize.js +220 -0
- package/lib/adapters/wechat/wechat-adapter.js +205 -0
- package/lib/analysis-skills/base.js +113 -0
- package/lib/analysis-skills/footprint.js +167 -0
- package/lib/analysis-skills/index.js +58 -0
- package/lib/analysis-skills/interests.js +161 -0
- package/lib/analysis-skills/relations.js +226 -0
- package/lib/analysis-skills/spending.js +219 -0
- package/lib/analysis-skills/timeline.js +167 -0
- package/lib/analysis.js +191 -2
- package/lib/entity-resolver/embedding-stage.js +198 -0
- package/lib/entity-resolver/entity-resolver.js +384 -0
- package/lib/entity-resolver/index.js +42 -0
- package/lib/entity-resolver/llm-stage.js +191 -0
- package/lib/entity-resolver/rule-stage.js +208 -0
- package/lib/entity-resolver/worker.js +149 -0
- package/lib/index.js +131 -0
- package/lib/migrations.js +73 -0
- package/lib/mobile-extractor/android.js +193 -0
- package/lib/mobile-extractor/index.js +9 -0
- package/lib/mobile-extractor/ios.js +223 -0
- package/lib/prompt-builder.js +11 -1
- package/lib/query-parser.js +7 -1
- package/lib/registry.js +42 -0
- package/lib/sidecar/index.js +15 -0
- package/lib/sidecar/supervisor.js +359 -0
- package/lib/vault.js +343 -0
- package/package.json +36 -3
- package/scripts/_make-fixture-all.js +126 -0
- package/scripts/_make-fixture-contacts.js +84 -0
- package/scripts/evaluate-entity-resolver.js +213 -0
- package/scripts/smoke-phase-5-5.js +196 -0
- package/scripts/smoke-phase-5-7.js +181 -0
- package/scripts/smoke-system-data-contacts.js +309 -0
- package/scripts/smoke-system-data.js +312 -0
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dreamina / 即梦 (ByteDance AI image / video) vendor adapter — Phase 10.2.
|
|
3
|
+
*
|
|
4
|
+
* Reference: docs/design/Adapter_AIChat_History.md §6.8
|
|
5
|
+
* - login https://jimeng.jianying.com/ (国内即梦)
|
|
6
|
+
* - convs POST /api/workspace/list ("workspace" = creative project)
|
|
7
|
+
* - msgs POST /api/workspace/<id>/items (every item = prompt + generated images/videos)
|
|
8
|
+
* - 特别 — schema downstream:
|
|
9
|
+
* * message.subtype = "ai-image-generation" (set by schema-map.js
|
|
10
|
+
* when content.generatedImages is non-empty).
|
|
11
|
+
* * content.generatedImages = [{url, prompt, model, params}].
|
|
12
|
+
*
|
|
13
|
+
* Item shape mapping: a Dreamina "item" is one prompt + N output images.
|
|
14
|
+
* We emit:
|
|
15
|
+
* - 1 user-role RawMessage carrying the prompt text
|
|
16
|
+
* - 1 assistant-role RawMessage carrying generatedImages[].
|
|
17
|
+
*
|
|
18
|
+
* Image CDN URLs are SIGNED and expire — schema-map records URL as-is, the
|
|
19
|
+
* UI must download thumbnails on first display + cache locally.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
"use strict";
|
|
23
|
+
|
|
24
|
+
const BASE = "https://jimeng.jianying.com";
|
|
25
|
+
const WORKSPACE_LIST_PATH = "/api/workspace/list";
|
|
26
|
+
const WORKSPACE_ITEMS_PATH = (id) => `/api/workspace/${encodeURIComponent(id)}/items`;
|
|
27
|
+
const USER_INFO_PATH = "/api/user/info";
|
|
28
|
+
|
|
29
|
+
const DEFAULT_PAGE_SIZE = 30;
|
|
30
|
+
|
|
31
|
+
function _ensureClient(ctx) {
|
|
32
|
+
if (!ctx || !ctx.httpClient) throw new Error("dreamina: ctx.httpClient required");
|
|
33
|
+
return ctx.httpClient;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async function validateCookie(ctx) {
|
|
37
|
+
const client = _ensureClient(ctx);
|
|
38
|
+
try {
|
|
39
|
+
const data = await client.getJson(BASE + USER_INFO_PATH, { session: ctx.session });
|
|
40
|
+
if (data && data.code === 0 && data.data) {
|
|
41
|
+
return { ok: true, userId: data.data.user_id || data.data.uid };
|
|
42
|
+
}
|
|
43
|
+
return { ok: false, reason: "UNEXPECTED_RESPONSE_SHAPE" };
|
|
44
|
+
} catch (err) {
|
|
45
|
+
return { ok: false, reason: err.code || err.message };
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async function *listConversations(ctx, opts = {}) {
|
|
50
|
+
const client = _ensureClient(ctx);
|
|
51
|
+
const pageSize = Number.isFinite(opts.pageSize) ? opts.pageSize : DEFAULT_PAGE_SIZE;
|
|
52
|
+
const sinceTs = opts.since && opts.since.lastUpdatedAt ? Number(opts.since.lastUpdatedAt) : 0;
|
|
53
|
+
|
|
54
|
+
let page = 1;
|
|
55
|
+
while (true) {
|
|
56
|
+
const body = { page, page_size: pageSize };
|
|
57
|
+
const data = await client.postJson(BASE + WORKSPACE_LIST_PATH, body, { session: ctx.session });
|
|
58
|
+
const list = _extractList(data);
|
|
59
|
+
if (list.length === 0) return;
|
|
60
|
+
|
|
61
|
+
let stopped = false;
|
|
62
|
+
for (const w of list) {
|
|
63
|
+
const updatedAt = _toMs(w.update_time || w.updated_at || w.create_time);
|
|
64
|
+
if (sinceTs && updatedAt <= sinceTs) {
|
|
65
|
+
stopped = true;
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
yield {
|
|
69
|
+
vendor: "dreamina",
|
|
70
|
+
originalId: String(w.workspace_id || w.id),
|
|
71
|
+
title: w.name || w.title || "(无标题作品集)",
|
|
72
|
+
modelName: w.default_model || undefined,
|
|
73
|
+
createdAt: _toMs(w.create_time),
|
|
74
|
+
updatedAt,
|
|
75
|
+
messageCount: w.item_count || undefined,
|
|
76
|
+
archived: Boolean(w.archived),
|
|
77
|
+
extra: { kind: "creative-workspace" },
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
if (stopped) return;
|
|
81
|
+
if (list.length < pageSize) return;
|
|
82
|
+
page++;
|
|
83
|
+
if (page > 200) return;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async function *listMessages(ctx, workspaceId, _opts = {}) {
|
|
88
|
+
const client = _ensureClient(ctx);
|
|
89
|
+
const url = BASE + WORKSPACE_ITEMS_PATH(workspaceId);
|
|
90
|
+
const data = await client.postJson(url, { workspace_id: String(workspaceId), page_size: 200 }, { session: ctx.session });
|
|
91
|
+
const items = _extractList(data);
|
|
92
|
+
|
|
93
|
+
items.sort((a, b) => _toMs(a.create_time) - _toMs(b.create_time));
|
|
94
|
+
|
|
95
|
+
for (const item of items) {
|
|
96
|
+
// Yield user prompt message
|
|
97
|
+
const promptText = item.prompt || item.prompt_text || "";
|
|
98
|
+
if (promptText) {
|
|
99
|
+
yield {
|
|
100
|
+
vendor: "dreamina",
|
|
101
|
+
originalId: String(item.id) + ":prompt",
|
|
102
|
+
conversationId: String(workspaceId),
|
|
103
|
+
role: "user",
|
|
104
|
+
content: { text: promptText },
|
|
105
|
+
createdAt: _toMs(item.create_time),
|
|
106
|
+
modelName: item.model,
|
|
107
|
+
extra: { itemId: item.id },
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
// Yield assistant message with generated images
|
|
111
|
+
const generatedImages = (Array.isArray(item.outputs) ? item.outputs : [])
|
|
112
|
+
.map((o) => ({
|
|
113
|
+
url: o.url || o.image_url,
|
|
114
|
+
prompt: promptText,
|
|
115
|
+
model: item.model,
|
|
116
|
+
params: o.params || item.params,
|
|
117
|
+
}))
|
|
118
|
+
.filter((g) => g.url);
|
|
119
|
+
if (generatedImages.length > 0) {
|
|
120
|
+
yield {
|
|
121
|
+
vendor: "dreamina",
|
|
122
|
+
originalId: String(item.id) + ":output",
|
|
123
|
+
conversationId: String(workspaceId),
|
|
124
|
+
role: "assistant",
|
|
125
|
+
content: {
|
|
126
|
+
text: undefined,
|
|
127
|
+
generatedImages,
|
|
128
|
+
},
|
|
129
|
+
createdAt: _toMs(item.complete_time || item.create_time),
|
|
130
|
+
parentMessageId: String(item.id) + ":prompt",
|
|
131
|
+
modelName: item.model,
|
|
132
|
+
extra: { itemId: item.id, status: item.status },
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function _extractList(data) {
|
|
139
|
+
if (!data) return [];
|
|
140
|
+
if (data.data && Array.isArray(data.data.workspaces)) return data.data.workspaces;
|
|
141
|
+
if (data.data && Array.isArray(data.data.list)) return data.data.list;
|
|
142
|
+
if (data.data && Array.isArray(data.data.items)) return data.data.items;
|
|
143
|
+
if (Array.isArray(data.data)) return data.data;
|
|
144
|
+
return [];
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function _toMs(t) {
|
|
148
|
+
if (typeof t === "number") return t > 1e12 ? t : t * 1000;
|
|
149
|
+
if (typeof t === "string") {
|
|
150
|
+
const n = Number(t);
|
|
151
|
+
if (Number.isFinite(n)) return n > 1e12 ? n : n * 1000;
|
|
152
|
+
const d = Date.parse(t);
|
|
153
|
+
return Number.isFinite(d) ? d : 0;
|
|
154
|
+
}
|
|
155
|
+
return 0;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const SPEC = {
|
|
159
|
+
name: "dreamina",
|
|
160
|
+
displayName: "Dreamina",
|
|
161
|
+
androidPackage: "com.bytedance.dreamina",
|
|
162
|
+
loginUrl: "https://jimeng.jianying.com/",
|
|
163
|
+
cookieDomains: ["jimeng.jianying.com", ".jianying.com"],
|
|
164
|
+
rateLimits: { perMinute: 15, minIntervalMs: 2500 },
|
|
165
|
+
|
|
166
|
+
validateCookie,
|
|
167
|
+
listConversations,
|
|
168
|
+
listMessages,
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
module.exports = {
|
|
172
|
+
SPEC,
|
|
173
|
+
_internal: { _toMs, _extractList, BASE },
|
|
174
|
+
};
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 腾讯混元 / 元宝 (Tencent Hunyuan / Yuanbao) vendor adapter — Phase 10.2.
|
|
3
|
+
*
|
|
4
|
+
* Reference: docs/design/Adapter_AIChat_History.md §6.5
|
|
5
|
+
* - login https://yuanbao.tencent.com/
|
|
6
|
+
* - convs POST /api/user/conv/list
|
|
7
|
+
* - msgs POST /api/user/conv/<id>/message/list
|
|
8
|
+
* - cookies hy_token + 腾讯 uin series
|
|
9
|
+
* - 风控 strong — keep conservative rateLimits.
|
|
10
|
+
*
|
|
11
|
+
* Special: 微信生态联动 messages may reference 微信公众号 articles via
|
|
12
|
+
* `extra.linkedArticles` (link cards). We preserve them as attachments[].
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
"use strict";
|
|
16
|
+
|
|
17
|
+
const BASE = "https://yuanbao.tencent.com";
|
|
18
|
+
const CONV_LIST_PATH = "/api/user/conv/list";
|
|
19
|
+
const MSG_LIST_PATH = (id) => `/api/user/conv/${encodeURIComponent(id)}/message/list`;
|
|
20
|
+
const USER_INFO_PATH = "/api/user/info";
|
|
21
|
+
|
|
22
|
+
const DEFAULT_PAGE_SIZE = 30;
|
|
23
|
+
|
|
24
|
+
function _ensureClient(ctx) {
|
|
25
|
+
if (!ctx || !ctx.httpClient) throw new Error("hunyuan: ctx.httpClient required");
|
|
26
|
+
return ctx.httpClient;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function validateCookie(ctx) {
|
|
30
|
+
const client = _ensureClient(ctx);
|
|
31
|
+
try {
|
|
32
|
+
const data = await client.getJson(BASE + USER_INFO_PATH, { session: ctx.session });
|
|
33
|
+
if (data && (data.ret === 0 || data.code === 0) && (data.data || data.user)) {
|
|
34
|
+
const u = data.data || data.user;
|
|
35
|
+
return { ok: true, userId: u.userId || u.uin || u.uid };
|
|
36
|
+
}
|
|
37
|
+
return { ok: false, reason: "UNEXPECTED_RESPONSE_SHAPE" };
|
|
38
|
+
} catch (err) {
|
|
39
|
+
return { ok: false, reason: err.code || err.message };
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function *listConversations(ctx, opts = {}) {
|
|
44
|
+
const client = _ensureClient(ctx);
|
|
45
|
+
const pageSize = Number.isFinite(opts.pageSize) ? opts.pageSize : DEFAULT_PAGE_SIZE;
|
|
46
|
+
const sinceTs = opts.since && opts.since.lastUpdatedAt ? Number(opts.since.lastUpdatedAt) : 0;
|
|
47
|
+
|
|
48
|
+
let cursor = null;
|
|
49
|
+
let safety = 0;
|
|
50
|
+
while (safety < 200) {
|
|
51
|
+
safety++;
|
|
52
|
+
const body = { count: pageSize, ...(cursor ? { cursor } : {}) };
|
|
53
|
+
const data = await client.postJson(BASE + CONV_LIST_PATH, body, { session: ctx.session });
|
|
54
|
+
const items = _extractList(data);
|
|
55
|
+
if (items.length === 0) return;
|
|
56
|
+
|
|
57
|
+
let stopped = false;
|
|
58
|
+
for (const c of items) {
|
|
59
|
+
const updatedAt = _toMs(c.updateTime || c.update_time || c.createTime);
|
|
60
|
+
if (sinceTs && updatedAt <= sinceTs) {
|
|
61
|
+
stopped = true;
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
yield {
|
|
65
|
+
vendor: "hunyuan",
|
|
66
|
+
originalId: String(c.convId || c.conv_id || c.id),
|
|
67
|
+
title: c.title || c.summary || undefined,
|
|
68
|
+
modelName: c.model || undefined,
|
|
69
|
+
createdAt: _toMs(c.createTime || c.create_time),
|
|
70
|
+
updatedAt,
|
|
71
|
+
messageCount: c.msgCount || c.message_count,
|
|
72
|
+
archived: Boolean(c.archived),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
if (stopped) return;
|
|
76
|
+
cursor = (data && data.data && data.data.cursor) || (items[items.length - 1] && items[items.length - 1].cursor);
|
|
77
|
+
if (!cursor || items.length < pageSize) return;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function *listMessages(ctx, conversationId, _opts = {}) {
|
|
82
|
+
const client = _ensureClient(ctx);
|
|
83
|
+
const url = BASE + MSG_LIST_PATH(conversationId);
|
|
84
|
+
const data = await client.postJson(url, { convId: String(conversationId), count: 200 }, { session: ctx.session });
|
|
85
|
+
const msgs = _extractList(data);
|
|
86
|
+
|
|
87
|
+
msgs.sort((a, b) => _toMs(a.createTime || a.create_time) - _toMs(b.createTime || b.create_time));
|
|
88
|
+
|
|
89
|
+
for (const m of msgs) {
|
|
90
|
+
yield {
|
|
91
|
+
vendor: "hunyuan",
|
|
92
|
+
originalId: String(m.msgId || m.msg_id || m.id),
|
|
93
|
+
conversationId: String(conversationId),
|
|
94
|
+
role: _normalizeRole(m.speaker || m.role || m.type),
|
|
95
|
+
content: _buildContent(m),
|
|
96
|
+
createdAt: _toMs(m.createTime || m.create_time),
|
|
97
|
+
parentMessageId: m.parentMsgId ? String(m.parentMsgId) : undefined,
|
|
98
|
+
modelName: m.model || undefined,
|
|
99
|
+
extra: m.linkedArticles ? { linkedArticles: m.linkedArticles } : undefined,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function _extractList(data) {
|
|
105
|
+
if (!data) return [];
|
|
106
|
+
if (data.data && Array.isArray(data.data.list)) return data.data.list;
|
|
107
|
+
if (data.data && Array.isArray(data.data.convs)) return data.data.convs;
|
|
108
|
+
if (data.data && Array.isArray(data.data.messages)) return data.data.messages;
|
|
109
|
+
if (Array.isArray(data.data)) return data.data;
|
|
110
|
+
if (Array.isArray(data.list)) return data.list;
|
|
111
|
+
return [];
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function _normalizeRole(r) {
|
|
115
|
+
if (r === "user" || r === "USER" || r === "human") return "user";
|
|
116
|
+
if (r === "assistant" || r === "ASSISTANT" || r === "bot" || r === "ai") return "assistant";
|
|
117
|
+
if (r === "system" || r === "SYSTEM") return "system";
|
|
118
|
+
return r ? String(r).toLowerCase() : "assistant";
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function _buildContent(m) {
|
|
122
|
+
const content = { text: m.content || m.text || "" };
|
|
123
|
+
const attachments = [];
|
|
124
|
+
if (Array.isArray(m.files)) {
|
|
125
|
+
for (const f of m.files) {
|
|
126
|
+
attachments.push({
|
|
127
|
+
type: (f.type === "image" || /image/i.test(f.mimeType || "")) ? "image" : "file",
|
|
128
|
+
filename: f.name,
|
|
129
|
+
url: f.url,
|
|
130
|
+
mimeType: f.mimeType,
|
|
131
|
+
size: f.size,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (Array.isArray(m.linkedArticles)) {
|
|
136
|
+
for (const link of m.linkedArticles) {
|
|
137
|
+
attachments.push({
|
|
138
|
+
type: "file",
|
|
139
|
+
filename: link.title,
|
|
140
|
+
url: link.url,
|
|
141
|
+
mimeType: "text/html",
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
if (attachments.length > 0) content.attachments = attachments;
|
|
146
|
+
return content;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function _toMs(t) {
|
|
150
|
+
if (typeof t === "number") return t > 1e12 ? t : t * 1000;
|
|
151
|
+
if (typeof t === "string") {
|
|
152
|
+
const n = Number(t);
|
|
153
|
+
if (Number.isFinite(n)) return n > 1e12 ? n : n * 1000;
|
|
154
|
+
const d = Date.parse(t);
|
|
155
|
+
return Number.isFinite(d) ? d : 0;
|
|
156
|
+
}
|
|
157
|
+
return 0;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const SPEC = {
|
|
161
|
+
name: "hunyuan",
|
|
162
|
+
displayName: "腾讯元宝",
|
|
163
|
+
androidPackage: "com.tencent.hunyuan.app.chat",
|
|
164
|
+
loginUrl: "https://yuanbao.tencent.com/",
|
|
165
|
+
cookieDomains: ["yuanbao.tencent.com", ".tencent.com"],
|
|
166
|
+
rateLimits: { perMinute: 20, minIntervalMs: 2000 },
|
|
167
|
+
|
|
168
|
+
validateCookie,
|
|
169
|
+
listConversations,
|
|
170
|
+
listMessages,
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
module.exports = {
|
|
174
|
+
SPEC,
|
|
175
|
+
_internal: { _toMs, _normalizeRole, _buildContent, _extractList, BASE },
|
|
176
|
+
};
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kimi (Moonshot) vendor adapter — Phase 10.2 wiring.
|
|
3
|
+
*
|
|
4
|
+
* Reference: docs/design/Adapter_AIChat_History.md §6.2
|
|
5
|
+
* - login https://kimi.moonshot.cn/
|
|
6
|
+
* - convs GET https://kimi.moonshot.cn/api/chat/list?offset=N&size=M
|
|
7
|
+
* - msgs POST https://kimi.moonshot.cn/api/chat/{convId}/segment/scroll
|
|
8
|
+
* body {last:0|"<id>", limit:N}
|
|
9
|
+
* - userinfo GET https://kimi.moonshot.cn/api/user
|
|
10
|
+
*
|
|
11
|
+
* Kimi pages messages via a `last` cursor (message id) rather than offset.
|
|
12
|
+
* `last:0` returns the most recent N; subsequent calls pass the oldest id
|
|
13
|
+
* from the previous page to walk backward. We collect all then sort by
|
|
14
|
+
* `created_at` ascending before yielding.
|
|
15
|
+
*
|
|
16
|
+
* Response shape (subject to drift; capture fixtures before re-parsing):
|
|
17
|
+
*
|
|
18
|
+
* /api/chat/list →
|
|
19
|
+
* { items: [{ id, name, created_at, updated_at, status, last_message,
|
|
20
|
+
* message_count, status }], total }
|
|
21
|
+
*
|
|
22
|
+
* /api/chat/{id}/segment/scroll →
|
|
23
|
+
* { items: [{ id, role, content, created_at, group_id, files: [...] }],
|
|
24
|
+
* has_more, last_id }
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
"use strict";
|
|
28
|
+
|
|
29
|
+
const BASE = "https://kimi.moonshot.cn";
|
|
30
|
+
const CONV_LIST_PATH = "/api/chat/list";
|
|
31
|
+
const USER_PATH = "/api/user";
|
|
32
|
+
|
|
33
|
+
const DEFAULT_PAGE_SIZE = 30;
|
|
34
|
+
|
|
35
|
+
function _ensureClient(ctx) {
|
|
36
|
+
if (!ctx || !ctx.httpClient) {
|
|
37
|
+
throw new Error("kimi: ctx.httpClient required");
|
|
38
|
+
}
|
|
39
|
+
return ctx.httpClient;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function validateCookie(ctx) {
|
|
43
|
+
const client = _ensureClient(ctx);
|
|
44
|
+
try {
|
|
45
|
+
const data = await client.getJson(BASE + USER_PATH, { session: ctx.session });
|
|
46
|
+
if (data && (data.id || data.user_id)) {
|
|
47
|
+
return { ok: true, userId: data.id || data.user_id };
|
|
48
|
+
}
|
|
49
|
+
return { ok: false, reason: "UNEXPECTED_RESPONSE_SHAPE" };
|
|
50
|
+
} catch (err) {
|
|
51
|
+
return { ok: false, reason: err.code || err.message };
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async function *listConversations(ctx, opts = {}) {
|
|
56
|
+
const client = _ensureClient(ctx);
|
|
57
|
+
const pageSize = Number.isFinite(opts.pageSize) ? opts.pageSize : DEFAULT_PAGE_SIZE;
|
|
58
|
+
const sinceTs = opts.since && opts.since.lastUpdatedAt ? Number(opts.since.lastUpdatedAt) : 0;
|
|
59
|
+
|
|
60
|
+
let offset = 0;
|
|
61
|
+
while (true) {
|
|
62
|
+
const url = new URL(BASE + CONV_LIST_PATH);
|
|
63
|
+
url.searchParams.set("offset", String(offset));
|
|
64
|
+
url.searchParams.set("size", String(pageSize));
|
|
65
|
+
|
|
66
|
+
const data = await client.getJson(url.toString(), { session: ctx.session });
|
|
67
|
+
const items = data && Array.isArray(data.items) ? data.items : [];
|
|
68
|
+
if (items.length === 0) return;
|
|
69
|
+
|
|
70
|
+
let stopped = false;
|
|
71
|
+
for (const c of items) {
|
|
72
|
+
const updatedAt = _toMs(c.updated_at || c.created_at);
|
|
73
|
+
if (sinceTs && updatedAt <= sinceTs) {
|
|
74
|
+
stopped = true;
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
yield {
|
|
78
|
+
vendor: "kimi",
|
|
79
|
+
originalId: String(c.id),
|
|
80
|
+
title: c.name || undefined,
|
|
81
|
+
modelName: undefined, // kimi doesn't expose per-conv model on list
|
|
82
|
+
createdAt: _toMs(c.created_at),
|
|
83
|
+
updatedAt,
|
|
84
|
+
messageCount: c.message_count || undefined,
|
|
85
|
+
archived: c.status === "archived",
|
|
86
|
+
extra: {
|
|
87
|
+
lastMessage: c.last_message,
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
if (stopped) return;
|
|
92
|
+
offset += items.length;
|
|
93
|
+
if (typeof data.total === "number" && offset >= data.total) return;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async function *listMessages(ctx, conversationId, opts = {}) {
|
|
98
|
+
const client = _ensureClient(ctx);
|
|
99
|
+
const pageSize = Number.isFinite(opts.pageSize) ? opts.pageSize : DEFAULT_PAGE_SIZE;
|
|
100
|
+
const url = BASE + "/api/chat/" + encodeURIComponent(conversationId) + "/segment/scroll";
|
|
101
|
+
|
|
102
|
+
const all = [];
|
|
103
|
+
let last = "0"; // kimi convention: 0 = most recent
|
|
104
|
+
let safety = 0;
|
|
105
|
+
while (safety < 50) {
|
|
106
|
+
safety++;
|
|
107
|
+
const data = await client.postJson(url, { last, limit: pageSize }, { session: ctx.session });
|
|
108
|
+
const items = data && Array.isArray(data.items) ? data.items : [];
|
|
109
|
+
if (items.length === 0) break;
|
|
110
|
+
all.push(...items);
|
|
111
|
+
if (!data.has_more) break;
|
|
112
|
+
last = String(data.last_id || items[items.length - 1].id);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Sort by created_at ascending for stable chronological yield.
|
|
116
|
+
all.sort((a, b) => _toMs(a.created_at) - _toMs(b.created_at));
|
|
117
|
+
for (const m of all) {
|
|
118
|
+
yield {
|
|
119
|
+
vendor: "kimi",
|
|
120
|
+
originalId: String(m.id),
|
|
121
|
+
conversationId: String(conversationId),
|
|
122
|
+
role: _normalizeRole(m.role),
|
|
123
|
+
content: _buildContent(m),
|
|
124
|
+
createdAt: _toMs(m.created_at),
|
|
125
|
+
parentMessageId: m.parent_id ? String(m.parent_id) : undefined,
|
|
126
|
+
modelName: m.model || undefined,
|
|
127
|
+
extra: m.group_id ? { groupId: m.group_id } : undefined,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function _normalizeRole(r) {
|
|
133
|
+
if (r === "user" || r === "USER") return "user";
|
|
134
|
+
if (r === "assistant" || r === "ASSISTANT" || r === "bot") return "assistant";
|
|
135
|
+
if (r === "system" || r === "SYSTEM") return "system";
|
|
136
|
+
return r || "assistant";
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function _buildContent(m) {
|
|
140
|
+
const content = { text: m.content || "" };
|
|
141
|
+
if (Array.isArray(m.files) && m.files.length > 0) {
|
|
142
|
+
content.attachments = m.files
|
|
143
|
+
.map((f) => ({
|
|
144
|
+
type: (f.type === "image" || /image/i.test(f.mime_type || "")) ? "image" : "file",
|
|
145
|
+
filename: f.name,
|
|
146
|
+
url: f.preview_url || f.url || f.download_url,
|
|
147
|
+
size: f.size,
|
|
148
|
+
mimeType: f.mime_type,
|
|
149
|
+
}))
|
|
150
|
+
.filter((a) => a.url || a.filename);
|
|
151
|
+
}
|
|
152
|
+
return content;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function _toMs(t) {
|
|
156
|
+
if (typeof t === "number") return t > 1e12 ? t : t * 1000;
|
|
157
|
+
if (typeof t === "string") {
|
|
158
|
+
const n = Number(t);
|
|
159
|
+
if (Number.isFinite(n)) return n > 1e12 ? n : n * 1000;
|
|
160
|
+
const d = Date.parse(t);
|
|
161
|
+
return Number.isFinite(d) ? d : 0;
|
|
162
|
+
}
|
|
163
|
+
return 0;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const SPEC = {
|
|
167
|
+
name: "kimi",
|
|
168
|
+
displayName: "Kimi",
|
|
169
|
+
androidPackage: "com.moonshot.kimichat",
|
|
170
|
+
loginUrl: "https://kimi.moonshot.cn/",
|
|
171
|
+
cookieDomains: ["kimi.moonshot.cn", ".moonshot.cn"],
|
|
172
|
+
rateLimits: { perMinute: 30, minIntervalMs: 1500 },
|
|
173
|
+
|
|
174
|
+
validateCookie,
|
|
175
|
+
listConversations,
|
|
176
|
+
listMessages,
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
module.exports = {
|
|
180
|
+
SPEC,
|
|
181
|
+
_internal: { _toMs, _normalizeRole, _buildContent, BASE, CONV_LIST_PATH },
|
|
182
|
+
};
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 百度千帆 / 文心一言 (Baidu Qianfan / Yiyan) vendor adapter — Phase 10.2.
|
|
3
|
+
*
|
|
4
|
+
* Reference: docs/design/Adapter_AIChat_History.md §6.6
|
|
5
|
+
* - login https://yiyan.baidu.com/ (个人) or https://qianfan.cloud.baidu.com/
|
|
6
|
+
* - convs POST /aichat/conversation/list
|
|
7
|
+
* - msgs POST /aichat/conversation/getMessages
|
|
8
|
+
* - cookies BAIDUID + BDUSS
|
|
9
|
+
* - 风控 strong — Baidu full-stack anti-bot.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
"use strict";
|
|
13
|
+
|
|
14
|
+
const BASE = "https://yiyan.baidu.com";
|
|
15
|
+
const CONV_LIST_PATH = "/aichat/conversation/list";
|
|
16
|
+
const MSG_LIST_PATH = "/aichat/conversation/getMessages";
|
|
17
|
+
const USER_INFO_PATH = "/aichat/user/info";
|
|
18
|
+
|
|
19
|
+
const DEFAULT_PAGE_SIZE = 30;
|
|
20
|
+
|
|
21
|
+
function _ensureClient(ctx) {
|
|
22
|
+
if (!ctx || !ctx.httpClient) throw new Error("qianfan: ctx.httpClient required");
|
|
23
|
+
return ctx.httpClient;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async function validateCookie(ctx) {
|
|
27
|
+
const client = _ensureClient(ctx);
|
|
28
|
+
try {
|
|
29
|
+
const data = await client.postJson(BASE + USER_INFO_PATH, {}, { session: ctx.session });
|
|
30
|
+
if (data && (data.code === 0 || data.errno === 0) && (data.data || data.user)) {
|
|
31
|
+
const u = data.data || data.user;
|
|
32
|
+
return { ok: true, userId: u.uk || u.userId || u.uid };
|
|
33
|
+
}
|
|
34
|
+
return { ok: false, reason: "UNEXPECTED_RESPONSE_SHAPE" };
|
|
35
|
+
} catch (err) {
|
|
36
|
+
return { ok: false, reason: err.code || err.message };
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function *listConversations(ctx, opts = {}) {
|
|
41
|
+
const client = _ensureClient(ctx);
|
|
42
|
+
const pageSize = Number.isFinite(opts.pageSize) ? opts.pageSize : DEFAULT_PAGE_SIZE;
|
|
43
|
+
const sinceTs = opts.since && opts.since.lastUpdatedAt ? Number(opts.since.lastUpdatedAt) : 0;
|
|
44
|
+
|
|
45
|
+
let pageNo = 1;
|
|
46
|
+
while (true) {
|
|
47
|
+
const body = { pageNo, pageSize };
|
|
48
|
+
const data = await client.postJson(BASE + CONV_LIST_PATH, body, { session: ctx.session });
|
|
49
|
+
const list = _extractList(data);
|
|
50
|
+
if (list.length === 0) return;
|
|
51
|
+
|
|
52
|
+
let stopped = false;
|
|
53
|
+
for (const c of list) {
|
|
54
|
+
const updatedAt = _toMs(c.updateTime || c.update_time);
|
|
55
|
+
if (sinceTs && updatedAt <= sinceTs) {
|
|
56
|
+
stopped = true;
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
yield {
|
|
60
|
+
vendor: "qianfan",
|
|
61
|
+
originalId: String(c.sessionId || c.session_id || c.id),
|
|
62
|
+
title: c.sessionName || c.title || undefined,
|
|
63
|
+
modelName: c.model || c.modelName || undefined,
|
|
64
|
+
createdAt: _toMs(c.createTime || c.create_time),
|
|
65
|
+
updatedAt,
|
|
66
|
+
messageCount: c.messageCount || undefined,
|
|
67
|
+
archived: Boolean(c.archived),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
if (stopped) return;
|
|
71
|
+
if (list.length < pageSize) return;
|
|
72
|
+
pageNo++;
|
|
73
|
+
if (pageNo > 200) return;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async function *listMessages(ctx, conversationId, _opts = {}) {
|
|
78
|
+
const client = _ensureClient(ctx);
|
|
79
|
+
const body = { sessionId: String(conversationId), pageSize: 200 };
|
|
80
|
+
const data = await client.postJson(BASE + MSG_LIST_PATH, body, { session: ctx.session });
|
|
81
|
+
const msgs = _extractList(data);
|
|
82
|
+
|
|
83
|
+
msgs.sort((a, b) => _toMs(a.createTime || a.create_time) - _toMs(b.createTime || b.create_time));
|
|
84
|
+
|
|
85
|
+
for (const m of msgs) {
|
|
86
|
+
yield {
|
|
87
|
+
vendor: "qianfan",
|
|
88
|
+
originalId: String(m.messageId || m.message_id || m.id),
|
|
89
|
+
conversationId: String(conversationId),
|
|
90
|
+
role: _normalizeRole(m.role || m.type || (m.fromUser ? "user" : "assistant")),
|
|
91
|
+
content: _buildContent(m),
|
|
92
|
+
createdAt: _toMs(m.createTime || m.create_time),
|
|
93
|
+
parentMessageId: m.parentMessageId ? String(m.parentMessageId) : undefined,
|
|
94
|
+
modelName: m.model || undefined,
|
|
95
|
+
extra: m.references ? { references: m.references } : undefined,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function _extractList(data) {
|
|
101
|
+
if (!data) return [];
|
|
102
|
+
if (data.data && Array.isArray(data.data.list)) return data.data.list;
|
|
103
|
+
if (data.data && Array.isArray(data.data.sessions)) return data.data.sessions;
|
|
104
|
+
if (data.data && Array.isArray(data.data.messages)) return data.data.messages;
|
|
105
|
+
if (Array.isArray(data.data)) return data.data;
|
|
106
|
+
if (Array.isArray(data.list)) return data.list;
|
|
107
|
+
return [];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function _normalizeRole(r) {
|
|
111
|
+
if (r === "user" || r === "USER" || r === 1) return "user";
|
|
112
|
+
if (r === "assistant" || r === "ASSISTANT" || r === "bot" || r === 2) return "assistant";
|
|
113
|
+
if (r === "system" || r === "SYSTEM" || r === 0) return "system";
|
|
114
|
+
return r ? String(r).toLowerCase() : "assistant";
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function _buildContent(m) {
|
|
118
|
+
const content = { text: m.content || m.text || "" };
|
|
119
|
+
if (Array.isArray(m.attachments) && m.attachments.length > 0) {
|
|
120
|
+
content.attachments = m.attachments
|
|
121
|
+
.map((a) => ({
|
|
122
|
+
type: (a.type === "image" || /image/i.test(a.mimeType || "")) ? "image" : "file",
|
|
123
|
+
filename: a.name,
|
|
124
|
+
url: a.url,
|
|
125
|
+
mimeType: a.mimeType,
|
|
126
|
+
size: a.size,
|
|
127
|
+
}))
|
|
128
|
+
.filter((a) => a.url || a.filename);
|
|
129
|
+
}
|
|
130
|
+
return content;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function _toMs(t) {
|
|
134
|
+
if (typeof t === "number") return t > 1e12 ? t : t * 1000;
|
|
135
|
+
if (typeof t === "string") {
|
|
136
|
+
const n = Number(t);
|
|
137
|
+
if (Number.isFinite(n)) return n > 1e12 ? n : n * 1000;
|
|
138
|
+
const d = Date.parse(t);
|
|
139
|
+
return Number.isFinite(d) ? d : 0;
|
|
140
|
+
}
|
|
141
|
+
return 0;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const SPEC = {
|
|
145
|
+
name: "qianfan",
|
|
146
|
+
displayName: "百度千帆",
|
|
147
|
+
androidPackage: "com.baidu.qianfan.llmkitchat",
|
|
148
|
+
loginUrl: "https://yiyan.baidu.com/",
|
|
149
|
+
cookieDomains: ["yiyan.baidu.com", ".baidu.com"],
|
|
150
|
+
rateLimits: { perMinute: 20, minIntervalMs: 2000 },
|
|
151
|
+
|
|
152
|
+
validateCookie,
|
|
153
|
+
listConversations,
|
|
154
|
+
listMessages,
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
module.exports = {
|
|
158
|
+
SPEC,
|
|
159
|
+
_internal: { _toMs, _normalizeRole, _buildContent, _extractList, BASE },
|
|
160
|
+
};
|