@klarkxy/dsh-zhihu 0.1.0
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/LICENSE +41 -0
- package/README.md +45 -0
- package/cordis.patch.yml +3 -0
- package/lib/client.js +2246 -0
- package/lib/contracts.d.ts +23 -0
- package/lib/contracts.js +13 -0
- package/lib/contracts.js.map +1 -0
- package/lib/index.d.ts +53 -0
- package/lib/index.js +461 -0
- package/lib/index.js.map +1 -0
- package/lib/operations.js +378 -0
- package/lib/operations.js.map +1 -0
- package/lib/tools.d.ts +8 -0
- package/lib/tools.js +494 -0
- package/lib/tools.js.map +1 -0
- package/lib/usage.d.ts +54 -0
- package/lib/usage.js +96 -0
- package/lib/usage.js.map +1 -0
- package/package.json +135 -0
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
//#region src/zhihu-client.ts
|
|
5
|
+
/**
|
|
6
|
+
* 知乎开放平台共享请求层:Access Secret 解析、超时封装、信封解析与执行计量。
|
|
7
|
+
*
|
|
8
|
+
* 端点规范(来源:github.com/klarkxy/zhihu-search,src/zhihu_search/upstream/http_client.py):
|
|
9
|
+
* - Base URL: https://developer.zhihu.com
|
|
10
|
+
* - 公共 Headers: Authorization: Bearer <access_secret>、X-Request-Timestamp、Accept
|
|
11
|
+
* - 除直答外的 REST 接口返回 {Code, Message, Data} 信封,Code 非 0 表示业务错误。
|
|
12
|
+
*
|
|
13
|
+
* Token 解析顺序(按契约,**不**把 token 放进 schema 参数):
|
|
14
|
+
* 1. 可选的 credential 解析器(应用设置界面写入 `ZHIHU_ACCESS_TOKEN` 后,
|
|
15
|
+
* 通过 `ctx.credentials.resolve` 取回);命中即返回,source 标记为 `credential`。
|
|
16
|
+
* 2. 环境变量 ZHIHU_ACCESS_TOKEN
|
|
17
|
+
* 3. 环境变量 ZHIHU_ACCESS_SECRET(zhihu-search CLI 同名变量)
|
|
18
|
+
* 4. ~/.config/zhihu-search/credentials.json 的 access_secret 字段
|
|
19
|
+
* 都没有则抛出带中文配置指引的 TOKEN_MISSING。
|
|
20
|
+
*/
|
|
21
|
+
const ZHIHU_BASE_URL = "https://developer.zhihu.com";
|
|
22
|
+
const ZHIHU_CREDENTIALS_PRIMARY = "ZHIHU_ACCESS_TOKEN";
|
|
23
|
+
const ZHIHU_CREDENTIALS_FALLBACK = "ZHIHU_ACCESS_SECRET";
|
|
24
|
+
const ZHIHU_CREDENTIALS_FILE = join(homedir(), ".config", "zhihu-search", "credentials.json");
|
|
25
|
+
const globalFetch = typeof globalThis !== "undefined" && typeof globalThis.fetch === "function" ? (input, init) => {
|
|
26
|
+
const f = globalThis.fetch;
|
|
27
|
+
return f(input, init);
|
|
28
|
+
} : void 0;
|
|
29
|
+
function looksLikeToken(value) {
|
|
30
|
+
const trimmed = value.trim();
|
|
31
|
+
return trimmed.length >= 8 && trimmed.length <= 256;
|
|
32
|
+
}
|
|
33
|
+
async function readCredentialsFile(path) {
|
|
34
|
+
try {
|
|
35
|
+
const raw = await readFile(path, "utf8");
|
|
36
|
+
const parsed = JSON.parse(raw);
|
|
37
|
+
if (parsed && typeof parsed === "object" && typeof parsed.access_secret === "string" && looksLikeToken(parsed.access_secret)) return parsed.access_secret.trim();
|
|
38
|
+
} catch {}
|
|
39
|
+
}
|
|
40
|
+
async function resolveZhihuToken(env = process.env, options = {}) {
|
|
41
|
+
if (options.resolveCredential) {
|
|
42
|
+
const fromCredential = (await options.resolveCredential())?.trim();
|
|
43
|
+
if (fromCredential && looksLikeToken(fromCredential)) return {
|
|
44
|
+
token: fromCredential,
|
|
45
|
+
source: "credential"
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
const primary = env[ZHIHU_CREDENTIALS_PRIMARY]?.trim();
|
|
49
|
+
if (primary && looksLikeToken(primary)) return {
|
|
50
|
+
token: primary,
|
|
51
|
+
source: "env-primary"
|
|
52
|
+
};
|
|
53
|
+
const fallback = env[ZHIHU_CREDENTIALS_FALLBACK]?.trim();
|
|
54
|
+
if (fallback && looksLikeToken(fallback)) return {
|
|
55
|
+
token: fallback,
|
|
56
|
+
source: "env-fallback"
|
|
57
|
+
};
|
|
58
|
+
const fromFile = await readCredentialsFile(ZHIHU_CREDENTIALS_FILE);
|
|
59
|
+
if (fromFile) return {
|
|
60
|
+
token: fromFile,
|
|
61
|
+
source: "file"
|
|
62
|
+
};
|
|
63
|
+
throw new ZhihuSearchError("TOKEN_MISSING", ["未找到知乎 Access Secret。请在「设置 → 知乎」中填写;", `也可设置环境变量 ${ZHIHU_CREDENTIALS_PRIMARY} 或写入 ~/.config/zhihu-search/credentials.json。`].join(""));
|
|
64
|
+
}
|
|
65
|
+
var ZhihuSearchError = class extends Error {
|
|
66
|
+
code;
|
|
67
|
+
status;
|
|
68
|
+
constructor(code, message, status) {
|
|
69
|
+
super(message);
|
|
70
|
+
this.name = "ZhihuSearchError";
|
|
71
|
+
this.code = code;
|
|
72
|
+
if (status !== void 0) this.status = status;
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
/** Metering must never break the tool itself. */
|
|
76
|
+
function reportExecuted(hook, event) {
|
|
77
|
+
if (!hook) return;
|
|
78
|
+
try {
|
|
79
|
+
hook(event);
|
|
80
|
+
} catch {}
|
|
81
|
+
}
|
|
82
|
+
function isAbortError(error) {
|
|
83
|
+
if (error instanceof Error && error.name === "AbortError") return true;
|
|
84
|
+
return typeof error === "object" && error !== null && error.name === "AbortError";
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* 发起一次带鉴权与超时的知乎请求,返回解析后的 JSON 原文(信封由调用方处理)。
|
|
88
|
+
* `label` 用于中文错误文案,如「知乎搜索」「知乎热榜」。
|
|
89
|
+
*/
|
|
90
|
+
async function zhihuFetchJson(path, init, label, options = {}) {
|
|
91
|
+
const token = await resolveZhihuToken(options.env, { resolveCredential: options.resolveCredential });
|
|
92
|
+
const fetcher = options.fetcher ?? globalFetch;
|
|
93
|
+
if (!fetcher) throw new ZhihuSearchError("BAD_RESPONSE", "当前环境没有可用的 fetch。");
|
|
94
|
+
const url = new URL(path, ZHIHU_BASE_URL);
|
|
95
|
+
for (const [key, value] of Object.entries(init.params ?? {})) url.searchParams.set(key, value);
|
|
96
|
+
const method = init.method ?? (init.formData || init.body !== void 0 ? "POST" : "GET");
|
|
97
|
+
const timeout = options.timeoutMs ?? 15e3;
|
|
98
|
+
const controller = new AbortController();
|
|
99
|
+
const linked = options.signal;
|
|
100
|
+
const onAbort = () => controller.abort(linked?.reason);
|
|
101
|
+
if (linked) {
|
|
102
|
+
if (linked.aborted) controller.abort(linked.reason);
|
|
103
|
+
else linked.addEventListener("abort", onAbort, { once: true });
|
|
104
|
+
}
|
|
105
|
+
const timer = setTimeout(() => controller.abort(/* @__PURE__ */ new Error("zhihu request timeout")), timeout);
|
|
106
|
+
let response;
|
|
107
|
+
try {
|
|
108
|
+
response = await fetcher(url.toString(), {
|
|
109
|
+
method,
|
|
110
|
+
headers: {
|
|
111
|
+
Authorization: `Bearer ${token.token}`,
|
|
112
|
+
"X-Request-Timestamp": String(Math.floor(Date.now() / 1e3)),
|
|
113
|
+
Accept: "application/json",
|
|
114
|
+
...method === "POST" && !init.formData ? { "Content-Type": "application/json" } : {}
|
|
115
|
+
},
|
|
116
|
+
signal: controller.signal,
|
|
117
|
+
...init.formData ? { body: init.formData } : init.body !== void 0 ? { body: JSON.stringify(init.body) } : {}
|
|
118
|
+
});
|
|
119
|
+
} catch (error) {
|
|
120
|
+
if (isAbortError(error) || controller.signal.aborted) throw new ZhihuSearchError("TIMEOUT", `${label}超时(${Math.round(timeout / 1e3)} 秒),请稍后再试。`);
|
|
121
|
+
throw new ZhihuSearchError("BAD_RESPONSE", `${label}失败:${error instanceof Error ? error.message : String(error)}`);
|
|
122
|
+
} finally {
|
|
123
|
+
clearTimeout(timer);
|
|
124
|
+
if (linked) linked.removeEventListener("abort", onAbort);
|
|
125
|
+
}
|
|
126
|
+
if (!response.ok) throw new ZhihuSearchError("HTTP_ERROR", `${label}返回 HTTP ${response.status}。`, response.status);
|
|
127
|
+
try {
|
|
128
|
+
return await response.json();
|
|
129
|
+
} catch (error) {
|
|
130
|
+
throw new ZhihuSearchError("BAD_RESPONSE", `${label}响应不是合法 JSON:${error instanceof Error ? error.message : String(error)}`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/** 解析 {Code, Message, Data} 信封,Code 非 0 抛出 HTTP_ERROR;返回 Data(缺省为 {})。 */
|
|
134
|
+
function parseZhihuEnvelope(body) {
|
|
135
|
+
if (!body || typeof body !== "object" || Array.isArray(body)) throw new ZhihuSearchError("BAD_RESPONSE", "知乎响应缺少 {Code,Message,Data} 信封。");
|
|
136
|
+
const envelope = body;
|
|
137
|
+
const code = Number(envelope.Code);
|
|
138
|
+
if (!Number.isFinite(code) || code !== 0) {
|
|
139
|
+
const msg = typeof envelope.Message === "string" ? envelope.Message : "未知错误";
|
|
140
|
+
throw new ZhihuSearchError("HTTP_ERROR", `知乎开放平台返回错误(code=${envelope.Code ?? "?"}):${msg}`);
|
|
141
|
+
}
|
|
142
|
+
return envelope.Data ?? {};
|
|
143
|
+
}
|
|
144
|
+
function truncateSummary(text, limit) {
|
|
145
|
+
if (text.length <= limit) return text;
|
|
146
|
+
return `${text.slice(0, limit)}…`;
|
|
147
|
+
}
|
|
148
|
+
function normalizeSearchItem(raw) {
|
|
149
|
+
const summary = typeof raw.ContentText === "string" ? raw.ContentText : "";
|
|
150
|
+
return {
|
|
151
|
+
title: typeof raw.Title === "string" && raw.Title.length > 0 ? raw.Title : "(无标题)",
|
|
152
|
+
type: typeof raw.ContentType === "string" && raw.ContentType.length > 0 ? raw.ContentType : "内容",
|
|
153
|
+
url: typeof raw.Url === "string" ? raw.Url : "",
|
|
154
|
+
summary: truncateSummary(summary.trim(), 200),
|
|
155
|
+
votes: typeof raw.VoteUpCount === "number" ? raw.VoteUpCount : 0,
|
|
156
|
+
comments: typeof raw.CommentCount === "number" ? raw.CommentCount : 0,
|
|
157
|
+
author: typeof raw.AuthorName === "string" && raw.AuthorName.length > 0 ? raw.AuthorName : "匿名",
|
|
158
|
+
authority: typeof raw.AuthorityLevel === "string" && raw.AuthorityLevel.length > 0 ? raw.AuthorityLevel : "?",
|
|
159
|
+
editTime: typeof raw.EditTime === "number" ? String(raw.EditTime) : ""
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
const ZHIHU_SEARCH_PATH = "/api/v1/content/zhihu_search";
|
|
163
|
+
function clampCount(value) {
|
|
164
|
+
const n = typeof value === "number" && Number.isFinite(value) ? Math.trunc(value) : 5;
|
|
165
|
+
if (n < 1) return 1;
|
|
166
|
+
if (n > 10) return 10;
|
|
167
|
+
return n;
|
|
168
|
+
}
|
|
169
|
+
function filterItems(data) {
|
|
170
|
+
const row = data ?? {};
|
|
171
|
+
return {
|
|
172
|
+
items: Array.isArray(row.Items) ? row.Items.filter((item) => !!item && typeof item === "object" && !Array.isArray(item)) : [],
|
|
173
|
+
emptyReason: typeof row.EmptyReason === "string" ? row.EmptyReason : void 0
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
async function executeZhihuSearch(query, count, options = {}) {
|
|
177
|
+
const trimmed = query.trim();
|
|
178
|
+
if (!trimmed) throw new ZhihuSearchError("BAD_RESPONSE", "搜索词不能为空。");
|
|
179
|
+
const resolvedCount = clampCount(count);
|
|
180
|
+
const { items, emptyReason } = filterItems(parseZhihuEnvelope(await zhihuFetchJson(ZHIHU_SEARCH_PATH, { params: {
|
|
181
|
+
Query: trimmed,
|
|
182
|
+
Count: String(resolvedCount)
|
|
183
|
+
} }, "知乎搜索", options)));
|
|
184
|
+
return {
|
|
185
|
+
version: 1,
|
|
186
|
+
query: trimmed,
|
|
187
|
+
count: resolvedCount,
|
|
188
|
+
items: items.map(normalizeSearchItem),
|
|
189
|
+
emptyReason
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
function renderZhihuSearch(result) {
|
|
193
|
+
if (result.items.length === 0) return `知乎站内搜索「${result.query}」无结果${result.emptyReason ? `(${result.emptyReason})` : ""}。`;
|
|
194
|
+
const lines = [`知乎站内搜索「${result.query}」共 ${result.items.length} 条:`, ""];
|
|
195
|
+
result.items.forEach((item, index) => {
|
|
196
|
+
lines.push(`${index + 1}. [${item.title}](${item.url || "(无链接)"})`);
|
|
197
|
+
lines.push(` - 类型:${item.type} 作者:${item.author} 权威:${item.authority}`);
|
|
198
|
+
if (item.editTime) lines.push(` - 时间:${item.editTime}`);
|
|
199
|
+
lines.push(` - 赞同 ${item.votes} 评论 ${item.comments}`);
|
|
200
|
+
if (item.summary) lines.push(` - 摘要:${item.summary}`);
|
|
201
|
+
lines.push("");
|
|
202
|
+
});
|
|
203
|
+
return lines.join("\n").trimEnd();
|
|
204
|
+
}
|
|
205
|
+
const GLOBAL_SEARCH_PATH = "/api/v1/content/global_search";
|
|
206
|
+
const HOT_LIST_PATH = "/api/v1/content/hot_list";
|
|
207
|
+
const ASK_PATH = "/v1/chat/completions";
|
|
208
|
+
const KNOWLEDGE_SEARCH_PATH = "/api/v1/knowledge/search";
|
|
209
|
+
const ZHIHU_ASK_MODELS = [
|
|
210
|
+
"zhida-fast-1p5",
|
|
211
|
+
"zhida-thinking-1p5",
|
|
212
|
+
"zhida-agent"
|
|
213
|
+
];
|
|
214
|
+
/** 直答的价值在于比本机模型更强的综合考据,默认用思考档;fast 只配简单事实查询。 */
|
|
215
|
+
const ZHIHU_ASK_DEFAULT_MODEL = "zhida-thinking-1p5";
|
|
216
|
+
function clampInt(value, fallback, max) {
|
|
217
|
+
const n = typeof value === "number" && Number.isFinite(value) ? Math.trunc(value) : fallback;
|
|
218
|
+
if (n < 1) return 1;
|
|
219
|
+
if (n > max) return max;
|
|
220
|
+
return n;
|
|
221
|
+
}
|
|
222
|
+
function requireQuery(query, hint) {
|
|
223
|
+
const trimmed = query.trim();
|
|
224
|
+
if (!trimmed) throw new ZhihuSearchError("BAD_RESPONSE", hint);
|
|
225
|
+
return trimmed;
|
|
226
|
+
}
|
|
227
|
+
function objectItems(data, key = "Items") {
|
|
228
|
+
const items = (data ?? {})[key];
|
|
229
|
+
return Array.isArray(items) ? items.filter((item) => !!item && typeof item === "object" && !Array.isArray(item)) : [];
|
|
230
|
+
}
|
|
231
|
+
async function executeZhihuGlobalSearch(query, count, searchDb, options = {}) {
|
|
232
|
+
const trimmed = requireQuery(query, "搜索词不能为空。");
|
|
233
|
+
const resolvedCount = clampInt(count, 10, 20);
|
|
234
|
+
const data = parseZhihuEnvelope(await zhihuFetchJson(GLOBAL_SEARCH_PATH, { params: {
|
|
235
|
+
Query: trimmed,
|
|
236
|
+
Count: String(resolvedCount),
|
|
237
|
+
SearchDB: searchDb
|
|
238
|
+
} }, "知乎全网搜索", options));
|
|
239
|
+
return {
|
|
240
|
+
version: 1,
|
|
241
|
+
query: trimmed,
|
|
242
|
+
count: resolvedCount,
|
|
243
|
+
searchDb,
|
|
244
|
+
items: objectItems(data).map(normalizeSearchItem),
|
|
245
|
+
emptyReason: typeof data.EmptyReason === "string" ? data.EmptyReason : void 0
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
function renderZhihuGlobalSearch(result) {
|
|
249
|
+
if (result.items.length === 0) return `全网搜索「${result.query}」无结果${result.emptyReason ? `(${result.emptyReason})` : ""}。`;
|
|
250
|
+
const lines = [`全网搜索「${result.query}」共 ${result.items.length} 条:`, ""];
|
|
251
|
+
result.items.forEach((item, index) => {
|
|
252
|
+
lines.push(`${index + 1}. [${item.title}](${item.url || "(无链接)"})`);
|
|
253
|
+
lines.push(` - 类型:${item.type} 作者:${item.author}`);
|
|
254
|
+
if (item.summary) lines.push(` - 摘要:${item.summary}`);
|
|
255
|
+
lines.push("");
|
|
256
|
+
});
|
|
257
|
+
return lines.join("\n").trimEnd();
|
|
258
|
+
}
|
|
259
|
+
const ZHIHU_HOT_LIST_SUMMARY_MAX_CHARS = 200;
|
|
260
|
+
function normalizeHotItem(raw) {
|
|
261
|
+
const summary = typeof raw.Summary === "string" ? raw.Summary.trim() : "";
|
|
262
|
+
return {
|
|
263
|
+
title: typeof raw.Title === "string" && raw.Title.length > 0 ? raw.Title : "(无标题)",
|
|
264
|
+
url: typeof raw.Url === "string" ? raw.Url : "",
|
|
265
|
+
summary: summary.length > ZHIHU_HOT_LIST_SUMMARY_MAX_CHARS ? `${summary.slice(0, ZHIHU_HOT_LIST_SUMMARY_MAX_CHARS)}…` : summary
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
async function executeZhihuHotList(limit, options = {}) {
|
|
269
|
+
const resolvedLimit = clampInt(limit, 10, 30);
|
|
270
|
+
return {
|
|
271
|
+
version: 1,
|
|
272
|
+
limit: resolvedLimit,
|
|
273
|
+
items: objectItems(parseZhihuEnvelope(await zhihuFetchJson(HOT_LIST_PATH, { params: { Limit: String(resolvedLimit) } }, "知乎热榜", options))).map(normalizeHotItem)
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
function renderZhihuHotList(result) {
|
|
277
|
+
if (result.items.length === 0) return "知乎热榜为空。";
|
|
278
|
+
const lines = ["知乎热榜:", ""];
|
|
279
|
+
result.items.forEach((item, index) => {
|
|
280
|
+
lines.push(`${index + 1}. [${item.title}](${item.url || "(无链接)"})`);
|
|
281
|
+
if (item.summary) lines.push(` - ${item.summary}`);
|
|
282
|
+
lines.push("");
|
|
283
|
+
});
|
|
284
|
+
return lines.join("\n").trimEnd();
|
|
285
|
+
}
|
|
286
|
+
async function executeZhihuAsk(query, model, options = {}) {
|
|
287
|
+
const trimmed = requireQuery(query, "直答问题不能为空。");
|
|
288
|
+
const body = await zhihuFetchJson(ASK_PATH, {
|
|
289
|
+
method: "POST",
|
|
290
|
+
body: {
|
|
291
|
+
model,
|
|
292
|
+
messages: [{
|
|
293
|
+
role: "user",
|
|
294
|
+
content: trimmed
|
|
295
|
+
}],
|
|
296
|
+
stream: false
|
|
297
|
+
}
|
|
298
|
+
}, "知乎直答", {
|
|
299
|
+
...options,
|
|
300
|
+
timeoutMs: options.timeoutMs ?? 12e4
|
|
301
|
+
});
|
|
302
|
+
if (!body || typeof body !== "object" || Array.isArray(body)) throw new ZhihuSearchError("BAD_RESPONSE", "知乎直答响应不是 OpenAI 兼容格式。");
|
|
303
|
+
const payload = body;
|
|
304
|
+
if (payload.error) throw new ZhihuSearchError("HTTP_ERROR", `知乎直答返回错误:${typeof payload.error.message === "string" ? payload.error.message : "未知错误"}`);
|
|
305
|
+
const message = (Array.isArray(payload.choices) ? payload.choices[0] : void 0)?.message ?? {};
|
|
306
|
+
const content = typeof message.content === "string" ? message.content.trim() : "";
|
|
307
|
+
const reasoning = typeof message.reasoning_content === "string" ? message.reasoning_content.trim() : "";
|
|
308
|
+
if (!content) throw new ZhihuSearchError("BAD_RESPONSE", "知乎直答返回为空。");
|
|
309
|
+
return {
|
|
310
|
+
version: 1,
|
|
311
|
+
query: trimmed,
|
|
312
|
+
model,
|
|
313
|
+
content,
|
|
314
|
+
reasoning
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
function renderZhihuAsk(result) {
|
|
318
|
+
const parts = [];
|
|
319
|
+
if (result.reasoning) parts.push(`【思考过程】\n${result.reasoning}`);
|
|
320
|
+
parts.push(result.content);
|
|
321
|
+
return `知乎直答「${result.query}」:\n\n${parts.join("\n\n")}`;
|
|
322
|
+
}
|
|
323
|
+
const ZHIHU_RECALL_SCOPES = [
|
|
324
|
+
"personal",
|
|
325
|
+
"subscription",
|
|
326
|
+
"public"
|
|
327
|
+
];
|
|
328
|
+
function normalizeKnowledgeItem(raw) {
|
|
329
|
+
const content = raw.Content;
|
|
330
|
+
const snippets = Array.isArray(content) ? content.map((snippet) => String(snippet).trim()).filter((snippet) => snippet.length > 0) : [];
|
|
331
|
+
return {
|
|
332
|
+
docName: typeof raw.DocName === "string" && raw.DocName.length > 0 ? raw.DocName : "(未命名文档)",
|
|
333
|
+
originUrl: typeof raw.OriginUrl === "string" ? raw.OriginUrl : "",
|
|
334
|
+
snippets
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
/** 收敛召回范围:只保留合法档位、去重;为空或未提供时回退公开库。 */
|
|
338
|
+
function normalizeRecallScopes(value) {
|
|
339
|
+
if (!Array.isArray(value)) return ["public"];
|
|
340
|
+
const scopes = [...new Set(value.filter((scope) => typeof scope === "string" && ZHIHU_RECALL_SCOPES.includes(scope)))];
|
|
341
|
+
return scopes.length > 0 ? scopes : ["public"];
|
|
342
|
+
}
|
|
343
|
+
async function executeZhihuKnowledgeSearch(query, limit, recallScopes, options = {}) {
|
|
344
|
+
const trimmed = requireQuery(query, "知识库检索词不能为空。");
|
|
345
|
+
const resolvedLimit = clampInt(limit, 5, 10);
|
|
346
|
+
return {
|
|
347
|
+
version: 1,
|
|
348
|
+
query: trimmed,
|
|
349
|
+
limit: resolvedLimit,
|
|
350
|
+
recallScopes,
|
|
351
|
+
items: objectItems(parseZhihuEnvelope(await zhihuFetchJson(KNOWLEDGE_SEARCH_PATH, {
|
|
352
|
+
method: "POST",
|
|
353
|
+
body: {
|
|
354
|
+
Query: trimmed,
|
|
355
|
+
Limit: resolvedLimit,
|
|
356
|
+
RecallScopes: recallScopes
|
|
357
|
+
}
|
|
358
|
+
}, "知乎知识库检索", {
|
|
359
|
+
...options,
|
|
360
|
+
timeoutMs: options.timeoutMs ?? 3e4
|
|
361
|
+
}))).map(normalizeKnowledgeItem)
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
function renderZhihuKnowledgeSearch(result) {
|
|
365
|
+
if (result.items.length === 0) return `知乎知识库检索「${result.query}」无匹配结果。`;
|
|
366
|
+
const lines = [`知乎知识库检索「${result.query}」共 ${result.items.length} 条:`, ""];
|
|
367
|
+
result.items.forEach((item, index) => {
|
|
368
|
+
lines.push(`${index + 1}. ${item.docName}`);
|
|
369
|
+
if (item.originUrl) lines.push(` - 来源:${item.originUrl}`);
|
|
370
|
+
for (const snippet of item.snippets) lines.push(` - ${snippet}`);
|
|
371
|
+
lines.push("");
|
|
372
|
+
});
|
|
373
|
+
return lines.join("\n").trimEnd();
|
|
374
|
+
}
|
|
375
|
+
//#endregion
|
|
376
|
+
export { reportExecuted as _, executeZhihuGlobalSearch as a, normalizeRecallScopes as c, renderZhihuHotList as d, renderZhihuKnowledgeSearch as f, parseZhihuEnvelope as g, ZhihuSearchError as h, executeZhihuAsk as i, renderZhihuAsk as l, renderZhihuSearch as m, ZHIHU_ASK_MODELS as n, executeZhihuHotList as o, executeZhihuSearch as p, ZHIHU_RECALL_SCOPES as r, executeZhihuKnowledgeSearch as s, ZHIHU_ASK_DEFAULT_MODEL as t, renderZhihuGlobalSearch as u, zhihuFetchJson as v };
|
|
377
|
+
|
|
378
|
+
//# sourceMappingURL=operations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"operations.js","names":[],"sources":["../src/zhihu-client.ts","../src/search-api.ts","../src/operations.ts"],"sourcesContent":["/**\n * 知乎开放平台共享请求层:Access Secret 解析、超时封装、信封解析与执行计量。\n *\n * 端点规范(来源:github.com/klarkxy/zhihu-search,src/zhihu_search/upstream/http_client.py):\n * - Base URL: https://developer.zhihu.com\n * - 公共 Headers: Authorization: Bearer <access_secret>、X-Request-Timestamp、Accept\n * - 除直答外的 REST 接口返回 {Code, Message, Data} 信封,Code 非 0 表示业务错误。\n *\n * Token 解析顺序(按契约,**不**把 token 放进 schema 参数):\n * 1. 可选的 credential 解析器(应用设置界面写入 `ZHIHU_ACCESS_TOKEN` 后,\n * 通过 `ctx.credentials.resolve` 取回);命中即返回,source 标记为 `credential`。\n * 2. 环境变量 ZHIHU_ACCESS_TOKEN\n * 3. 环境变量 ZHIHU_ACCESS_SECRET(zhihu-search CLI 同名变量)\n * 4. ~/.config/zhihu-search/credentials.json 的 access_secret 字段\n * 都没有则抛出带中文配置指引的 TOKEN_MISSING。\n */\nimport { readFile } from 'node:fs/promises'\nimport { homedir } from 'node:os'\nimport { join } from 'node:path'\n\nexport const ZHIHU_BASE_URL = 'https://developer.zhihu.com'\nexport const ZHIHU_DEFAULT_TIMEOUT_MS = 15_000\nexport const ZHIHU_SUMMARY_MAX_CHARS = 200\n\nconst ZHIHU_CREDENTIALS_PRIMARY = 'ZHIHU_ACCESS_TOKEN'\nconst ZHIHU_CREDENTIALS_FALLBACK = 'ZHIHU_ACCESS_SECRET'\nconst ZHIHU_CREDENTIALS_FILE = join(homedir(), '.config', 'zhihu-search', 'credentials.json')\n\ntype TokenSource = 'credential' | 'env-primary' | 'env-fallback' | 'file'\n\nexport type ZhihuSearchToken = { token: string; source: TokenSource }\n\nexport type ZhihuSearchFetcher = (input: string, init?: { method?: string; headers?: Record<string, string>; body?: string | FormData; signal?: AbortSignal }) => Promise<{\n ok: boolean\n status: number\n text(): Promise<string>\n json(): Promise<unknown>\n}>\n\nconst globalFetch: ZhihuSearchFetcher | undefined = typeof globalThis !== 'undefined' && typeof (globalThis as { fetch?: unknown }).fetch === 'function'\n ? (input, init) => {\n const f = (globalThis as { fetch: typeof globalThis.fetch }).fetch\n return f(input as Parameters<typeof f>[0], init as Parameters<typeof f>[1] | undefined) as unknown as ReturnType<ZhihuSearchFetcher>\n }\n : undefined\n\nfunction looksLikeToken(value: string): boolean {\n const trimmed = value.trim()\n return trimmed.length >= 8 && trimmed.length <= 256\n}\n\nasync function readCredentialsFile(path: string): Promise<string | undefined> {\n try {\n const raw = await readFile(path, 'utf8')\n const parsed = JSON.parse(raw) as { access_secret?: unknown } | null\n if (parsed && typeof parsed === 'object' && typeof parsed.access_secret === 'string' && looksLikeToken(parsed.access_secret)) {\n return parsed.access_secret.trim()\n }\n } catch {\n // 文件不存在或 JSON 解析失败都不算硬错误,让上层回退到 isError 提示\n }\n return undefined\n}\n\nexport type ZhihuSearchResolveOptions = {\n /** Resolve the Access Secret via host-managed credentials (e.g. the settings UI). */\n resolveCredential?: () => Promise<string | undefined>\n}\n\nexport async function resolveZhihuToken(\n env: NodeJS.ProcessEnv = process.env,\n options: ZhihuSearchResolveOptions = {},\n): Promise<ZhihuSearchToken> {\n if (options.resolveCredential) {\n const fromCredential = (await options.resolveCredential())?.trim()\n if (fromCredential && looksLikeToken(fromCredential)) {\n return { token: fromCredential, source: 'credential' }\n }\n }\n const primary = env[ZHIHU_CREDENTIALS_PRIMARY]?.trim()\n if (primary && looksLikeToken(primary)) return { token: primary, source: 'env-primary' }\n const fallback = env[ZHIHU_CREDENTIALS_FALLBACK]?.trim()\n if (fallback && looksLikeToken(fallback)) return { token: fallback, source: 'env-fallback' }\n const fromFile = await readCredentialsFile(ZHIHU_CREDENTIALS_FILE)\n if (fromFile) return { token: fromFile, source: 'file' }\n throw new ZhihuSearchError('TOKEN_MISSING', [\n '未找到知乎 Access Secret。请在「设置 → 知乎」中填写;',\n `也可设置环境变量 ${ZHIHU_CREDENTIALS_PRIMARY} 或写入 ~/.config/zhihu-search/credentials.json。`,\n ].join(''))\n}\n\nexport class ZhihuSearchError extends Error {\n readonly code: 'TOKEN_MISSING' | 'HTTP_ERROR' | 'TIMEOUT' | 'BAD_RESPONSE'\n readonly status?: number\n constructor(code: ZhihuSearchError['code'], message: string, status?: number) {\n super(message)\n this.name = 'ZhihuSearchError'\n this.code = code\n if (status !== undefined) this.status = status\n }\n}\n\n/** Metering payload emitted after each zhihu tool execution. */\nexport type ZhihuSearchExecuted = { ok: boolean; results: number }\n\n/** Metering must never break the tool itself. */\nexport function reportExecuted(hook: ((event: ZhihuSearchExecuted) => void) | undefined, event: ZhihuSearchExecuted): void {\n if (!hook) return\n try {\n hook(event)\n } catch {\n // Best-effort metering; swallow hook failures.\n }\n}\n\nexport type ZhihuClientOptions = ZhihuSearchResolveOptions & {\n fetcher?: ZhihuSearchFetcher\n /** Allows tests to bypass process env; defaults to process.env. */\n env?: NodeJS.ProcessEnv\n signal?: AbortSignal\n /** Override the request timeout in ms. Defaults to {@link ZHIHU_DEFAULT_TIMEOUT_MS}. */\n timeoutMs?: number\n}\n\nfunction isAbortError(error: unknown): boolean {\n if (error instanceof Error && error.name === 'AbortError') return true\n return typeof error === 'object' && error !== null && (error as { name?: unknown }).name === 'AbortError'\n}\n\n/**\n * 发起一次带鉴权与超时的知乎请求,返回解析后的 JSON 原文(信封由调用方处理)。\n * `label` 用于中文错误文案,如「知乎搜索」「知乎热榜」。\n */\nexport async function zhihuFetchJson(\n path: string,\n init: { method?: 'GET' | 'POST'; params?: Record<string, string>; body?: unknown; formData?: FormData },\n label: string,\n options: ZhihuClientOptions = {},\n): Promise<unknown> {\n const token = await resolveZhihuToken(options.env, { resolveCredential: options.resolveCredential })\n const fetcher = options.fetcher ?? globalFetch\n if (!fetcher) throw new ZhihuSearchError('BAD_RESPONSE', '当前环境没有可用的 fetch。')\n\n const url = new URL(path, ZHIHU_BASE_URL)\n for (const [key, value] of Object.entries(init.params ?? {})) url.searchParams.set(key, value)\n\n const method = init.method ?? (init.formData || init.body !== undefined ? 'POST' : 'GET')\n const timeout = options.timeoutMs ?? ZHIHU_DEFAULT_TIMEOUT_MS\n const controller = new AbortController()\n const linked = options.signal\n const onAbort = () => controller.abort(linked?.reason)\n if (linked) {\n if (linked.aborted) controller.abort(linked.reason)\n else linked.addEventListener('abort', onAbort, { once: true })\n }\n const timer = setTimeout(() => controller.abort(new Error('zhihu request timeout')), timeout)\n let response: Awaited<ReturnType<ZhihuSearchFetcher>>\n try {\n response = await fetcher(url.toString(), {\n method,\n headers: {\n Authorization: `Bearer ${token.token}`,\n 'X-Request-Timestamp': String(Math.floor(Date.now() / 1000)),\n Accept: 'application/json',\n // multipart 的 Content-Type(含 boundary)由 fetch 自动生成,不能手写。\n ...(method === 'POST' && !init.formData ? { 'Content-Type': 'application/json' } : {}),\n },\n signal: controller.signal,\n ...(init.formData ? { body: init.formData } : init.body !== undefined ? { body: JSON.stringify(init.body) } : {}),\n })\n } catch (error) {\n if (isAbortError(error) || controller.signal.aborted) {\n throw new ZhihuSearchError('TIMEOUT', `${label}超时(${Math.round(timeout / 1000)} 秒),请稍后再试。`)\n }\n throw new ZhihuSearchError('BAD_RESPONSE', `${label}失败:${error instanceof Error ? error.message : String(error)}`)\n } finally {\n clearTimeout(timer)\n if (linked) linked.removeEventListener('abort', onAbort)\n }\n\n if (!response.ok) {\n throw new ZhihuSearchError('HTTP_ERROR', `${label}返回 HTTP ${response.status}。`, response.status)\n }\n\n try {\n return await response.json()\n } catch (error) {\n throw new ZhihuSearchError('BAD_RESPONSE', `${label}响应不是合法 JSON:${error instanceof Error ? error.message : String(error)}`)\n }\n}\n\n/** 解析 {Code, Message, Data} 信封,Code 非 0 抛出 HTTP_ERROR;返回 Data(缺省为 {})。 */\nexport function parseZhihuEnvelope(body: unknown): unknown {\n if (!body || typeof body !== 'object' || Array.isArray(body)) {\n throw new ZhihuSearchError('BAD_RESPONSE', '知乎响应缺少 {Code,Message,Data} 信封。')\n }\n const envelope = body as { Code?: unknown; Message?: unknown; Data?: unknown }\n const code = Number(envelope.Code)\n if (!Number.isFinite(code) || code !== 0) {\n const msg = typeof envelope.Message === 'string' ? envelope.Message : '未知错误'\n throw new ZhihuSearchError('HTTP_ERROR', `知乎开放平台返回错误(code=${envelope.Code ?? '?'}):${msg}`)\n }\n return envelope.Data ?? {}\n}\n\nfunction truncateSummary(text: string, limit: number): string {\n if (text.length <= limit) return text\n return `${text.slice(0, limit)}…`\n}\n\n/** 搜索类接口(站内/全网)共用的条目结构。 */\nexport type ZhihuSearchItem = {\n title: string\n type: string\n url: string\n summary: string\n votes: number\n comments: number\n author: string\n authority: string\n editTime: string\n}\n\nexport function normalizeSearchItem(raw: Record<string, unknown>): ZhihuSearchItem {\n const summary = typeof raw.ContentText === 'string' ? raw.ContentText : ''\n return {\n title: typeof raw.Title === 'string' && raw.Title.length > 0 ? raw.Title : '(无标题)',\n type: typeof raw.ContentType === 'string' && raw.ContentType.length > 0 ? raw.ContentType : '内容',\n url: typeof raw.Url === 'string' ? raw.Url : '',\n summary: truncateSummary(summary.trim(), ZHIHU_SUMMARY_MAX_CHARS),\n votes: typeof raw.VoteUpCount === 'number' ? raw.VoteUpCount : 0,\n comments: typeof raw.CommentCount === 'number' ? raw.CommentCount : 0,\n author: typeof raw.AuthorName === 'string' && raw.AuthorName.length > 0 ? raw.AuthorName : '匿名',\n authority: typeof raw.AuthorityLevel === 'string' && raw.AuthorityLevel.length > 0 ? raw.AuthorityLevel : '?',\n editTime: typeof raw.EditTime === 'number' ? String(raw.EditTime) : '',\n }\n}\n","/**\n * 知乎开放平台站内搜索工具。\n *\n * 端点调研结论(来源:github.com/klarkxy/zhihu-search,src/zhihu_search/upstream/http_client.py):\n * - 知乎站内搜索:GET /api/v1/content/zhihu_search?Query=<q>&Count=<n>\n * - count 上限 10\n * - 响应信封:{ Code, Message, Data };Code 非 0 表示业务错误。\n * - Data.Items: 每条 { Title, ContentType, Url, ContentText, VoteUpCount,\n * CommentCount, AuthorName, AuthorityLevel, EditTime }。\n *\n * 鉴权、超时、信封解析与计量走 ./zhihu-client.ts 的共享实现;此处只保留\n * 站内搜索的参数收敛、结果规整与工具定义。\n */\nimport { ZHIHU_SEARCH_TOOL_NAME } from './contracts.ts'\nimport {\n normalizeSearchItem,\n parseZhihuEnvelope,\n reportExecuted,\n zhihuFetchJson,\n ZhihuSearchError,\n type ZhihuClientOptions,\n type ZhihuSearchItem,\n} from './zhihu-client.ts'\n\nexport { ZHIHU_SEARCH_TOOL_NAME } from './contracts.ts'\nexport {\n resolveZhihuToken,\n ZhihuSearchError,\n type ZhihuSearchExecuted,\n type ZhihuSearchFetcher,\n type ZhihuSearchItem,\n type ZhihuSearchResolveOptions,\n type ZhihuSearchToken,\n} from './zhihu-client.ts'\nexport const ZHIHU_SEARCH_VERSION = 1\nexport const ZHIHU_SEARCH_DEFAULT_COUNT = 5\nexport const ZHIHU_SEARCH_MAX_COUNT = 10\nexport const ZHIHU_SEARCH_TIMEOUT_MS = 15_000\nexport const ZHIHU_SUMMARY_MAX_CHARS = 200\n\nconst ZHIHU_SEARCH_PATH = '/api/v1/content/zhihu_search'\n\nexport type ZhihuSearchResult = {\n version: typeof ZHIHU_SEARCH_VERSION\n query: string\n count: number\n items: ZhihuSearchItem[]\n emptyReason?: string\n}\n\nexport type ZhihuSearchExecuteOptions = ZhihuClientOptions\n\nfunction clampCount(value: unknown): number {\n const n = typeof value === 'number' && Number.isFinite(value) ? Math.trunc(value) : ZHIHU_SEARCH_DEFAULT_COUNT\n if (n < 1) return 1\n if (n > ZHIHU_SEARCH_MAX_COUNT) return ZHIHU_SEARCH_MAX_COUNT\n return n\n}\n\nfunction filterItems(data: unknown): { items: Record<string, unknown>[]; emptyReason?: string } {\n const row = (data ?? {}) as { Items?: unknown; EmptyReason?: unknown }\n const items = Array.isArray(row.Items) ? row.Items.filter((item): item is Record<string, unknown> => !!item && typeof item === 'object' && !Array.isArray(item)) : []\n const emptyReason = typeof row.EmptyReason === 'string' ? row.EmptyReason : undefined\n return { items, emptyReason }\n}\n\nexport async function executeZhihuSearch(\n query: string,\n count: number,\n options: ZhihuSearchExecuteOptions = {},\n): Promise<ZhihuSearchResult> {\n const trimmed = query.trim()\n if (!trimmed) throw new ZhihuSearchError('BAD_RESPONSE', '搜索词不能为空。')\n const resolvedCount = clampCount(count)\n const body = await zhihuFetchJson(ZHIHU_SEARCH_PATH, {\n params: { Query: trimmed, Count: String(resolvedCount) },\n }, '知乎搜索', options)\n const { items, emptyReason } = filterItems(parseZhihuEnvelope(body))\n return {\n version: ZHIHU_SEARCH_VERSION,\n query: trimmed,\n count: resolvedCount,\n items: items.map(normalizeSearchItem),\n emptyReason,\n }\n}\n\nexport function renderZhihuSearch(result: ZhihuSearchResult): string {\n if (result.items.length === 0) {\n return `知乎站内搜索「${result.query}」无结果${result.emptyReason ? `(${result.emptyReason})` : ''}。`\n }\n const lines: string[] = [`知乎站内搜索「${result.query}」共 ${result.items.length} 条:`, '']\n result.items.forEach((item, index) => {\n lines.push(`${index + 1}. [${item.title}](${item.url || '(无链接)'})`)\n lines.push(` - 类型:${item.type} 作者:${item.author} 权威:${item.authority}`)\n if (item.editTime) lines.push(` - 时间:${item.editTime}`)\n lines.push(` - 赞同 ${item.votes} 评论 ${item.comments}`)\n if (item.summary) lines.push(` - 摘要:${item.summary}`)\n lines.push('')\n })\n return lines.join('\\n').trimEnd()\n}\n\n","/**\n * 知乎开放平台的其余写作向能力:全网搜索、热榜、直答、公开知识库检索。\n * 共享 ./zhihu-client.ts 的鉴权、超时、信封解析与计量;每个工具只做参数收敛、\n * 结果规整与渲染。结果一律只作社区参考,不构成 canon,也不写入项目文件。\n *\n * 端点(来源:github.com/klarkxy/zhihu-search,src/zhihu_search/upstream/http_client.py):\n * - 全网搜索:GET /api/v1/content/global_search?Query&Count(<=20)&SearchDB(all|realtime|static)\n * - 热榜:GET /api/v1/content/hot_list?Limit(<=30)\n * - 直答:POST /v1/chat/completions(OpenAI 兼容,非信封;模型 zhida-fast-1p5 / zhida-thinking-1p5)\n * - 知识库检索:POST /api/v1/knowledge/search { Query, Limit(<=10), RecallScopes }\n */\nimport {\n ZHIHU_ASK_TOOL_NAME,\n ZHIHU_GLOBAL_SEARCH_TOOL_NAME,\n ZHIHU_HOT_LIST_TOOL_NAME,\n ZHIHU_KNOWLEDGE_SEARCH_TOOL_NAME,\n} from './contracts.ts'\nimport {\n normalizeSearchItem,\n parseZhihuEnvelope,\n reportExecuted,\n zhihuFetchJson,\n ZhihuSearchError,\n type ZhihuClientOptions,\n type ZhihuSearchExecuted,\n type ZhihuSearchItem,\n} from './zhihu-client.ts'\n\nexport const ZHIHU_GLOBAL_SEARCH_VERSION = 1\nexport const ZHIHU_GLOBAL_SEARCH_DEFAULT_COUNT = 10\nexport const ZHIHU_GLOBAL_SEARCH_MAX_COUNT = 20\nexport const ZHIHU_HOT_LIST_VERSION = 1\nexport const ZHIHU_HOT_LIST_DEFAULT_LIMIT = 10\nexport const ZHIHU_HOT_LIST_MAX_LIMIT = 30\nexport const ZHIHU_ASK_VERSION = 1\nexport const ZHIHU_ASK_TIMEOUT_MS = 120_000\nexport const ZHIHU_KNOWLEDGE_SEARCH_VERSION = 1\nexport const ZHIHU_KNOWLEDGE_SEARCH_DEFAULT_LIMIT = 5\nexport const ZHIHU_KNOWLEDGE_SEARCH_MAX_LIMIT = 10\n\nconst GLOBAL_SEARCH_PATH = '/api/v1/content/global_search'\nconst HOT_LIST_PATH = '/api/v1/content/hot_list'\nconst ASK_PATH = '/v1/chat/completions'\nconst KNOWLEDGE_SEARCH_PATH = '/api/v1/knowledge/search'\n\nexport const ZHIHU_ASK_MODELS = ['zhida-fast-1p5', 'zhida-thinking-1p5', 'zhida-agent'] as const\nexport type ZhihuAskModel = (typeof ZHIHU_ASK_MODELS)[number]\n/** 直答的价值在于比本机模型更强的综合考据,默认用思考档;fast 只配简单事实查询。 */\nexport const ZHIHU_ASK_DEFAULT_MODEL: ZhihuAskModel = 'zhida-thinking-1p5'\n\nexport type ZhihuToolOptions = ZhihuClientOptions & {\n /** Best-effort metering hook; invoked after every execution, failures included. */\n onExecuted?: (event: ZhihuSearchExecuted) => void\n}\n\nfunction clampInt(value: unknown, fallback: number, max: number): number {\n const n = typeof value === 'number' && Number.isFinite(value) ? Math.trunc(value) : fallback\n if (n < 1) return 1\n if (n > max) return max\n return n\n}\n\nfunction requireQuery(query: string, hint: string): string {\n const trimmed = query.trim()\n if (!trimmed) throw new ZhihuSearchError('BAD_RESPONSE', hint)\n return trimmed\n}\n\nfunction objectItems(data: unknown, key = 'Items'): Record<string, unknown>[] {\n const row = (data ?? {}) as Record<string, unknown>\n const items = row[key]\n return Array.isArray(items) ? items.filter((item): item is Record<string, unknown> => !!item && typeof item === 'object' && !Array.isArray(item)) : []\n}\n\n// ---------------------------------------------------------------------------\n// 全网搜索\n// ---------------------------------------------------------------------------\n\nexport type ZhihuGlobalSearchDb = 'all' | 'realtime' | 'static'\n\nexport type ZhihuGlobalSearchResult = {\n version: typeof ZHIHU_GLOBAL_SEARCH_VERSION\n query: string\n count: number\n searchDb: ZhihuGlobalSearchDb\n items: ZhihuSearchItem[]\n emptyReason?: string\n}\n\nexport async function executeZhihuGlobalSearch(\n query: string,\n count: number,\n searchDb: ZhihuGlobalSearchDb,\n options: ZhihuClientOptions = {},\n): Promise<ZhihuGlobalSearchResult> {\n const trimmed = requireQuery(query, '搜索词不能为空。')\n const resolvedCount = clampInt(count, ZHIHU_GLOBAL_SEARCH_DEFAULT_COUNT, ZHIHU_GLOBAL_SEARCH_MAX_COUNT)\n const body = await zhihuFetchJson(GLOBAL_SEARCH_PATH, {\n params: { Query: trimmed, Count: String(resolvedCount), SearchDB: searchDb },\n }, '知乎全网搜索', options)\n const data = parseZhihuEnvelope(body) as { Items?: unknown; EmptyReason?: unknown }\n return {\n version: ZHIHU_GLOBAL_SEARCH_VERSION,\n query: trimmed,\n count: resolvedCount,\n searchDb,\n items: objectItems(data).map(normalizeSearchItem),\n emptyReason: typeof data.EmptyReason === 'string' ? data.EmptyReason : undefined,\n }\n}\n\nexport function renderZhihuGlobalSearch(result: ZhihuGlobalSearchResult): string {\n if (result.items.length === 0) {\n return `全网搜索「${result.query}」无结果${result.emptyReason ? `(${result.emptyReason})` : ''}。`\n }\n const lines: string[] = [`全网搜索「${result.query}」共 ${result.items.length} 条:`, '']\n result.items.forEach((item, index) => {\n lines.push(`${index + 1}. [${item.title}](${item.url || '(无链接)'})`)\n lines.push(` - 类型:${item.type} 作者:${item.author}`)\n if (item.summary) lines.push(` - 摘要:${item.summary}`)\n lines.push('')\n })\n return lines.join('\\n').trimEnd()\n}\n\n// ---------------------------------------------------------------------------\n// 热榜\n// ---------------------------------------------------------------------------\n\nexport type ZhihuHotListItem = {\n title: string\n url: string\n summary: string\n}\n\nexport type ZhihuHotListResult = {\n version: typeof ZHIHU_HOT_LIST_VERSION\n limit: number\n items: ZhihuHotListItem[]\n}\n\nconst ZHIHU_HOT_LIST_SUMMARY_MAX_CHARS = 200\n\nfunction normalizeHotItem(raw: Record<string, unknown>): ZhihuHotListItem {\n const summary = typeof raw.Summary === 'string' ? raw.Summary.trim() : ''\n return {\n title: typeof raw.Title === 'string' && raw.Title.length > 0 ? raw.Title : '(无标题)',\n url: typeof raw.Url === 'string' ? raw.Url : '',\n summary: summary.length > ZHIHU_HOT_LIST_SUMMARY_MAX_CHARS ? `${summary.slice(0, ZHIHU_HOT_LIST_SUMMARY_MAX_CHARS)}…` : summary,\n }\n}\n\nexport async function executeZhihuHotList(limit: number, options: ZhihuClientOptions = {}): Promise<ZhihuHotListResult> {\n const resolvedLimit = clampInt(limit, ZHIHU_HOT_LIST_DEFAULT_LIMIT, ZHIHU_HOT_LIST_MAX_LIMIT)\n const body = await zhihuFetchJson(HOT_LIST_PATH, {\n params: { Limit: String(resolvedLimit) },\n }, '知乎热榜', options)\n const items = objectItems(parseZhihuEnvelope(body)).map(normalizeHotItem)\n return { version: ZHIHU_HOT_LIST_VERSION, limit: resolvedLimit, items }\n}\n\nexport function renderZhihuHotList(result: ZhihuHotListResult): string {\n if (result.items.length === 0) return '知乎热榜为空。'\n const lines: string[] = ['知乎热榜:', '']\n result.items.forEach((item, index) => {\n lines.push(`${index + 1}. [${item.title}](${item.url || '(无链接)'})`)\n if (item.summary) lines.push(` - ${item.summary}`)\n lines.push('')\n })\n return lines.join('\\n').trimEnd()\n}\n\n// ---------------------------------------------------------------------------\n// 直答(OpenAI 兼容,非信封)\n// ---------------------------------------------------------------------------\n\nexport type ZhihuAskResult = {\n version: typeof ZHIHU_ASK_VERSION\n query: string\n model: ZhihuAskModel\n content: string\n reasoning: string\n}\n\nexport async function executeZhihuAsk(\n query: string,\n model: ZhihuAskModel,\n options: ZhihuClientOptions = {},\n): Promise<ZhihuAskResult> {\n const trimmed = requireQuery(query, '直答问题不能为空。')\n const body = await zhihuFetchJson(ASK_PATH, {\n method: 'POST',\n body: { model, messages: [{ role: 'user', content: trimmed }], stream: false },\n }, '知乎直答', { ...options, timeoutMs: options.timeoutMs ?? ZHIHU_ASK_TIMEOUT_MS })\n if (!body || typeof body !== 'object' || Array.isArray(body)) {\n throw new ZhihuSearchError('BAD_RESPONSE', '知乎直答响应不是 OpenAI 兼容格式。')\n }\n const payload = body as {\n error?: { message?: unknown }\n choices?: Array<{ message?: { content?: unknown; reasoning_content?: unknown } }>\n }\n if (payload.error) {\n const msg = typeof payload.error.message === 'string' ? payload.error.message : '未知错误'\n throw new ZhihuSearchError('HTTP_ERROR', `知乎直答返回错误:${msg}`)\n }\n const choice = Array.isArray(payload.choices) ? payload.choices[0] : undefined\n const message = choice?.message ?? {}\n const content = typeof message.content === 'string' ? message.content.trim() : ''\n const reasoning = typeof message.reasoning_content === 'string' ? message.reasoning_content.trim() : ''\n if (!content) throw new ZhihuSearchError('BAD_RESPONSE', '知乎直答返回为空。')\n return { version: ZHIHU_ASK_VERSION, query: trimmed, model, content, reasoning }\n}\n\nexport function renderZhihuAsk(result: ZhihuAskResult): string {\n const parts: string[] = []\n if (result.reasoning) parts.push(`【思考过程】\\n${result.reasoning}`)\n parts.push(result.content)\n return `知乎直答「${result.query}」:\\n\\n${parts.join('\\n\\n')}`\n}\n\n// ---------------------------------------------------------------------------\n// 公开知识库检索\n// ---------------------------------------------------------------------------\n\nexport type ZhihuKnowledgeItem = {\n docName: string\n originUrl: string\n snippets: string[]\n}\n\nexport type ZhihuRecallScope = 'personal' | 'subscription' | 'public'\nexport const ZHIHU_RECALL_SCOPES: readonly ZhihuRecallScope[] = ['personal', 'subscription', 'public']\n\nexport type ZhihuKnowledgeSearchResult = {\n version: typeof ZHIHU_KNOWLEDGE_SEARCH_VERSION\n query: string\n limit: number\n recallScopes: ZhihuRecallScope[]\n items: ZhihuKnowledgeItem[]\n}\n\nfunction normalizeKnowledgeItem(raw: Record<string, unknown>): ZhihuKnowledgeItem {\n const content = raw.Content\n const snippets = Array.isArray(content)\n ? content.map((snippet) => String(snippet).trim()).filter((snippet) => snippet.length > 0)\n : []\n return {\n docName: typeof raw.DocName === 'string' && raw.DocName.length > 0 ? raw.DocName : '(未命名文档)',\n originUrl: typeof raw.OriginUrl === 'string' ? raw.OriginUrl : '',\n snippets,\n }\n}\n\n/** 收敛召回范围:只保留合法档位、去重;为空或未提供时回退公开库。 */\nexport function normalizeRecallScopes(value: unknown): ZhihuRecallScope[] {\n if (!Array.isArray(value)) return ['public']\n const scopes = [...new Set(value.filter((scope): scope is ZhihuRecallScope => typeof scope === 'string' && (ZHIHU_RECALL_SCOPES as readonly string[]).includes(scope)))]\n return scopes.length > 0 ? scopes : ['public']\n}\n\nexport async function executeZhihuKnowledgeSearch(\n query: string,\n limit: number,\n recallScopes: ZhihuRecallScope[],\n options: ZhihuClientOptions = {},\n): Promise<ZhihuKnowledgeSearchResult> {\n const trimmed = requireQuery(query, '知识库检索词不能为空。')\n const resolvedLimit = clampInt(limit, ZHIHU_KNOWLEDGE_SEARCH_DEFAULT_LIMIT, ZHIHU_KNOWLEDGE_SEARCH_MAX_LIMIT)\n const body = await zhihuFetchJson(KNOWLEDGE_SEARCH_PATH, {\n method: 'POST',\n body: { Query: trimmed, Limit: resolvedLimit, RecallScopes: recallScopes },\n }, '知乎知识库检索', { ...options, timeoutMs: options.timeoutMs ?? 30_000 })\n const items = objectItems(parseZhihuEnvelope(body)).map(normalizeKnowledgeItem)\n return { version: ZHIHU_KNOWLEDGE_SEARCH_VERSION, query: trimmed, limit: resolvedLimit, recallScopes, items }\n}\n\nexport function renderZhihuKnowledgeSearch(result: ZhihuKnowledgeSearchResult): string {\n if (result.items.length === 0) return `知乎知识库检索「${result.query}」无匹配结果。`\n const lines: string[] = [`知乎知识库检索「${result.query}」共 ${result.items.length} 条:`, '']\n result.items.forEach((item, index) => {\n lines.push(`${index + 1}. ${item.docName}`)\n if (item.originUrl) lines.push(` - 来源:${item.originUrl}`)\n for (const snippet of item.snippets) lines.push(` - ${snippet}`)\n lines.push('')\n })\n return lines.join('\\n').trimEnd()\n}\n\n// ---------------------------------------------------------------------------\n// 工具定义\n// ---------------------------------------------------------------------------\n\n"],"mappings":"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"}
|
package/lib/tools.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Context } from "@deepseek-ai/cordis";
|
|
2
|
+
//#region src/tools.d.ts
|
|
3
|
+
declare const name = "dsh-zhihu-tools";
|
|
4
|
+
declare const inject: readonly ["zhihu", "tools"];
|
|
5
|
+
declare function apply(ctx: Context): void;
|
|
6
|
+
//#endregion
|
|
7
|
+
export { apply, inject, name };
|
|
8
|
+
//# sourceMappingURL=tools.d.ts.map
|