@klarkxy/dsh-zhihu 0.2.9 → 0.2.10

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.
@@ -216,7 +216,7 @@ async function resolveZhihuToken(env = process.env, options = {}) {
216
216
  token: fromCredential,
217
217
  source: "credential"
218
218
  };
219
- throw new ZhihuSearchError("TOKEN_MISSING", "未配置有效的知乎 Access Secret。请在「插件 → 知乎 → 设置」中填写。");
219
+ throw new ZhihuSearchError("TOKEN_MISSING", "未配置有效的知乎 Access Secret。请在「设置 → 知乎」中填写。");
220
220
  }
221
221
  const primary = env[ZHIHU_CREDENTIALS_PRIMARY]?.trim();
222
222
  if (primary && looksLikeToken(primary)) return {
@@ -1 +1 @@
1
- {"version":3,"file":"open-platform.js","names":["delay"],"sources":["../src/call-policy.ts","../src/zhihu-client.ts","../src/search-api.ts","../src/operations.ts","../src/open-platform.ts"],"sourcesContent":["import { mkdir, open, readFile, rename, unlink, rmdir } from 'node:fs/promises'\nimport { homedir } from 'node:os'\nimport { join } from 'node:path'\nimport { randomUUID } from 'node:crypto'\nimport { setTimeout as delay } from 'node:timers/promises'\n\n/** Compatible with zhihu-search/call_policy.py; never contains credentials. */\nexport type ZhihuCallPolicyOptions = { allowPaid?: boolean; dailyCallLimit?: number; directory?: string; lockTimeoutMs?: number }\nexport class ZhihuCallPolicyError extends Error {}\nconst blocked = (message: string): never => { throw new ZhihuCallPolicyError(message) }\nconst GROUPS: Record<string, string> = {\n '/api/v1/content/zhihu_search': 'zhihu_search', '/api/v1/content/global_search': 'global_search',\n '/api/v1/content/hot_list': 'hot_list', '/api/v1/content/question_answers': 'question_discovery',\n '/api/v1/user/contents': 'user_data', '/api/v1/user/followees': 'user_data',\n '/api/v1/user/collections': 'user_data', '/api/v1/user/favlists': 'user_data', '/api/v1/user/favlist_contents': 'user_data',\n '/api/v1/user/content_detail': 'creator', '/api/v1/user/content_comments': 'creator',\n '/api/v1/user/creator_account_stats': 'creator', '/api/v1/user/creator_content_stats': 'creator',\n '/api/v1/knowledge/bases': 'knowledge', '/api/v1/knowledge/files': 'knowledge', '/api/v1/knowledge/search': 'knowledge',\n}\nconst PRICED_PATHS = new Set(Object.keys(GROUPS))\nGROUPS['/api/v1/user/question_recommendations'] = 'question_discovery'\nexport function zhihuQuotaGroup(path: string, body: unknown): string {\n if (path === '/v1/chat/completions' && body && typeof body === 'object' && ['zhida-agent', 'zhida-fast-1p5', 'zhida-thinking-1p5'].includes(String((body as { model?: unknown }).model))) return 'zhida_openai'\n if (/^\\/api\\/v1\\/knowledge\\/bases\\/[^/]+\\/items$/.test(path)) return 'knowledge'\n if (/^\\/api\\/v1\\/(?:pdf-parse|ppt-generation)\\/tasks(?:\\/[^/]+)?$/.test(path)) return 'tools'\n return GROUPS[path] ?? blocked('调用已阻止:该能力的官方额度映射未确认;不能擅自套价。')\n}\nexport function resolveZhihuCallPolicy(env: NodeJS.ProcessEnv = process.env, options: ZhihuCallPolicyOptions = {}) {\n const raw = (env.ZHIHU_ALLOW_PAID ?? 'false').toLowerCase()\n if (options.allowPaid === undefined && !['true', 'false', '1', '0'].includes(raw)) blocked('ZHIHU_ALLOW_PAID 必须是 true/false 或 1/0。')\n const allowPaid = options.allowPaid ?? ['true', '1'].includes(raw)\n const capText = env.ZHIHU_DAILY_CALL_LIMIT ?? '100'\n if (options.dailyCallLimit === undefined && !/^\\d+$/.test(capText)) blocked('每日调用上限必须是 1-100000 的整数。')\n const dailyCallLimit = options.dailyCallLimit ?? Number(capText)\n if (typeof allowPaid !== 'boolean' || !Number.isInteger(dailyCallLimit) || dailyCallLimit < 1 || dailyCallLimit > 100000) blocked('付费开关必须是布尔值,每日调用上限必须是 1-100000 的整数。')\n return { allowPaid, dailyCallLimit }\n}\ntype Ledger = { version: 1; day: string; calls: number; uncertain: Record<string, number>; free: Record<string, number> }\nconst integer = (value: unknown): value is number => typeof value === 'number' && Number.isSafeInteger(value) && value >= 0\nconst day = () => new Date(Date.now() + 8 * 3600_000).toISOString().slice(0, 10)\nconst code = (error: unknown) => (error as NodeJS.ErrnoException)?.code\nasync function readLedger(directory: string): Promise<Ledger> {\n let value: unknown\n try { value = JSON.parse(await readFile(join(directory, 'daily.json'), 'utf8')) }\n catch (error) {\n if (code(error) === 'ENOENT') return { version: 1, day: day(), calls: 0, uncertain: {}, free: {} }\n return blocked('本地调用记录不可读;已阻止请求,请检查 call-policy/daily.json。')\n }\n const row = value as Ledger\n if (!row || row.version !== 1 || typeof row.day !== 'string' || !/^\\d{4}-\\d{2}-\\d{2}$/.test(row.day) || !integer(row.calls) || !row.uncertain || Array.isArray(row.uncertain) || typeof row.uncertain !== 'object' || !Object.values(row.uncertain).every(integer)) blocked('本地调用记录无效;已阻止请求,不能自动清零。')\n if (row.free === undefined) row.free = {} // Backward-compatible version-1 ledger.\n if (!row.free || Array.isArray(row.free) || typeof row.free !== 'object' || !Object.values(row.free).every(integer)) blocked('本地免费预留记录无效;已阻止请求。')\n if (row.day !== day()) { row.day = day(); row.calls = 0; row.free = {} }\n return row\n}\nasync function writeLedger(directory: string, row: Ledger) {\n const temporary = join(directory, `daily-${randomUUID()}.tmp`)\n try {\n const file = await open(temporary, 'wx')\n try { await file.writeFile(JSON.stringify(row), 'utf8'); await file.sync() } finally { await file.close() }\n await rename(temporary, join(directory, 'daily.json'))\n } catch { blocked('本地调用记录保存失败;已阻止请求。') }\n finally { await unlink(temporary).catch(error => { if (code(error) !== 'ENOENT') throw error }) }\n}\nexport async function withZhihuCallPolicy<T>(path: string, body: unknown, quota: (group: string) => Promise<unknown>, execute: () => Promise<T>, options: ZhihuCallPolicyOptions & { env?: NodeJS.ProcessEnv; signal?: AbortSignal } = {}): Promise<T> {\n const group = zhihuQuotaGroup(path, body)\n const env = options.env ?? process.env\n const policy = resolveZhihuCallPolicy(env, options)\n const priced = PRICED_PATHS.has(path) || /^\\/api\\/v1\\/knowledge\\/bases\\/[^/]+\\/items$/.test(path) || (path === '/v1/chat/completions' && body && typeof body === 'object' && (body as { model?: unknown }).model === 'zhida-agent')\n if (policy.allowPaid && !priced) blocked('付费调用已阻止:该能力的官方计费未确认;请使用免费保护模式。')\n const directory = options.directory ?? env.ZHIHU_CALL_POLICY_DIR ?? join(env.ZHIHU_SEARCH_HOME ?? join(homedir(), '.config', 'zhihu-search'), 'call-policy')\n const lock = join(directory, 'request.lock')\n let acquired = false\n const deadline = Date.now() + (options.lockTimeoutMs ?? 30_000)\n try {\n options.signal?.throwIfAborted()\n await mkdir(directory, { recursive: true })\n while (!acquired) {\n options.signal?.throwIfAborted()\n try { await mkdir(lock); acquired = true }\n catch (error) {\n if (code(error) !== 'EEXIST') throw error\n if (Date.now() >= deadline) blocked('本地保护锁忙或遗留;已阻止请求。确认两个客户端均无在途请求后再移除 call-policy/request.lock。')\n await delay(50, undefined, { signal: options.signal })\n }\n }\n options.signal?.throwIfAborted()\n const row = await readLedger(directory)\n if (row.calls >= policy.dailyCallLimit) blocked('已达到本地北京时间每日业务调用上限;额度查询仍可使用。')\n if (!policy.allowPaid) {\n let rows: unknown\n try { rows = await quota(group) }\n catch { options.signal?.throwIfAborted(); blocked('免费额度预查失败;未发送业务请求,请稍后手动查询官方额度。') }\n options.signal?.throwIfAborted()\n const matches = Array.isArray(rows) ? rows.filter(value => value && typeof value === 'object' && value.APIID === group) : []\n if (matches.length !== 1 || !integer(matches[0].RemainingQuota)) blocked('官方未返回有效的免费剩余额度;未发送业务请求。')\n const available = Math.min(matches[0].RemainingQuota - (row.uncertain[group] ?? 0), row.free[group] ?? matches[0].RemainingQuota)\n if (available <= 0) blocked('免费额度不足,或有结果未知的已发送请求;未发送业务请求。')\n row.free[group] = available - 1\n } else if (row.free[group] !== undefined) row.free[group] = Math.max(0, row.free[group] - 1)\n options.signal?.throwIfAborted()\n // Count reservations before dispatch; cancellation/unknown results are not refunded.\n row.calls++\n row.uncertain[group] = (row.uncertain[group] ?? 0) + 1\n await writeLedger(directory, row)\n options.signal?.throwIfAborted()\n const result = await execute()\n row.uncertain[group]--\n await writeLedger(directory, row)\n return result\n } catch (error) {\n options.signal?.throwIfAborted()\n if (error instanceof ZhihuCallPolicyError) throw error\n if (['EACCES', 'EPERM', 'ENOSPC', 'EIO', 'EROFS', 'ENOENT', 'ENOTDIR'].includes(String(code(error)))) blocked('本地保护存储不可用;已阻止业务请求。')\n throw error\n } finally { if (acquired) await rmdir(lock) }\n}\n","/**\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 * - 提供 credential 解析器时只使用它,缺失或无效时抛出 TOKEN_MISSING。\n * 宿主通过 `ctx.credentials.resolve` 统一管理凭据来源和清除操作。\n * - 未提供解析器的独立客户端依次兼容 ZHIHU_ACCESS_TOKEN、\n * ZHIHU_ACCESS_SECRET 和 ~/.config/zhihu-search/credentials.json。\n */\nimport { readFile } from 'node:fs/promises'\nimport { homedir } from 'node:os'\nimport { join } from 'node:path'\nimport { withZhihuCallPolicy, ZhihuCallPolicyError, type ZhihuCallPolicyOptions } from './call-policy.ts'\nimport type { ZhihuQuotaId } from './contracts.ts'\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 headers?: { get(name: string): string | null }\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 /** Authoritative Access Secret resolver; absence/invalid values never fall back to env or CLI files. */\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 value = await options.resolveCredential()\n const fromCredential = typeof value === 'string' ? value.trim() : undefined\n if (fromCredential && looksLikeToken(fromCredential)) {\n return { token: fromCredential, source: 'credential' }\n }\n throw new ZhihuSearchError('TOKEN_MISSING', '未配置有效的知乎 Access Secret。请在「插件 → 知乎 → 设置」中填写。')\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 type ZhihuErrorDetails = {\n upstreamCode?: number\n retryAfter?: number\n retryable?: boolean\n}\n\nexport class ZhihuSearchError extends Error {\n readonly code: 'TOKEN_MISSING' | 'HTTP_ERROR' | 'TIMEOUT' | 'BAD_RESPONSE'\n | 'CALL_BLOCKED' | 'INVALID_ARGUMENTS' | 'TOKEN_INVALID' | 'RATE_LIMITED' | 'UPSTREAM_UNAVAILABLE' | 'CANCELLED'\n readonly status?: number\n readonly upstreamCode?: number\n readonly retryAfter?: number\n readonly retryable?: boolean\n constructor(code: ZhihuSearchError['code'], message: string, status?: number, details: ZhihuErrorDetails = {}) {\n super(message)\n this.name = 'ZhihuSearchError'\n this.code = code\n if (status !== undefined) this.status = status\n if (details.upstreamCode !== undefined) this.upstreamCode = details.upstreamCode\n if (details.retryAfter !== undefined) this.retryAfter = details.retryAfter\n if (details.retryable !== undefined) this.retryable = details.retryable\n }\n}\n\n/** Metering payload emitted after each zhihu tool execution. */\nexport type ZhihuSearchExecuted = { ok: boolean; results: number; quotaId?: ZhihuQuotaId }\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 callPolicy?: ZhihuCallPolicyOptions\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/** Parse Retry-After seconds or an HTTP date; never invent a quota reset time. */\nexport function parseZhihuRetryAfter(value: string | null | undefined, now = Date.now()): number | undefined {\n if (!value?.trim()) return undefined\n const text = value.trim()\n if (/^\\d+(?:\\.\\d+)?$/.test(text)) {\n const seconds = Number(text)\n return Number.isFinite(seconds) ? Math.ceil(seconds) : undefined\n }\n // Do not let Date.parse interpret negative numbers as calendar dates.\n if (!/[a-z]/i.test(text)) return undefined\n const date = Date.parse(text)\n return Number.isFinite(date) ? Math.max(0, Math.ceil((date - now) / 1000)) : undefined\n}\n\n/**\n * Shared authenticated request. envelope=true opts into official error semantics\n * and returns Data. Legacy callers still receive the full JSON / chat response.\n * The timeout and cancellation cover BOTH headers and response-body consumption.\n * No requests are retried, including 429, exhausted quota, or risk-control denial.\n */\nexport async function zhihuFetchJson(\n path: string,\n init: { method?: 'GET' | 'POST'; params?: Record<string, string>; body?: unknown; formData?: FormData; envelope?: boolean },\n label: string,\n options: ZhihuClientOptions = {},\n): Promise<unknown> {\n const linked = options.signal\n if (linked?.aborted) throw new ZhihuSearchError('CANCELLED', '请求已取消')\n const url = new URL(path, ZHIHU_BASE_URL)\n if (url.origin !== ZHIHU_BASE_URL || url.username || url.password) {\n throw new ZhihuSearchError('INVALID_ARGUMENTS', '只允许请求知乎开放平台。')\n }\n for (const [key, value] of Object.entries(init.params ?? {})) url.searchParams.set(key, value)\n const token = await resolveZhihuToken(options.env, { resolveCredential: options.resolveCredential })\n if (linked?.aborted) throw new ZhihuSearchError('CANCELLED', '请求已取消')\n const fetcher = options.fetcher ?? globalFetch\n if (!fetcher) throw new ZhihuSearchError('BAD_RESPONSE', '当前环境没有可用的 fetch。')\n const method = init.method ?? (init.formData || init.body !== undefined ? 'POST' : 'GET')\n const timeout = options.timeoutMs !== undefined && Number.isFinite(options.timeoutMs) && options.timeoutMs > 0\n ? options.timeoutMs : ZHIHU_DEFAULT_TIMEOUT_MS\n const controller = new AbortController()\n const onAbort = () => controller.abort(linked?.reason)\n if (linked?.aborted) onAbort()\n else linked?.addEventListener('abort', onAbort, { once: true })\n const timer = setTimeout(() => controller.abort(new Error('zhihu request timeout')), timeout)\n let stopWaiting = () => {}\n const aborted = new Promise<never>((_resolve, reject) => {\n const rejectAbort = () => reject(controller.signal.reason)\n if (controller.signal.aborted) rejectAbort()\n else {\n controller.signal.addEventListener('abort', rejectAbort, { once: true })\n stopWaiting = () => controller.signal.removeEventListener('abort', rejectAbort)\n }\n })\n try {\n const request = async () => {\n controller.signal.throwIfAborted()\n const 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 // Let fetch supply the multipart boundary.\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 const retryAfter = parseZhihuRetryAfter(response.headers?.get('Retry-After'))\n const httpError = () => {\n const code = response.status === 429 ? 'RATE_LIMITED'\n : init.envelope && (response.status === 401 || response.status === 403) ? 'TOKEN_INVALID'\n : 'HTTP_ERROR'\n return new ZhihuSearchError(code, `${label}返回 HTTP ${response.status}。`, response.status,\n code === 'RATE_LIMITED' ? { retryAfter, retryable: true } : {})\n }\n let body: unknown\n try { body = await response.json() }\n catch (error) {\n if (controller.signal.aborted) throw error\n if (!response.ok) throw httpError()\n throw new ZhihuSearchError('BAD_RESPONSE', `${label}响应不是合法 JSON。`)\n }\n controller.signal.throwIfAborted()\n // A business error remains meaningful even when the HTTP status is 400.\n const isEnvelope = body !== null && typeof body === 'object' && !Array.isArray(body) && 'Code' in body\n if (!response.ok && !isEnvelope) throw httpError()\n if (init.envelope || (isEnvelope && (body as { Code: unknown }).Code !== 0 && (body as { Code: unknown }).Code !== '0')) {\n const data = parseZhihuEnvelope(body, { status: response.status, retryAfter, officialErrors: init.envelope })\n if (!response.ok) throw httpError()\n if (init.envelope) return data\n }\n if (!response.ok) throw httpError()\n return body\n }\n const quota = async (apiId: string) => {\n const quotaUrl = new URL('/api/v1/quota', ZHIHU_BASE_URL)\n quotaUrl.searchParams.set('APIIDs', apiId)\n const response = await fetcher(quotaUrl.toString(), {\n method: 'GET', signal: controller.signal,\n headers: { Authorization: `Bearer ${token.token}`, 'X-Request-Timestamp': String(Math.floor(Date.now() / 1000)), Accept: 'application/json' },\n })\n const body = await response.json()\n const data = parseZhihuEnvelope(body, { status: response.status, officialErrors: true })\n if (!response.ok) throw new Error('官方额度查询失败')\n return data\n }\n const guarded = path === '/api/v1/quota' && method === 'GET' ? Promise.race([request(), aborted]) : withZhihuCallPolicy(\n path, init.body, apiId => Promise.race([quota(apiId), aborted]),\n () => Promise.race([request(), aborted]),\n { ...options.callPolicy, env: options.env, signal: controller.signal },\n )\n return await guarded\n } catch (error) {\n if (linked?.aborted) throw new ZhihuSearchError('CANCELLED', '请求已取消')\n if (controller.signal.aborted || isAbortError(error)) {\n throw new ZhihuSearchError('TIMEOUT', `${label}超时(${Math.round(timeout / 1000)} 秒),请稍后再试。`)\n }\n if (error instanceof ZhihuCallPolicyError) throw new ZhihuSearchError('CALL_BLOCKED', error.message, undefined, { retryable: false })\n if (error instanceof ZhihuSearchError) {\n error.message = error.message.replaceAll(token.token, '[REDACTED]')\n throw error\n }\n const message = (error instanceof Error ? error.message : String(error)).replaceAll(token.token, '[REDACTED]')\n throw new ZhihuSearchError('BAD_RESPONSE', `${label}失败:${message}`)\n } finally {\n clearTimeout(timer)\n stopWaiting()\n linked?.removeEventListener('abort', onAbort)\n }\n}\n\nconst ZHIHU_BUSINESS_ERRORS: Record<number, ZhihuSearchError['code']> = {\n 10001: 'INVALID_ARGUMENTS', 20001: 'TOKEN_INVALID',\n 30001: 'RATE_LIMITED', 30002: 'RATE_LIMITED',\n 40001: 'INVALID_ARGUMENTS', 40002: 'INVALID_ARGUMENTS', 40003: 'RATE_LIMITED',\n 40004: 'INVALID_ARGUMENTS', 40005: 'INVALID_ARGUMENTS', 40006: 'INVALID_ARGUMENTS',\n 50002: 'UPSTREAM_UNAVAILABLE', 90001: 'UPSTREAM_UNAVAILABLE',\n}\n\n/** Parse the official envelope without coercing null/false/empty Code into success. */\nexport function parseZhihuEnvelope(body: unknown, meta: { status?: number; retryAfter?: number; officialErrors?: boolean } = {}): unknown {\n if (!body || typeof body !== 'object' || Array.isArray(body)) {\n throw new ZhihuSearchError('BAD_RESPONSE', '知乎响应缺少 {Code,Message,Data} 信封。', meta.status)\n }\n const envelope = body as { Code?: unknown; Message?: unknown; Data?: unknown }\n const rawCode = envelope.Code\n if (!((typeof rawCode === 'number' && Number.isSafeInteger(rawCode))\n || (typeof rawCode === 'string' && /^\\d+$/.test(rawCode) && Number.isSafeInteger(Number(rawCode))))) {\n throw new ZhihuSearchError('BAD_RESPONSE', '知乎响应缺少有效的 Code 字段。', meta.status)\n }\n const code = Number(rawCode)\n if (code !== 0) {\n const message = typeof envelope.Message === 'string' && envelope.Message ? envelope.Message : '未知错误'\n if (code === 30003) {\n throw new ZhihuSearchError('UPSTREAM_UNAVAILABLE', `知乎开放平台被风控拒绝:${message}。请不要立即重试。`,\n meta.status, { upstreamCode: code, retryable: false })\n }\n const kind = ZHIHU_BUSINESS_ERRORS[code] ?? (meta.officialErrors ? 'UPSTREAM_UNAVAILABLE' : 'HTTP_ERROR')\n throw new ZhihuSearchError(kind, `知乎开放平台返回错误(code=${code}):${message}`, meta.status,\n { upstreamCode: code, ...(kind === 'RATE_LIMITED' ? { retryAfter: meta.retryAfter, retryable: true } : {}) })\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 {\n normalizeSearchItem,\n parseZhihuEnvelope,\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 !== undefined ? { 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 normalizeSearchItem,\n parseZhihuEnvelope,\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 ...(typeof data.EmptyReason === 'string' ? { emptyReason: data.EmptyReason } : {}),\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","/**\n * Contract source: klarkxy/zhihu-search@62fd3ce5, upstream/http_client.py.\n * Six question/creator APIs use the current Access Secret only, never OAuth.\n * This module has no DSH/UI dependencies; RPC and tools share these validators.\n */\nimport { zhihuFetchJson, ZhihuSearchError, type ZhihuClientOptions } from './zhihu-client.ts'\nimport { ZHIHU_QUOTA_IDS, normalizeZhihuQuotaId, type ZhihuQuotaId } from './contracts.ts'\n\nexport const ZHIHU_OPEN_PLATFORM_APIS = {\n 'question.recommendations': { path: '/api/v1/user/question_recommendations', label: '适合回答的问题', quotaId: 'question_discovery' },\n 'question.answers': { path: '/api/v1/content/question_answers', label: '问题回答摘要', quotaId: 'question_discovery' },\n 'content.detail': { path: '/api/v1/user/content_detail', label: '本人创作全文', quotaId: 'creator' },\n 'content.comments': { path: '/api/v1/user/content_comments', label: '本人创作评论', quotaId: 'creator' },\n 'creator.account.stats': { path: '/api/v1/user/creator_account_stats', label: '账号创作数据', quotaId: 'creator' },\n 'creator.content.stats': { path: '/api/v1/user/creator_content_stats', label: '单篇创作数据', quotaId: 'creator' },\n quota: { path: '/api/v1/quota', label: '官方每日额度', quotaId: undefined },\n} as const\nexport type ZhihuOpenPlatformOperation = keyof typeof ZHIHU_OPEN_PLATFORM_APIS\n\nconst ENDPOINT_ALIASES: Record<string, ZhihuOpenPlatformOperation> = {\n question_recommendations: 'question.recommendations', question_answers: 'question.answers',\n content_detail: 'content.detail', user_content_detail: 'content.detail', 'user.content.detail': 'content.detail',\n content_comments: 'content.comments', user_content_comments: 'content.comments', 'user.content.comments': 'content.comments',\n creator_account_stats: 'creator.account.stats', creator_content_stats: 'creator.content.stats',\n}\nexport function resolveZhihuOpenPlatformOperation(endpoint: string): ZhihuOpenPlatformOperation | undefined {\n if (Object.hasOwn(ZHIHU_OPEN_PLATFORM_APIS, endpoint)) return endpoint as ZhihuOpenPlatformOperation\n return Object.hasOwn(ENDPOINT_ALIASES, endpoint) ? ENDPOINT_ALIASES[endpoint] : undefined\n}\n\nexport type ZhihuPaging = {\n canContinue: boolean\n isEnd?: boolean\n /** Decimal Int64 string. Never synthesize this from the number of items. */\n nextOffset?: string\n warning?: string\n}\nexport type ZhihuJsonValue = string | number | boolean | null | ZhihuJsonValue[] | { [key: string]: ZhihuJsonValue }\nexport type ZhihuOpenPlatformResult = {\n version: 1\n operation: ZhihuOpenPlatformOperation\n quotaId?: ZhihuQuotaId\n /** Original Data fields. Quota's top-level array is wrapped in { Items }. */\n data: Record<string, ZhihuJsonValue>\n paging?: ZhihuPaging\n}\n\nconst record = (value: unknown): value is Record<string, unknown> => value !== null && typeof value === 'object' && !Array.isArray(value)\n// Keep the free-form tool result compatible with the SDK's JSON value contract.\nfunction jsonValue(value: unknown): value is ZhihuJsonValue {\n if (value === null || typeof value === 'string' || typeof value === 'boolean') return true\n if (typeof value === 'number') return Number.isFinite(value)\n if (Array.isArray(value)) return value.every(jsonValue)\n return record(value) && Object.values(value).every(jsonValue)\n}\nfunction jsonRecord(value: unknown): value is Record<string, ZhihuJsonValue> {\n return record(value) && Object.values(value).every(jsonValue)\n}\nfunction invalid(message: string): never { throw new ZhihuSearchError('INVALID_ARGUMENTS', message) }\n\nfunction inputObject(input: unknown): Record<string, unknown> {\n if (!record(input)) return invalid('参数必须是对象。')\n // Reject misleading identity overrides, even over raw RPC. Credentials belong to the host.\n for (const key of ['token', 'accessSecret', 'access_secret', 'accessToken', 'access_token', 'oauthToken', 'oauth_token', 'useConfiguredOauthUser', 'use_configured_oauth_user']) {\n if (Object.hasOwn(input, key)) invalid('这些接口仅使用当前 Access Secret,不接受 token 参数或 OAuth 身份切换。')\n }\n return { ...input }\n}\nfunction limit(value: unknown, fallback: number, maximum: number, name = 'limit'): string {\n const count = value === undefined ? fallback : value\n if (typeof count !== 'number' || !Number.isInteger(count) || count < 1 || count > maximum) {\n return invalid(`${name} 必须是 1-${maximum} 的整数。`)\n }\n return String(count)\n}\n\n/** Accept all nonnegative Int64 values as strings, but never accept an unsafe JS number. */\nexport function normalizeZhihuOffset(value: unknown): string {\n let decimal: string\n if (typeof value === 'number' && Number.isSafeInteger(value) && value >= 0) decimal = String(value)\n else if (typeof value === 'string' && /^\\d+$/.test(value)) decimal = value.replace(/^0+(?=\\d)/, '')\n else return invalid('offset 必须是非负 Int64 整数;超过 JavaScript 安全整数范围时请用十进制字符串,不能使用不透明游标。')\n if (decimal.length > 19 || BigInt(decimal) > 9223372036854775807n) return invalid('offset 超出 Int64 范围。')\n return decimal\n}\n\nfunction zhihuUrl(value: unknown, question: boolean): string {\n if (typeof value !== 'string' || !value.trim()) return invalid(question ? 'questionUrl 不能为空。' : 'contentUrl 不能为空。')\n const cleaned = value.trim()\n if (/[\\u0000-\\u0020\\u007f\\\\]/.test(cleaned)) return invalid('知乎链接不能包含空白、控制字符或反斜线。')\n let url: URL\n try { url = new URL(cleaned) } catch { return invalid('不是有效的知乎 URL。') }\n if (url.protocol !== 'https:' || url.username || url.password || url.port) return invalid('必须使用无账号密码、无非标准端口的 HTTPS 知乎链接。')\n const valid = question\n ? url.hostname === 'www.zhihu.com' && /^\\/question\\/\\d+\\/?$/.test(url.pathname)\n : (url.hostname === 'www.zhihu.com' && /^\\/(?:answer\\/\\d+|question\\/\\d+\\/answer\\/\\d+|pin\\/\\d+|zvideo\\/\\d+)\\/?$/.test(url.pathname))\n || (url.hostname === 'zhuanlan.zhihu.com' && /^\\/p\\/\\d+\\/?$/.test(url.pathname))\n if (!valid) return invalid(question ? 'questionUrl 仅支持 https://www.zhihu.com/question/{id}。' : 'contentUrl 仅支持知乎回答、专栏文章、想法或视频链接。')\n return cleaned\n}\n\nfunction dates(input: Record<string, unknown>, params: Record<string, string>): void {\n const { startDate, endDate } = input\n if (startDate === undefined && endDate === undefined) return\n if (startDate === undefined || endDate === undefined) invalid('startDate 与 endDate 必须同时提供或同时省略。')\n for (const [key, value] of [['StartDate', startDate], ['EndDate', endDate]] as const) {\n if (typeof value !== 'string' || !/^\\d{4}-\\d{2}-\\d{2}$/.test(value) || value.slice(0, 4) === '0000') invalid(`${key} 必须是有效的 YYYY-MM-DD 公历日期。`)\n const time = new Date(`${value}T00:00:00Z`)\n if (!Number.isFinite(time.getTime()) || time.toISOString().slice(0, 10) !== value) invalid(`${key} 不是有效公历日期。`)\n params[key] = value\n }\n if (params.EndDate! < params.StartDate!) invalid('endDate 不得早于 startDate。')\n}\n\nfunction paramsFor(operation: ZhihuOpenPlatformOperation, input: Record<string, unknown>): Record<string, string> {\n const params: Record<string, string> = {}\n if (operation === 'question.recommendations') {\n // Explicit null/blank is not the same as omitting Query.\n if (input.query !== undefined) {\n if (typeof input.query !== 'string' || !input.query.trim()) invalid('主题关键词不能为空;省略 query 才会按画像推荐。')\n params.Query = input.query.trim()\n }\n params.Count = limit(input.count, 5, 20, 'count')\n } else if (operation === 'question.answers') {\n params.QuestionUrl = zhihuUrl(input.questionUrl, true)\n } else if (operation === 'content.detail' || operation === 'content.comments' || operation === 'creator.content.stats') {\n params.ContentUrl = zhihuUrl(input.contentUrl, false)\n } else if (operation === 'creator.account.stats') {\n const type = input.contentType === undefined ? 'all' : input.contentType\n if (typeof type !== 'string' || !['all', 'answer', 'article', 'pin', 'zvideo'].includes(type)) invalid('contentType 必须是 all、answer、article、pin 或 zvideo。')\n params.ContentType = type\n } else if (operation === 'quota' && input.apiIds !== undefined) {\n if (Array.isArray(input.apiIds)) input.apiIds = input.apiIds.map(normalizeZhihuQuotaId)\n if (!Array.isArray(input.apiIds) || input.apiIds.some(id => typeof id !== 'string' || !(ZHIHU_QUOTA_IDS as readonly string[]).includes(id))) invalid('apiIds 包含未知官方额度项。')\n if (input.apiIds.length) params.APIIDs = [...new Set(input.apiIds)].join(',')\n }\n if (operation === 'question.answers' || operation === 'content.comments') {\n params.Offset = normalizeZhihuOffset(input.offset === undefined ? 0 : input.offset)\n params.Limit = limit(input.limit, 20, 50)\n }\n if (operation === 'content.comments') {\n const order = input.order === undefined ? 'score' : input.order\n if (typeof order !== 'string' || !['score', 'reverse', 'ascending'].includes(order)) invalid('order 必须是 score、reverse 或 ascending。')\n params.Order = order\n }\n if (operation === 'creator.account.stats' || operation === 'creator.content.stats') dates(input, params)\n return params\n}\n\nexport function normalizeZhihuPaging(data: Record<string, unknown>, offset: string): ZhihuPaging {\n const paging = data.Paging\n if (!record(paging) || typeof paging.IsEnd !== 'boolean') {\n return { canContinue: false, warning: '分页信息不完整:缺少有效的 Paging.IsEnd,请停止翻页。' }\n }\n if (paging.IsEnd) return { isEnd: true, canContinue: false }\n try {\n const nextOffset = normalizeZhihuOffset(paging.NextOffset)\n if (BigInt(nextOffset) <= BigInt(offset)) throw new Error('non-advancing offset')\n return { isEnd: false, canContinue: true, nextOffset }\n } catch {\n return { isEnd: false, canContinue: false, warning: '分页信息不完整:NextOffset 缺失、无效或未前进,请停止翻页;不要按条数计算偏移。' }\n }\n}\n\nexport async function executeZhihuOpenPlatform(\n operation: ZhihuOpenPlatformOperation,\n input: unknown = {},\n options: ZhihuClientOptions = {},\n): Promise<ZhihuOpenPlatformResult> {\n const args = inputObject(input)\n const params = paramsFor(operation, args)\n const api = ZHIHU_OPEN_PLATFORM_APIS[operation]\n const raw = await zhihuFetchJson(api.path, { params, envelope: true }, api.label, options)\n const data = operation === 'quota' && Array.isArray(raw) ? { Items: raw } : raw\n if (!jsonRecord(data)) throw new ZhihuSearchError('BAD_RESPONSE', `${api.label}的 Data 必须是 JSON 对象。`)\n return {\n version: 1, operation, data,\n ...(api.quotaId ? { quotaId: api.quotaId } : {}),\n ...(operation === 'question.answers' || operation === 'content.comments'\n ? { paging: normalizeZhihuPaging(data, params.Offset!) } : {}),\n }\n}\n\nexport function zhihuOpenPlatformResultCount(result: ZhihuOpenPlatformResult): number {\n if (result.operation === 'quota') return 0\n if (result.operation === 'content.detail') return typeof result.data.Body === 'string' && result.data.Body.trim() ? 1 : 0\n if (result.operation === 'creator.account.stats') return Object.keys(result.data).length ? 1 : 0\n return Array.isArray(result.data.Items) ? result.data.Items.filter(record).length : 0\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"open-platform.js","names":["delay"],"sources":["../src/call-policy.ts","../src/zhihu-client.ts","../src/search-api.ts","../src/operations.ts","../src/open-platform.ts"],"sourcesContent":["import { mkdir, open, readFile, rename, unlink, rmdir } from 'node:fs/promises'\nimport { homedir } from 'node:os'\nimport { join } from 'node:path'\nimport { randomUUID } from 'node:crypto'\nimport { setTimeout as delay } from 'node:timers/promises'\n\n/** Compatible with zhihu-search/call_policy.py; never contains credentials. */\nexport type ZhihuCallPolicyOptions = { allowPaid?: boolean; dailyCallLimit?: number; directory?: string; lockTimeoutMs?: number }\nexport class ZhihuCallPolicyError extends Error {}\nconst blocked = (message: string): never => { throw new ZhihuCallPolicyError(message) }\nconst GROUPS: Record<string, string> = {\n '/api/v1/content/zhihu_search': 'zhihu_search', '/api/v1/content/global_search': 'global_search',\n '/api/v1/content/hot_list': 'hot_list', '/api/v1/content/question_answers': 'question_discovery',\n '/api/v1/user/contents': 'user_data', '/api/v1/user/followees': 'user_data',\n '/api/v1/user/collections': 'user_data', '/api/v1/user/favlists': 'user_data', '/api/v1/user/favlist_contents': 'user_data',\n '/api/v1/user/content_detail': 'creator', '/api/v1/user/content_comments': 'creator',\n '/api/v1/user/creator_account_stats': 'creator', '/api/v1/user/creator_content_stats': 'creator',\n '/api/v1/knowledge/bases': 'knowledge', '/api/v1/knowledge/files': 'knowledge', '/api/v1/knowledge/search': 'knowledge',\n}\nconst PRICED_PATHS = new Set(Object.keys(GROUPS))\nGROUPS['/api/v1/user/question_recommendations'] = 'question_discovery'\nexport function zhihuQuotaGroup(path: string, body: unknown): string {\n if (path === '/v1/chat/completions' && body && typeof body === 'object' && ['zhida-agent', 'zhida-fast-1p5', 'zhida-thinking-1p5'].includes(String((body as { model?: unknown }).model))) return 'zhida_openai'\n if (/^\\/api\\/v1\\/knowledge\\/bases\\/[^/]+\\/items$/.test(path)) return 'knowledge'\n if (/^\\/api\\/v1\\/(?:pdf-parse|ppt-generation)\\/tasks(?:\\/[^/]+)?$/.test(path)) return 'tools'\n return GROUPS[path] ?? blocked('调用已阻止:该能力的官方额度映射未确认;不能擅自套价。')\n}\nexport function resolveZhihuCallPolicy(env: NodeJS.ProcessEnv = process.env, options: ZhihuCallPolicyOptions = {}) {\n const raw = (env.ZHIHU_ALLOW_PAID ?? 'false').toLowerCase()\n if (options.allowPaid === undefined && !['true', 'false', '1', '0'].includes(raw)) blocked('ZHIHU_ALLOW_PAID 必须是 true/false 或 1/0。')\n const allowPaid = options.allowPaid ?? ['true', '1'].includes(raw)\n const capText = env.ZHIHU_DAILY_CALL_LIMIT ?? '100'\n if (options.dailyCallLimit === undefined && !/^\\d+$/.test(capText)) blocked('每日调用上限必须是 1-100000 的整数。')\n const dailyCallLimit = options.dailyCallLimit ?? Number(capText)\n if (typeof allowPaid !== 'boolean' || !Number.isInteger(dailyCallLimit) || dailyCallLimit < 1 || dailyCallLimit > 100000) blocked('付费开关必须是布尔值,每日调用上限必须是 1-100000 的整数。')\n return { allowPaid, dailyCallLimit }\n}\ntype Ledger = { version: 1; day: string; calls: number; uncertain: Record<string, number>; free: Record<string, number> }\nconst integer = (value: unknown): value is number => typeof value === 'number' && Number.isSafeInteger(value) && value >= 0\nconst day = () => new Date(Date.now() + 8 * 3600_000).toISOString().slice(0, 10)\nconst code = (error: unknown) => (error as NodeJS.ErrnoException)?.code\nasync function readLedger(directory: string): Promise<Ledger> {\n let value: unknown\n try { value = JSON.parse(await readFile(join(directory, 'daily.json'), 'utf8')) }\n catch (error) {\n if (code(error) === 'ENOENT') return { version: 1, day: day(), calls: 0, uncertain: {}, free: {} }\n return blocked('本地调用记录不可读;已阻止请求,请检查 call-policy/daily.json。')\n }\n const row = value as Ledger\n if (!row || row.version !== 1 || typeof row.day !== 'string' || !/^\\d{4}-\\d{2}-\\d{2}$/.test(row.day) || !integer(row.calls) || !row.uncertain || Array.isArray(row.uncertain) || typeof row.uncertain !== 'object' || !Object.values(row.uncertain).every(integer)) blocked('本地调用记录无效;已阻止请求,不能自动清零。')\n if (row.free === undefined) row.free = {} // Backward-compatible version-1 ledger.\n if (!row.free || Array.isArray(row.free) || typeof row.free !== 'object' || !Object.values(row.free).every(integer)) blocked('本地免费预留记录无效;已阻止请求。')\n if (row.day !== day()) { row.day = day(); row.calls = 0; row.free = {} }\n return row\n}\nasync function writeLedger(directory: string, row: Ledger) {\n const temporary = join(directory, `daily-${randomUUID()}.tmp`)\n try {\n const file = await open(temporary, 'wx')\n try { await file.writeFile(JSON.stringify(row), 'utf8'); await file.sync() } finally { await file.close() }\n await rename(temporary, join(directory, 'daily.json'))\n } catch { blocked('本地调用记录保存失败;已阻止请求。') }\n finally { await unlink(temporary).catch(error => { if (code(error) !== 'ENOENT') throw error }) }\n}\nexport async function withZhihuCallPolicy<T>(path: string, body: unknown, quota: (group: string) => Promise<unknown>, execute: () => Promise<T>, options: ZhihuCallPolicyOptions & { env?: NodeJS.ProcessEnv; signal?: AbortSignal } = {}): Promise<T> {\n const group = zhihuQuotaGroup(path, body)\n const env = options.env ?? process.env\n const policy = resolveZhihuCallPolicy(env, options)\n const priced = PRICED_PATHS.has(path) || /^\\/api\\/v1\\/knowledge\\/bases\\/[^/]+\\/items$/.test(path) || (path === '/v1/chat/completions' && body && typeof body === 'object' && (body as { model?: unknown }).model === 'zhida-agent')\n if (policy.allowPaid && !priced) blocked('付费调用已阻止:该能力的官方计费未确认;请使用免费保护模式。')\n const directory = options.directory ?? env.ZHIHU_CALL_POLICY_DIR ?? join(env.ZHIHU_SEARCH_HOME ?? join(homedir(), '.config', 'zhihu-search'), 'call-policy')\n const lock = join(directory, 'request.lock')\n let acquired = false\n const deadline = Date.now() + (options.lockTimeoutMs ?? 30_000)\n try {\n options.signal?.throwIfAborted()\n await mkdir(directory, { recursive: true })\n while (!acquired) {\n options.signal?.throwIfAborted()\n try { await mkdir(lock); acquired = true }\n catch (error) {\n if (code(error) !== 'EEXIST') throw error\n if (Date.now() >= deadline) blocked('本地保护锁忙或遗留;已阻止请求。确认两个客户端均无在途请求后再移除 call-policy/request.lock。')\n await delay(50, undefined, { signal: options.signal })\n }\n }\n options.signal?.throwIfAborted()\n const row = await readLedger(directory)\n if (row.calls >= policy.dailyCallLimit) blocked('已达到本地北京时间每日业务调用上限;额度查询仍可使用。')\n if (!policy.allowPaid) {\n let rows: unknown\n try { rows = await quota(group) }\n catch { options.signal?.throwIfAborted(); blocked('免费额度预查失败;未发送业务请求,请稍后手动查询官方额度。') }\n options.signal?.throwIfAborted()\n const matches = Array.isArray(rows) ? rows.filter(value => value && typeof value === 'object' && value.APIID === group) : []\n if (matches.length !== 1 || !integer(matches[0].RemainingQuota)) blocked('官方未返回有效的免费剩余额度;未发送业务请求。')\n const available = Math.min(matches[0].RemainingQuota - (row.uncertain[group] ?? 0), row.free[group] ?? matches[0].RemainingQuota)\n if (available <= 0) blocked('免费额度不足,或有结果未知的已发送请求;未发送业务请求。')\n row.free[group] = available - 1\n } else if (row.free[group] !== undefined) row.free[group] = Math.max(0, row.free[group] - 1)\n options.signal?.throwIfAborted()\n // Count reservations before dispatch; cancellation/unknown results are not refunded.\n row.calls++\n row.uncertain[group] = (row.uncertain[group] ?? 0) + 1\n await writeLedger(directory, row)\n options.signal?.throwIfAborted()\n const result = await execute()\n row.uncertain[group]--\n await writeLedger(directory, row)\n return result\n } catch (error) {\n options.signal?.throwIfAborted()\n if (error instanceof ZhihuCallPolicyError) throw error\n if (['EACCES', 'EPERM', 'ENOSPC', 'EIO', 'EROFS', 'ENOENT', 'ENOTDIR'].includes(String(code(error)))) blocked('本地保护存储不可用;已阻止业务请求。')\n throw error\n } finally { if (acquired) await rmdir(lock) }\n}\n","/**\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 * - 提供 credential 解析器时只使用它,缺失或无效时抛出 TOKEN_MISSING。\n * 宿主通过 `ctx.credentials.resolve` 统一管理凭据来源和清除操作。\n * - 未提供解析器的独立客户端依次兼容 ZHIHU_ACCESS_TOKEN、\n * ZHIHU_ACCESS_SECRET 和 ~/.config/zhihu-search/credentials.json。\n */\nimport { readFile } from 'node:fs/promises'\nimport { homedir } from 'node:os'\nimport { join } from 'node:path'\nimport { withZhihuCallPolicy, ZhihuCallPolicyError, type ZhihuCallPolicyOptions } from './call-policy.ts'\nimport type { ZhihuQuotaId } from './contracts.ts'\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 headers?: { get(name: string): string | null }\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 /** Authoritative Access Secret resolver; absence/invalid values never fall back to env or CLI files. */\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 value = await options.resolveCredential()\n const fromCredential = typeof value === 'string' ? value.trim() : undefined\n if (fromCredential && looksLikeToken(fromCredential)) {\n return { token: fromCredential, source: 'credential' }\n }\n throw new ZhihuSearchError('TOKEN_MISSING', '未配置有效的知乎 Access Secret。请在「设置 → 知乎」中填写。')\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 type ZhihuErrorDetails = {\n upstreamCode?: number\n retryAfter?: number\n retryable?: boolean\n}\n\nexport class ZhihuSearchError extends Error {\n readonly code: 'TOKEN_MISSING' | 'HTTP_ERROR' | 'TIMEOUT' | 'BAD_RESPONSE'\n | 'CALL_BLOCKED' | 'INVALID_ARGUMENTS' | 'TOKEN_INVALID' | 'RATE_LIMITED' | 'UPSTREAM_UNAVAILABLE' | 'CANCELLED'\n readonly status?: number\n readonly upstreamCode?: number\n readonly retryAfter?: number\n readonly retryable?: boolean\n constructor(code: ZhihuSearchError['code'], message: string, status?: number, details: ZhihuErrorDetails = {}) {\n super(message)\n this.name = 'ZhihuSearchError'\n this.code = code\n if (status !== undefined) this.status = status\n if (details.upstreamCode !== undefined) this.upstreamCode = details.upstreamCode\n if (details.retryAfter !== undefined) this.retryAfter = details.retryAfter\n if (details.retryable !== undefined) this.retryable = details.retryable\n }\n}\n\n/** Metering payload emitted after each zhihu tool execution. */\nexport type ZhihuSearchExecuted = { ok: boolean; results: number; quotaId?: ZhihuQuotaId }\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 callPolicy?: ZhihuCallPolicyOptions\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/** Parse Retry-After seconds or an HTTP date; never invent a quota reset time. */\nexport function parseZhihuRetryAfter(value: string | null | undefined, now = Date.now()): number | undefined {\n if (!value?.trim()) return undefined\n const text = value.trim()\n if (/^\\d+(?:\\.\\d+)?$/.test(text)) {\n const seconds = Number(text)\n return Number.isFinite(seconds) ? Math.ceil(seconds) : undefined\n }\n // Do not let Date.parse interpret negative numbers as calendar dates.\n if (!/[a-z]/i.test(text)) return undefined\n const date = Date.parse(text)\n return Number.isFinite(date) ? Math.max(0, Math.ceil((date - now) / 1000)) : undefined\n}\n\n/**\n * Shared authenticated request. envelope=true opts into official error semantics\n * and returns Data. Legacy callers still receive the full JSON / chat response.\n * The timeout and cancellation cover BOTH headers and response-body consumption.\n * No requests are retried, including 429, exhausted quota, or risk-control denial.\n */\nexport async function zhihuFetchJson(\n path: string,\n init: { method?: 'GET' | 'POST'; params?: Record<string, string>; body?: unknown; formData?: FormData; envelope?: boolean },\n label: string,\n options: ZhihuClientOptions = {},\n): Promise<unknown> {\n const linked = options.signal\n if (linked?.aborted) throw new ZhihuSearchError('CANCELLED', '请求已取消')\n const url = new URL(path, ZHIHU_BASE_URL)\n if (url.origin !== ZHIHU_BASE_URL || url.username || url.password) {\n throw new ZhihuSearchError('INVALID_ARGUMENTS', '只允许请求知乎开放平台。')\n }\n for (const [key, value] of Object.entries(init.params ?? {})) url.searchParams.set(key, value)\n const token = await resolveZhihuToken(options.env, { resolveCredential: options.resolveCredential })\n if (linked?.aborted) throw new ZhihuSearchError('CANCELLED', '请求已取消')\n const fetcher = options.fetcher ?? globalFetch\n if (!fetcher) throw new ZhihuSearchError('BAD_RESPONSE', '当前环境没有可用的 fetch。')\n const method = init.method ?? (init.formData || init.body !== undefined ? 'POST' : 'GET')\n const timeout = options.timeoutMs !== undefined && Number.isFinite(options.timeoutMs) && options.timeoutMs > 0\n ? options.timeoutMs : ZHIHU_DEFAULT_TIMEOUT_MS\n const controller = new AbortController()\n const onAbort = () => controller.abort(linked?.reason)\n if (linked?.aborted) onAbort()\n else linked?.addEventListener('abort', onAbort, { once: true })\n const timer = setTimeout(() => controller.abort(new Error('zhihu request timeout')), timeout)\n let stopWaiting = () => {}\n const aborted = new Promise<never>((_resolve, reject) => {\n const rejectAbort = () => reject(controller.signal.reason)\n if (controller.signal.aborted) rejectAbort()\n else {\n controller.signal.addEventListener('abort', rejectAbort, { once: true })\n stopWaiting = () => controller.signal.removeEventListener('abort', rejectAbort)\n }\n })\n try {\n const request = async () => {\n controller.signal.throwIfAborted()\n const 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 // Let fetch supply the multipart boundary.\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 const retryAfter = parseZhihuRetryAfter(response.headers?.get('Retry-After'))\n const httpError = () => {\n const code = response.status === 429 ? 'RATE_LIMITED'\n : init.envelope && (response.status === 401 || response.status === 403) ? 'TOKEN_INVALID'\n : 'HTTP_ERROR'\n return new ZhihuSearchError(code, `${label}返回 HTTP ${response.status}。`, response.status,\n code === 'RATE_LIMITED' ? { retryAfter, retryable: true } : {})\n }\n let body: unknown\n try { body = await response.json() }\n catch (error) {\n if (controller.signal.aborted) throw error\n if (!response.ok) throw httpError()\n throw new ZhihuSearchError('BAD_RESPONSE', `${label}响应不是合法 JSON。`)\n }\n controller.signal.throwIfAborted()\n // A business error remains meaningful even when the HTTP status is 400.\n const isEnvelope = body !== null && typeof body === 'object' && !Array.isArray(body) && 'Code' in body\n if (!response.ok && !isEnvelope) throw httpError()\n if (init.envelope || (isEnvelope && (body as { Code: unknown }).Code !== 0 && (body as { Code: unknown }).Code !== '0')) {\n const data = parseZhihuEnvelope(body, { status: response.status, retryAfter, officialErrors: init.envelope })\n if (!response.ok) throw httpError()\n if (init.envelope) return data\n }\n if (!response.ok) throw httpError()\n return body\n }\n const quota = async (apiId: string) => {\n const quotaUrl = new URL('/api/v1/quota', ZHIHU_BASE_URL)\n quotaUrl.searchParams.set('APIIDs', apiId)\n const response = await fetcher(quotaUrl.toString(), {\n method: 'GET', signal: controller.signal,\n headers: { Authorization: `Bearer ${token.token}`, 'X-Request-Timestamp': String(Math.floor(Date.now() / 1000)), Accept: 'application/json' },\n })\n const body = await response.json()\n const data = parseZhihuEnvelope(body, { status: response.status, officialErrors: true })\n if (!response.ok) throw new Error('官方额度查询失败')\n return data\n }\n const guarded = path === '/api/v1/quota' && method === 'GET' ? Promise.race([request(), aborted]) : withZhihuCallPolicy(\n path, init.body, apiId => Promise.race([quota(apiId), aborted]),\n () => Promise.race([request(), aborted]),\n { ...options.callPolicy, env: options.env, signal: controller.signal },\n )\n return await guarded\n } catch (error) {\n if (linked?.aborted) throw new ZhihuSearchError('CANCELLED', '请求已取消')\n if (controller.signal.aborted || isAbortError(error)) {\n throw new ZhihuSearchError('TIMEOUT', `${label}超时(${Math.round(timeout / 1000)} 秒),请稍后再试。`)\n }\n if (error instanceof ZhihuCallPolicyError) throw new ZhihuSearchError('CALL_BLOCKED', error.message, undefined, { retryable: false })\n if (error instanceof ZhihuSearchError) {\n error.message = error.message.replaceAll(token.token, '[REDACTED]')\n throw error\n }\n const message = (error instanceof Error ? error.message : String(error)).replaceAll(token.token, '[REDACTED]')\n throw new ZhihuSearchError('BAD_RESPONSE', `${label}失败:${message}`)\n } finally {\n clearTimeout(timer)\n stopWaiting()\n linked?.removeEventListener('abort', onAbort)\n }\n}\n\nconst ZHIHU_BUSINESS_ERRORS: Record<number, ZhihuSearchError['code']> = {\n 10001: 'INVALID_ARGUMENTS', 20001: 'TOKEN_INVALID',\n 30001: 'RATE_LIMITED', 30002: 'RATE_LIMITED',\n 40001: 'INVALID_ARGUMENTS', 40002: 'INVALID_ARGUMENTS', 40003: 'RATE_LIMITED',\n 40004: 'INVALID_ARGUMENTS', 40005: 'INVALID_ARGUMENTS', 40006: 'INVALID_ARGUMENTS',\n 50002: 'UPSTREAM_UNAVAILABLE', 90001: 'UPSTREAM_UNAVAILABLE',\n}\n\n/** Parse the official envelope without coercing null/false/empty Code into success. */\nexport function parseZhihuEnvelope(body: unknown, meta: { status?: number; retryAfter?: number; officialErrors?: boolean } = {}): unknown {\n if (!body || typeof body !== 'object' || Array.isArray(body)) {\n throw new ZhihuSearchError('BAD_RESPONSE', '知乎响应缺少 {Code,Message,Data} 信封。', meta.status)\n }\n const envelope = body as { Code?: unknown; Message?: unknown; Data?: unknown }\n const rawCode = envelope.Code\n if (!((typeof rawCode === 'number' && Number.isSafeInteger(rawCode))\n || (typeof rawCode === 'string' && /^\\d+$/.test(rawCode) && Number.isSafeInteger(Number(rawCode))))) {\n throw new ZhihuSearchError('BAD_RESPONSE', '知乎响应缺少有效的 Code 字段。', meta.status)\n }\n const code = Number(rawCode)\n if (code !== 0) {\n const message = typeof envelope.Message === 'string' && envelope.Message ? envelope.Message : '未知错误'\n if (code === 30003) {\n throw new ZhihuSearchError('UPSTREAM_UNAVAILABLE', `知乎开放平台被风控拒绝:${message}。请不要立即重试。`,\n meta.status, { upstreamCode: code, retryable: false })\n }\n const kind = ZHIHU_BUSINESS_ERRORS[code] ?? (meta.officialErrors ? 'UPSTREAM_UNAVAILABLE' : 'HTTP_ERROR')\n throw new ZhihuSearchError(kind, `知乎开放平台返回错误(code=${code}):${message}`, meta.status,\n { upstreamCode: code, ...(kind === 'RATE_LIMITED' ? { retryAfter: meta.retryAfter, retryable: true } : {}) })\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 {\n normalizeSearchItem,\n parseZhihuEnvelope,\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 !== undefined ? { 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 normalizeSearchItem,\n parseZhihuEnvelope,\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 ...(typeof data.EmptyReason === 'string' ? { emptyReason: data.EmptyReason } : {}),\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","/**\n * Contract source: klarkxy/zhihu-search@62fd3ce5, upstream/http_client.py.\n * Six question/creator APIs use the current Access Secret only, never OAuth.\n * This module has no DSH/UI dependencies; RPC and tools share these validators.\n */\nimport { zhihuFetchJson, ZhihuSearchError, type ZhihuClientOptions } from './zhihu-client.ts'\nimport { ZHIHU_QUOTA_IDS, normalizeZhihuQuotaId, type ZhihuQuotaId } from './contracts.ts'\n\nexport const ZHIHU_OPEN_PLATFORM_APIS = {\n 'question.recommendations': { path: '/api/v1/user/question_recommendations', label: '适合回答的问题', quotaId: 'question_discovery' },\n 'question.answers': { path: '/api/v1/content/question_answers', label: '问题回答摘要', quotaId: 'question_discovery' },\n 'content.detail': { path: '/api/v1/user/content_detail', label: '本人创作全文', quotaId: 'creator' },\n 'content.comments': { path: '/api/v1/user/content_comments', label: '本人创作评论', quotaId: 'creator' },\n 'creator.account.stats': { path: '/api/v1/user/creator_account_stats', label: '账号创作数据', quotaId: 'creator' },\n 'creator.content.stats': { path: '/api/v1/user/creator_content_stats', label: '单篇创作数据', quotaId: 'creator' },\n quota: { path: '/api/v1/quota', label: '官方每日额度', quotaId: undefined },\n} as const\nexport type ZhihuOpenPlatformOperation = keyof typeof ZHIHU_OPEN_PLATFORM_APIS\n\nconst ENDPOINT_ALIASES: Record<string, ZhihuOpenPlatformOperation> = {\n question_recommendations: 'question.recommendations', question_answers: 'question.answers',\n content_detail: 'content.detail', user_content_detail: 'content.detail', 'user.content.detail': 'content.detail',\n content_comments: 'content.comments', user_content_comments: 'content.comments', 'user.content.comments': 'content.comments',\n creator_account_stats: 'creator.account.stats', creator_content_stats: 'creator.content.stats',\n}\nexport function resolveZhihuOpenPlatformOperation(endpoint: string): ZhihuOpenPlatformOperation | undefined {\n if (Object.hasOwn(ZHIHU_OPEN_PLATFORM_APIS, endpoint)) return endpoint as ZhihuOpenPlatformOperation\n return Object.hasOwn(ENDPOINT_ALIASES, endpoint) ? ENDPOINT_ALIASES[endpoint] : undefined\n}\n\nexport type ZhihuPaging = {\n canContinue: boolean\n isEnd?: boolean\n /** Decimal Int64 string. Never synthesize this from the number of items. */\n nextOffset?: string\n warning?: string\n}\nexport type ZhihuJsonValue = string | number | boolean | null | ZhihuJsonValue[] | { [key: string]: ZhihuJsonValue }\nexport type ZhihuOpenPlatformResult = {\n version: 1\n operation: ZhihuOpenPlatformOperation\n quotaId?: ZhihuQuotaId\n /** Original Data fields. Quota's top-level array is wrapped in { Items }. */\n data: Record<string, ZhihuJsonValue>\n paging?: ZhihuPaging\n}\n\nconst record = (value: unknown): value is Record<string, unknown> => value !== null && typeof value === 'object' && !Array.isArray(value)\n// Keep the free-form tool result compatible with the SDK's JSON value contract.\nfunction jsonValue(value: unknown): value is ZhihuJsonValue {\n if (value === null || typeof value === 'string' || typeof value === 'boolean') return true\n if (typeof value === 'number') return Number.isFinite(value)\n if (Array.isArray(value)) return value.every(jsonValue)\n return record(value) && Object.values(value).every(jsonValue)\n}\nfunction jsonRecord(value: unknown): value is Record<string, ZhihuJsonValue> {\n return record(value) && Object.values(value).every(jsonValue)\n}\nfunction invalid(message: string): never { throw new ZhihuSearchError('INVALID_ARGUMENTS', message) }\n\nfunction inputObject(input: unknown): Record<string, unknown> {\n if (!record(input)) return invalid('参数必须是对象。')\n // Reject misleading identity overrides, even over raw RPC. Credentials belong to the host.\n for (const key of ['token', 'accessSecret', 'access_secret', 'accessToken', 'access_token', 'oauthToken', 'oauth_token', 'useConfiguredOauthUser', 'use_configured_oauth_user']) {\n if (Object.hasOwn(input, key)) invalid('这些接口仅使用当前 Access Secret,不接受 token 参数或 OAuth 身份切换。')\n }\n return { ...input }\n}\nfunction limit(value: unknown, fallback: number, maximum: number, name = 'limit'): string {\n const count = value === undefined ? fallback : value\n if (typeof count !== 'number' || !Number.isInteger(count) || count < 1 || count > maximum) {\n return invalid(`${name} 必须是 1-${maximum} 的整数。`)\n }\n return String(count)\n}\n\n/** Accept all nonnegative Int64 values as strings, but never accept an unsafe JS number. */\nexport function normalizeZhihuOffset(value: unknown): string {\n let decimal: string\n if (typeof value === 'number' && Number.isSafeInteger(value) && value >= 0) decimal = String(value)\n else if (typeof value === 'string' && /^\\d+$/.test(value)) decimal = value.replace(/^0+(?=\\d)/, '')\n else return invalid('offset 必须是非负 Int64 整数;超过 JavaScript 安全整数范围时请用十进制字符串,不能使用不透明游标。')\n if (decimal.length > 19 || BigInt(decimal) > 9223372036854775807n) return invalid('offset 超出 Int64 范围。')\n return decimal\n}\n\nfunction zhihuUrl(value: unknown, question: boolean): string {\n if (typeof value !== 'string' || !value.trim()) return invalid(question ? 'questionUrl 不能为空。' : 'contentUrl 不能为空。')\n const cleaned = value.trim()\n if (/[\\u0000-\\u0020\\u007f\\\\]/.test(cleaned)) return invalid('知乎链接不能包含空白、控制字符或反斜线。')\n let url: URL\n try { url = new URL(cleaned) } catch { return invalid('不是有效的知乎 URL。') }\n if (url.protocol !== 'https:' || url.username || url.password || url.port) return invalid('必须使用无账号密码、无非标准端口的 HTTPS 知乎链接。')\n const valid = question\n ? url.hostname === 'www.zhihu.com' && /^\\/question\\/\\d+\\/?$/.test(url.pathname)\n : (url.hostname === 'www.zhihu.com' && /^\\/(?:answer\\/\\d+|question\\/\\d+\\/answer\\/\\d+|pin\\/\\d+|zvideo\\/\\d+)\\/?$/.test(url.pathname))\n || (url.hostname === 'zhuanlan.zhihu.com' && /^\\/p\\/\\d+\\/?$/.test(url.pathname))\n if (!valid) return invalid(question ? 'questionUrl 仅支持 https://www.zhihu.com/question/{id}。' : 'contentUrl 仅支持知乎回答、专栏文章、想法或视频链接。')\n return cleaned\n}\n\nfunction dates(input: Record<string, unknown>, params: Record<string, string>): void {\n const { startDate, endDate } = input\n if (startDate === undefined && endDate === undefined) return\n if (startDate === undefined || endDate === undefined) invalid('startDate 与 endDate 必须同时提供或同时省略。')\n for (const [key, value] of [['StartDate', startDate], ['EndDate', endDate]] as const) {\n if (typeof value !== 'string' || !/^\\d{4}-\\d{2}-\\d{2}$/.test(value) || value.slice(0, 4) === '0000') invalid(`${key} 必须是有效的 YYYY-MM-DD 公历日期。`)\n const time = new Date(`${value}T00:00:00Z`)\n if (!Number.isFinite(time.getTime()) || time.toISOString().slice(0, 10) !== value) invalid(`${key} 不是有效公历日期。`)\n params[key] = value\n }\n if (params.EndDate! < params.StartDate!) invalid('endDate 不得早于 startDate。')\n}\n\nfunction paramsFor(operation: ZhihuOpenPlatformOperation, input: Record<string, unknown>): Record<string, string> {\n const params: Record<string, string> = {}\n if (operation === 'question.recommendations') {\n // Explicit null/blank is not the same as omitting Query.\n if (input.query !== undefined) {\n if (typeof input.query !== 'string' || !input.query.trim()) invalid('主题关键词不能为空;省略 query 才会按画像推荐。')\n params.Query = input.query.trim()\n }\n params.Count = limit(input.count, 5, 20, 'count')\n } else if (operation === 'question.answers') {\n params.QuestionUrl = zhihuUrl(input.questionUrl, true)\n } else if (operation === 'content.detail' || operation === 'content.comments' || operation === 'creator.content.stats') {\n params.ContentUrl = zhihuUrl(input.contentUrl, false)\n } else if (operation === 'creator.account.stats') {\n const type = input.contentType === undefined ? 'all' : input.contentType\n if (typeof type !== 'string' || !['all', 'answer', 'article', 'pin', 'zvideo'].includes(type)) invalid('contentType 必须是 all、answer、article、pin 或 zvideo。')\n params.ContentType = type\n } else if (operation === 'quota' && input.apiIds !== undefined) {\n if (Array.isArray(input.apiIds)) input.apiIds = input.apiIds.map(normalizeZhihuQuotaId)\n if (!Array.isArray(input.apiIds) || input.apiIds.some(id => typeof id !== 'string' || !(ZHIHU_QUOTA_IDS as readonly string[]).includes(id))) invalid('apiIds 包含未知官方额度项。')\n if (input.apiIds.length) params.APIIDs = [...new Set(input.apiIds)].join(',')\n }\n if (operation === 'question.answers' || operation === 'content.comments') {\n params.Offset = normalizeZhihuOffset(input.offset === undefined ? 0 : input.offset)\n params.Limit = limit(input.limit, 20, 50)\n }\n if (operation === 'content.comments') {\n const order = input.order === undefined ? 'score' : input.order\n if (typeof order !== 'string' || !['score', 'reverse', 'ascending'].includes(order)) invalid('order 必须是 score、reverse 或 ascending。')\n params.Order = order\n }\n if (operation === 'creator.account.stats' || operation === 'creator.content.stats') dates(input, params)\n return params\n}\n\nexport function normalizeZhihuPaging(data: Record<string, unknown>, offset: string): ZhihuPaging {\n const paging = data.Paging\n if (!record(paging) || typeof paging.IsEnd !== 'boolean') {\n return { canContinue: false, warning: '分页信息不完整:缺少有效的 Paging.IsEnd,请停止翻页。' }\n }\n if (paging.IsEnd) return { isEnd: true, canContinue: false }\n try {\n const nextOffset = normalizeZhihuOffset(paging.NextOffset)\n if (BigInt(nextOffset) <= BigInt(offset)) throw new Error('non-advancing offset')\n return { isEnd: false, canContinue: true, nextOffset }\n } catch {\n return { isEnd: false, canContinue: false, warning: '分页信息不完整:NextOffset 缺失、无效或未前进,请停止翻页;不要按条数计算偏移。' }\n }\n}\n\nexport async function executeZhihuOpenPlatform(\n operation: ZhihuOpenPlatformOperation,\n input: unknown = {},\n options: ZhihuClientOptions = {},\n): Promise<ZhihuOpenPlatformResult> {\n const args = inputObject(input)\n const params = paramsFor(operation, args)\n const api = ZHIHU_OPEN_PLATFORM_APIS[operation]\n const raw = await zhihuFetchJson(api.path, { params, envelope: true }, api.label, options)\n const data = operation === 'quota' && Array.isArray(raw) ? { Items: raw } : raw\n if (!jsonRecord(data)) throw new ZhihuSearchError('BAD_RESPONSE', `${api.label}的 Data 必须是 JSON 对象。`)\n return {\n version: 1, operation, data,\n ...(api.quotaId ? { quotaId: api.quotaId } : {}),\n ...(operation === 'question.answers' || operation === 'content.comments'\n ? { paging: normalizeZhihuPaging(data, params.Offset!) } : {}),\n }\n}\n\nexport function zhihuOpenPlatformResultCount(result: ZhihuOpenPlatformResult): number {\n if (result.operation === 'quota') return 0\n if (result.operation === 'content.detail') return typeof result.data.Body === 'string' && result.data.Body.trim() ? 1 : 0\n if (result.operation === 'creator.account.stats') return Object.keys(result.data).length ? 1 : 0\n return Array.isArray(result.data.Items) ? result.data.Items.filter(record).length : 0\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@klarkxy/dsh-zhihu",
3
- "version": "0.2.9",
3
+ "version": "0.2.10",
4
4
  "description": "Zhihu search, agent tools, knowledge bases, usage tracking, and draft management for DeepSeek Harness.",
5
5
  "icon": "./icon.svg",
6
6
  "license": "SEE LICENSE IN LICENSE",
@@ -98,7 +98,7 @@
98
98
  "marked": "^18.0.14",
99
99
  "parse5": "^8.0.1",
100
100
  "zod": "^4.1.5",
101
- "@klarkxy/dsh-plugin-kit": "0.5.0"
101
+ "@klarkxy/dsh-plugin-kit": "0.6.0"
102
102
  },
103
103
  "peerDependencies": {
104
104
  "@deepseek-ai/cordis": ">=4.0.4",
@@ -155,6 +155,6 @@
155
155
  "registry": "https://registry.npmjs.org/"
156
156
  },
157
157
  "dshRelease": {
158
- "contentHash": "f779e43a9c5243207ab47db1003f5149a382fc5d2ac4bd8c9128179a5107ff29"
158
+ "contentHash": "0ce23946fd3ff1b7f9edd5f967edfba42713e80ec7448db88d05da93a3f435ad"
159
159
  }
160
160
  }