dsh-zhihu-search 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +71 -0
- package/cordis.patch.yml +13 -0
- package/lib/client.js +308 -0
- package/lib/credentials.d.ts +30 -0
- package/lib/credentials.d.ts.map +1 -0
- package/lib/credentials.js +35 -0
- package/lib/credentials.js.map +1 -0
- package/lib/index.d.ts +96 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +164 -0
- package/lib/index.js.map +1 -0
- package/lib/present/search.d.ts +78 -0
- package/lib/present/search.d.ts.map +1 -0
- package/lib/present/search.js +131 -0
- package/lib/present/search.js.map +1 -0
- package/lib/present/zhida.d.ts +44 -0
- package/lib/present/zhida.d.ts.map +1 -0
- package/lib/present/zhida.js +57 -0
- package/lib/present/zhida.js.map +1 -0
- package/lib/state.d.ts +138 -0
- package/lib/state.d.ts.map +1 -0
- package/lib/state.js +220 -0
- package/lib/state.js.map +1 -0
- package/lib/tools/deps.d.ts +39 -0
- package/lib/tools/deps.d.ts.map +1 -0
- package/lib/tools/deps.js +25 -0
- package/lib/tools/deps.js.map +1 -0
- package/lib/tools/global-search.d.ts +23 -0
- package/lib/tools/global-search.d.ts.map +1 -0
- package/lib/tools/global-search.js +150 -0
- package/lib/tools/global-search.js.map +1 -0
- package/lib/tools/search.d.ts +22 -0
- package/lib/tools/search.d.ts.map +1 -0
- package/lib/tools/search.js +168 -0
- package/lib/tools/search.js.map +1 -0
- package/lib/tools/zhida.d.ts +21 -0
- package/lib/tools/zhida.d.ts.map +1 -0
- package/lib/tools/zhida.js +125 -0
- package/lib/tools/zhida.js.map +1 -0
- package/lib/transport.d.ts +179 -0
- package/lib/transport.d.ts.map +1 -0
- package/lib/transport.js +536 -0
- package/lib/transport.js.map +1 -0
- package/lib/types/client/index.d.ts +36 -0
- package/lib/types.d.ts +159 -0
- package/lib/types.d.ts.map +1 -0
- package/lib/types.js +12 -0
- package/lib/types.js.map +1 -0
- package/lib/utils/compiler.d.ts +95 -0
- package/lib/utils/compiler.d.ts.map +1 -0
- package/lib/utils/compiler.js +149 -0
- package/lib/utils/compiler.js.map +1 -0
- package/lib/utils/errors.d.ts +27 -0
- package/lib/utils/errors.d.ts.map +1 -0
- package/lib/utils/errors.js +54 -0
- package/lib/utils/errors.js.map +1 -0
- package/lib/utils/text.d.ts +30 -0
- package/lib/utils/text.d.ts.map +1 -0
- package/lib/utils/text.js +73 -0
- package/lib/utils/text.js.map +1 -0
- package/package.json +110 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 zlZayn
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# dsh-zhihu-search
|
|
2
|
+
|
|
3
|
+
[](LICENSE)
|
|
4
|
+
[](.node-version)
|
|
5
|
+
[](package.json)
|
|
6
|
+
|
|
7
|
+
给 DSH 装上知乎:站内检索、全网检索与直答三个工具,返回可引用的来源列表,而不是一段无法核对的摘要。
|
|
8
|
+
|
|
9
|
+

|
|
10
|
+
|
|
11
|
+
*在 **设置 → 插件 → 插件配置** 中与其他插件并排,Access Secret 就地填写、立即生效。*
|
|
12
|
+
|
|
13
|
+
## 能力
|
|
14
|
+
|
|
15
|
+
- 检索知乎站内问答与文章:按点赞数、评论数或更新时间排序,可按发布时间范围过滤。
|
|
16
|
+
- 检索知乎索引中的公开网页:按站点域名与发布时间过滤。
|
|
17
|
+
- 调用知乎直答:快问快答、深度思考、智能体三个档位。
|
|
18
|
+
- 相同查询在缓存 TTL 内复用结果,减少每日额度消耗。
|
|
19
|
+
- 本地令牌桶在额度之外再兜一层,约束单轮对话内的突发请求。
|
|
20
|
+
- 搜索结果显示为带作者与点赞数的来源卡片,同时保留纯 Markdown 回退。
|
|
21
|
+
|
|
22
|
+
## 安装
|
|
23
|
+
|
|
24
|
+
### 前置
|
|
25
|
+
|
|
26
|
+
- DSH `0.1.5-rc.2`
|
|
27
|
+
- Node `>= 20`
|
|
28
|
+
|
|
29
|
+
### 从源码安装
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
git clone https://github.com/zlZayn/dsh-zhihu-search.git
|
|
33
|
+
cd dsh-zhihu-search
|
|
34
|
+
npm install && npm run build
|
|
35
|
+
|
|
36
|
+
dsh plugin --profile web add "$PWD"
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
`dsh plugin` 会把本包装进 profile 并挂进 `dsh.profile.bundles`。重启 `dsh web` 后生效。
|
|
40
|
+
|
|
41
|
+
### 从 npm 安装
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
dsh plugin --profile web add dsh-zhihu-search
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## 配置
|
|
48
|
+
|
|
49
|
+
### 在设置界面填写
|
|
50
|
+
|
|
51
|
+
打开 **设置 → 插件 → 插件配置 → 知乎搜索**,填入 Access Secret 并保存。保存后立即生效,无需重启 DSH。
|
|
52
|
+
|
|
53
|
+
Access Secret 在知乎开放平台的[个人中心](https://developer.zhihu.com/profile)获取。
|
|
54
|
+
|
|
55
|
+
### 用环境变量代替
|
|
56
|
+
|
|
57
|
+
不想把密钥存在设置里时,改用环境变量 `ZHIHU_ACCESS_SECRET`;或在卡片的「凭据引用名」里填别的名字,指向另一个环境变量或凭据记录。
|
|
58
|
+
|
|
59
|
+
## 安全与边界
|
|
60
|
+
|
|
61
|
+
- Access Secret 只在设置界面、凭据域与环境变量之间流转:不写日志、不以明文进入缓存键、不进仓库。
|
|
62
|
+
- 返回内容按外部不可信数据处理:摘要剥离 HTML 标签,链接剥离跟踪参数。
|
|
63
|
+
- 只访问 `developer.zhihu.com`,不代理、不转发其他流量。
|
|
64
|
+
|
|
65
|
+
## 许可
|
|
66
|
+
|
|
67
|
+
[MIT](LICENSE)。
|
|
68
|
+
|
|
69
|
+
## 贡献
|
|
70
|
+
|
|
71
|
+
维护者文档地图见 [AGENTS.md](AGENTS.md);不变的设计约束见 [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md);发布流程见 [docs/PUBLISHING.md](docs/PUBLISHING.md)。
|
package/cordis.patch.yml
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# dsh-zhihu-search bundle patch —— 把本插件作为一行插入 dsh profile。
|
|
2
|
+
#
|
|
3
|
+
# 格式对齐 DSH 官方 bundle patch(见 @deepseek-ai/dsh-base/cordis.patch.yml):
|
|
4
|
+
# 顶层是 loader patch 条目数组;`insert` 在 profile 根上追加插件行。
|
|
5
|
+
#
|
|
6
|
+
# 关于 name:必须是 loader 能从 profile 的 node_modules 解析到的模块 id,即本包自身的包名。
|
|
7
|
+
#
|
|
8
|
+
# 关于 config:**刻意不写 accessSecret**。写了空串会让设置卡片的「已配置」判据
|
|
9
|
+
# (DSH 的 RedactedSecret.set = value !== undefined)把空串当成已设值,卡片一打开就显示
|
|
10
|
+
# 「已配置」,而实际没有任何密钥。密钥应当只从设置界面写入。
|
|
11
|
+
- insert:
|
|
12
|
+
- id: dsh-zhihu-search
|
|
13
|
+
name: dsh-zhihu-search
|
package/lib/client.js
ADDED
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
window.__ModuleLoader__.load({
|
|
2
|
+
id: "dsh-zhihu-search",
|
|
3
|
+
factory: (require) => {
|
|
4
|
+
var module = { exports: {} };
|
|
5
|
+
var exports = module.exports;
|
|
6
|
+
"use strict";
|
|
7
|
+
var __defProp = Object.defineProperty;
|
|
8
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
9
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
10
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
11
|
+
var __export = (target, all) => {
|
|
12
|
+
for (var name in all)
|
|
13
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
14
|
+
};
|
|
15
|
+
var __copyProps = (to, from, except, desc) => {
|
|
16
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
17
|
+
for (let key of __getOwnPropNames(from))
|
|
18
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
19
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
20
|
+
}
|
|
21
|
+
return to;
|
|
22
|
+
};
|
|
23
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
24
|
+
|
|
25
|
+
// src/client/index.tsx
|
|
26
|
+
var client_exports = {};
|
|
27
|
+
__export(client_exports, {
|
|
28
|
+
apply: () => apply,
|
|
29
|
+
inject: () => inject,
|
|
30
|
+
readConfigured: () => readConfigured
|
|
31
|
+
});
|
|
32
|
+
module.exports = __toCommonJS(client_exports);
|
|
33
|
+
var import_react = require("react");
|
|
34
|
+
var import_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
35
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
36
|
+
var inject = ["slots", "settingsScope"];
|
|
37
|
+
var NAMESPACE = "zhihu-search";
|
|
38
|
+
var SECRET_FIELD = "accessSecret";
|
|
39
|
+
var REF_FIELD = "accessSecretRef";
|
|
40
|
+
function asRecord(value) {
|
|
41
|
+
return typeof value === "object" && value !== null ? value : void 0;
|
|
42
|
+
}
|
|
43
|
+
function readConfigured(mirror) {
|
|
44
|
+
const view = mirror.getSnapshot().view?.namespaces.find((row) => row.ns === NAMESPACE);
|
|
45
|
+
const slot = view?.secrets?.find(
|
|
46
|
+
(entry) => entry.path.length === 1 && entry.path[0] === SECRET_FIELD
|
|
47
|
+
);
|
|
48
|
+
return slot?.set === true;
|
|
49
|
+
}
|
|
50
|
+
var S = {
|
|
51
|
+
card: {
|
|
52
|
+
listStyle: "none",
|
|
53
|
+
border: "0.5px solid var(--dsw-alias-border-l4)",
|
|
54
|
+
borderRadius: 16,
|
|
55
|
+
background: "var(--dsw-alias-bg-layer-3)",
|
|
56
|
+
transition: "border-color .16s, background .16s"
|
|
57
|
+
},
|
|
58
|
+
cardOpen: {
|
|
59
|
+
listStyle: "none",
|
|
60
|
+
border: "0.5px solid var(--dsw-alias-label-dimmed)",
|
|
61
|
+
borderRadius: 16,
|
|
62
|
+
background: "var(--dsw-alias-bg-layer-2)",
|
|
63
|
+
transition: "border-color .16s, background .16s"
|
|
64
|
+
},
|
|
65
|
+
header: {
|
|
66
|
+
width: "100%",
|
|
67
|
+
appearance: "none",
|
|
68
|
+
border: 0,
|
|
69
|
+
background: "none",
|
|
70
|
+
font: "inherit",
|
|
71
|
+
color: "inherit",
|
|
72
|
+
textAlign: "left",
|
|
73
|
+
cursor: "pointer",
|
|
74
|
+
display: "flex",
|
|
75
|
+
alignItems: "center",
|
|
76
|
+
gap: 12,
|
|
77
|
+
padding: "14px 16px",
|
|
78
|
+
borderRadius: 12
|
|
79
|
+
},
|
|
80
|
+
headText: { flex: 1, minWidth: 0, display: "flex", flexDirection: "column", gap: 4 },
|
|
81
|
+
name: { fontSize: 15, fontWeight: 600, lineHeight: 1.4, color: "var(--dsw-alias-label-primary)" },
|
|
82
|
+
description: { fontSize: 13, lineHeight: 1.5, color: "var(--dsw-alias-label-tertiary)" },
|
|
83
|
+
chevron: { flex: "none", color: "var(--dsw-alias-label-tertiary)", transition: "transform .16s" },
|
|
84
|
+
chevronOpen: { flex: "none", color: "var(--dsw-alias-label-tertiary)", transform: "rotate(180deg)", transition: "transform .16s" },
|
|
85
|
+
body: { borderTop: "0.5px solid var(--dsw-alias-border-l2)", margin: "0 16px", paddingBottom: 8 },
|
|
86
|
+
readOnly: { margin: "12px 0 0", fontSize: 12, lineHeight: 1.5, color: "var(--dsw-alias-label-tertiary)" },
|
|
87
|
+
field: { display: "flex", flexDirection: "column", gap: 6, padding: "12px 0" },
|
|
88
|
+
fieldDivider: { display: "flex", flexDirection: "column", gap: 6, padding: "12px 0", borderTop: "0.5px solid var(--dsw-alias-border-l2)" },
|
|
89
|
+
head: { display: "flex", alignItems: "center", gap: 8 },
|
|
90
|
+
label: { flex: 1, minWidth: 0, fontSize: 13, fontWeight: 500, lineHeight: 1.5, color: "var(--dsw-alias-label-primary)" },
|
|
91
|
+
badges: { display: "inline-flex", alignItems: "center", gap: 8 },
|
|
92
|
+
reset: {
|
|
93
|
+
border: "none",
|
|
94
|
+
background: "none",
|
|
95
|
+
padding: 0,
|
|
96
|
+
font: "inherit",
|
|
97
|
+
fontSize: 12,
|
|
98
|
+
lineHeight: 1.5,
|
|
99
|
+
color: "var(--dsw-alias-label-secondary)",
|
|
100
|
+
cursor: "pointer"
|
|
101
|
+
},
|
|
102
|
+
input: {
|
|
103
|
+
height: 34,
|
|
104
|
+
padding: "0 12px",
|
|
105
|
+
border: "0.5px solid var(--dsw-alias-border-l4)",
|
|
106
|
+
borderRadius: 8,
|
|
107
|
+
background: "var(--dsw-alias-bg-layer-3)",
|
|
108
|
+
font: "inherit",
|
|
109
|
+
fontSize: 13,
|
|
110
|
+
lineHeight: 1.5,
|
|
111
|
+
color: "var(--dsw-alias-label-primary)",
|
|
112
|
+
boxSizing: "border-box",
|
|
113
|
+
width: "100%"
|
|
114
|
+
},
|
|
115
|
+
hint: { margin: 0, fontSize: 12, lineHeight: 1.5, color: "var(--dsw-alias-label-tertiary)" },
|
|
116
|
+
footer: {
|
|
117
|
+
display: "flex",
|
|
118
|
+
alignItems: "center",
|
|
119
|
+
justifyContent: "flex-end",
|
|
120
|
+
gap: 8,
|
|
121
|
+
padding: "12px 0 4px",
|
|
122
|
+
borderTop: "0.5px solid var(--dsw-alias-border-l2)"
|
|
123
|
+
},
|
|
124
|
+
failed: { flex: 1, minWidth: 0, margin: 0, fontSize: 12, lineHeight: 1.5, color: "var(--dsw-alias-label-error)" },
|
|
125
|
+
discard: {
|
|
126
|
+
appearance: "none",
|
|
127
|
+
border: "1px solid var(--dsw-alias-border-l2)",
|
|
128
|
+
borderRadius: 8,
|
|
129
|
+
padding: "5px 14px",
|
|
130
|
+
font: "inherit",
|
|
131
|
+
fontSize: 13,
|
|
132
|
+
lineHeight: 1.5,
|
|
133
|
+
cursor: "pointer",
|
|
134
|
+
background: "none",
|
|
135
|
+
color: "var(--dsw-alias-label-secondary)"
|
|
136
|
+
},
|
|
137
|
+
save: {
|
|
138
|
+
appearance: "none",
|
|
139
|
+
border: "1px solid transparent",
|
|
140
|
+
borderRadius: 8,
|
|
141
|
+
padding: "5px 14px",
|
|
142
|
+
font: "inherit",
|
|
143
|
+
fontSize: 13,
|
|
144
|
+
lineHeight: 1.5,
|
|
145
|
+
cursor: "pointer",
|
|
146
|
+
background: "var(--dsw-alias-label-primary)",
|
|
147
|
+
color: "var(--dsw-alias-bg-layer-3)"
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
function dimStyle(base, disabled) {
|
|
151
|
+
return disabled ? { ...base, opacity: 0.4, cursor: "default" } : base;
|
|
152
|
+
}
|
|
153
|
+
function ZhihuCard({ scope, mirror }) {
|
|
154
|
+
const snapshot = (0, import_react.useSyncExternalStore)(
|
|
155
|
+
(onChange) => scope.subscribe(onChange),
|
|
156
|
+
() => scope.getSnapshot()
|
|
157
|
+
);
|
|
158
|
+
const configured = (0, import_react.useSyncExternalStore)(
|
|
159
|
+
(onChange) => mirror.subscribe(onChange),
|
|
160
|
+
() => readConfigured(mirror)
|
|
161
|
+
);
|
|
162
|
+
const [open, setOpen] = (0, import_react.useState)(false);
|
|
163
|
+
const [secret, setSecret] = (0, import_react.useState)("");
|
|
164
|
+
const [refDraft, setRefDraft] = (0, import_react.useState)(void 0);
|
|
165
|
+
const [saving, setSaving] = (0, import_react.useState)(false);
|
|
166
|
+
const [failed, setFailed] = (0, import_react.useState)("");
|
|
167
|
+
const writable = snapshot.writable && snapshot.status === "ready";
|
|
168
|
+
const disabled = !writable || saving;
|
|
169
|
+
const effectiveRef = typeof snapshot.value?.[REF_FIELD] === "string" ? snapshot.value[REF_FIELD] : "";
|
|
170
|
+
const refText = refDraft ?? effectiveRef;
|
|
171
|
+
const refOverridden = asRecord(snapshot.user)?.[REF_FIELD] !== void 0;
|
|
172
|
+
const refDirty = refText !== effectiveRef;
|
|
173
|
+
const dirty = secret !== "" || refDirty;
|
|
174
|
+
const blocked = !dirty || disabled;
|
|
175
|
+
const discard = () => {
|
|
176
|
+
setSecret("");
|
|
177
|
+
setRefDraft(void 0);
|
|
178
|
+
setFailed("");
|
|
179
|
+
};
|
|
180
|
+
const save = async () => {
|
|
181
|
+
setSaving(true);
|
|
182
|
+
setFailed("");
|
|
183
|
+
try {
|
|
184
|
+
if (secret !== "") await scope.set(SECRET_FIELD, secret);
|
|
185
|
+
if (refDirty) {
|
|
186
|
+
if (refText === "") await scope.unset(REF_FIELD);
|
|
187
|
+
else await scope.set(REF_FIELD, refText);
|
|
188
|
+
}
|
|
189
|
+
setSecret("");
|
|
190
|
+
setRefDraft(void 0);
|
|
191
|
+
setOpen(false);
|
|
192
|
+
} catch (error) {
|
|
193
|
+
setFailed(error instanceof Error ? error.message : String(error));
|
|
194
|
+
} finally {
|
|
195
|
+
setSaving(false);
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { style: open ? S.cardOpen : S.card, children: [
|
|
199
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
200
|
+
"button",
|
|
201
|
+
{
|
|
202
|
+
type: "button",
|
|
203
|
+
style: S.header,
|
|
204
|
+
"aria-expanded": open,
|
|
205
|
+
onClick: () => {
|
|
206
|
+
setOpen(!open);
|
|
207
|
+
},
|
|
208
|
+
children: [
|
|
209
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { style: S.headText, children: [
|
|
210
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: S.name, children: "\u77E5\u4E4E\u641C\u7D22" }),
|
|
211
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: S.description, children: "\u77E5\u4E4E\u5F00\u653E\u5E73\u53F0\u7684\u7AD9\u5185\u68C0\u7D22\u3001\u5168\u7F51\u68C0\u7D22\u4E0E\u76F4\u7B54\u5DE5\u5177\u3002" })
|
|
212
|
+
] }),
|
|
213
|
+
dirty ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Tag, { tone: "neutral", children: "\u672A\u4FDD\u5B58" }) : null,
|
|
214
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: open ? S.chevronOpen : S.chevron, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.IconChevronDownOutline14, {}) })
|
|
215
|
+
]
|
|
216
|
+
}
|
|
217
|
+
),
|
|
218
|
+
open ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: S.body, children: [
|
|
219
|
+
!writable && snapshot.status !== "loading" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: S.readOnly, role: "status", children: "\u5F53\u524D\u8FDE\u63A5\u4E0D\u5141\u8BB8\u5199\u5165\u8BBE\u7F6E\u3002" }) : null,
|
|
220
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: S.field, children: [
|
|
221
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: S.head, children: [
|
|
222
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { style: S.label, htmlFor: "zhihu-access-secret", children: "Access Secret" }),
|
|
223
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: S.badges, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Tag, { tone: configured ? "neutral" : "quiet", children: configured ? "\u5DF2\u914D\u7F6E" : "\u672A\u914D\u7F6E" }) })
|
|
224
|
+
] }),
|
|
225
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
226
|
+
"input",
|
|
227
|
+
{
|
|
228
|
+
id: "zhihu-access-secret",
|
|
229
|
+
style: S.input,
|
|
230
|
+
type: "password",
|
|
231
|
+
autoComplete: "off",
|
|
232
|
+
value: secret,
|
|
233
|
+
disabled,
|
|
234
|
+
onChange: (event) => {
|
|
235
|
+
setSecret(event.target.value);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
),
|
|
239
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: S.hint, children: "\u5728 developer.zhihu.com \u4E2A\u4EBA\u4E2D\u5FC3\u83B7\u53D6\u3002\u7559\u7A7A\u5219\u4E0D\u4FEE\u6539\u5DF2\u4FDD\u5B58\u7684\u5BC6\u94A5\u3002" })
|
|
240
|
+
] }),
|
|
241
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: S.fieldDivider, children: [
|
|
242
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: S.head, children: [
|
|
243
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { style: S.label, htmlFor: "zhihu-access-secret-ref", children: "\u51ED\u636E\u5F15\u7528\u540D" }),
|
|
244
|
+
refOverridden ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { style: S.badges, children: [
|
|
245
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Tag, { tone: "neutral", children: "\u5DF2\u8986\u76D6" }),
|
|
246
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
247
|
+
"button",
|
|
248
|
+
{
|
|
249
|
+
type: "button",
|
|
250
|
+
style: dimStyle(S.reset, disabled),
|
|
251
|
+
disabled,
|
|
252
|
+
onClick: () => {
|
|
253
|
+
setRefDraft("");
|
|
254
|
+
},
|
|
255
|
+
children: "\u91CD\u7F6E"
|
|
256
|
+
}
|
|
257
|
+
)
|
|
258
|
+
] }) : null
|
|
259
|
+
] }),
|
|
260
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
261
|
+
"input",
|
|
262
|
+
{
|
|
263
|
+
id: "zhihu-access-secret-ref",
|
|
264
|
+
style: S.input,
|
|
265
|
+
type: "text",
|
|
266
|
+
spellCheck: false,
|
|
267
|
+
value: refText,
|
|
268
|
+
disabled,
|
|
269
|
+
onChange: (event) => {
|
|
270
|
+
setRefDraft(event.target.value);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
),
|
|
274
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: S.hint, children: "\u73AF\u5883\u53D8\u91CF\u6216\u51ED\u636E\u8BB0\u5F55\u7684\u540D\u5B57\uFF1B\u7559\u7A7A\u5E76\u4FDD\u5B58\u4F1A\u6E05\u6389\u8986\u76D6\uFF0C\u56DE\u843D\u5230\u9ED8\u8BA4\u7684 ZHIHU_ACCESS_SECRET\u3002" })
|
|
275
|
+
] }),
|
|
276
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: S.footer, children: [
|
|
277
|
+
failed !== "" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: S.failed, role: "status", children: failed }) : null,
|
|
278
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", style: dimStyle(S.discard, !dirty || saving), disabled: !dirty || saving, onClick: discard, children: "\u653E\u5F03" }),
|
|
279
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
280
|
+
"button",
|
|
281
|
+
{
|
|
282
|
+
type: "button",
|
|
283
|
+
style: dimStyle(S.save, blocked),
|
|
284
|
+
disabled: blocked,
|
|
285
|
+
onClick: () => {
|
|
286
|
+
void save();
|
|
287
|
+
},
|
|
288
|
+
children: saving ? "\u4FDD\u5B58\u4E2D\u2026" : "\u4FDD\u5B58"
|
|
289
|
+
}
|
|
290
|
+
)
|
|
291
|
+
] })
|
|
292
|
+
] }) : null
|
|
293
|
+
] });
|
|
294
|
+
}
|
|
295
|
+
function apply(ctx) {
|
|
296
|
+
const scope = ctx.settingsScope.bind({ namespace: NAMESPACE });
|
|
297
|
+
const mirror = ctx.settingsScope.describe();
|
|
298
|
+
ctx.slots.inject(
|
|
299
|
+
"settings.plugin.item",
|
|
300
|
+
() => ctx.slots.register(
|
|
301
|
+
{ name: "settings.plugin.item", key: NAMESPACE },
|
|
302
|
+
() => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ZhihuCard, { scope, mirror })
|
|
303
|
+
)
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
return module.exports;
|
|
307
|
+
}
|
|
308
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Access Secret 的解析:三条来源、固定优先级、可单测。
|
|
3
|
+
*
|
|
4
|
+
* 为什么独立成模块:优先级是**契约**而不是实现细节 ——
|
|
5
|
+
* 设置界面填的值、凭据记录、环境变量三者谁赢,直接决定用户改了设置为什么不生效。
|
|
6
|
+
* 写在 `apply()` 的闭包里就没法单测,而这类问题只在真机上暴露。
|
|
7
|
+
*/
|
|
8
|
+
/** 解析所需的三条来源。全部是函数,因为密钥可以在运行期被改动。 */
|
|
9
|
+
export interface SecretSources {
|
|
10
|
+
/** 凭据域查询。返回空白串按「未配置」处理。 */
|
|
11
|
+
readonly fromCredentials: (reference: string) => Promise<string | undefined>;
|
|
12
|
+
/** 设置 section 里由用户直接填写的值。 */
|
|
13
|
+
readonly fromSettings: () => string | undefined;
|
|
14
|
+
/** 环境变量查询。 */
|
|
15
|
+
readonly fromEnvironment: (name: string) => string | undefined;
|
|
16
|
+
/** 凭据引用名(默认 `ZHIHU_ACCESS_SECRET`)。 */
|
|
17
|
+
readonly referenceName: () => string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* 按固定优先级解析 Access Secret。
|
|
21
|
+
*
|
|
22
|
+
* 优先级:**凭据域 → 设置 section → 环境变量**。
|
|
23
|
+
* 凭据域排在第一位,是因为它是设置界面与凭据记录的落点;
|
|
24
|
+
* 排在它前面的任何来源都会让「我在设置里填了却不生效」变成一个真实故障。
|
|
25
|
+
*
|
|
26
|
+
* @param sources - 三条来源。
|
|
27
|
+
* @returns 可用的密钥;三条都空时返回 `undefined`,由调用方转成鉴权错误。
|
|
28
|
+
*/
|
|
29
|
+
export declare function resolveAccessSecret(sources: SecretSources): Promise<string | undefined>;
|
|
30
|
+
//# sourceMappingURL=credentials.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../src/credentials.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,qCAAqC;AACrC,MAAM,WAAW,aAAa;IAC5B,2BAA2B;IAC3B,QAAQ,CAAC,eAAe,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IAC7E,6BAA6B;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,MAAM,GAAG,SAAS,CAAC;IAChD,cAAc;IACd,QAAQ,CAAC,eAAe,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IAC/D,uCAAuC;IACvC,QAAQ,CAAC,aAAa,EAAE,MAAM,MAAM,CAAC;CACtC;AAOD;;;;;;;;;GASG;AACH,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAa7F"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Access Secret 的解析:三条来源、固定优先级、可单测。
|
|
3
|
+
*
|
|
4
|
+
* 为什么独立成模块:优先级是**契约**而不是实现细节 ——
|
|
5
|
+
* 设置界面填的值、凭据记录、环境变量三者谁赢,直接决定用户改了设置为什么不生效。
|
|
6
|
+
* 写在 `apply()` 的闭包里就没法单测,而这类问题只在真机上暴露。
|
|
7
|
+
*/
|
|
8
|
+
/** 判定一个候选值是否可用:必须是含非空白字符的字符串。 */
|
|
9
|
+
function usable(value) {
|
|
10
|
+
return value !== undefined && value.trim() !== '';
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* 按固定优先级解析 Access Secret。
|
|
14
|
+
*
|
|
15
|
+
* 优先级:**凭据域 → 设置 section → 环境变量**。
|
|
16
|
+
* 凭据域排在第一位,是因为它是设置界面与凭据记录的落点;
|
|
17
|
+
* 排在它前面的任何来源都会让「我在设置里填了却不生效」变成一个真实故障。
|
|
18
|
+
*
|
|
19
|
+
* @param sources - 三条来源。
|
|
20
|
+
* @returns 可用的密钥;三条都空时返回 `undefined`,由调用方转成鉴权错误。
|
|
21
|
+
*/
|
|
22
|
+
export async function resolveAccessSecret(sources) {
|
|
23
|
+
const reference = sources.referenceName();
|
|
24
|
+
const fromCredentials = await sources.fromCredentials(reference);
|
|
25
|
+
if (usable(fromCredentials))
|
|
26
|
+
return fromCredentials;
|
|
27
|
+
const fromSettings = sources.fromSettings();
|
|
28
|
+
if (usable(fromSettings))
|
|
29
|
+
return fromSettings;
|
|
30
|
+
const fromEnvironment = sources.fromEnvironment(reference);
|
|
31
|
+
if (usable(fromEnvironment))
|
|
32
|
+
return fromEnvironment;
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=credentials.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credentials.js","sourceRoot":"","sources":["../src/credentials.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAcH,iCAAiC;AACjC,SAAS,MAAM,CAAC,KAAyB;IACvC,OAAO,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;AACpD,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,OAAsB;IAC9D,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;IAE1C,MAAM,eAAe,GAAG,MAAM,OAAO,CAAC,eAAe,CAAC,SAAS,CAAC,CAAC;IACjE,IAAI,MAAM,CAAC,eAAe,CAAC;QAAE,OAAO,eAAe,CAAC;IAEpD,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAC5C,IAAI,MAAM,CAAC,YAAY,CAAC;QAAE,OAAO,YAAY,CAAC;IAE9C,MAAM,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC,SAAS,CAAC,CAAC;IAC3D,IAAI,MAAM,CAAC,eAAe,CAAC;QAAE,OAAO,eAAe,CAAC;IAEpD,OAAO,SAAS,CAAC;AACnB,CAAC"}
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-zhihu-search 插件入口。
|
|
3
|
+
*
|
|
4
|
+
* 本文件是**唯一**接触 Cordis 的地方:生命周期、配置校验、设置命名空间、工具注册。
|
|
5
|
+
* 工具实现本身不依赖 Cordis,因此可以用普通对象直接单测。
|
|
6
|
+
*
|
|
7
|
+
* 生命周期纪律(红线 4):
|
|
8
|
+
* 客户端与全部状态(缓存、令牌桶)都在 `ctx.effect()` 内创建,
|
|
9
|
+
* 由返回的 disposer 释放。模块顶层**不持有任何可变状态** ——
|
|
10
|
+
* 否则 HMR 重载后会留下两代插件共用缓存与限流桶的幽灵故障。
|
|
11
|
+
*/
|
|
12
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
13
|
+
import z from '@deepseek-ai/schemastery';
|
|
14
|
+
/** Cordis 插件名,用于 loader 诊断。 */
|
|
15
|
+
export declare const name = "zhihu-search";
|
|
16
|
+
/** 本插件依赖的工具注册表服务。 */
|
|
17
|
+
export declare const inject: string[];
|
|
18
|
+
/**
|
|
19
|
+
* 设置命名空间。
|
|
20
|
+
*
|
|
21
|
+
* 同时是 client 半体注册设置卡片时的 `key`:官方「插件」设置页按命名空间
|
|
22
|
+
* 分派卡片,两端必须用同一个字符串,否则卡片不会被渲染。
|
|
23
|
+
*/
|
|
24
|
+
export declare const ZHIHU_SETTINGS_NAMESPACE = "zhihu-search";
|
|
25
|
+
/** 凭据引用的默认名,按 DSH 约定取环境变量名。 */
|
|
26
|
+
export declare const DEFAULT_ACCESS_SECRET_REF = "ZHIHU_ACCESS_SECRET";
|
|
27
|
+
/** 插件配置。 */
|
|
28
|
+
export interface Config {
|
|
29
|
+
/**
|
|
30
|
+
* 字面量 Access Secret。
|
|
31
|
+
*
|
|
32
|
+
* `role('secret')` 让设置界面把它渲染成掩码输入框,并在下行描述里隐藏其值。
|
|
33
|
+
* 留空时回落到 {@link Config.accessSecretRef} 指向的凭据记录。
|
|
34
|
+
*/
|
|
35
|
+
accessSecret?: string;
|
|
36
|
+
/** 凭据引用名(环境变量名或凭据记录名)。 */
|
|
37
|
+
accessSecretRef?: string;
|
|
38
|
+
/** 覆盖接入域名,便于指向沙箱或代理。 */
|
|
39
|
+
baseUrl?: string;
|
|
40
|
+
/** 单次 HTTP 请求的超时预算(毫秒)。 */
|
|
41
|
+
timeoutMs?: number;
|
|
42
|
+
/** 搜索结果缓存存活时长(毫秒)。 */
|
|
43
|
+
cacheTtlMs?: number;
|
|
44
|
+
/** 搜索结果缓存条目上限。 */
|
|
45
|
+
cacheMaxEntries?: number;
|
|
46
|
+
/** 搜索类工具的本地每分钟上限(知乎额度之外的额外保护)。 */
|
|
47
|
+
searchPerMinute?: number;
|
|
48
|
+
/** 直答的本地每分钟上限。 */
|
|
49
|
+
zhidaPerMinute?: number;
|
|
50
|
+
/** 是否注册站内搜索工具。 */
|
|
51
|
+
enableSearch?: boolean;
|
|
52
|
+
/** 是否注册全网搜索工具。 */
|
|
53
|
+
enableGlobalSearch?: boolean;
|
|
54
|
+
/** 是否注册直答工具。 */
|
|
55
|
+
enableZhida?: boolean;
|
|
56
|
+
}
|
|
57
|
+
/** 配置的运行时校验 schema;默认值同时是文档。 */
|
|
58
|
+
export declare const Config: z<Schemastery.ObjectS<{
|
|
59
|
+
accessSecret: z<string, string>;
|
|
60
|
+
accessSecretRef: z<string, string>;
|
|
61
|
+
baseUrl: z<string, string>;
|
|
62
|
+
timeoutMs: z<number, number>;
|
|
63
|
+
cacheTtlMs: z<number, number>;
|
|
64
|
+
cacheMaxEntries: z<number, number>;
|
|
65
|
+
searchPerMinute: z<number, number>;
|
|
66
|
+
zhidaPerMinute: z<number, number>;
|
|
67
|
+
enableSearch: z<boolean, boolean>;
|
|
68
|
+
enableGlobalSearch: z<boolean, boolean>;
|
|
69
|
+
enableZhida: z<boolean, boolean>;
|
|
70
|
+
}>, Schemastery.ObjectT<{
|
|
71
|
+
accessSecret: z<string, string>;
|
|
72
|
+
accessSecretRef: z<string, string>;
|
|
73
|
+
baseUrl: z<string, string>;
|
|
74
|
+
timeoutMs: z<number, number>;
|
|
75
|
+
cacheTtlMs: z<number, number>;
|
|
76
|
+
cacheMaxEntries: z<number, number>;
|
|
77
|
+
searchPerMinute: z<number, number>;
|
|
78
|
+
zhidaPerMinute: z<number, number>;
|
|
79
|
+
enableSearch: z<boolean, boolean>;
|
|
80
|
+
enableGlobalSearch: z<boolean, boolean>;
|
|
81
|
+
enableZhida: z<boolean, boolean>;
|
|
82
|
+
}>>;
|
|
83
|
+
/**
|
|
84
|
+
* 注册插件:创建状态与客户端,注册设置命名空间,并按配置注册各工具。
|
|
85
|
+
*
|
|
86
|
+
* @param ctx - Cordis 上下文,其 `tools` 注册表接收工具定义。
|
|
87
|
+
* @param config - 已由 {@link Config} 校验并填充默认值的配置。
|
|
88
|
+
*/
|
|
89
|
+
export declare function apply(ctx: Context, config: Config): void;
|
|
90
|
+
export { ZHIHU_BASE_URL, ZhihuClient } from './transport.js';
|
|
91
|
+
export { resolveAccessSecret } from './credentials.js';
|
|
92
|
+
export type { SecretSources } from './credentials.js';
|
|
93
|
+
export { createState, LocalRateLimitError, MemoryCache, TokenBucket } from './state.js';
|
|
94
|
+
export { compileFilter, compileSortBy, CompileError } from './utils/compiler.js';
|
|
95
|
+
export type { ZhidaOutput, SearchOutput } from './types.js';
|
|
96
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAGnD,OAAO,CAAC,MAAM,0BAA0B,CAAC;AASzC,+BAA+B;AAC/B,eAAO,MAAM,IAAI,iBAAiB,CAAC;AAEnC,qBAAqB;AACrB,eAAO,MAAM,MAAM,UAAY,CAAC;AAEhC;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,iBAAiB,CAAC;AAEvD,+BAA+B;AAC/B,eAAO,MAAM,yBAAyB,wBAAwB,CAAC;AAE/D,YAAY;AACZ,MAAM,WAAW,MAAM;IACrB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,0BAA0B;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,wBAAwB;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,2BAA2B;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sBAAsB;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,kBAAkB;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,kCAAkC;IAClC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,kBAAkB;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,kBAAkB;IAClB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,kBAAkB;IAClB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,gBAAgB;IAChB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,gCAAgC;AAChC,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;GAcjB,CAAC;AAwBH;;;;;GAKG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CA8FxD;AAED,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AACvD,YAAY,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACxF,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACjF,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC"}
|