@klarkxy/dsh-zhihu 0.1.9 → 0.1.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.
package/lib/index.js CHANGED
@@ -120,7 +120,12 @@ function endpointFromRpcPath(channel, pathname) {
120
120
  return endpoint;
121
121
  }
122
122
  async function dispatchHostRpc(ctx, channel, handler, req, res) {
123
- const rejection = ctx.connection.requestRejection?.(req);
123
+ if (typeof ctx.connection.requestRejection !== "function") {
124
+ res.writeHead(503);
125
+ res.end("host authorization policy is unavailable");
126
+ return;
127
+ }
128
+ const rejection = ctx.connection.requestRejection(req);
124
129
  if (rejection !== void 0) {
125
130
  res.writeHead(rejection);
126
131
  res.end(rejection === 401 ? "unauthorized" : "forbidden");
@@ -367,7 +372,7 @@ function createZhihuService(options, usage, notify = () => {}) {
367
372
  "global.search",
368
373
  "ask",
369
374
  "knowledge.search"
370
- ].includes(endpoint) && (!query.trim() || query.length > 4e3)) return fail("bad-request", "请输入不超过 4000 字符的查询");
375
+ ].includes(endpoint) && (!query.trim() || query.length > 4e3)) return fail("bad-request", "查询不超过 4000 字符");
371
376
  let value;
372
377
  let results = 0;
373
378
  counted = true;
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/zhihu-knowledge.ts","../src/host-rpc.ts","../src/web-search-provider.ts","../src/index.ts"],"sourcesContent":["/**\n * 知乎知识库的管理侧能力:列知识库、上传文件。仅供设置页 RPC 使用,\n * 不暴露为模型工具——上传会把文件送入知乎云端,必须是用户在界面上显式发起。\n *\n * 端点(来源:github.com/klarkxy/zhihu-search,src/zhihu_search/upstream/http_client.py):\n * - 列表:GET /api/v1/knowledge/bases?Scope=all|created|subscribed\n * - 上传:POST /api/v1/knowledge/files(multipart: File + 可选 KnowledgeBaseID),同步解析\n */\nimport {\n parseZhihuEnvelope,\n zhihuFetchJson,\n ZhihuSearchError,\n type ZhihuClientOptions,\n} from './zhihu-client.ts'\n\nexport const ZHIHU_KNOWLEDGE_LIST_VERSION = 1\nexport const ZHIHU_KNOWLEDGE_UPLOAD_VERSION = 1\n/** 设置页 RPC 走 JSON/base64 传输,限额压到 20MB(平台上限 100MB)。 */\nexport const ZHIHU_KNOWLEDGE_UPLOAD_MAX_BYTES = 20 * 1024 * 1024\nexport const ZHIHU_KNOWLEDGE_UPLOAD_TIMEOUT_MS = 180_000\n\nconst KNOWLEDGE_BASES_PATH = '/api/v1/knowledge/bases'\nconst KNOWLEDGE_FILES_PATH = '/api/v1/knowledge/files'\n\nexport const ZHIHU_KNOWLEDGE_FILE_EXTENSIONS = [\n '.pdf', '.md', '.txt', '.ppt', '.pptx', '.xlsx', '.xls', '.docx', '.doc',\n '.webp', '.png', '.jpg', '.mobi', '.epub', '.csv', '.azw3',\n] as const\n\nconst KNOWLEDGE_CONTENT_TYPES: Record<string, string> = {\n '.pdf': 'application/pdf',\n '.md': 'text/markdown',\n '.txt': 'text/plain',\n '.ppt': 'application/vnd.ms-powerpoint',\n '.pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',\n '.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n '.xls': 'application/vnd.ms-excel',\n '.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n '.doc': 'application/msword',\n '.webp': 'image/webp',\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.mobi': 'application/x-mobipocket-ebook',\n '.epub': 'application/epub+zip',\n '.csv': 'text/csv',\n '.azw3': 'application/vnd.amazon.ebook',\n}\n\nexport type ZhihuKnowledgeBase = {\n id: string\n name: string\n relation: string\n visibility: string\n isDefault: boolean\n contentCount: number\n}\n\nexport type ZhihuKnowledgeBaseList = {\n version: typeof ZHIHU_KNOWLEDGE_LIST_VERSION\n bases: ZhihuKnowledgeBase[]\n}\n\nfunction normalizeBase(raw: Record<string, unknown>): ZhihuKnowledgeBase {\n return {\n id: typeof raw.KnowledgeBaseID === 'string' ? raw.KnowledgeBaseID : '',\n name: typeof raw.Name === 'string' && raw.Name.length > 0 ? raw.Name : '(未命名知识库)',\n relation: typeof raw.Relation === 'string' ? raw.Relation : '',\n visibility: typeof raw.Visibility === 'string' ? raw.Visibility : '',\n isDefault: raw.IsDefault === true,\n contentCount: typeof raw.ContentCount === 'number' ? raw.ContentCount : 0,\n }\n}\n\nexport async function listZhihuKnowledgeBases(options: ZhihuClientOptions = {}): Promise<ZhihuKnowledgeBaseList> {\n const body = await zhihuFetchJson(KNOWLEDGE_BASES_PATH, {\n params: { Scope: 'all' },\n }, '知乎知识库列表', options)\n const data = parseZhihuEnvelope(body) as { Items?: unknown }\n const items = Array.isArray(data.Items) ? data.Items : []\n return {\n version: ZHIHU_KNOWLEDGE_LIST_VERSION,\n bases: items\n .filter((item): item is Record<string, unknown> => !!item && typeof item === 'object' && !Array.isArray(item))\n .map(normalizeBase)\n .filter((base) => base.id.length > 0),\n }\n}\n\nexport type ZhihuKnowledgeUploadInput = {\n fileName: string\n data: Uint8Array\n /** 指定目标知识库;缺省进默认库。 */\n knowledgeBaseId?: string\n}\n\nexport type ZhihuKnowledgeUpload = {\n version: typeof ZHIHU_KNOWLEDGE_UPLOAD_VERSION\n knowledgeBaseId: string\n recallContentId: string\n fileName: string\n fileSize: number\n title: string\n abstract: string\n originUrl: string\n}\n\n/** 校验文件名与大小,返回小写扩展名。 */\nexport function checkKnowledgeFile(fileName: string, size: number): string {\n const cleaned = fileName.trim()\n if (!cleaned) throw new ZhihuSearchError('BAD_RESPONSE', '知识库文件名不能为空。')\n // 去掉路径成分,只保留文件名\n const base = cleaned.split(/[\\\\/]/).pop() ?? cleaned\n for (const ch of base) {\n const code = ch.codePointAt(0) ?? 0\n if (code < 32 || code === 127) throw new ZhihuSearchError('BAD_RESPONSE', '知识库文件名不能包含控制字符。')\n }\n if (new TextEncoder().encode(base).length > 255) {\n throw new ZhihuSearchError('BAD_RESPONSE', '知识库文件名不能超过 255 个 UTF-8 字节。')\n }\n if (size <= 0) throw new ZhihuSearchError('BAD_RESPONSE', '知识库文件内容不能为空。')\n if (size > ZHIHU_KNOWLEDGE_UPLOAD_MAX_BYTES) {\n throw new ZhihuSearchError('BAD_RESPONSE', `知识库文件大小不能超过 ${ZHIHU_KNOWLEDGE_UPLOAD_MAX_BYTES / 1024 / 1024}MB。`)\n }\n const dot = base.lastIndexOf('.')\n const ext = dot >= 0 ? base.slice(dot).toLowerCase() : ''\n if (!(ZHIHU_KNOWLEDGE_FILE_EXTENSIONS as readonly string[]).includes(ext)) {\n throw new ZhihuSearchError('BAD_RESPONSE', `知识库上传仅支持 ${ZHIHU_KNOWLEDGE_FILE_EXTENSIONS.join('、')}。`)\n }\n return ext\n}\n\nexport async function uploadZhihuKnowledgeFile(\n input: ZhihuKnowledgeUploadInput,\n options: ZhihuClientOptions = {},\n): Promise<ZhihuKnowledgeUpload> {\n const cleaned = (input.fileName.trim().split(/[\\\\/]/).pop() ?? input.fileName.trim())\n const ext = checkKnowledgeFile(input.fileName, input.data.byteLength)\n const form = new FormData()\n if (input.knowledgeBaseId) form.set('KnowledgeBaseID', input.knowledgeBaseId)\n form.set('File', new Blob([input.data.buffer as ArrayBuffer], { type: KNOWLEDGE_CONTENT_TYPES[ext] ?? 'application/octet-stream' }), cleaned)\n const body = await zhihuFetchJson(KNOWLEDGE_FILES_PATH, {\n formData: form,\n }, '知乎知识库上传', { ...options, timeoutMs: options.timeoutMs ?? ZHIHU_KNOWLEDGE_UPLOAD_TIMEOUT_MS })\n const data = parseZhihuEnvelope(body) as Record<string, unknown>\n return {\n version: ZHIHU_KNOWLEDGE_UPLOAD_VERSION,\n knowledgeBaseId: typeof data.KnowledgeBaseID === 'string' ? data.KnowledgeBaseID : '',\n recallContentId: typeof data.RecallContentID === 'string' ? data.RecallContentID : '',\n fileName: typeof data.FileName === 'string' ? data.FileName : cleaned,\n fileSize: typeof data.FileSize === 'number' ? data.FileSize : input.data.byteLength,\n title: typeof data.Title === 'string' ? data.Title : '',\n abstract: typeof data.Abstract === 'string' ? data.Abstract : '',\n originUrl: typeof data.OriginUrl === 'string' ? data.OriginUrl : '',\n }\n}\n","import type { IncomingMessage, ServerResponse } from 'node:http'\n\nconst CHANNEL_PATTERN = /^\\/[A-Za-z0-9._~-]+$/\nconst ENDPOINT_SEGMENT = /^[A-Za-z0-9_$.-]+$/\nconst MAX_BODY_BYTES = 64 * 1024 * 1024\n\nexport type HostRpcHandler = (endpoint: string, payload: unknown, signal: AbortSignal) => Promise<unknown>\n\nexport type HostRpcContext = {\n webServer: {\n register: (route: {\n kind: 'prefix'\n path: string\n handler: (req: IncomingMessage, res: ServerResponse) => void | Promise<void>\n }) => () => void\n }\n connection: {\n requestRejection?: (request: IncomingMessage) => number | undefined\n }\n}\n\n/** Standalone copy of manuscript Host RPC registration; this package cannot depend on manuscript. */\nexport function registerHostRpc(ctx: HostRpcContext, channel: string, handler: HostRpcHandler): () => void {\n if (!CHANNEL_PATTERN.test(channel) || channel === '/api') {\n throw new Error(`invalid RPC channel ${JSON.stringify(channel)}`)\n }\n return ctx.webServer.register({\n kind: 'prefix',\n path: channel,\n handler: (req, res) => dispatchHostRpc(ctx, channel, handler, req, res),\n })\n}\n\nfunction endpointFromRpcPath(channel: string, pathname: string): string | undefined {\n if (!pathname.startsWith(`${channel}/`)) return undefined\n const endpoint = pathname.slice(channel.length + 1)\n if (endpoint.split('/').some((segment) => segment === '' || segment === '.' || segment === '..' || !ENDPOINT_SEGMENT.test(segment))) {\n return undefined\n }\n return endpoint\n}\n\nasync function dispatchHostRpc(\n ctx: HostRpcContext,\n channel: string,\n handler: HostRpcHandler,\n req: IncomingMessage,\n res: ServerResponse,\n): Promise<void> {\n const rejection = ctx.connection.requestRejection?.(req)\n if (rejection !== undefined) {\n res.writeHead(rejection)\n res.end(rejection === 401 ? 'unauthorized' : 'forbidden')\n return\n }\n const abort = new AbortController()\n res.on('close', () => {\n if (!res.writableEnded) abort.abort()\n })\n const url = new URL(req.url ?? '/', 'http://dsh.internal')\n const endpoint = endpointFromRpcPath(channel, url.pathname)\n if (req.method !== 'POST' || endpoint === undefined) {\n res.writeHead(404)\n res.end('not found')\n return\n }\n const contentType = String(req.headers['content-type'] ?? '').split(';', 1)[0]?.trim().toLowerCase()\n if (contentType !== 'application/json') {\n res.writeHead(415)\n res.end('content type must be application/json')\n return\n }\n const chunks: Buffer[] = []\n let received = 0\n for await (const chunk of req) {\n const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)\n received += buffer.byteLength\n if (received > MAX_BODY_BYTES) {\n res.writeHead(413, { connection: 'close' })\n res.end()\n req.destroy()\n return\n }\n chunks.push(buffer)\n }\n let body: unknown\n try {\n body = JSON.parse(Buffer.concat(chunks).toString('utf8') || 'null')\n } catch {\n res.writeHead(400)\n res.end('body is not JSON')\n return\n }\n const record = body && typeof body === 'object' && !Array.isArray(body) ? body as Record<string, unknown> : {}\n const rpcId = typeof record.rpcId === 'string' ? record.rpcId : 'invalid-request'\n if (record.type !== 'client-request' || record.method !== endpoint) {\n res.writeHead(200, { 'content-type': 'application/json' })\n res.end(JSON.stringify({\n type: 'server-response',\n rpcId,\n result: {\n ok: false,\n error: {\n code: 'gateway/bad-request',\n message: record.method !== endpoint\n ? `method ${JSON.stringify(record.method)} does not match endpoint ${JSON.stringify(endpoint)}`\n : 'invalid client-request message',\n details: { issues: [] },\n },\n },\n }))\n return\n }\n try {\n const result = await handler(endpoint, record.payload, abort.signal)\n res.writeHead(200, { 'content-type': 'application/json' })\n res.end(JSON.stringify({ type: 'server-response', rpcId, result }))\n } catch (error) {\n res.writeHead(500)\n res.end(`handler failure: ${String(error)}`)\n }\n}\n","/**\n * Optional web-search-manager backend. Registers only when this profile already\n * provides `webSearchManager`; Zhihu still works without that plugin.\n * Official model tool remains `web_search`. Dedicated `zhihu_global_search` is unchanged.\n */\nimport { ZHIHU_CREDENTIAL_REF } from './contracts.ts'\nimport { reportExecuted, type ZhihuClientOptions, type ZhihuSearchExecuted } from './zhihu-client.ts'\nimport {\n executeZhihuGlobalSearch,\n ZHIHU_GLOBAL_SEARCH_MAX_COUNT,\n} from './operations.ts'\n\ntype ZhihuWebSearchHost = {\n run<T>(execute: (signal: AbortSignal) => Promise<T>, signal: AbortSignal): Promise<T>\n toolOptions: ZhihuClientOptions & { onExecuted: (event: ZhihuSearchExecuted) => void }\n}\n\nexport const ZHIHU_WEB_SEARCH_ID = 'zhihu-global'\n\nexport const ZHIHU_WEB_SEARCH_DESCRIPTOR = {\n id: ZHIHU_WEB_SEARCH_ID,\n label: '知乎全网搜索',\n description: '融合知乎问答与全网内容,返回可溯源的搜索结果。',\n credentialRef: ZHIHU_CREDENTIAL_REF,\n credentialShared: true,\n credentialHint: '与「知乎资料」共用 Access Secret',\n billing: 'request' as const,\n pricing: '注册可获 5,000 次/天试用额度;超额价格需向平台咨询。',\n pricingUrl: 'https://developer.zhihu.com/',\n signupUrl: 'https://developer.zhihu.com',\n}\n\nexport type ZhihuWebSearchSource = { url: string; title?: string; snippet?: string }\nexport type ZhihuWebSearchResult = { sources: ZhihuWebSearchSource[]; truncated?: boolean }\nexport type ZhihuWebSearchRequest = { query: string; maxResults?: number }\n\nexport type ZhihuWebSearchProvider = {\n readonly id: string\n available(): boolean\n search(request: ZhihuWebSearchRequest, signal?: AbortSignal): Promise<ZhihuWebSearchResult>\n}\n\nexport type ZhihuWebSearchManager = {\n registerSearchProvider(\n descriptor: typeof ZHIHU_WEB_SEARCH_DESCRIPTOR,\n factory: (options: { apiKey?: string; timeoutMs: number }) => ZhihuWebSearchProvider,\n ): () => void\n}\n\nexport type ZhihuWebSearchProviderOptions = ZhihuClientOptions & {\n apiKey?: string\n onExecuted?: (event: ZhihuSearchExecuted) => void\n}\n\nfunction sourceFrom(url: string, title: string, snippet: string): ZhihuWebSearchSource | undefined {\n let parsed: URL\n try { parsed = new URL(url) } catch { return }\n if (!['https:', 'http:'].includes(parsed.protocol) || parsed.username || parsed.password) return\n return {\n url: parsed.href,\n ...(title ? { title: title.slice(0, 1000) } : {}),\n ...(snippet ? { snippet: snippet.slice(0, 8000) } : {}),\n }\n}\n\nfunction isWebSearchManager(value: unknown): value is ZhihuWebSearchManager {\n return !!value && typeof value === 'object' && typeof (value as ZhihuWebSearchManager).registerSearchProvider === 'function'\n}\n\nexport function createZhihuWebSearchProvider(options: ZhihuWebSearchProviderOptions): ZhihuWebSearchProvider {\n const { apiKey, onExecuted, ...client } = options\n return {\n id: ZHIHU_WEB_SEARCH_ID,\n available() { return Boolean(apiKey?.trim()) },\n async search(request, signal) {\n if (!apiKey?.trim()) throw new Error('知乎全网搜索未配置 Access Secret。')\n if (!request.query?.trim() || request.query.length > 4000) throw new Error('查询无效。')\n const count = request.maxResults ?? 5\n if (!Number.isInteger(count) || count < 1 || count > ZHIHU_GLOBAL_SEARCH_MAX_COUNT) {\n throw new Error(`结果数须为 1 至 ${ZHIHU_GLOBAL_SEARCH_MAX_COUNT}。`)\n }\n try {\n signal?.throwIfAborted()\n const result = await executeZhihuGlobalSearch(request.query.trim(), count, 'all', {\n ...client,\n signal,\n env: {},\n resolveCredential: async () => apiKey,\n })\n reportExecuted(onExecuted, { ok: true, results: result.items.length })\n const sources = result.items.flatMap(item => sourceFrom(item.url, item.title, item.summary) ?? [])\n return { sources: sources.slice(0, count), truncated: sources.length > count }\n } catch (error) {\n reportExecuted(onExecuted, { ok: false, results: 0 })\n throw error\n }\n },\n }\n}\n\nexport function registerZhihuGlobalSearchProvider(\n manager: ZhihuWebSearchManager,\n service: ZhihuWebSearchHost,\n): () => void {\n return manager.registerSearchProvider(ZHIHU_WEB_SEARCH_DESCRIPTOR, options => {\n const provider = createZhihuWebSearchProvider({\n ...service.toolOptions,\n apiKey: options.apiKey,\n timeoutMs: options.timeoutMs,\n })\n return {\n id: provider.id,\n available: () => provider.available(),\n search: (request, signal) => service.run(\n combined => provider.search(request, combined),\n signal ?? new AbortController().signal,\n ),\n }\n })\n}\n\n/** Wait for `webSearchManager` if this profile loaded it; no-op otherwise. */\nexport function bindZhihuWebSearch(\n ctx: { inject(deps: readonly string[], apply: (inner: { get(name: string): unknown }) => (() => void) | void): unknown },\n service: ZhihuWebSearchHost,\n): void {\n ctx.inject(['webSearchManager'], (inner) => {\n let manager: unknown\n try { manager = inner.get('webSearchManager') }\n catch { return }\n if (!isWebSearchManager(manager)) return\n return registerZhihuGlobalSearchProvider(manager, service)\n })\n}\n","import type { Context } from '@deepseek-ai/cordis'\nimport { credentialRef } from '@deepseek-ai/dsh-credentials'\nimport { executeZhihuSearch, ZHIHU_SEARCH_DEFAULT_COUNT } from './search-api.ts'\nimport {\n executeZhihuGlobalSearch, executeZhihuHotList, executeZhihuAsk, executeZhihuKnowledgeSearch,\n normalizeRecallScopes, ZHIHU_ASK_MODELS, ZHIHU_ASK_DEFAULT_MODEL,\n} from './operations.ts'\nimport { listZhihuKnowledgeBases, uploadZhihuKnowledgeFile, ZHIHU_KNOWLEDGE_UPLOAD_MAX_BYTES } from './zhihu-knowledge.ts'\nimport { type ZhihuClientOptions, type ZhihuSearchExecuted, ZhihuSearchError } from './zhihu-client.ts'\nimport { createZhihuUsageRecorder, resolveDays, zhihuUsageDomainSpec, type ZhihuUsageRecorder } from './usage.ts'\nimport { ZHIHU_RPC_CHANNEL, ZHIHU_CREDENTIAL_REF, ZHIHU_SEARCH_EVENT, type ZhihuRpcResult } from './contracts.ts'\nimport { registerHostRpc, type HostRpcContext } from './host-rpc.ts'\nimport { bindZhihuWebSearch } from './web-search-provider.ts'\nimport { executeZhihuOpenPlatform, resolveZhihuOpenPlatformOperation, ZHIHU_OPEN_PLATFORM_APIS, zhihuOpenPlatformResultCount } from './open-platform.ts'\n\nexport const name = '@klarkxy/dsh-zhihu'\nexport const inject = ['connection', 'credentials', 'storageDomain', 'webServer'] as const\nexport type ZhihuService = {\n call(endpoint: string, payload: unknown, signal: AbortSignal): Promise<ZhihuRpcResult>\n run<T>(execute: (signal: AbortSignal) => Promise<T>, signal: AbortSignal): Promise<T>\n dispose(): Promise<void>\n usageSummary(days: unknown): Promise<{ days: unknown[] }>\n toolOptions: ZhihuClientOptions & { onExecuted: (event: ZhihuSearchExecuted) => void }\n}\n\ntype Host = Context & HostRpcContext & {\n credentials: { resolve: (ref: ReturnType<typeof credentialRef>) => Promise<{ value: string } | undefined> }\n}\nconst fail = (code: string, message: string, details: Record<string, unknown> = {}): ZhihuRpcResult => ({ ok: false, error: { code, message, details } })\n\n/** One shared service for direct UI RPC and optional Tool wrappers. */\nexport function createZhihuService(options: ZhihuClientOptions, usage: ZhihuUsageRecorder, notify: (event: ZhihuSearchExecuted) => void = () => {}): ZhihuService {\n // Serialize read-modify-write counters; concurrent tools must not lose increments.\n let pendingUsage = Promise.resolve()\n const lifetime = new AbortController()\n const active = new Set<Promise<unknown>>()\n const run: ZhihuService['run'] = (execute, signal) => {\n const combined = AbortSignal.any([signal, lifetime.signal])\n const request = Promise.resolve().then(() => { combined.throwIfAborted(); return execute(combined) })\n active.add(request)\n return request.finally(() => active.delete(request))\n }\n const record = (event: ZhihuSearchExecuted) => {\n pendingUsage = pendingUsage.then(() => usage.record(event)).catch(() => {})\n try { notify(event) } catch { /* observer failure does not change a request */ }\n }\n const service = {\n toolOptions: { ...options, onExecuted: record },\n async usageSummary(days: unknown) { await pendingUsage; return { days: await usage.read(resolveDays(days)) } },\n async call(endpoint: string, payload: unknown, signal: AbortSignal): Promise<ZhihuRpcResult> {\n if (signal.aborted) return fail('cancelled', '请求已取消')\n const body = payload && typeof payload === 'object' && !Array.isArray(payload) ? payload as Record<string, unknown> : {}\n const client = { ...options, signal }\n let counted = false\n const operation = resolveZhihuOpenPlatformOperation(endpoint)\n const quotaId = operation ? ZHIHU_OPEN_PLATFORM_APIS[operation].quotaId : undefined\n const metering = quotaId ? { quotaId } : {}\n try {\n if (endpoint === 'usage.summary') { await pendingUsage; return { ok: true, value: { days: await usage.read(resolveDays(body.days)) } } }\n const query = typeof body.query === 'string' ? body.query : ''\n if (['search', 'global.search', 'ask', 'knowledge.search'].includes(endpoint) && (!query.trim() || query.length > 4000)) {\n return fail('bad-request', '请输入不超过 4000 字符的查询')\n }\n let value: unknown\n let results = 0\n counted = true\n if (operation) {\n counted = operation !== 'quota'\n const result = await executeZhihuOpenPlatform(operation, payload === undefined ? {} : payload, client)\n value = result; results = zhihuOpenPlatformResultCount(result)\n } else if (endpoint === 'search') {\n const result = await executeZhihuSearch(query, typeof body.count === 'number' ? body.count : ZHIHU_SEARCH_DEFAULT_COUNT, client)\n value = result; results = result.items.length\n } else if (endpoint === 'global.search') {\n const result = await executeZhihuGlobalSearch(query, typeof body.count === 'number' ? body.count : 10,\n body.searchDb === 'realtime' || body.searchDb === 'static' ? body.searchDb : 'all', client)\n value = result; results = result.items.length\n } else if (endpoint === 'hot.list') {\n const result = await executeZhihuHotList(typeof body.limit === 'number' ? body.limit : 10, client)\n value = result; results = result.items.length\n } else if (endpoint === 'ask') {\n const model = (ZHIHU_ASK_MODELS as readonly unknown[]).includes(body.model) ? body.model as typeof ZHIHU_ASK_MODELS[number] : ZHIHU_ASK_DEFAULT_MODEL\n value = await executeZhihuAsk(query, model, client); results = 1\n } else if (endpoint === 'knowledge.search') {\n const result = await executeZhihuKnowledgeSearch(query, typeof body.limit === 'number' ? body.limit : 5, normalizeRecallScopes(body.recallScopes), client)\n value = result; results = result.items.length\n } else if (endpoint === 'knowledge.bases') {\n const result = await listZhihuKnowledgeBases(client); value = result; results = result.bases.length\n } else if (endpoint === 'knowledge.upload') {\n const contentBase64 = typeof body.contentBase64 === 'string' ? body.contentBase64 : ''\n if (!contentBase64) throw new Error('缺少文件内容。')\n if (contentBase64.length > Math.ceil(ZHIHU_KNOWLEDGE_UPLOAD_MAX_BYTES / 3) * 4 || !/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(contentBase64)) {\n return fail('bad-request', '文件内容无效或超过 20 MB')\n }\n const upload = await uploadZhihuKnowledgeFile({\n fileName: typeof body.fileName === 'string' ? body.fileName : '',\n data: new Uint8Array(Buffer.from(contentBase64, 'base64')),\n knowledgeBaseId: typeof body.knowledgeBaseId === 'string' && body.knowledgeBaseId.trim() ? body.knowledgeBaseId.trim() : undefined,\n }, client)\n value = { upload }; results = 1\n } else {\n counted = false\n return fail('bad-request', `unknown endpoint ${endpoint}`)\n }\n if (counted) record({ ok: true, results, ...metering })\n return { ok: true, value }\n } catch (error) {\n if (counted) record({ ok: false, results: 0, ...metering })\n if (signal.aborted) return fail('cancelled', '请求已取消')\n const code = error instanceof ZhihuSearchError ? error.code.toLowerCase().replaceAll('_', '-') : 'internal'\n const details: Record<string, unknown> = {}\n if (error instanceof ZhihuSearchError) {\n for (const key of ['status', 'upstreamCode', 'retryAfter', 'retryable'] as const) {\n if (error[key] !== undefined) details[key] = error[key]\n }\n }\n return fail(code, error instanceof Error ? error.message : '知乎请求失败', details)\n }\n },\n }\n return {\n ...service,\n run,\n async call(endpoint, payload, signal) {\n if (signal.aborted || lifetime.signal.aborted) return fail('cancelled', '请求已取消')\n try { return await run(combined => service.call(endpoint, payload, combined), signal) }\n catch (error) {\n if (signal.aborted || lifetime.signal.aborted) return fail('cancelled', '请求已取消')\n throw error\n }\n },\n async dispose() {\n lifetime.abort()\n await Promise.allSettled([...active])\n await pendingUsage\n },\n }\n}\n\nexport async function apply(ctx: Context): Promise<void> {\n const host = ctx as Host\n const domain = await ctx.storageDomain.open(zhihuUsageDomainSpec)\n const service = createZhihuService({\n resolveCredential: async () => (await host.credentials.resolve(credentialRef(ZHIHU_CREDENTIAL_REF)))?.value,\n }, createZhihuUsageRecorder(domain.table('daily')), event => {\n const emit = ctx.emit as unknown as (name: string, event: ZhihuSearchExecuted) => void\n emit.call(ctx, ZHIHU_SEARCH_EVENT, event)\n })\n ctx.effect(() => async () => { await service.dispose(); await domain.close() }, 'zhihu.usageDomainClose')\n ctx.provide('zhihu', service)\n ctx.effect(() => registerHostRpc(host, ZHIHU_RPC_CHANNEL, service.call))\n bindZhihuWebSearch(ctx, service)\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/zhihu-knowledge.ts","../src/host-rpc.ts","../src/web-search-provider.ts","../src/index.ts"],"sourcesContent":["/**\n * 知乎知识库的管理侧能力:列知识库、上传文件。仅供设置页 RPC 使用,\n * 不暴露为模型工具——上传会把文件送入知乎云端,必须是用户在界面上显式发起。\n *\n * 端点(来源:github.com/klarkxy/zhihu-search,src/zhihu_search/upstream/http_client.py):\n * - 列表:GET /api/v1/knowledge/bases?Scope=all|created|subscribed\n * - 上传:POST /api/v1/knowledge/files(multipart: File + 可选 KnowledgeBaseID),同步解析\n */\nimport {\n parseZhihuEnvelope,\n zhihuFetchJson,\n ZhihuSearchError,\n type ZhihuClientOptions,\n} from './zhihu-client.ts'\n\nexport const ZHIHU_KNOWLEDGE_LIST_VERSION = 1\nexport const ZHIHU_KNOWLEDGE_UPLOAD_VERSION = 1\n/** 设置页 RPC 走 JSON/base64 传输,限额压到 20MB(平台上限 100MB)。 */\nexport const ZHIHU_KNOWLEDGE_UPLOAD_MAX_BYTES = 20 * 1024 * 1024\nexport const ZHIHU_KNOWLEDGE_UPLOAD_TIMEOUT_MS = 180_000\n\nconst KNOWLEDGE_BASES_PATH = '/api/v1/knowledge/bases'\nconst KNOWLEDGE_FILES_PATH = '/api/v1/knowledge/files'\n\nexport const ZHIHU_KNOWLEDGE_FILE_EXTENSIONS = [\n '.pdf', '.md', '.txt', '.ppt', '.pptx', '.xlsx', '.xls', '.docx', '.doc',\n '.webp', '.png', '.jpg', '.mobi', '.epub', '.csv', '.azw3',\n] as const\n\nconst KNOWLEDGE_CONTENT_TYPES: Record<string, string> = {\n '.pdf': 'application/pdf',\n '.md': 'text/markdown',\n '.txt': 'text/plain',\n '.ppt': 'application/vnd.ms-powerpoint',\n '.pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',\n '.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n '.xls': 'application/vnd.ms-excel',\n '.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n '.doc': 'application/msword',\n '.webp': 'image/webp',\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.mobi': 'application/x-mobipocket-ebook',\n '.epub': 'application/epub+zip',\n '.csv': 'text/csv',\n '.azw3': 'application/vnd.amazon.ebook',\n}\n\nexport type ZhihuKnowledgeBase = {\n id: string\n name: string\n relation: string\n visibility: string\n isDefault: boolean\n contentCount: number\n}\n\nexport type ZhihuKnowledgeBaseList = {\n version: typeof ZHIHU_KNOWLEDGE_LIST_VERSION\n bases: ZhihuKnowledgeBase[]\n}\n\nfunction normalizeBase(raw: Record<string, unknown>): ZhihuKnowledgeBase {\n return {\n id: typeof raw.KnowledgeBaseID === 'string' ? raw.KnowledgeBaseID : '',\n name: typeof raw.Name === 'string' && raw.Name.length > 0 ? raw.Name : '(未命名知识库)',\n relation: typeof raw.Relation === 'string' ? raw.Relation : '',\n visibility: typeof raw.Visibility === 'string' ? raw.Visibility : '',\n isDefault: raw.IsDefault === true,\n contentCount: typeof raw.ContentCount === 'number' ? raw.ContentCount : 0,\n }\n}\n\nexport async function listZhihuKnowledgeBases(options: ZhihuClientOptions = {}): Promise<ZhihuKnowledgeBaseList> {\n const body = await zhihuFetchJson(KNOWLEDGE_BASES_PATH, {\n params: { Scope: 'all' },\n }, '知乎知识库列表', options)\n const data = parseZhihuEnvelope(body) as { Items?: unknown }\n const items = Array.isArray(data.Items) ? data.Items : []\n return {\n version: ZHIHU_KNOWLEDGE_LIST_VERSION,\n bases: items\n .filter((item): item is Record<string, unknown> => !!item && typeof item === 'object' && !Array.isArray(item))\n .map(normalizeBase)\n .filter((base) => base.id.length > 0),\n }\n}\n\nexport type ZhihuKnowledgeUploadInput = {\n fileName: string\n data: Uint8Array\n /** 指定目标知识库;缺省进默认库。 */\n knowledgeBaseId?: string\n}\n\nexport type ZhihuKnowledgeUpload = {\n version: typeof ZHIHU_KNOWLEDGE_UPLOAD_VERSION\n knowledgeBaseId: string\n recallContentId: string\n fileName: string\n fileSize: number\n title: string\n abstract: string\n originUrl: string\n}\n\n/** 校验文件名与大小,返回小写扩展名。 */\nexport function checkKnowledgeFile(fileName: string, size: number): string {\n const cleaned = fileName.trim()\n if (!cleaned) throw new ZhihuSearchError('BAD_RESPONSE', '知识库文件名不能为空。')\n // 去掉路径成分,只保留文件名\n const base = cleaned.split(/[\\\\/]/).pop() ?? cleaned\n for (const ch of base) {\n const code = ch.codePointAt(0) ?? 0\n if (code < 32 || code === 127) throw new ZhihuSearchError('BAD_RESPONSE', '知识库文件名不能包含控制字符。')\n }\n if (new TextEncoder().encode(base).length > 255) {\n throw new ZhihuSearchError('BAD_RESPONSE', '知识库文件名不能超过 255 个 UTF-8 字节。')\n }\n if (size <= 0) throw new ZhihuSearchError('BAD_RESPONSE', '知识库文件内容不能为空。')\n if (size > ZHIHU_KNOWLEDGE_UPLOAD_MAX_BYTES) {\n throw new ZhihuSearchError('BAD_RESPONSE', `知识库文件大小不能超过 ${ZHIHU_KNOWLEDGE_UPLOAD_MAX_BYTES / 1024 / 1024}MB。`)\n }\n const dot = base.lastIndexOf('.')\n const ext = dot >= 0 ? base.slice(dot).toLowerCase() : ''\n if (!(ZHIHU_KNOWLEDGE_FILE_EXTENSIONS as readonly string[]).includes(ext)) {\n throw new ZhihuSearchError('BAD_RESPONSE', `知识库上传仅支持 ${ZHIHU_KNOWLEDGE_FILE_EXTENSIONS.join('、')}。`)\n }\n return ext\n}\n\nexport async function uploadZhihuKnowledgeFile(\n input: ZhihuKnowledgeUploadInput,\n options: ZhihuClientOptions = {},\n): Promise<ZhihuKnowledgeUpload> {\n const cleaned = (input.fileName.trim().split(/[\\\\/]/).pop() ?? input.fileName.trim())\n const ext = checkKnowledgeFile(input.fileName, input.data.byteLength)\n const form = new FormData()\n if (input.knowledgeBaseId) form.set('KnowledgeBaseID', input.knowledgeBaseId)\n form.set('File', new Blob([input.data.buffer as ArrayBuffer], { type: KNOWLEDGE_CONTENT_TYPES[ext] ?? 'application/octet-stream' }), cleaned)\n const body = await zhihuFetchJson(KNOWLEDGE_FILES_PATH, {\n formData: form,\n }, '知乎知识库上传', { ...options, timeoutMs: options.timeoutMs ?? ZHIHU_KNOWLEDGE_UPLOAD_TIMEOUT_MS })\n const data = parseZhihuEnvelope(body) as Record<string, unknown>\n return {\n version: ZHIHU_KNOWLEDGE_UPLOAD_VERSION,\n knowledgeBaseId: typeof data.KnowledgeBaseID === 'string' ? data.KnowledgeBaseID : '',\n recallContentId: typeof data.RecallContentID === 'string' ? data.RecallContentID : '',\n fileName: typeof data.FileName === 'string' ? data.FileName : cleaned,\n fileSize: typeof data.FileSize === 'number' ? data.FileSize : input.data.byteLength,\n title: typeof data.Title === 'string' ? data.Title : '',\n abstract: typeof data.Abstract === 'string' ? data.Abstract : '',\n originUrl: typeof data.OriginUrl === 'string' ? data.OriginUrl : '',\n }\n}\n","import type { IncomingMessage, ServerResponse } from 'node:http'\n\nconst CHANNEL_PATTERN = /^\\/[A-Za-z0-9._~-]+$/\nconst ENDPOINT_SEGMENT = /^[A-Za-z0-9_$.-]+$/\nconst MAX_BODY_BYTES = 64 * 1024 * 1024\n\nexport type HostRpcHandler = (endpoint: string, payload: unknown, signal: AbortSignal) => Promise<unknown>\n\nexport type HostRpcContext = {\n webServer: {\n register: (route: {\n kind: 'prefix'\n path: string\n handler: (req: IncomingMessage, res: ServerResponse) => void | Promise<void>\n }) => () => void\n }\n connection: {\n requestRejection?: (request: IncomingMessage) => number | undefined\n }\n}\n\n/** Standalone copy of manuscript Host RPC registration; this package cannot depend on manuscript. */\nexport function registerHostRpc(ctx: HostRpcContext, channel: string, handler: HostRpcHandler): () => void {\n if (!CHANNEL_PATTERN.test(channel) || channel === '/api') {\n throw new Error(`invalid RPC channel ${JSON.stringify(channel)}`)\n }\n return ctx.webServer.register({\n kind: 'prefix',\n path: channel,\n handler: (req, res) => dispatchHostRpc(ctx, channel, handler, req, res),\n })\n}\n\nfunction endpointFromRpcPath(channel: string, pathname: string): string | undefined {\n if (!pathname.startsWith(`${channel}/`)) return undefined\n const endpoint = pathname.slice(channel.length + 1)\n if (endpoint.split('/').some((segment) => segment === '' || segment === '.' || segment === '..' || !ENDPOINT_SEGMENT.test(segment))) {\n return undefined\n }\n return endpoint\n}\n\nasync function dispatchHostRpc(\n ctx: HostRpcContext,\n channel: string,\n handler: HostRpcHandler,\n req: IncomingMessage,\n res: ServerResponse,\n): Promise<void> {\n if (typeof ctx.connection.requestRejection !== 'function') {\n res.writeHead(503)\n res.end('host authorization policy is unavailable')\n return\n }\n const rejection = ctx.connection.requestRejection(req)\n if (rejection !== undefined) {\n res.writeHead(rejection)\n res.end(rejection === 401 ? 'unauthorized' : 'forbidden')\n return\n }\n const abort = new AbortController()\n res.on('close', () => {\n if (!res.writableEnded) abort.abort()\n })\n const url = new URL(req.url ?? '/', 'http://dsh.internal')\n const endpoint = endpointFromRpcPath(channel, url.pathname)\n if (req.method !== 'POST' || endpoint === undefined) {\n res.writeHead(404)\n res.end('not found')\n return\n }\n const contentType = String(req.headers['content-type'] ?? '').split(';', 1)[0]?.trim().toLowerCase()\n if (contentType !== 'application/json') {\n res.writeHead(415)\n res.end('content type must be application/json')\n return\n }\n const chunks: Buffer[] = []\n let received = 0\n for await (const chunk of req) {\n const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)\n received += buffer.byteLength\n if (received > MAX_BODY_BYTES) {\n res.writeHead(413, { connection: 'close' })\n res.end()\n req.destroy()\n return\n }\n chunks.push(buffer)\n }\n let body: unknown\n try {\n body = JSON.parse(Buffer.concat(chunks).toString('utf8') || 'null')\n } catch {\n res.writeHead(400)\n res.end('body is not JSON')\n return\n }\n const record = body && typeof body === 'object' && !Array.isArray(body) ? body as Record<string, unknown> : {}\n const rpcId = typeof record.rpcId === 'string' ? record.rpcId : 'invalid-request'\n if (record.type !== 'client-request' || record.method !== endpoint) {\n res.writeHead(200, { 'content-type': 'application/json' })\n res.end(JSON.stringify({\n type: 'server-response',\n rpcId,\n result: {\n ok: false,\n error: {\n code: 'gateway/bad-request',\n message: record.method !== endpoint\n ? `method ${JSON.stringify(record.method)} does not match endpoint ${JSON.stringify(endpoint)}`\n : 'invalid client-request message',\n details: { issues: [] },\n },\n },\n }))\n return\n }\n try {\n const result = await handler(endpoint, record.payload, abort.signal)\n res.writeHead(200, { 'content-type': 'application/json' })\n res.end(JSON.stringify({ type: 'server-response', rpcId, result }))\n } catch (error) {\n res.writeHead(500)\n res.end(`handler failure: ${String(error)}`)\n }\n}\n","/**\n * Optional web-search-manager backend. Registers only when this profile already\n * provides `webSearchManager`; Zhihu still works without that plugin.\n * Official model tool remains `web_search`. Dedicated `zhihu_global_search` is unchanged.\n */\nimport { ZHIHU_CREDENTIAL_REF } from './contracts.ts'\nimport { reportExecuted, type ZhihuClientOptions, type ZhihuSearchExecuted } from './zhihu-client.ts'\nimport {\n executeZhihuGlobalSearch,\n ZHIHU_GLOBAL_SEARCH_MAX_COUNT,\n} from './operations.ts'\n\ntype ZhihuWebSearchHost = {\n run<T>(execute: (signal: AbortSignal) => Promise<T>, signal: AbortSignal): Promise<T>\n toolOptions: ZhihuClientOptions & { onExecuted: (event: ZhihuSearchExecuted) => void }\n}\n\nexport const ZHIHU_WEB_SEARCH_ID = 'zhihu-global'\n\nexport const ZHIHU_WEB_SEARCH_DESCRIPTOR = {\n id: ZHIHU_WEB_SEARCH_ID,\n label: '知乎全网搜索',\n description: '融合知乎问答与全网内容,返回可溯源的搜索结果。',\n credentialRef: ZHIHU_CREDENTIAL_REF,\n credentialShared: true,\n credentialHint: '与「知乎资料」共用 Access Secret',\n billing: 'request' as const,\n pricing: '注册可获 5,000 次/天试用额度;超额价格需向平台咨询。',\n pricingUrl: 'https://developer.zhihu.com/',\n signupUrl: 'https://developer.zhihu.com',\n}\n\nexport type ZhihuWebSearchSource = { url: string; title?: string; snippet?: string }\nexport type ZhihuWebSearchResult = { sources: ZhihuWebSearchSource[]; truncated?: boolean }\nexport type ZhihuWebSearchRequest = { query: string; maxResults?: number }\n\nexport type ZhihuWebSearchProvider = {\n readonly id: string\n available(): boolean\n search(request: ZhihuWebSearchRequest, signal?: AbortSignal): Promise<ZhihuWebSearchResult>\n}\n\nexport type ZhihuWebSearchManager = {\n registerSearchProvider(\n descriptor: typeof ZHIHU_WEB_SEARCH_DESCRIPTOR,\n factory: (options: { apiKey?: string; timeoutMs: number }) => ZhihuWebSearchProvider,\n ): () => void\n}\n\nexport type ZhihuWebSearchProviderOptions = ZhihuClientOptions & {\n apiKey?: string\n onExecuted?: (event: ZhihuSearchExecuted) => void\n}\n\nfunction sourceFrom(url: string, title: string, snippet: string): ZhihuWebSearchSource | undefined {\n let parsed: URL\n try { parsed = new URL(url) } catch { return }\n if (!['https:', 'http:'].includes(parsed.protocol) || parsed.username || parsed.password) return\n return {\n url: parsed.href,\n ...(title ? { title: title.slice(0, 1000) } : {}),\n ...(snippet ? { snippet: snippet.slice(0, 8000) } : {}),\n }\n}\n\nfunction isWebSearchManager(value: unknown): value is ZhihuWebSearchManager {\n return !!value && typeof value === 'object' && typeof (value as ZhihuWebSearchManager).registerSearchProvider === 'function'\n}\n\nexport function createZhihuWebSearchProvider(options: ZhihuWebSearchProviderOptions): ZhihuWebSearchProvider {\n const { apiKey, onExecuted, ...client } = options\n return {\n id: ZHIHU_WEB_SEARCH_ID,\n available() { return Boolean(apiKey?.trim()) },\n async search(request, signal) {\n if (!apiKey?.trim()) throw new Error('知乎全网搜索未配置 Access Secret。')\n if (!request.query?.trim() || request.query.length > 4000) throw new Error('查询无效。')\n const count = request.maxResults ?? 5\n if (!Number.isInteger(count) || count < 1 || count > ZHIHU_GLOBAL_SEARCH_MAX_COUNT) {\n throw new Error(`结果数须为 1 至 ${ZHIHU_GLOBAL_SEARCH_MAX_COUNT}。`)\n }\n try {\n signal?.throwIfAborted()\n const result = await executeZhihuGlobalSearch(request.query.trim(), count, 'all', {\n ...client,\n signal,\n env: {},\n resolveCredential: async () => apiKey,\n })\n reportExecuted(onExecuted, { ok: true, results: result.items.length })\n const sources = result.items.flatMap(item => sourceFrom(item.url, item.title, item.summary) ?? [])\n return { sources: sources.slice(0, count), truncated: sources.length > count }\n } catch (error) {\n reportExecuted(onExecuted, { ok: false, results: 0 })\n throw error\n }\n },\n }\n}\n\nexport function registerZhihuGlobalSearchProvider(\n manager: ZhihuWebSearchManager,\n service: ZhihuWebSearchHost,\n): () => void {\n return manager.registerSearchProvider(ZHIHU_WEB_SEARCH_DESCRIPTOR, options => {\n const provider = createZhihuWebSearchProvider({\n ...service.toolOptions,\n apiKey: options.apiKey,\n timeoutMs: options.timeoutMs,\n })\n return {\n id: provider.id,\n available: () => provider.available(),\n search: (request, signal) => service.run(\n combined => provider.search(request, combined),\n signal ?? new AbortController().signal,\n ),\n }\n })\n}\n\n/** Wait for `webSearchManager` if this profile loaded it; no-op otherwise. */\nexport function bindZhihuWebSearch(\n ctx: { inject(deps: readonly string[], apply: (inner: { get(name: string): unknown }) => (() => void) | void): unknown },\n service: ZhihuWebSearchHost,\n): void {\n ctx.inject(['webSearchManager'], (inner) => {\n let manager: unknown\n try { manager = inner.get('webSearchManager') }\n catch { return }\n if (!isWebSearchManager(manager)) return\n return registerZhihuGlobalSearchProvider(manager, service)\n })\n}\n","import type { Context } from '@deepseek-ai/cordis'\nimport { credentialRef } from '@deepseek-ai/dsh-credentials'\nimport { executeZhihuSearch, ZHIHU_SEARCH_DEFAULT_COUNT } from './search-api.ts'\nimport {\n executeZhihuGlobalSearch, executeZhihuHotList, executeZhihuAsk, executeZhihuKnowledgeSearch,\n normalizeRecallScopes, ZHIHU_ASK_MODELS, ZHIHU_ASK_DEFAULT_MODEL,\n} from './operations.ts'\nimport { listZhihuKnowledgeBases, uploadZhihuKnowledgeFile, ZHIHU_KNOWLEDGE_UPLOAD_MAX_BYTES } from './zhihu-knowledge.ts'\nimport { type ZhihuClientOptions, type ZhihuSearchExecuted, ZhihuSearchError } from './zhihu-client.ts'\nimport { createZhihuUsageRecorder, resolveDays, zhihuUsageDomainSpec, type ZhihuUsageRecorder } from './usage.ts'\nimport { ZHIHU_RPC_CHANNEL, ZHIHU_CREDENTIAL_REF, ZHIHU_SEARCH_EVENT, type ZhihuRpcResult } from './contracts.ts'\nimport { registerHostRpc, type HostRpcContext } from './host-rpc.ts'\nimport { bindZhihuWebSearch } from './web-search-provider.ts'\nimport { executeZhihuOpenPlatform, resolveZhihuOpenPlatformOperation, ZHIHU_OPEN_PLATFORM_APIS, zhihuOpenPlatformResultCount } from './open-platform.ts'\n\nexport const name = '@klarkxy/dsh-zhihu'\nexport const inject = ['connection', 'credentials', 'storageDomain', 'webServer'] as const\nexport type ZhihuService = {\n call(endpoint: string, payload: unknown, signal: AbortSignal): Promise<ZhihuRpcResult>\n run<T>(execute: (signal: AbortSignal) => Promise<T>, signal: AbortSignal): Promise<T>\n dispose(): Promise<void>\n usageSummary(days: unknown): Promise<{ days: unknown[] }>\n toolOptions: ZhihuClientOptions & { onExecuted: (event: ZhihuSearchExecuted) => void }\n}\n\ntype Host = Context & HostRpcContext & {\n credentials: { resolve: (ref: ReturnType<typeof credentialRef>) => Promise<{ value: string } | undefined> }\n}\nconst fail = (code: string, message: string, details: Record<string, unknown> = {}): ZhihuRpcResult => ({ ok: false, error: { code, message, details } })\n\n/** One shared service for direct UI RPC and optional Tool wrappers. */\nexport function createZhihuService(options: ZhihuClientOptions, usage: ZhihuUsageRecorder, notify: (event: ZhihuSearchExecuted) => void = () => {}): ZhihuService {\n // Serialize read-modify-write counters; concurrent tools must not lose increments.\n let pendingUsage = Promise.resolve()\n const lifetime = new AbortController()\n const active = new Set<Promise<unknown>>()\n const run: ZhihuService['run'] = (execute, signal) => {\n const combined = AbortSignal.any([signal, lifetime.signal])\n const request = Promise.resolve().then(() => { combined.throwIfAborted(); return execute(combined) })\n active.add(request)\n return request.finally(() => active.delete(request))\n }\n const record = (event: ZhihuSearchExecuted) => {\n pendingUsage = pendingUsage.then(() => usage.record(event)).catch(() => {})\n try { notify(event) } catch { /* observer failure does not change a request */ }\n }\n const service = {\n toolOptions: { ...options, onExecuted: record },\n async usageSummary(days: unknown) { await pendingUsage; return { days: await usage.read(resolveDays(days)) } },\n async call(endpoint: string, payload: unknown, signal: AbortSignal): Promise<ZhihuRpcResult> {\n if (signal.aborted) return fail('cancelled', '请求已取消')\n const body = payload && typeof payload === 'object' && !Array.isArray(payload) ? payload as Record<string, unknown> : {}\n const client = { ...options, signal }\n let counted = false\n const operation = resolveZhihuOpenPlatformOperation(endpoint)\n const quotaId = operation ? ZHIHU_OPEN_PLATFORM_APIS[operation].quotaId : undefined\n const metering = quotaId ? { quotaId } : {}\n try {\n if (endpoint === 'usage.summary') { await pendingUsage; return { ok: true, value: { days: await usage.read(resolveDays(body.days)) } } }\n const query = typeof body.query === 'string' ? body.query : ''\n if (['search', 'global.search', 'ask', 'knowledge.search'].includes(endpoint) && (!query.trim() || query.length > 4000)) {\n return fail('bad-request', '查询不超过 4000 字符')\n }\n let value: unknown\n let results = 0\n counted = true\n if (operation) {\n counted = operation !== 'quota'\n const result = await executeZhihuOpenPlatform(operation, payload === undefined ? {} : payload, client)\n value = result; results = zhihuOpenPlatformResultCount(result)\n } else if (endpoint === 'search') {\n const result = await executeZhihuSearch(query, typeof body.count === 'number' ? body.count : ZHIHU_SEARCH_DEFAULT_COUNT, client)\n value = result; results = result.items.length\n } else if (endpoint === 'global.search') {\n const result = await executeZhihuGlobalSearch(query, typeof body.count === 'number' ? body.count : 10,\n body.searchDb === 'realtime' || body.searchDb === 'static' ? body.searchDb : 'all', client)\n value = result; results = result.items.length\n } else if (endpoint === 'hot.list') {\n const result = await executeZhihuHotList(typeof body.limit === 'number' ? body.limit : 10, client)\n value = result; results = result.items.length\n } else if (endpoint === 'ask') {\n const model = (ZHIHU_ASK_MODELS as readonly unknown[]).includes(body.model) ? body.model as typeof ZHIHU_ASK_MODELS[number] : ZHIHU_ASK_DEFAULT_MODEL\n value = await executeZhihuAsk(query, model, client); results = 1\n } else if (endpoint === 'knowledge.search') {\n const result = await executeZhihuKnowledgeSearch(query, typeof body.limit === 'number' ? body.limit : 5, normalizeRecallScopes(body.recallScopes), client)\n value = result; results = result.items.length\n } else if (endpoint === 'knowledge.bases') {\n const result = await listZhihuKnowledgeBases(client); value = result; results = result.bases.length\n } else if (endpoint === 'knowledge.upload') {\n const contentBase64 = typeof body.contentBase64 === 'string' ? body.contentBase64 : ''\n if (!contentBase64) throw new Error('缺少文件内容。')\n if (contentBase64.length > Math.ceil(ZHIHU_KNOWLEDGE_UPLOAD_MAX_BYTES / 3) * 4 || !/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(contentBase64)) {\n return fail('bad-request', '文件内容无效或超过 20 MB')\n }\n const upload = await uploadZhihuKnowledgeFile({\n fileName: typeof body.fileName === 'string' ? body.fileName : '',\n data: new Uint8Array(Buffer.from(contentBase64, 'base64')),\n knowledgeBaseId: typeof body.knowledgeBaseId === 'string' && body.knowledgeBaseId.trim() ? body.knowledgeBaseId.trim() : undefined,\n }, client)\n value = { upload }; results = 1\n } else {\n counted = false\n return fail('bad-request', `unknown endpoint ${endpoint}`)\n }\n if (counted) record({ ok: true, results, ...metering })\n return { ok: true, value }\n } catch (error) {\n if (counted) record({ ok: false, results: 0, ...metering })\n if (signal.aborted) return fail('cancelled', '请求已取消')\n const code = error instanceof ZhihuSearchError ? error.code.toLowerCase().replaceAll('_', '-') : 'internal'\n const details: Record<string, unknown> = {}\n if (error instanceof ZhihuSearchError) {\n for (const key of ['status', 'upstreamCode', 'retryAfter', 'retryable'] as const) {\n if (error[key] !== undefined) details[key] = error[key]\n }\n }\n return fail(code, error instanceof Error ? error.message : '知乎请求失败', details)\n }\n },\n }\n return {\n ...service,\n run,\n async call(endpoint, payload, signal) {\n if (signal.aborted || lifetime.signal.aborted) return fail('cancelled', '请求已取消')\n try { return await run(combined => service.call(endpoint, payload, combined), signal) }\n catch (error) {\n if (signal.aborted || lifetime.signal.aborted) return fail('cancelled', '请求已取消')\n throw error\n }\n },\n async dispose() {\n lifetime.abort()\n await Promise.allSettled([...active])\n await pendingUsage\n },\n }\n}\n\nexport async function apply(ctx: Context): Promise<void> {\n const host = ctx as Host\n const domain = await ctx.storageDomain.open(zhihuUsageDomainSpec)\n const service = createZhihuService({\n resolveCredential: async () => (await host.credentials.resolve(credentialRef(ZHIHU_CREDENTIAL_REF)))?.value,\n }, createZhihuUsageRecorder(domain.table('daily')), event => {\n const emit = ctx.emit as unknown as (name: string, event: ZhihuSearchExecuted) => void\n emit.call(ctx, ZHIHU_SEARCH_EVENT, event)\n })\n ctx.effect(() => async () => { await service.dispose(); await domain.close() }, 'zhihu.usageDomainClose')\n ctx.provide('zhihu', service)\n ctx.effect(() => registerHostRpc(host, ZHIHU_RPC_CHANNEL, service.call))\n bindZhihuWebSearch(ctx, service)\n}\n"],"mappings":";;;;;AAkBA,MAAa,mCAAmC;AAGhD,MAAM,uBAAuB;AAC7B,MAAM,uBAAuB;AAE7B,MAAa,kCAAkC;CAC7C;CAAQ;CAAO;CAAQ;CAAQ;CAAS;CAAS;CAAQ;CAAS;CAClE;CAAS;CAAQ;CAAQ;CAAS;CAAS;CAAQ;AACrD;AAEA,MAAM,0BAAkD;CACtD,QAAQ;CACR,OAAO;CACP,QAAQ;CACR,QAAQ;CACR,SAAS;CACT,SAAS;CACT,QAAQ;CACR,SAAS;CACT,QAAQ;CACR,SAAS;CACT,QAAQ;CACR,QAAQ;CACR,SAAS;CACT,SAAS;CACT,QAAQ;CACR,SAAS;AACX;AAgBA,SAAS,cAAc,KAAkD;CACvE,OAAO;EACL,IAAI,OAAO,IAAI,oBAAoB,WAAW,IAAI,kBAAkB;EACpE,MAAM,OAAO,IAAI,SAAS,YAAY,IAAI,KAAK,SAAS,IAAI,IAAI,OAAO;EACvE,UAAU,OAAO,IAAI,aAAa,WAAW,IAAI,WAAW;EAC5D,YAAY,OAAO,IAAI,eAAe,WAAW,IAAI,aAAa;EAClE,WAAW,IAAI,cAAc;EAC7B,cAAc,OAAO,IAAI,iBAAiB,WAAW,IAAI,eAAe;CAC1E;AACF;AAEA,eAAsB,wBAAwB,UAA8B,CAAC,GAAoC;CAC/G,MAAM,OAAO,MAAM,eAAe,sBAAsB,EACtD,QAAQ,EAAE,OAAO,MAAM,EACzB,GAAG,WAAW,OAAO;CACrB,MAAM,OAAO,mBAAmB,IAAI;CAEpC,OAAO;EACL,SAAA;EACA,QAHY,MAAM,QAAQ,KAAK,KAAK,IAAI,KAAK,QAAQ,CAAC,EAG/C,CACJ,QAAQ,SAA0C,CAAC,CAAC,QAAQ,OAAO,SAAS,YAAY,CAAC,MAAM,QAAQ,IAAI,CAAC,CAAC,CAC7G,IAAI,aAAa,CAAC,CAClB,QAAQ,SAAS,KAAK,GAAG,SAAS,CAAC;CACxC;AACF;;AAqBA,SAAgB,mBAAmB,UAAkB,MAAsB;CACzE,MAAM,UAAU,SAAS,KAAK;CAC9B,IAAI,CAAC,SAAS,MAAM,IAAI,iBAAiB,gBAAgB,aAAa;CAEtE,MAAM,OAAO,QAAQ,MAAM,OAAO,CAAC,CAAC,IAAI,KAAK;CAC7C,KAAK,MAAM,MAAM,MAAM;EACrB,MAAM,OAAO,GAAG,YAAY,CAAC,KAAK;EAClC,IAAI,OAAO,MAAM,SAAS,KAAK,MAAM,IAAI,iBAAiB,gBAAgB,iBAAiB;CAC7F;CACA,IAAI,IAAI,YAAY,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,SAAS,KAC1C,MAAM,IAAI,iBAAiB,gBAAgB,4BAA4B;CAEzE,IAAI,QAAQ,GAAG,MAAM,IAAI,iBAAiB,gBAAgB,cAAc;CACxE,IAAI,OAAA,UACF,MAAM,IAAI,iBAAiB,gBAAgB,eAAe,mCAAmC,OAAO,KAAK,IAAI;CAE/G,MAAM,MAAM,KAAK,YAAY,GAAG;CAChC,MAAM,MAAM,OAAO,IAAI,KAAK,MAAM,GAAG,CAAC,CAAC,YAAY,IAAI;CACvD,IAAI,CAAE,gCAAsD,SAAS,GAAG,GACtE,MAAM,IAAI,iBAAiB,gBAAgB,YAAY,gCAAgC,KAAK,GAAG,EAAE,EAAE;CAErG,OAAO;AACT;AAEA,eAAsB,yBACpB,OACA,UAA8B,CAAC,GACA;CAC/B,MAAM,UAAW,MAAM,SAAS,KAAK,CAAC,CAAC,MAAM,OAAO,CAAC,CAAC,IAAI,KAAK,MAAM,SAAS,KAAK;CACnF,MAAM,MAAM,mBAAmB,MAAM,UAAU,MAAM,KAAK,UAAU;CACpE,MAAM,OAAO,IAAI,SAAS;CAC1B,IAAI,MAAM,iBAAiB,KAAK,IAAI,mBAAmB,MAAM,eAAe;CAC5E,KAAK,IAAI,QAAQ,IAAI,KAAK,CAAC,MAAM,KAAK,MAAqB,GAAG,EAAE,MAAM,wBAAwB,QAAQ,2BAA2B,CAAC,GAAG,OAAO;CAC5I,MAAM,OAAO,MAAM,eAAe,sBAAsB,EACtD,UAAU,KACZ,GAAG,WAAW;EAAE,GAAG;EAAS,WAAW,QAAQ,aAAA;CAA+C,CAAC;CAC/F,MAAM,OAAO,mBAAmB,IAAI;CACpC,OAAO;EACL,SAAA;EACA,iBAAiB,OAAO,KAAK,oBAAoB,WAAW,KAAK,kBAAkB;EACnF,iBAAiB,OAAO,KAAK,oBAAoB,WAAW,KAAK,kBAAkB;EACnF,UAAU,OAAO,KAAK,aAAa,WAAW,KAAK,WAAW;EAC9D,UAAU,OAAO,KAAK,aAAa,WAAW,KAAK,WAAW,MAAM,KAAK;EACzE,OAAO,OAAO,KAAK,UAAU,WAAW,KAAK,QAAQ;EACrD,UAAU,OAAO,KAAK,aAAa,WAAW,KAAK,WAAW;EAC9D,WAAW,OAAO,KAAK,cAAc,WAAW,KAAK,YAAY;CACnE;AACF;;;ACxJA,MAAM,kBAAkB;AACxB,MAAM,mBAAmB;AACzB,MAAM,iBAAiB;;AAkBvB,SAAgB,gBAAgB,KAAqB,SAAiB,SAAqC;CACzG,IAAI,CAAC,gBAAgB,KAAK,OAAO,KAAK,YAAY,QAChD,MAAM,IAAI,MAAM,uBAAuB,KAAK,UAAU,OAAO,GAAG;CAElE,OAAO,IAAI,UAAU,SAAS;EAC5B,MAAM;EACN,MAAM;EACN,UAAU,KAAK,QAAQ,gBAAgB,KAAK,SAAS,SAAS,KAAK,GAAG;CACxE,CAAC;AACH;AAEA,SAAS,oBAAoB,SAAiB,UAAsC;CAClF,IAAI,CAAC,SAAS,WAAW,GAAG,QAAQ,EAAE,GAAG,OAAO,KAAA;CAChD,MAAM,WAAW,SAAS,MAAM,QAAQ,SAAS,CAAC;CAClD,IAAI,SAAS,MAAM,GAAG,CAAC,CAAC,MAAM,YAAY,YAAY,MAAM,YAAY,OAAO,YAAY,QAAQ,CAAC,iBAAiB,KAAK,OAAO,CAAC,GAChI;CAEF,OAAO;AACT;AAEA,eAAe,gBACb,KACA,SACA,SACA,KACA,KACe;CACf,IAAI,OAAO,IAAI,WAAW,qBAAqB,YAAY;EACzD,IAAI,UAAU,GAAG;EACjB,IAAI,IAAI,0CAA0C;EAClD;CACF;CACA,MAAM,YAAY,IAAI,WAAW,iBAAiB,GAAG;CACrD,IAAI,cAAc,KAAA,GAAW;EAC3B,IAAI,UAAU,SAAS;EACvB,IAAI,IAAI,cAAc,MAAM,iBAAiB,WAAW;EACxD;CACF;CACA,MAAM,QAAQ,IAAI,gBAAgB;CAClC,IAAI,GAAG,eAAe;EACpB,IAAI,CAAC,IAAI,eAAe,MAAM,MAAM;CACtC,CAAC;CAED,MAAM,WAAW,oBAAoB,SAAS,IAD9B,IAAI,IAAI,OAAO,KAAK,qBACY,CAAC,CAAC,QAAQ;CAC1D,IAAI,IAAI,WAAW,UAAU,aAAa,KAAA,GAAW;EACnD,IAAI,UAAU,GAAG;EACjB,IAAI,IAAI,WAAW;EACnB;CACF;CAEA,IADoB,OAAO,IAAI,QAAQ,mBAAmB,EAAE,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC,YAAY,MAC/E,oBAAoB;EACtC,IAAI,UAAU,GAAG;EACjB,IAAI,IAAI,uCAAuC;EAC/C;CACF;CACA,MAAM,SAAmB,CAAC;CAC1B,IAAI,WAAW;CACf,WAAW,MAAM,SAAS,KAAK;EAC7B,MAAM,SAAS,OAAO,SAAS,KAAK,IAAI,QAAQ,OAAO,KAAK,KAAK;EACjE,YAAY,OAAO;EACnB,IAAI,WAAW,gBAAgB;GAC7B,IAAI,UAAU,KAAK,EAAE,YAAY,QAAQ,CAAC;GAC1C,IAAI,IAAI;GACR,IAAI,QAAQ;GACZ;EACF;EACA,OAAO,KAAK,MAAM;CACpB;CACA,IAAI;CACJ,IAAI;EACF,OAAO,KAAK,MAAM,OAAO,OAAO,MAAM,CAAC,CAAC,SAAS,MAAM,KAAK,MAAM;CACpE,QAAQ;EACN,IAAI,UAAU,GAAG;EACjB,IAAI,IAAI,kBAAkB;EAC1B;CACF;CACA,MAAM,SAAS,QAAQ,OAAO,SAAS,YAAY,CAAC,MAAM,QAAQ,IAAI,IAAI,OAAkC,CAAC;CAC7G,MAAM,QAAQ,OAAO,OAAO,UAAU,WAAW,OAAO,QAAQ;CAChE,IAAI,OAAO,SAAS,oBAAoB,OAAO,WAAW,UAAU;EAClE,IAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;EACzD,IAAI,IAAI,KAAK,UAAU;GACrB,MAAM;GACN;GACA,QAAQ;IACN,IAAI;IACJ,OAAO;KACL,MAAM;KACN,SAAS,OAAO,WAAW,WACvB,UAAU,KAAK,UAAU,OAAO,MAAM,EAAE,2BAA2B,KAAK,UAAU,QAAQ,MAC1F;KACJ,SAAS,EAAE,QAAQ,CAAC,EAAE;IACxB;GACF;EACF,CAAC,CAAC;EACF;CACF;CACA,IAAI;EACF,MAAM,SAAS,MAAM,QAAQ,UAAU,OAAO,SAAS,MAAM,MAAM;EACnE,IAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;EACzD,IAAI,IAAI,KAAK,UAAU;GAAE,MAAM;GAAmB;GAAO;EAAO,CAAC,CAAC;CACpE,SAAS,OAAO;EACd,IAAI,UAAU,GAAG;EACjB,IAAI,IAAI,oBAAoB,OAAO,KAAK,GAAG;CAC7C;AACF;;;;;;;;AC7GA,MAAa,sBAAsB;AAEnC,MAAa,8BAA8B;CACzC,IAAI;CACJ,OAAO;CACP,aAAa;CACb,eAAe;CACf,kBAAkB;CAClB,gBAAgB;CAChB,SAAS;CACT,SAAS;CACT,YAAY;CACZ,WAAW;AACb;AAwBA,SAAS,WAAW,KAAa,OAAe,SAAmD;CACjG,IAAI;CACJ,IAAI;EAAE,SAAS,IAAI,IAAI,GAAG;CAAE,QAAQ;EAAE;CAAO;CAC7C,IAAI,CAAC,CAAC,UAAU,OAAO,CAAC,CAAC,SAAS,OAAO,QAAQ,KAAK,OAAO,YAAY,OAAO,UAAU;CAC1F,OAAO;EACL,KAAK,OAAO;EACZ,GAAI,QAAQ,EAAE,OAAO,MAAM,MAAM,GAAG,GAAI,EAAE,IAAI,CAAC;EAC/C,GAAI,UAAU,EAAE,SAAS,QAAQ,MAAM,GAAG,GAAI,EAAE,IAAI,CAAC;CACvD;AACF;AAEA,SAAS,mBAAmB,OAAgD;CAC1E,OAAO,CAAC,CAAC,SAAS,OAAO,UAAU,YAAY,OAAQ,MAAgC,2BAA2B;AACpH;AAEA,SAAgB,6BAA6B,SAAgE;CAC3G,MAAM,EAAE,QAAQ,YAAY,GAAG,WAAW;CAC1C,OAAO;EACL,IAAI;EACJ,YAAY;GAAE,OAAO,QAAQ,QAAQ,KAAK,CAAC;EAAE;EAC7C,MAAM,OAAO,SAAS,QAAQ;GAC5B,IAAI,CAAC,QAAQ,KAAK,GAAG,MAAM,IAAI,MAAM,0BAA0B;GAC/D,IAAI,CAAC,QAAQ,OAAO,KAAK,KAAK,QAAQ,MAAM,SAAS,KAAM,MAAM,IAAI,MAAM,OAAO;GAClF,MAAM,QAAQ,QAAQ,cAAc;GACpC,IAAI,CAAC,OAAO,UAAU,KAAK,KAAK,QAAQ,KAAK,QAAA,IAC3C,MAAM,IAAI,MAAM,eAA6C;GAE/D,IAAI;IACF,QAAQ,eAAe;IACvB,MAAM,SAAS,MAAM,yBAAyB,QAAQ,MAAM,KAAK,GAAG,OAAO,OAAO;KAChF,GAAG;KACH;KACA,KAAK,CAAC;KACN,mBAAmB,YAAY;IACjC,CAAC;IACD,eAAe,YAAY;KAAE,IAAI;KAAM,SAAS,OAAO,MAAM;IAAO,CAAC;IACrE,MAAM,UAAU,OAAO,MAAM,SAAQ,SAAQ,WAAW,KAAK,KAAK,KAAK,OAAO,KAAK,OAAO,KAAK,CAAC,CAAC;IACjG,OAAO;KAAE,SAAS,QAAQ,MAAM,GAAG,KAAK;KAAG,WAAW,QAAQ,SAAS;IAAM;GAC/E,SAAS,OAAO;IACd,eAAe,YAAY;KAAE,IAAI;KAAO,SAAS;IAAE,CAAC;IACpD,MAAM;GACR;EACF;CACF;AACF;AAEA,SAAgB,kCACd,SACA,SACY;CACZ,OAAO,QAAQ,uBAAuB,8BAA6B,YAAW;EAC5E,MAAM,WAAW,6BAA6B;GAC5C,GAAG,QAAQ;GACX,QAAQ,QAAQ;GAChB,WAAW,QAAQ;EACrB,CAAC;EACD,OAAO;GACL,IAAI,SAAS;GACb,iBAAiB,SAAS,UAAU;GACpC,SAAS,SAAS,WAAW,QAAQ,KACnC,aAAY,SAAS,OAAO,SAAS,QAAQ,GAC7C,UAAU,IAAI,gBAAgB,CAAC,CAAC,MAClC;EACF;CACF,CAAC;AACH;;AAGA,SAAgB,mBACd,KACA,SACM;CACN,IAAI,OAAO,CAAC,kBAAkB,IAAI,UAAU;EAC1C,IAAI;EACJ,IAAI;GAAE,UAAU,MAAM,IAAI,kBAAkB;EAAE,QACxC;GAAE;EAAO;EACf,IAAI,CAAC,mBAAmB,OAAO,GAAG;EAClC,OAAO,kCAAkC,SAAS,OAAO;CAC3D,CAAC;AACH;;;ACtHA,MAAa,OAAO;AACpB,MAAa,SAAS;CAAC;CAAc;CAAe;CAAiB;AAAW;AAYhF,MAAM,QAAQ,MAAc,SAAiB,UAAmC,CAAC,OAAuB;CAAE,IAAI;CAAO,OAAO;EAAE;EAAM;EAAS;CAAQ;AAAE;;AAGvJ,SAAgB,mBAAmB,SAA6B,OAA2B,eAAqD,CAAC,GAAiB;CAEhK,IAAI,eAAe,QAAQ,QAAQ;CACnC,MAAM,WAAW,IAAI,gBAAgB;CACrC,MAAM,yBAAS,IAAI,IAAsB;CACzC,MAAM,OAA4B,SAAS,WAAW;EACpD,MAAM,WAAW,YAAY,IAAI,CAAC,QAAQ,SAAS,MAAM,CAAC;EAC1D,MAAM,UAAU,QAAQ,QAAQ,CAAC,CAAC,WAAW;GAAE,SAAS,eAAe;GAAG,OAAO,QAAQ,QAAQ;EAAE,CAAC;EACpG,OAAO,IAAI,OAAO;EAClB,OAAO,QAAQ,cAAc,OAAO,OAAO,OAAO,CAAC;CACrD;CACA,MAAM,UAAU,UAA+B;EAC7C,eAAe,aAAa,WAAW,MAAM,OAAO,KAAK,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC;EAC1E,IAAI;GAAE,OAAO,KAAK;EAAE,QAAQ,CAAmD;CACjF;CACA,MAAM,UAAU;EACd,aAAa;GAAE,GAAG;GAAS,YAAY;EAAO;EAC9C,MAAM,aAAa,MAAe;GAAE,MAAM;GAAc,OAAO,EAAE,MAAM,MAAM,MAAM,KAAK,YAAY,IAAI,CAAC,EAAE;EAAE;EAC7G,MAAM,KAAK,UAAkB,SAAkB,QAA8C;GAC3F,IAAI,OAAO,SAAS,OAAO,KAAK,aAAa,OAAO;GACpD,MAAM,OAAO,WAAW,OAAO,YAAY,YAAY,CAAC,MAAM,QAAQ,OAAO,IAAI,UAAqC,CAAC;GACvH,MAAM,SAAS;IAAE,GAAG;IAAS;GAAO;GACpC,IAAI,UAAU;GACd,MAAM,YAAY,kCAAkC,QAAQ;GAC5D,MAAM,UAAU,YAAY,yBAAyB,UAAU,CAAC,UAAU,KAAA;GAC1E,MAAM,WAAW,UAAU,EAAE,QAAQ,IAAI,CAAC;GAC1C,IAAI;IACF,IAAI,aAAa,iBAAiB;KAAE,MAAM;KAAc,OAAO;MAAE,IAAI;MAAM,OAAO,EAAE,MAAM,MAAM,MAAM,KAAK,YAAY,KAAK,IAAI,CAAC,EAAE;KAAE;IAAE;IACvI,MAAM,QAAQ,OAAO,KAAK,UAAU,WAAW,KAAK,QAAQ;IAC5D,IAAI;KAAC;KAAU;KAAiB;KAAO;IAAkB,CAAC,CAAC,SAAS,QAAQ,MAAM,CAAC,MAAM,KAAK,KAAK,MAAM,SAAS,MAChH,OAAO,KAAK,eAAe,eAAe;IAE5C,IAAI;IACJ,IAAI,UAAU;IACd,UAAU;IACV,IAAI,WAAW;KACb,UAAU,cAAc;KACxB,MAAM,SAAS,MAAM,yBAAyB,WAAW,YAAY,KAAA,IAAY,CAAC,IAAI,SAAS,MAAM;KACrG,QAAQ;KAAQ,UAAU,6BAA6B,MAAM;IAC/D,OAAO,IAAI,aAAa,UAAU;KAChC,MAAM,SAAS,MAAM,mBAAmB,OAAO,OAAO,KAAK,UAAU,WAAW,KAAK,QAAA,GAAoC,MAAM;KAC/H,QAAQ;KAAQ,UAAU,OAAO,MAAM;IACzC,OAAO,IAAI,aAAa,iBAAiB;KACvC,MAAM,SAAS,MAAM,yBAAyB,OAAO,OAAO,KAAK,UAAU,WAAW,KAAK,QAAQ,IACjG,KAAK,aAAa,cAAc,KAAK,aAAa,WAAW,KAAK,WAAW,OAAO,MAAM;KAC5F,QAAQ;KAAQ,UAAU,OAAO,MAAM;IACzC,OAAO,IAAI,aAAa,YAAY;KAClC,MAAM,SAAS,MAAM,oBAAoB,OAAO,KAAK,UAAU,WAAW,KAAK,QAAQ,IAAI,MAAM;KACjG,QAAQ;KAAQ,UAAU,OAAO,MAAM;IACzC,OAAO,IAAI,aAAa,OAAO;KAC7B,MAAM,QAAS,iBAAwC,SAAS,KAAK,KAAK,IAAI,KAAK,QAA2C;KAC9H,QAAQ,MAAM,gBAAgB,OAAO,OAAO,MAAM;KAAG,UAAU;IACjE,OAAO,IAAI,aAAa,oBAAoB;KAC1C,MAAM,SAAS,MAAM,4BAA4B,OAAO,OAAO,KAAK,UAAU,WAAW,KAAK,QAAQ,GAAG,sBAAsB,KAAK,YAAY,GAAG,MAAM;KACzJ,QAAQ;KAAQ,UAAU,OAAO,MAAM;IACzC,OAAO,IAAI,aAAa,mBAAmB;KACzC,MAAM,SAAS,MAAM,wBAAwB,MAAM;KAAG,QAAQ;KAAQ,UAAU,OAAO,MAAM;IAC/F,OAAO,IAAI,aAAa,oBAAoB;KAC1C,MAAM,gBAAgB,OAAO,KAAK,kBAAkB,WAAW,KAAK,gBAAgB;KACpF,IAAI,CAAC,eAAe,MAAM,IAAI,MAAM,SAAS;KAC7C,IAAI,cAAc,SAAS,KAAK,KAAA,WAAwC,CAAC,IAAI,KAAK,CAAC,mEAAmE,KAAK,aAAa,GACtK,OAAO,KAAK,eAAe,iBAAiB;KAO9C,QAAQ,EAAE,QAAA,MALW,yBAAyB;MAC5C,UAAU,OAAO,KAAK,aAAa,WAAW,KAAK,WAAW;MAC9D,MAAM,IAAI,WAAW,OAAO,KAAK,eAAe,QAAQ,CAAC;MACzD,iBAAiB,OAAO,KAAK,oBAAoB,YAAY,KAAK,gBAAgB,KAAK,IAAI,KAAK,gBAAgB,KAAK,IAAI,KAAA;KAC3H,GAAG,MAAM,EACQ;KAAG,UAAU;IAChC,OAAO;KACL,UAAU;KACV,OAAO,KAAK,eAAe,oBAAoB,UAAU;IAC3D;IACA,IAAI,SAAS,OAAO;KAAE,IAAI;KAAM;KAAS,GAAG;IAAS,CAAC;IACtD,OAAO;KAAE,IAAI;KAAM;IAAM;GAC3B,SAAS,OAAO;IACd,IAAI,SAAS,OAAO;KAAE,IAAI;KAAO,SAAS;KAAG,GAAG;IAAS,CAAC;IAC1D,IAAI,OAAO,SAAS,OAAO,KAAK,aAAa,OAAO;IACpD,MAAM,OAAO,iBAAiB,mBAAmB,MAAM,KAAK,YAAY,CAAC,CAAC,WAAW,KAAK,GAAG,IAAI;IACjG,MAAM,UAAmC,CAAC;IAC1C,IAAI,iBAAiB,kBACd;UAAA,MAAM,OAAO;MAAC;MAAU;MAAgB;MAAc;KAAW,GACpE,IAAI,MAAM,SAAS,KAAA,GAAW,QAAQ,OAAO,MAAM;IAAA;IAGvD,OAAO,KAAK,MAAM,iBAAiB,QAAQ,MAAM,UAAU,UAAU,OAAO;GAC9E;EACF;CACF;CACA,OAAO;EACL,GAAG;EACH;EACA,MAAM,KAAK,UAAU,SAAS,QAAQ;GACpC,IAAI,OAAO,WAAW,SAAS,OAAO,SAAS,OAAO,KAAK,aAAa,OAAO;GAC/E,IAAI;IAAE,OAAO,MAAM,KAAI,aAAY,QAAQ,KAAK,UAAU,SAAS,QAAQ,GAAG,MAAM;GAAE,SAC/E,OAAO;IACZ,IAAI,OAAO,WAAW,SAAS,OAAO,SAAS,OAAO,KAAK,aAAa,OAAO;IAC/E,MAAM;GACR;EACF;EACA,MAAM,UAAU;GACd,SAAS,MAAM;GACf,MAAM,QAAQ,WAAW,CAAC,GAAG,MAAM,CAAC;GACpC,MAAM;EACR;CACF;AACF;AAEA,eAAsB,MAAM,KAA6B;CACvD,MAAM,OAAO;CACb,MAAM,SAAS,MAAM,IAAI,cAAc,KAAK,oBAAoB;CAChE,MAAM,UAAU,mBAAmB,EACjC,mBAAmB,aAAa,MAAM,KAAK,YAAY,QAAQ,cAAc,oBAAoB,CAAC,EAAA,EAAI,MACxG,GAAG,yBAAyB,OAAO,MAAM,OAAO,CAAC,IAAG,UAAS;EAE3D,IADiB,KACZ,KAAK,KAAK,oBAAoB,KAAK;CAC1C,CAAC;CACD,IAAI,aAAa,YAAY;EAAE,MAAM,QAAQ,QAAQ;EAAG,MAAM,OAAO,MAAM;CAAE,GAAG,wBAAwB;CACxG,IAAI,QAAQ,SAAS,OAAO;CAC5B,IAAI,aAAa,gBAAgB,MAAM,mBAAmB,QAAQ,IAAI,CAAC;CACvE,mBAAmB,KAAK,OAAO;AACjC"}
package/lib/tools.js CHANGED
@@ -48,7 +48,7 @@ function createZhihuSearchTool(options = {}) {
48
48
  const { onExecuted, ...client } = options;
49
49
  return defineTool({
50
50
  name: ZHIHU_SEARCH_TOOL_NAME,
51
- description: "调用知乎开放平台站内搜索(GET /api/v1/content/zhihu_search)拉取社区证据;结果仅作社区/读者反馈参考,不构成 canon,也不直接写入项目文件。",
51
+ description: "知乎站内搜索(GET /api/v1/content/zhihu_search),取社区证据;仅作社区/读者反馈参考,不构成 canon,不写入项目文件。",
52
52
  parameters: {
53
53
  query: {
54
54
  type: "string",
@@ -180,7 +180,7 @@ function createZhihuGlobalSearchTool(options = {}) {
180
180
  const { onExecuted, ...client } = options;
181
181
  return defineTool({
182
182
  name: ZHIHU_GLOBAL_SEARCH_TOOL_NAME,
183
- description: "调用知乎开放平台全网搜索(GET /api/v1/content/global_search)检索站外公开网页资料;结果仅作参考,不构成 canon,也不直接写入项目文件。",
183
+ description: "知乎开放平台全网搜索(GET /api/v1/content/global_search),检索站外公开网页资料;仅作参考,不构成 canon,不写入项目文件。",
184
184
  parameters: {
185
185
  query: {
186
186
  type: "string",
@@ -249,7 +249,7 @@ function createZhihuHotListTool(options = {}) {
249
249
  const { onExecuted, ...client } = options;
250
250
  return defineTool({
251
251
  name: ZHIHU_HOT_LIST_TOOL_NAME,
252
- description: "拉取知乎热榜(GET /api/v1/content/hot_list)了解当前社区热点;结果仅作题材与热点参考,不构成 canon,也不直接写入项目文件。",
252
+ description: "知乎热榜(GET /api/v1/content/hot_list),了解当前社区热点;仅作题材参考,不构成 canon,不写入项目文件。",
253
253
  parameters: { limit: {
254
254
  type: "integer",
255
255
  description: `返回条数,1-30,默认 10。`
@@ -314,7 +314,7 @@ function createZhihuAskTool(options = {}) {
314
314
  const { onExecuted, ...client } = options;
315
315
  return defineTool({
316
316
  name: ZHIHU_ASK_TOOL_NAME,
317
- description: "调用知乎直答(POST /v1/chat/completions,OpenAI 兼容)基于知乎社区内容生成综合回答;适合考据与背景调研。结果仅作参考,不构成 canon,也不直接写入项目文件。",
317
+ description: "知乎直答(POST /v1/chat/completions,OpenAI 兼容),基于社区内容生成综合回答,适合考据与背景调研;仅作参考,不构成 canon,不写入项目文件。",
318
318
  parameters: {
319
319
  query: {
320
320
  type: "string",
@@ -377,7 +377,7 @@ function createZhihuKnowledgeSearchTool(options = {}) {
377
377
  const { onExecuted, ...client } = options;
378
378
  return defineTool({
379
379
  name: ZHIHU_KNOWLEDGE_SEARCH_TOOL_NAME,
380
- description: "检索知乎知识库(POST /api/v1/knowledge/search,RAG 片段),默认只查公开库;用户在知乎网页端上传过个人资料后可加 personal/subscription 召回。结果仅作背景参考,不构成 canon,也不直接写入项目文件。",
380
+ description: "知乎知识库检索(POST /api/v1/knowledge/search,RAG 片段),默认只查公开库;网页端上传过资料后可加 personal/subscription 召回;仅作背景参考,不构成 canon,不写入项目文件。",
381
381
  parameters: {
382
382
  query: {
383
383
  type: "string",
@@ -494,7 +494,7 @@ function renderZhihuOpenPlatform(result) {
494
494
  notes.push("省略主题按当前账号画像推荐;条目可能少于请求数量,不支持翻页。");
495
495
  break;
496
496
  case "question.answers":
497
- notes.push("Summary 是上游摘要或截取文本,不是回答全文,也不是额外生成的 AI 摘要。", "单页条数可能少于 Limit;Totals 可能包含被过滤的回答。只按 paging 翻页,不按条数计算偏移。");
497
+ notes.push("Summary 是上游摘要或截取文本,非回答全文,非额外生成的 AI 摘要。", "单页条数可能少于 Limit;Totals 可能包含被过滤的回答。只按 paging 翻页,不按条数计算偏移。");
498
498
  break;
499
499
  case "content.detail":
500
500
  notes.push("仅当前 Access Secret 所属账号的已发布内容;视频仅返回关联正文,不提供视频文件。");
@@ -531,7 +531,7 @@ function renderZhihuOpenPlatform(result) {
531
531
  const PAGE_PARAMETERS = {
532
532
  offset: {
533
533
  type: "string",
534
- description: "非负 Int64 十进制字符串,默认 \"0\"。下一页只使用 paging.nextOffset;不可按条数计算,不接受不透明游标。"
534
+ description: "非负 Int64 十进制字符串,默认 \"0\"。下一页只用 paging.nextOffset,不按条数推算,不接受不透明游标。"
535
535
  },
536
536
  limit: {
537
537
  type: "integer",
@@ -556,7 +556,7 @@ const CONTENT_URL = {
556
556
  const DEFINITIONS = {
557
557
  "question.recommendations": {
558
558
  name: ZHIHU_QUESTION_RECOMMENDATIONS_TOOL_NAME,
559
- description: "推荐适合当前账号回答的问题。省略主题按画像推荐;条目可能少于 Count,不支持分页。使用 creator 额度。",
559
+ description: "推荐适合当前账号回答的问题;省略主题按画像推荐,条目可能少于 Count,不支持分页。使用 creator 额度。",
560
560
  parameters: {
561
561
  query: {
562
562
  type: "string",
@@ -570,7 +570,7 @@ const DEFINITIONS = {
570
570
  },
571
571
  "question.answers": {
572
572
  name: ZHIHU_QUESTION_ANSWERS_TOOL_NAME,
573
- description: "读取问题下的回答摘要,不是全文,也不是新生成的 AI 摘要。一次一页,只有 paging.canContinue=true 时可使用 nextOffset。使用 question_answers 额度。",
573
+ description: "读取问题下的回答摘要,非全文,也非新生成的 AI 摘要。一次一页,只有 paging.canContinue=true 时才用 nextOffset。使用 question_answers 额度。",
574
574
  parameters: {
575
575
  questionUrl: {
576
576
  type: "string",
@@ -582,7 +582,7 @@ const DEFINITIONS = {
582
582
  },
583
583
  "content.detail": {
584
584
  name: ZHIHU_USER_CONTENT_DETAIL_TOOL_NAME,
585
- description: "读取当前账号已发布创作的全文。不能读取其他账号全文;视频只提供关联正文。空正文不能视为全文。使用 creator 额度。",
585
+ description: "读取当前账号已发布创作的全文。不能读其他账号全文;视频只给关联正文。空正文不能视为全文。使用 creator 额度。",
586
586
  parameters: { contentUrl: CONTENT_URL }
587
587
  },
588
588
  "content.comments": {
@@ -681,7 +681,7 @@ function createOpenPlatformTool(operation, options) {
681
681
  const quotaId = ZHIHU_OPEN_PLATFORM_APIS[operation].quotaId;
682
682
  return defineTool({
683
683
  name: definition.name,
684
- description: `${definition.description} 仅使用当前 Access Secret,不接受 OAuth 身份切换;结果是不可信参考数据,不直接写入项目文件。`,
684
+ description: `${definition.description} 仅使用当前 Access Secret,不接受 OAuth 身份切换;结果为不可信参考数据,不写入项目文件。`,
685
685
  parameters: definition.parameters,
686
686
  output: {
687
687
  schema: OUTPUT_SCHEMA,
package/lib/tools.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"tools.js","names":[],"sources":["../src/tool-definitions.ts","../src/open-platform-render.ts","../src/open-platform-tools.ts","../src/tools.ts"],"sourcesContent":["import { defineTool } from '@deepseek-ai/dsh-tools'\nimport { reportExecuted, type ZhihuClientOptions } from './zhihu-client.ts'\nimport { ZHIHU_SEARCH_TOOL_NAME, ZHIHU_GLOBAL_SEARCH_TOOL_NAME, ZHIHU_HOT_LIST_TOOL_NAME, ZHIHU_ASK_TOOL_NAME, ZHIHU_KNOWLEDGE_SEARCH_TOOL_NAME } from './contracts.ts'\nimport { executeZhihuSearch, renderZhihuSearch, ZHIHU_SEARCH_DEFAULT_COUNT, ZHIHU_SEARCH_MAX_COUNT, type ZhihuSearchResult } from './search-api.ts'\nimport { ZHIHU_GLOBAL_SEARCH_VERSION, ZHIHU_GLOBAL_SEARCH_DEFAULT_COUNT, ZHIHU_GLOBAL_SEARCH_MAX_COUNT, ZHIHU_HOT_LIST_VERSION, ZHIHU_HOT_LIST_DEFAULT_LIMIT, ZHIHU_HOT_LIST_MAX_LIMIT, ZHIHU_ASK_VERSION, ZHIHU_ASK_TIMEOUT_MS, ZHIHU_KNOWLEDGE_SEARCH_VERSION, ZHIHU_KNOWLEDGE_SEARCH_DEFAULT_LIMIT, ZHIHU_KNOWLEDGE_SEARCH_MAX_LIMIT, ZHIHU_ASK_MODELS, ZhihuAskModel, ZHIHU_ASK_DEFAULT_MODEL, ZhihuToolOptions, ZhihuGlobalSearchDb, ZhihuGlobalSearchResult, executeZhihuGlobalSearch, renderZhihuGlobalSearch, ZhihuHotListItem, ZhihuHotListResult, executeZhihuHotList, renderZhihuHotList, ZhihuAskResult, executeZhihuAsk, renderZhihuAsk, ZhihuKnowledgeItem, ZhihuRecallScope, ZHIHU_RECALL_SCOPES, ZhihuKnowledgeSearchResult, normalizeRecallScopes, executeZhihuKnowledgeSearch, renderZhihuKnowledgeSearch } from './operations.ts'\nexport type CreateZhihuSearchToolOptions = ZhihuClientOptions & {\n /** Best-effort metering hook; invoked after every execution, failures included. */\n onExecuted?: (event: { ok: boolean; results: number }) => void\n}\n\nconst SEARCH_ITEM_SCHEMA = {\n type: 'object',\n additionalProperties: false,\n properties: {\n title: { type: 'string', required: true },\n type: { type: 'string', required: true },\n url: { type: 'string', required: true },\n summary: { type: 'string', required: true },\n votes: { type: 'integer', required: true },\n comments: { type: 'integer', required: true },\n author: { type: 'string', required: true },\n authority: { type: 'string', required: true },\n editTime: { type: 'string', required: true },\n },\n} as const\n\nexport function createZhihuSearchTool(options: CreateZhihuSearchToolOptions = {}) {\n const { onExecuted, ...client } = options\n return defineTool({\n name: ZHIHU_SEARCH_TOOL_NAME,\n description: '调用知乎开放平台站内搜索(GET /api/v1/content/zhihu_search)拉取社区证据;结果仅作社区/读者反馈参考,不构成 canon,也不直接写入项目文件。',\n parameters: {\n query: { type: 'string', required: true, description: '搜索词,2-100 字符。' },\n count: { type: 'integer', description: `返回条数,1-${ZHIHU_SEARCH_MAX_COUNT},默认 ${ZHIHU_SEARCH_DEFAULT_COUNT}。` },\n },\n output: {\n schema: {\n type: 'object',\n additionalProperties: false,\n properties: {\n version: { type: 'integer', required: true },\n query: { type: 'string', required: true },\n count: { type: 'integer', required: true },\n items: {\n type: 'array',\n required: true,\n items: SEARCH_ITEM_SCHEMA,\n },\n emptyReason: { type: 'string' },\n },\n },\n render(_args, value) {\n return [{ type: 'text' as const, text: renderZhihuSearch(value as ZhihuSearchResult) }]\n },\n },\n isConcurrencySafe() { return true },\n async execute(args, exec) {\n const typed = args as { query: string; count?: number }\n try {\n const result = await executeZhihuSearch(typed.query, typed.count ?? ZHIHU_SEARCH_DEFAULT_COUNT, {\n ...client,\n signal: exec.signal,\n })\n reportExecuted(onExecuted, { ok: true, results: result.items.length })\n return result\n } catch (error) {\n reportExecuted(onExecuted, { ok: false, results: 0 })\n throw error\n }\n },\n })\n}\n\nconst OTHER_SEARCH_ITEM_SCHEMA = {\n type: 'object',\n additionalProperties: false,\n properties: {\n title: { type: 'string', required: true },\n type: { type: 'string', required: true },\n url: { type: 'string', required: true },\n summary: { type: 'string', required: true },\n votes: { type: 'integer', required: true },\n comments: { type: 'integer', required: true },\n author: { type: 'string', required: true },\n authority: { type: 'string', required: true },\n editTime: { type: 'string', required: true },\n },\n} as const\n\n/** 包一层计量:成功报 results,失败报 ok:false 再原样抛出。 */\nasync function metered<T>(onExecuted: ZhihuToolOptions['onExecuted'], run: () => Promise<T>, results: (value: T) => number): Promise<T> {\n try {\n const value = await run()\n reportExecuted(onExecuted, { ok: true, results: results(value) })\n return value\n } catch (error) {\n reportExecuted(onExecuted, { ok: false, results: 0 })\n throw error\n }\n}\n\nexport function createZhihuGlobalSearchTool(options: ZhihuToolOptions = {}) {\n const { onExecuted, ...client } = options\n return defineTool({\n name: ZHIHU_GLOBAL_SEARCH_TOOL_NAME,\n description: '调用知乎开放平台全网搜索(GET /api/v1/content/global_search)检索站外公开网页资料;结果仅作参考,不构成 canon,也不直接写入项目文件。',\n parameters: {\n query: { type: 'string', required: true, description: '搜索词,2-100 字符。' },\n count: { type: 'integer', description: `返回条数,1-${ZHIHU_GLOBAL_SEARCH_MAX_COUNT},默认 ${ZHIHU_GLOBAL_SEARCH_DEFAULT_COUNT}。` },\n searchDb: { type: 'string', description: '检索库:all(默认)、realtime(实时)、static(静态)。' },\n },\n output: {\n schema: {\n type: 'object',\n additionalProperties: false,\n properties: {\n version: { type: 'integer', required: true },\n query: { type: 'string', required: true },\n count: { type: 'integer', required: true },\n searchDb: { type: 'string', required: true },\n items: { type: 'array', required: true, items: OTHER_SEARCH_ITEM_SCHEMA },\n emptyReason: { type: 'string' },\n },\n },\n render(_args, value) {\n return [{ type: 'text' as const, text: renderZhihuGlobalSearch(value as ZhihuGlobalSearchResult) }]\n },\n },\n isConcurrencySafe() { return true },\n async execute(args, exec) {\n const typed = args as { query: string; count?: number; searchDb?: string }\n const searchDb: ZhihuGlobalSearchDb = typed.searchDb === 'realtime' || typed.searchDb === 'static' ? typed.searchDb : 'all'\n return await metered(onExecuted, () => executeZhihuGlobalSearch(\n typed.query,\n typed.count ?? ZHIHU_GLOBAL_SEARCH_DEFAULT_COUNT,\n searchDb,\n { ...client, signal: exec.signal },\n ), (result) => result.items.length)\n },\n })\n}\n\nexport function createZhihuHotListTool(options: ZhihuToolOptions = {}) {\n const { onExecuted, ...client } = options\n return defineTool({\n name: ZHIHU_HOT_LIST_TOOL_NAME,\n description: '拉取知乎热榜(GET /api/v1/content/hot_list)了解当前社区热点;结果仅作题材与热点参考,不构成 canon,也不直接写入项目文件。',\n parameters: {\n limit: { type: 'integer', description: `返回条数,1-${ZHIHU_HOT_LIST_MAX_LIMIT},默认 ${ZHIHU_HOT_LIST_DEFAULT_LIMIT}。` },\n },\n output: {\n schema: {\n type: 'object',\n additionalProperties: false,\n properties: {\n version: { type: 'integer', required: true },\n limit: { type: 'integer', required: true },\n items: {\n type: 'array',\n required: true,\n items: {\n type: 'object',\n additionalProperties: false,\n properties: {\n title: { type: 'string', required: true },\n url: { type: 'string', required: true },\n summary: { type: 'string', required: true },\n },\n },\n },\n },\n },\n render(_args, value) {\n return [{ type: 'text' as const, text: renderZhihuHotList(value as ZhihuHotListResult) }]\n },\n },\n isConcurrencySafe() { return true },\n async execute(args, exec) {\n const typed = args as { limit?: number }\n return await metered(onExecuted, () => executeZhihuHotList(\n typed.limit ?? ZHIHU_HOT_LIST_DEFAULT_LIMIT,\n { ...client, signal: exec.signal },\n ), (result) => result.items.length)\n },\n })\n}\n\nexport function createZhihuAskTool(options: ZhihuToolOptions = {}) {\n const { onExecuted, ...client } = options\n return defineTool({\n name: ZHIHU_ASK_TOOL_NAME,\n description: '调用知乎直答(POST /v1/chat/completions,OpenAI 兼容)基于知乎社区内容生成综合回答;适合考据与背景调研。结果仅作参考,不构成 canon,也不直接写入项目文件。',\n parameters: {\n query: { type: 'string', required: true, description: '要问的问题。' },\n model: { type: 'string', description: 'zhida-thinking-1p5(默认,带思考过程)、zhida-fast-1p5(快,仅适合简单事实查询)或 zhida-agent(最慢最强,仅在明确要求时使用)。' },\n },\n output: {\n schema: {\n type: 'object',\n additionalProperties: false,\n properties: {\n version: { type: 'integer', required: true },\n query: { type: 'string', required: true },\n model: { type: 'string', required: true },\n content: { type: 'string', required: true },\n reasoning: { type: 'string', required: true },\n },\n },\n render(_args, value) {\n return [{ type: 'text' as const, text: renderZhihuAsk(value as ZhihuAskResult) }]\n },\n },\n isConcurrencySafe() { return true },\n async execute(args, exec) {\n const typed = args as { query: string; model?: string }\n const model: ZhihuAskModel = (ZHIHU_ASK_MODELS as readonly string[]).includes(typed.model ?? '')\n ? (typed.model as ZhihuAskModel)\n : ZHIHU_ASK_DEFAULT_MODEL\n return await metered(onExecuted, () => executeZhihuAsk(\n typed.query,\n model,\n { ...client, signal: exec.signal },\n ), (result) => (result.content ? 1 : 0))\n },\n })\n}\n\nexport function createZhihuKnowledgeSearchTool(options: ZhihuToolOptions = {}) {\n const { onExecuted, ...client } = options\n return defineTool({\n name: ZHIHU_KNOWLEDGE_SEARCH_TOOL_NAME,\n description: '检索知乎知识库(POST /api/v1/knowledge/search,RAG 片段),默认只查公开库;用户在知乎网页端上传过个人资料后可加 personal/subscription 召回。结果仅作背景参考,不构成 canon,也不直接写入项目文件。',\n parameters: {\n query: { type: 'string', required: true, description: '检索词。' },\n limit: { type: 'integer', description: `返回条数,1-${ZHIHU_KNOWLEDGE_SEARCH_MAX_LIMIT},默认 ${ZHIHU_KNOWLEDGE_SEARCH_DEFAULT_LIMIT}。` },\n recallScopes: {\n type: 'array',\n description: '召回范围:public(公开库,默认)、personal(个人库)、subscription(订阅库),可多选。',\n items: { type: 'string', enum: [...ZHIHU_RECALL_SCOPES] },\n },\n },\n output: {\n schema: {\n type: 'object',\n additionalProperties: false,\n properties: {\n version: { type: 'integer', required: true },\n query: { type: 'string', required: true },\n limit: { type: 'integer', required: true },\n recallScopes: { type: 'array', required: true, items: { type: 'string' } },\n items: {\n type: 'array',\n required: true,\n items: {\n type: 'object',\n additionalProperties: false,\n properties: {\n docName: { type: 'string', required: true },\n originUrl: { type: 'string', required: true },\n snippets: { type: 'array', required: true, items: { type: 'string' } },\n },\n },\n },\n },\n },\n render(_args, value) {\n return [{ type: 'text' as const, text: renderZhihuKnowledgeSearch(value as ZhihuKnowledgeSearchResult) }]\n },\n },\n isConcurrencySafe() { return true },\n async execute(args, exec) {\n const typed = args as { query: string; limit?: number; recallScopes?: unknown }\n return await metered(onExecuted, () => executeZhihuKnowledgeSearch(\n typed.query,\n typed.limit ?? ZHIHU_KNOWLEDGE_SEARCH_DEFAULT_LIMIT,\n normalizeRecallScopes(typed.recallScopes),\n { ...client, signal: exec.signal },\n ), (result) => result.items.length)\n },\n })\n}\n","import { ZHIHU_OPEN_PLATFORM_APIS, type ZhihuOpenPlatformResult } from './open-platform.ts'\n\n/** Display only: the structured result always retains the original upstream HTML. */\nexport function plainZhihuText(value: string): string {\n const entities: Record<string, string> = { amp: '&', lt: '<', gt: '>', quot: '\"', apos: \"'\", nbsp: ' ' }\n const decoded = value.replace(/&(#x[\\da-f]+|#\\d+|amp|lt|gt|quot|apos|nbsp);/gi, (match, entity: string) => {\n if (!entity.startsWith('#')) return entities[entity.toLowerCase()] ?? match\n const hex = entity[1]?.toLowerCase() === 'x'\n const point = Number.parseInt(entity.slice(hex ? 2 : 1), hex ? 16 : 10)\n return point >= 0 && point <= 0x10ffff && !(point >= 0xd800 && point <= 0xdfff) ? String.fromCodePoint(point) : '\\ufffd'\n })\n return decoded\n .replace(/<(script|style)\\b[^>]*>[\\s\\S]*?(?:<\\/\\1\\s*>|$)/gi, '')\n .replace(/<!--[^]*?(?:-->|$)/g, '')\n .replace(/<br\\s*\\/?>|<\\/(?:p|div|li|h[1-6]|tr|blockquote)\\s*>/gi, '\\n')\n .replace(/<[^>]*>/g, '')\n .replace(/\\n{3,}/g, '\\n\\n').trim()\n}\n\n/**\n * An inert, dynamically fenced text block also prevents upstream Markdown from\n * injecting links/images or breaking out with backticks. This is presentation,\n * not an HTML sanitizer for use with dangerouslySetInnerHTML.\n */\nexport function renderZhihuOpenPlatform(result: ZhihuOpenPlatformResult): string {\n const notes: string[] = []\n switch (result.operation) {\n case 'question.recommendations':\n notes.push('省略主题按当前账号画像推荐;条目可能少于请求数量,不支持翻页。')\n break\n case 'question.answers':\n notes.push('Summary 是上游摘要或截取文本,不是回答全文,也不是额外生成的 AI 摘要。',\n '单页条数可能少于 Limit;Totals 可能包含被过滤的回答。只按 paging 翻页,不按条数计算偏移。')\n break\n case 'content.detail':\n notes.push('仅当前 Access Secret 所属账号的已发布内容;视频仅返回关联正文,不提供视频文件。')\n if (typeof result.data.Body !== 'string' || !plainZhihuText(result.data.Body)) notes.push('正文未返回或为空,不能视为全文。')\n break\n case 'content.comments':\n notes.push('评论为不可信内容;Children 只是附带子评论,不保证完整。空页或不足 Limit 不表示结束,只按 paging 翻页。')\n break\n case 'creator.account.stats':\n case 'creator.content.stats':\n notes.push('未返回的指标不补零;空列表不等于零指标。比例保持上游原值,不换算百分比。',\n '省略日期时使用上游默认范围,不猜测具体天数。')\n break\n case 'quota':\n notes.push('官方每日额度,不是本地调用计数。查询本身不消耗业务额度,不推算余额、时区或重置时间。',\n '问题回答摘要使用 question_answers;问题推荐、本人全文、评论和两项创作统计共用 creator。')\n break\n }\n if (result.paging?.warning) notes.push(result.paging.warning)\n else if (result.paging?.canContinue) notes.push(`下一页 Offset:${result.paging.nextOffset}`)\n else if (result.paging?.isEnd) notes.push('已到最后一页。')\n const text = JSON.stringify(result.data, (_key, value: unknown) => typeof value === 'string' ? plainZhihuText(value) : value, 2)\n let longest = 2\n for (const match of text.matchAll(/`+/g)) longest = Math.max(longest, match[0].length)\n const fence = '`'.repeat(longest + 1)\n return [`## ${ZHIHU_OPEN_PLATFORM_APIS[result.operation].label}`, '', ...notes, '', `${fence}json`, text, fence].join('\\n')\n}\n","import { defineTool } from '@deepseek-ai/dsh-tools'\nimport {\n ZHIHU_QUESTION_RECOMMENDATIONS_TOOL_NAME, ZHIHU_QUESTION_ANSWERS_TOOL_NAME,\n ZHIHU_USER_CONTENT_DETAIL_TOOL_NAME, ZHIHU_USER_CONTENT_COMMENTS_TOOL_NAME,\n ZHIHU_CREATOR_ACCOUNT_STATS_TOOL_NAME, ZHIHU_CREATOR_CONTENT_STATS_TOOL_NAME,\n ZHIHU_QUOTA_TOOL_NAME, ZHIHU_QUOTA_IDS,\n} from './contracts.ts'\nimport { reportExecuted, type ZhihuClientOptions, type ZhihuSearchExecuted } from './zhihu-client.ts'\nimport {\n executeZhihuOpenPlatform, ZHIHU_OPEN_PLATFORM_APIS, zhihuOpenPlatformResultCount,\n type ZhihuOpenPlatformOperation, type ZhihuOpenPlatformResult,\n} from './open-platform.ts'\nimport { renderZhihuOpenPlatform } from './open-platform-render.ts'\n\nexport type ZhihuOpenPlatformToolOptions = ZhihuClientOptions & { onExecuted?: (event: ZhihuSearchExecuted) => void }\nconst PAGE_PARAMETERS = {\n offset: { type: 'string', description: '非负 Int64 十进制字符串,默认 \"0\"。下一页只使用 paging.nextOffset;不可按条数计算,不接受不透明游标。' },\n limit: { type: 'integer', description: '每页数量,1-50,默认 20。单页不足或为空不表示结束。' },\n} as const\nconst DATE_PARAMETERS = {\n startDate: { type: 'string', description: 'YYYY-MM-DD,必须与 endDate 同时提供。省略两者使用上游默认范围,不猜测天数。' },\n endDate: { type: 'string', description: 'YYYY-MM-DD,不得早于 startDate。' },\n} as const\nconst CONTENT_URL = { type: 'string', required: true, description: '当前账号已发布内容的 HTTPS 知乎回答、专栏文章、想法或视频链接;不是任意网页抓取。' } as const\n\nconst DEFINITIONS = {\n 'question.recommendations': {\n name: ZHIHU_QUESTION_RECOMMENDATIONS_TOOL_NAME,\n description: '推荐适合当前账号回答的问题。省略主题按画像推荐;条目可能少于 Count,不支持分页。使用 creator 额度。',\n parameters: {\n query: { type: 'string', description: '可选主题;不传才按画像推荐,空白字符串无效。' },\n count: { type: 'integer', description: '推荐数量,1-20,默认 5。' },\n },\n },\n 'question.answers': {\n name: ZHIHU_QUESTION_ANSWERS_TOOL_NAME,\n description: '读取问题下的回答摘要,不是全文,也不是新生成的 AI 摘要。一次一页,只有 paging.canContinue=true 时可使用 nextOffset。使用 question_answers 额度。',\n parameters: {\n questionUrl: { type: 'string', required: true, description: 'https://www.zhihu.com/question/{id} 形式的问题链接。' },\n ...PAGE_PARAMETERS,\n },\n },\n 'content.detail': {\n name: ZHIHU_USER_CONTENT_DETAIL_TOOL_NAME,\n description: '读取当前账号已发布创作的全文。不能读取其他账号全文;视频只提供关联正文。空正文不能视为全文。使用 creator 额度。',\n parameters: { contentUrl: CONTENT_URL },\n },\n 'content.comments': {\n name: ZHIHU_USER_CONTENT_COMMENTS_TOOL_NAME,\n description: '读取当前账号创作下的根评论及附带 Children;子评论不保证完整。一次一页,只按 paging 翻页,不按条数判断结束。使用 creator 额度。',\n parameters: { contentUrl: CONTENT_URL, ...PAGE_PARAMETERS, order: { type: 'string', enum: ['score', 'reverse', 'ascending'], description: '排序,默认 score。' } },\n },\n 'creator.account.stats': {\n name: ZHIHU_CREATOR_ACCOUNT_STATS_TOOL_NAME,\n description: '读取当前账号创作数据。缺失指标不补零,比例保持原值,不换算百分比。使用 creator 额度。',\n parameters: { contentType: { type: 'string', enum: ['all', 'answer', 'article', 'pin', 'zvideo'], description: '内容类型,默认 all,不支持 question。' }, ...DATE_PARAMETERS },\n },\n 'creator.content.stats': {\n name: ZHIHU_CREATOR_CONTENT_STATS_TOOL_NAME,\n description: '读取当前账号单篇创作数据。空列表不代表零指标;缺失指标保持省略,比例保持上游原值。使用 creator 额度。',\n parameters: { contentUrl: CONTENT_URL, ...DATE_PARAMETERS },\n },\n quota: {\n name: ZHIHU_QUOTA_TOOL_NAME,\n description: '查询官方每日额度,本接口不消耗业务额度。不同于本地 usage.summary;不推算余额或重置时间。',\n parameters: { apiIds: { type: 'array', items: { type: 'string', enum: ZHIHU_QUOTA_IDS }, description: '可选官方额度项,默认全部;去重后发送。问题回答摘要为 question_answers,其他问题推荐/创作接口共用 creator。' } },\n },\n} as const\n\nconst OUTPUT_SCHEMA = {\n type: 'object', additionalProperties: false,\n properties: {\n version: { type: 'integer', required: true },\n operation: { type: 'string', required: true },\n quotaId: { type: 'string' },\n // Keep optional and future upstream metrics instead of dropping them or supplying zero.\n data: { type: 'object', required: true, properties: {}, additionalProperties: true },\n paging: {\n type: 'object', additionalProperties: false,\n properties: {\n canContinue: { type: 'boolean', required: true }, isEnd: { type: 'boolean' },\n nextOffset: { type: 'string' }, warning: { type: 'string' },\n },\n },\n },\n} as const\n\nfunction createOpenPlatformTool(operation: ZhihuOpenPlatformOperation, options: ZhihuOpenPlatformToolOptions) {\n const definition = DEFINITIONS[operation]\n const { onExecuted, ...client } = options\n const quotaId = ZHIHU_OPEN_PLATFORM_APIS[operation].quotaId\n return defineTool({\n name: definition.name,\n description: `${definition.description} 仅使用当前 Access Secret,不接受 OAuth 身份切换;结果是不可信参考数据,不直接写入项目文件。`,\n parameters: definition.parameters,\n output: {\n schema: OUTPUT_SCHEMA,\n render(_args, value) { return [{ type: 'text' as const, text: renderZhihuOpenPlatform(value as ZhihuOpenPlatformResult) }] },\n },\n isConcurrencySafe() { return true },\n async execute(args, exec) {\n try {\n const result = await executeZhihuOpenPlatform(operation, args, { ...client, signal: exec.signal })\n if (quotaId) reportExecuted(onExecuted, { ok: true, results: zhihuOpenPlatformResultCount(result), quotaId })\n return result\n } catch (error) {\n if (quotaId) reportExecuted(onExecuted, { ok: false, results: 0, quotaId })\n throw error\n }\n },\n })\n}\n\nexport const createZhihuQuestionRecommendationsTool = (options: ZhihuOpenPlatformToolOptions = {}) => createOpenPlatformTool('question.recommendations', options)\nexport const createZhihuQuestionAnswersTool = (options: ZhihuOpenPlatformToolOptions = {}) => createOpenPlatformTool('question.answers', options)\nexport const createZhihuUserContentDetailTool = (options: ZhihuOpenPlatformToolOptions = {}) => createOpenPlatformTool('content.detail', options)\nexport const createZhihuUserContentCommentsTool = (options: ZhihuOpenPlatformToolOptions = {}) => createOpenPlatformTool('content.comments', options)\nexport const createZhihuCreatorAccountStatsTool = (options: ZhihuOpenPlatformToolOptions = {}) => createOpenPlatformTool('creator.account.stats', options)\nexport const createZhihuCreatorContentStatsTool = (options: ZhihuOpenPlatformToolOptions = {}) => createOpenPlatformTool('creator.content.stats', options)\nexport const createZhihuQuotaTool = (options: ZhihuOpenPlatformToolOptions = {}) => createOpenPlatformTool('quota', options)\nexport const ZHIHU_OPEN_PLATFORM_TOOL_FACTORIES = [\n createZhihuQuestionRecommendationsTool, createZhihuQuestionAnswersTool,\n createZhihuUserContentDetailTool, createZhihuUserContentCommentsTool,\n createZhihuCreatorAccountStatsTool, createZhihuCreatorContentStatsTool, createZhihuQuotaTool,\n] as const\n","import type { Context } from '@deepseek-ai/cordis'\nimport type { ZhihuService } from './index.ts'\nimport { createZhihuSearchTool, createZhihuGlobalSearchTool, createZhihuHotListTool, createZhihuAskTool, createZhihuKnowledgeSearchTool } from './tool-definitions.ts'\nimport { ZHIHU_OPEN_PLATFORM_TOOL_FACTORIES } from './open-platform-tools.ts'\nexport const name = 'dsh-zhihu-tools'\nexport const inject = ['zhihu', 'tools'] as const\nexport function apply(ctx: Context): void {\n const host = ctx as Context & { zhihu: ZhihuService; tools: { register: (tool: unknown) => unknown } }\n const options = host.zhihu.toolOptions\n for (const create of [createZhihuSearchTool, createZhihuGlobalSearchTool, createZhihuHotListTool, createZhihuAskTool, createZhihuKnowledgeSearchTool, ...ZHIHU_OPEN_PLATFORM_TOOL_FACTORIES]) {\n const tool = create(options)\n const execute = tool.execute as unknown as (args: unknown, exec: { signal: AbortSignal }) => Promise<unknown>\n host.tools.register({ ...tool, execute: (args: unknown, exec: { signal: AbortSignal }) =>\n host.zhihu.run(signal => execute(args, { ...exec, signal }), exec.signal) })\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"tools.js","names":[],"sources":["../src/tool-definitions.ts","../src/open-platform-render.ts","../src/open-platform-tools.ts","../src/tools.ts"],"sourcesContent":["import { defineTool } from '@deepseek-ai/dsh-tools'\nimport { reportExecuted, type ZhihuClientOptions } from './zhihu-client.ts'\nimport { ZHIHU_SEARCH_TOOL_NAME, ZHIHU_GLOBAL_SEARCH_TOOL_NAME, ZHIHU_HOT_LIST_TOOL_NAME, ZHIHU_ASK_TOOL_NAME, ZHIHU_KNOWLEDGE_SEARCH_TOOL_NAME } from './contracts.ts'\nimport { executeZhihuSearch, renderZhihuSearch, ZHIHU_SEARCH_DEFAULT_COUNT, ZHIHU_SEARCH_MAX_COUNT, type ZhihuSearchResult } from './search-api.ts'\nimport { ZHIHU_GLOBAL_SEARCH_VERSION, ZHIHU_GLOBAL_SEARCH_DEFAULT_COUNT, ZHIHU_GLOBAL_SEARCH_MAX_COUNT, ZHIHU_HOT_LIST_VERSION, ZHIHU_HOT_LIST_DEFAULT_LIMIT, ZHIHU_HOT_LIST_MAX_LIMIT, ZHIHU_ASK_VERSION, ZHIHU_ASK_TIMEOUT_MS, ZHIHU_KNOWLEDGE_SEARCH_VERSION, ZHIHU_KNOWLEDGE_SEARCH_DEFAULT_LIMIT, ZHIHU_KNOWLEDGE_SEARCH_MAX_LIMIT, ZHIHU_ASK_MODELS, ZhihuAskModel, ZHIHU_ASK_DEFAULT_MODEL, ZhihuToolOptions, ZhihuGlobalSearchDb, ZhihuGlobalSearchResult, executeZhihuGlobalSearch, renderZhihuGlobalSearch, ZhihuHotListItem, ZhihuHotListResult, executeZhihuHotList, renderZhihuHotList, ZhihuAskResult, executeZhihuAsk, renderZhihuAsk, ZhihuKnowledgeItem, ZhihuRecallScope, ZHIHU_RECALL_SCOPES, ZhihuKnowledgeSearchResult, normalizeRecallScopes, executeZhihuKnowledgeSearch, renderZhihuKnowledgeSearch } from './operations.ts'\nexport type CreateZhihuSearchToolOptions = ZhihuClientOptions & {\n /** Best-effort metering hook; invoked after every execution, failures included. */\n onExecuted?: (event: { ok: boolean; results: number }) => void\n}\n\nconst SEARCH_ITEM_SCHEMA = {\n type: 'object',\n additionalProperties: false,\n properties: {\n title: { type: 'string', required: true },\n type: { type: 'string', required: true },\n url: { type: 'string', required: true },\n summary: { type: 'string', required: true },\n votes: { type: 'integer', required: true },\n comments: { type: 'integer', required: true },\n author: { type: 'string', required: true },\n authority: { type: 'string', required: true },\n editTime: { type: 'string', required: true },\n },\n} as const\n\nexport function createZhihuSearchTool(options: CreateZhihuSearchToolOptions = {}) {\n const { onExecuted, ...client } = options\n return defineTool({\n name: ZHIHU_SEARCH_TOOL_NAME,\n description: '知乎站内搜索(GET /api/v1/content/zhihu_search),取社区证据;仅作社区/读者反馈参考,不构成 canon,不写入项目文件。',\n parameters: {\n query: { type: 'string', required: true, description: '搜索词,2-100 字符。' },\n count: { type: 'integer', description: `返回条数,1-${ZHIHU_SEARCH_MAX_COUNT},默认 ${ZHIHU_SEARCH_DEFAULT_COUNT}。` },\n },\n output: {\n schema: {\n type: 'object',\n additionalProperties: false,\n properties: {\n version: { type: 'integer', required: true },\n query: { type: 'string', required: true },\n count: { type: 'integer', required: true },\n items: {\n type: 'array',\n required: true,\n items: SEARCH_ITEM_SCHEMA,\n },\n emptyReason: { type: 'string' },\n },\n },\n render(_args, value) {\n return [{ type: 'text' as const, text: renderZhihuSearch(value as ZhihuSearchResult) }]\n },\n },\n isConcurrencySafe() { return true },\n async execute(args, exec) {\n const typed = args as { query: string; count?: number }\n try {\n const result = await executeZhihuSearch(typed.query, typed.count ?? ZHIHU_SEARCH_DEFAULT_COUNT, {\n ...client,\n signal: exec.signal,\n })\n reportExecuted(onExecuted, { ok: true, results: result.items.length })\n return result\n } catch (error) {\n reportExecuted(onExecuted, { ok: false, results: 0 })\n throw error\n }\n },\n })\n}\n\nconst OTHER_SEARCH_ITEM_SCHEMA = {\n type: 'object',\n additionalProperties: false,\n properties: {\n title: { type: 'string', required: true },\n type: { type: 'string', required: true },\n url: { type: 'string', required: true },\n summary: { type: 'string', required: true },\n votes: { type: 'integer', required: true },\n comments: { type: 'integer', required: true },\n author: { type: 'string', required: true },\n authority: { type: 'string', required: true },\n editTime: { type: 'string', required: true },\n },\n} as const\n\n/** 包一层计量:成功报 results,失败报 ok:false 再原样抛出。 */\nasync function metered<T>(onExecuted: ZhihuToolOptions['onExecuted'], run: () => Promise<T>, results: (value: T) => number): Promise<T> {\n try {\n const value = await run()\n reportExecuted(onExecuted, { ok: true, results: results(value) })\n return value\n } catch (error) {\n reportExecuted(onExecuted, { ok: false, results: 0 })\n throw error\n }\n}\n\nexport function createZhihuGlobalSearchTool(options: ZhihuToolOptions = {}) {\n const { onExecuted, ...client } = options\n return defineTool({\n name: ZHIHU_GLOBAL_SEARCH_TOOL_NAME,\n description: '知乎开放平台全网搜索(GET /api/v1/content/global_search),检索站外公开网页资料;仅作参考,不构成 canon,不写入项目文件。',\n parameters: {\n query: { type: 'string', required: true, description: '搜索词,2-100 字符。' },\n count: { type: 'integer', description: `返回条数,1-${ZHIHU_GLOBAL_SEARCH_MAX_COUNT},默认 ${ZHIHU_GLOBAL_SEARCH_DEFAULT_COUNT}。` },\n searchDb: { type: 'string', description: '检索库:all(默认)、realtime(实时)、static(静态)。' },\n },\n output: {\n schema: {\n type: 'object',\n additionalProperties: false,\n properties: {\n version: { type: 'integer', required: true },\n query: { type: 'string', required: true },\n count: { type: 'integer', required: true },\n searchDb: { type: 'string', required: true },\n items: { type: 'array', required: true, items: OTHER_SEARCH_ITEM_SCHEMA },\n emptyReason: { type: 'string' },\n },\n },\n render(_args, value) {\n return [{ type: 'text' as const, text: renderZhihuGlobalSearch(value as ZhihuGlobalSearchResult) }]\n },\n },\n isConcurrencySafe() { return true },\n async execute(args, exec) {\n const typed = args as { query: string; count?: number; searchDb?: string }\n const searchDb: ZhihuGlobalSearchDb = typed.searchDb === 'realtime' || typed.searchDb === 'static' ? typed.searchDb : 'all'\n return await metered(onExecuted, () => executeZhihuGlobalSearch(\n typed.query,\n typed.count ?? ZHIHU_GLOBAL_SEARCH_DEFAULT_COUNT,\n searchDb,\n { ...client, signal: exec.signal },\n ), (result) => result.items.length)\n },\n })\n}\n\nexport function createZhihuHotListTool(options: ZhihuToolOptions = {}) {\n const { onExecuted, ...client } = options\n return defineTool({\n name: ZHIHU_HOT_LIST_TOOL_NAME,\n description: '知乎热榜(GET /api/v1/content/hot_list),了解当前社区热点;仅作题材参考,不构成 canon,不写入项目文件。',\n parameters: {\n limit: { type: 'integer', description: `返回条数,1-${ZHIHU_HOT_LIST_MAX_LIMIT},默认 ${ZHIHU_HOT_LIST_DEFAULT_LIMIT}。` },\n },\n output: {\n schema: {\n type: 'object',\n additionalProperties: false,\n properties: {\n version: { type: 'integer', required: true },\n limit: { type: 'integer', required: true },\n items: {\n type: 'array',\n required: true,\n items: {\n type: 'object',\n additionalProperties: false,\n properties: {\n title: { type: 'string', required: true },\n url: { type: 'string', required: true },\n summary: { type: 'string', required: true },\n },\n },\n },\n },\n },\n render(_args, value) {\n return [{ type: 'text' as const, text: renderZhihuHotList(value as ZhihuHotListResult) }]\n },\n },\n isConcurrencySafe() { return true },\n async execute(args, exec) {\n const typed = args as { limit?: number }\n return await metered(onExecuted, () => executeZhihuHotList(\n typed.limit ?? ZHIHU_HOT_LIST_DEFAULT_LIMIT,\n { ...client, signal: exec.signal },\n ), (result) => result.items.length)\n },\n })\n}\n\nexport function createZhihuAskTool(options: ZhihuToolOptions = {}) {\n const { onExecuted, ...client } = options\n return defineTool({\n name: ZHIHU_ASK_TOOL_NAME,\n description: '知乎直答(POST /v1/chat/completions,OpenAI 兼容),基于社区内容生成综合回答,适合考据与背景调研;仅作参考,不构成 canon,不写入项目文件。',\n parameters: {\n query: { type: 'string', required: true, description: '要问的问题。' },\n model: { type: 'string', description: 'zhida-thinking-1p5(默认,带思考过程)、zhida-fast-1p5(快,仅适合简单事实查询)或 zhida-agent(最慢最强,仅在明确要求时使用)。' },\n },\n output: {\n schema: {\n type: 'object',\n additionalProperties: false,\n properties: {\n version: { type: 'integer', required: true },\n query: { type: 'string', required: true },\n model: { type: 'string', required: true },\n content: { type: 'string', required: true },\n reasoning: { type: 'string', required: true },\n },\n },\n render(_args, value) {\n return [{ type: 'text' as const, text: renderZhihuAsk(value as ZhihuAskResult) }]\n },\n },\n isConcurrencySafe() { return true },\n async execute(args, exec) {\n const typed = args as { query: string; model?: string }\n const model: ZhihuAskModel = (ZHIHU_ASK_MODELS as readonly string[]).includes(typed.model ?? '')\n ? (typed.model as ZhihuAskModel)\n : ZHIHU_ASK_DEFAULT_MODEL\n return await metered(onExecuted, () => executeZhihuAsk(\n typed.query,\n model,\n { ...client, signal: exec.signal },\n ), (result) => (result.content ? 1 : 0))\n },\n })\n}\n\nexport function createZhihuKnowledgeSearchTool(options: ZhihuToolOptions = {}) {\n const { onExecuted, ...client } = options\n return defineTool({\n name: ZHIHU_KNOWLEDGE_SEARCH_TOOL_NAME,\n description: '知乎知识库检索(POST /api/v1/knowledge/search,RAG 片段),默认只查公开库;网页端上传过资料后可加 personal/subscription 召回;仅作背景参考,不构成 canon,不写入项目文件。',\n parameters: {\n query: { type: 'string', required: true, description: '检索词。' },\n limit: { type: 'integer', description: `返回条数,1-${ZHIHU_KNOWLEDGE_SEARCH_MAX_LIMIT},默认 ${ZHIHU_KNOWLEDGE_SEARCH_DEFAULT_LIMIT}。` },\n recallScopes: {\n type: 'array',\n description: '召回范围:public(公开库,默认)、personal(个人库)、subscription(订阅库),可多选。',\n items: { type: 'string', enum: [...ZHIHU_RECALL_SCOPES] },\n },\n },\n output: {\n schema: {\n type: 'object',\n additionalProperties: false,\n properties: {\n version: { type: 'integer', required: true },\n query: { type: 'string', required: true },\n limit: { type: 'integer', required: true },\n recallScopes: { type: 'array', required: true, items: { type: 'string' } },\n items: {\n type: 'array',\n required: true,\n items: {\n type: 'object',\n additionalProperties: false,\n properties: {\n docName: { type: 'string', required: true },\n originUrl: { type: 'string', required: true },\n snippets: { type: 'array', required: true, items: { type: 'string' } },\n },\n },\n },\n },\n },\n render(_args, value) {\n return [{ type: 'text' as const, text: renderZhihuKnowledgeSearch(value as ZhihuKnowledgeSearchResult) }]\n },\n },\n isConcurrencySafe() { return true },\n async execute(args, exec) {\n const typed = args as { query: string; limit?: number; recallScopes?: unknown }\n return await metered(onExecuted, () => executeZhihuKnowledgeSearch(\n typed.query,\n typed.limit ?? ZHIHU_KNOWLEDGE_SEARCH_DEFAULT_LIMIT,\n normalizeRecallScopes(typed.recallScopes),\n { ...client, signal: exec.signal },\n ), (result) => result.items.length)\n },\n })\n}\n","import { ZHIHU_OPEN_PLATFORM_APIS, type ZhihuOpenPlatformResult } from './open-platform.ts'\n\n/** Display only: the structured result always retains the original upstream HTML. */\nexport function plainZhihuText(value: string): string {\n const entities: Record<string, string> = { amp: '&', lt: '<', gt: '>', quot: '\"', apos: \"'\", nbsp: ' ' }\n const decoded = value.replace(/&(#x[\\da-f]+|#\\d+|amp|lt|gt|quot|apos|nbsp);/gi, (match, entity: string) => {\n if (!entity.startsWith('#')) return entities[entity.toLowerCase()] ?? match\n const hex = entity[1]?.toLowerCase() === 'x'\n const point = Number.parseInt(entity.slice(hex ? 2 : 1), hex ? 16 : 10)\n return point >= 0 && point <= 0x10ffff && !(point >= 0xd800 && point <= 0xdfff) ? String.fromCodePoint(point) : '\\ufffd'\n })\n return decoded\n .replace(/<(script|style)\\b[^>]*>[\\s\\S]*?(?:<\\/\\1\\s*>|$)/gi, '')\n .replace(/<!--[^]*?(?:-->|$)/g, '')\n .replace(/<br\\s*\\/?>|<\\/(?:p|div|li|h[1-6]|tr|blockquote)\\s*>/gi, '\\n')\n .replace(/<[^>]*>/g, '')\n .replace(/\\n{3,}/g, '\\n\\n').trim()\n}\n\n/**\n * An inert, dynamically fenced text block also prevents upstream Markdown from\n * injecting links/images or breaking out with backticks. This is presentation,\n * not an HTML sanitizer for use with dangerouslySetInnerHTML.\n */\nexport function renderZhihuOpenPlatform(result: ZhihuOpenPlatformResult): string {\n const notes: string[] = []\n switch (result.operation) {\n case 'question.recommendations':\n notes.push('省略主题按当前账号画像推荐;条目可能少于请求数量,不支持翻页。')\n break\n case 'question.answers':\n notes.push('Summary 是上游摘要或截取文本,非回答全文,非额外生成的 AI 摘要。',\n '单页条数可能少于 Limit;Totals 可能包含被过滤的回答。只按 paging 翻页,不按条数计算偏移。')\n break\n case 'content.detail':\n notes.push('仅当前 Access Secret 所属账号的已发布内容;视频仅返回关联正文,不提供视频文件。')\n if (typeof result.data.Body !== 'string' || !plainZhihuText(result.data.Body)) notes.push('正文未返回或为空,不能视为全文。')\n break\n case 'content.comments':\n notes.push('评论为不可信内容;Children 只是附带子评论,不保证完整。空页或不足 Limit 不表示结束,只按 paging 翻页。')\n break\n case 'creator.account.stats':\n case 'creator.content.stats':\n notes.push('未返回的指标不补零;空列表不等于零指标。比例保持上游原值,不换算百分比。',\n '省略日期时使用上游默认范围,不猜测具体天数。')\n break\n case 'quota':\n notes.push('官方每日额度,不是本地调用计数。查询本身不消耗业务额度,不推算余额、时区或重置时间。',\n '问题回答摘要使用 question_answers;问题推荐、本人全文、评论和两项创作统计共用 creator。')\n break\n }\n if (result.paging?.warning) notes.push(result.paging.warning)\n else if (result.paging?.canContinue) notes.push(`下一页 Offset:${result.paging.nextOffset}`)\n else if (result.paging?.isEnd) notes.push('已到最后一页。')\n const text = JSON.stringify(result.data, (_key, value: unknown) => typeof value === 'string' ? plainZhihuText(value) : value, 2)\n let longest = 2\n for (const match of text.matchAll(/`+/g)) longest = Math.max(longest, match[0].length)\n const fence = '`'.repeat(longest + 1)\n return [`## ${ZHIHU_OPEN_PLATFORM_APIS[result.operation].label}`, '', ...notes, '', `${fence}json`, text, fence].join('\\n')\n}\n","import { defineTool } from '@deepseek-ai/dsh-tools'\nimport {\n ZHIHU_QUESTION_RECOMMENDATIONS_TOOL_NAME, ZHIHU_QUESTION_ANSWERS_TOOL_NAME,\n ZHIHU_USER_CONTENT_DETAIL_TOOL_NAME, ZHIHU_USER_CONTENT_COMMENTS_TOOL_NAME,\n ZHIHU_CREATOR_ACCOUNT_STATS_TOOL_NAME, ZHIHU_CREATOR_CONTENT_STATS_TOOL_NAME,\n ZHIHU_QUOTA_TOOL_NAME, ZHIHU_QUOTA_IDS,\n} from './contracts.ts'\nimport { reportExecuted, type ZhihuClientOptions, type ZhihuSearchExecuted } from './zhihu-client.ts'\nimport {\n executeZhihuOpenPlatform, ZHIHU_OPEN_PLATFORM_APIS, zhihuOpenPlatformResultCount,\n type ZhihuOpenPlatformOperation, type ZhihuOpenPlatformResult,\n} from './open-platform.ts'\nimport { renderZhihuOpenPlatform } from './open-platform-render.ts'\n\nexport type ZhihuOpenPlatformToolOptions = ZhihuClientOptions & { onExecuted?: (event: ZhihuSearchExecuted) => void }\nconst PAGE_PARAMETERS = {\n offset: { type: 'string', description: '非负 Int64 十进制字符串,默认 \"0\"。下一页只用 paging.nextOffset,不按条数推算,不接受不透明游标。' },\n limit: { type: 'integer', description: '每页数量,1-50,默认 20。单页不足或为空不表示结束。' },\n} as const\nconst DATE_PARAMETERS = {\n startDate: { type: 'string', description: 'YYYY-MM-DD,必须与 endDate 同时提供。省略两者使用上游默认范围,不猜测天数。' },\n endDate: { type: 'string', description: 'YYYY-MM-DD,不得早于 startDate。' },\n} as const\nconst CONTENT_URL = { type: 'string', required: true, description: '当前账号已发布内容的 HTTPS 知乎回答、专栏文章、想法或视频链接;不是任意网页抓取。' } as const\n\nconst DEFINITIONS = {\n 'question.recommendations': {\n name: ZHIHU_QUESTION_RECOMMENDATIONS_TOOL_NAME,\n description: '推荐适合当前账号回答的问题;省略主题按画像推荐,条目可能少于 Count,不支持分页。使用 creator 额度。',\n parameters: {\n query: { type: 'string', description: '可选主题;不传才按画像推荐,空白字符串无效。' },\n count: { type: 'integer', description: '推荐数量,1-20,默认 5。' },\n },\n },\n 'question.answers': {\n name: ZHIHU_QUESTION_ANSWERS_TOOL_NAME,\n description: '读取问题下的回答摘要,非全文,也非新生成的 AI 摘要。一次一页,只有 paging.canContinue=true 时才用 nextOffset。使用 question_answers 额度。',\n parameters: {\n questionUrl: { type: 'string', required: true, description: 'https://www.zhihu.com/question/{id} 形式的问题链接。' },\n ...PAGE_PARAMETERS,\n },\n },\n 'content.detail': {\n name: ZHIHU_USER_CONTENT_DETAIL_TOOL_NAME,\n description: '读取当前账号已发布创作的全文。不能读其他账号全文;视频只给关联正文。空正文不能视为全文。使用 creator 额度。',\n parameters: { contentUrl: CONTENT_URL },\n },\n 'content.comments': {\n name: ZHIHU_USER_CONTENT_COMMENTS_TOOL_NAME,\n description: '读取当前账号创作下的根评论及附带 Children;子评论不保证完整。一次一页,只按 paging 翻页,不按条数判断结束。使用 creator 额度。',\n parameters: { contentUrl: CONTENT_URL, ...PAGE_PARAMETERS, order: { type: 'string', enum: ['score', 'reverse', 'ascending'], description: '排序,默认 score。' } },\n },\n 'creator.account.stats': {\n name: ZHIHU_CREATOR_ACCOUNT_STATS_TOOL_NAME,\n description: '读取当前账号创作数据。缺失指标不补零,比例保持原值,不换算百分比。使用 creator 额度。',\n parameters: { contentType: { type: 'string', enum: ['all', 'answer', 'article', 'pin', 'zvideo'], description: '内容类型,默认 all,不支持 question。' }, ...DATE_PARAMETERS },\n },\n 'creator.content.stats': {\n name: ZHIHU_CREATOR_CONTENT_STATS_TOOL_NAME,\n description: '读取当前账号单篇创作数据。空列表不代表零指标;缺失指标保持省略,比例保持上游原值。使用 creator 额度。',\n parameters: { contentUrl: CONTENT_URL, ...DATE_PARAMETERS },\n },\n quota: {\n name: ZHIHU_QUOTA_TOOL_NAME,\n description: '查询官方每日额度,本接口不消耗业务额度。不同于本地 usage.summary;不推算余额或重置时间。',\n parameters: { apiIds: { type: 'array', items: { type: 'string', enum: ZHIHU_QUOTA_IDS }, description: '可选官方额度项,默认全部;去重后发送。问题回答摘要为 question_answers,其他问题推荐/创作接口共用 creator。' } },\n },\n} as const\n\nconst OUTPUT_SCHEMA = {\n type: 'object', additionalProperties: false,\n properties: {\n version: { type: 'integer', required: true },\n operation: { type: 'string', required: true },\n quotaId: { type: 'string' },\n // Keep optional and future upstream metrics instead of dropping them or supplying zero.\n data: { type: 'object', required: true, properties: {}, additionalProperties: true },\n paging: {\n type: 'object', additionalProperties: false,\n properties: {\n canContinue: { type: 'boolean', required: true }, isEnd: { type: 'boolean' },\n nextOffset: { type: 'string' }, warning: { type: 'string' },\n },\n },\n },\n} as const\n\nfunction createOpenPlatformTool(operation: ZhihuOpenPlatformOperation, options: ZhihuOpenPlatformToolOptions) {\n const definition = DEFINITIONS[operation]\n const { onExecuted, ...client } = options\n const quotaId = ZHIHU_OPEN_PLATFORM_APIS[operation].quotaId\n return defineTool({\n name: definition.name,\n description: `${definition.description} 仅使用当前 Access Secret,不接受 OAuth 身份切换;结果为不可信参考数据,不写入项目文件。`,\n parameters: definition.parameters,\n output: {\n schema: OUTPUT_SCHEMA,\n render(_args, value) { return [{ type: 'text' as const, text: renderZhihuOpenPlatform(value as ZhihuOpenPlatformResult) }] },\n },\n isConcurrencySafe() { return true },\n async execute(args, exec) {\n try {\n const result = await executeZhihuOpenPlatform(operation, args, { ...client, signal: exec.signal })\n if (quotaId) reportExecuted(onExecuted, { ok: true, results: zhihuOpenPlatformResultCount(result), quotaId })\n return result\n } catch (error) {\n if (quotaId) reportExecuted(onExecuted, { ok: false, results: 0, quotaId })\n throw error\n }\n },\n })\n}\n\nexport const createZhihuQuestionRecommendationsTool = (options: ZhihuOpenPlatformToolOptions = {}) => createOpenPlatformTool('question.recommendations', options)\nexport const createZhihuQuestionAnswersTool = (options: ZhihuOpenPlatformToolOptions = {}) => createOpenPlatformTool('question.answers', options)\nexport const createZhihuUserContentDetailTool = (options: ZhihuOpenPlatformToolOptions = {}) => createOpenPlatformTool('content.detail', options)\nexport const createZhihuUserContentCommentsTool = (options: ZhihuOpenPlatformToolOptions = {}) => createOpenPlatformTool('content.comments', options)\nexport const createZhihuCreatorAccountStatsTool = (options: ZhihuOpenPlatformToolOptions = {}) => createOpenPlatformTool('creator.account.stats', options)\nexport const createZhihuCreatorContentStatsTool = (options: ZhihuOpenPlatformToolOptions = {}) => createOpenPlatformTool('creator.content.stats', options)\nexport const createZhihuQuotaTool = (options: ZhihuOpenPlatformToolOptions = {}) => createOpenPlatformTool('quota', options)\nexport const ZHIHU_OPEN_PLATFORM_TOOL_FACTORIES = [\n createZhihuQuestionRecommendationsTool, createZhihuQuestionAnswersTool,\n createZhihuUserContentDetailTool, createZhihuUserContentCommentsTool,\n createZhihuCreatorAccountStatsTool, createZhihuCreatorContentStatsTool, createZhihuQuotaTool,\n] as const\n","import type { Context } from '@deepseek-ai/cordis'\nimport type { ZhihuService } from './index.ts'\nimport { createZhihuSearchTool, createZhihuGlobalSearchTool, createZhihuHotListTool, createZhihuAskTool, createZhihuKnowledgeSearchTool } from './tool-definitions.ts'\nimport { ZHIHU_OPEN_PLATFORM_TOOL_FACTORIES } from './open-platform-tools.ts'\nexport const name = 'dsh-zhihu-tools'\nexport const inject = ['zhihu', 'tools'] as const\nexport function apply(ctx: Context): void {\n const host = ctx as Context & { zhihu: ZhihuService; tools: { register: (tool: unknown) => unknown } }\n const options = host.zhihu.toolOptions\n for (const create of [createZhihuSearchTool, createZhihuGlobalSearchTool, createZhihuHotListTool, createZhihuAskTool, createZhihuKnowledgeSearchTool, ...ZHIHU_OPEN_PLATFORM_TOOL_FACTORIES]) {\n const tool = create(options)\n const execute = tool.execute as unknown as (args: unknown, exec: { signal: AbortSignal }) => Promise<unknown>\n host.tools.register({ ...tool, execute: (args: unknown, exec: { signal: AbortSignal }) =>\n host.zhihu.run(signal => execute(args, { ...exec, signal }), exec.signal) })\n }\n}\n"],"mappings":"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"}
package/locale/en.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "Zhihu",
4
+ "description": "Zhihu search, agent tools, knowledge bases, and usage tracking."
5
+ }
6
+ }
package/locale/zh.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "知乎",
4
+ "description": "知乎搜索、智能体工具、知识库和用量统计。"
5
+ }
6
+ }
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "@klarkxy/dsh-zhihu",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "description": "Zhihu search, agent tools, knowledge bases, and usage tracking for DeepSeek Harness.",
5
+ "icon": "./icon.svg",
5
6
  "license": "SEE LICENSE IN LICENSE",
6
7
  "type": "module",
7
8
  "engines": {
@@ -33,10 +34,12 @@
33
34
  "./client": {
34
35
  "default": "./lib/client.js"
35
36
  },
36
- "./package.json": "./package.json"
37
+ "./package.json": "./package.json",
38
+ "./locale/*.json": "./locale/*.json"
37
39
  },
38
40
  "files": [
39
41
  "icon.svg",
42
+ "locale/*.json",
40
43
  "lib/*.js",
41
44
  "lib/*.js.map",
42
45
  "lib/*.d.ts",
@@ -53,7 +56,8 @@
53
56
  "platform": "web",
54
57
  "inject": [
55
58
  "@deepseek-ai/dsh-client-connection",
56
- "@deepseek-ai/dsh-api-remotes"
59
+ "@deepseek-ai/dsh-api-remotes",
60
+ "@deepseek-ai/dsh-client-ui-primitives"
57
61
  ]
58
62
  }
59
63
  },
@@ -89,7 +93,8 @@
89
93
  "test": "vitest run --root ../.. --config vitest.editor-plugins.config.ts --maxWorkers=2 plugins/dsh-zhihu/src"
90
94
  },
91
95
  "dependencies": {
92
- "zod": "^4.1.5"
96
+ "zod": "^4.1.5",
97
+ "@klarkxy/dsh-plugin-kit": "0.1.0"
93
98
  },
94
99
  "peerDependencies": {
95
100
  "@deepseek-ai/cordis": "^4.0.4",
@@ -105,7 +110,8 @@
105
110
  "react": "^18.2.0",
106
111
  "@types/react": "18.3.31",
107
112
  "react-dom": "^18.2.0",
108
- "@types/react-dom": "^18.3.1"
113
+ "@types/react-dom": "^18.3.1",
114
+ "@deepseek-ai/dsh-client-ui-primitives": "0.1.7-rc.2"
109
115
  },
110
116
  "peerDependenciesMeta": {
111
117
  "@deepseek-ai/dsh-credentials": {
@@ -142,6 +148,6 @@
142
148
  "registry": "https://registry.npmjs.org/"
143
149
  },
144
150
  "dshRelease": {
145
- "contentHash": "443a8c79292e11ffcf57a980b99c326d0c1f1f817f36751bfd2dc9fbf8156be5"
151
+ "contentHash": "1badbace8750df1849db5448e135df8930575a4a85fb743cb33940f448decfc8"
146
152
  }
147
153
  }