dsh-feishu-notifier 0.1.0 → 0.1.2
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/README.md +1 -1
- package/lib/client.js +66 -36
- package/lib/client.js.map +1 -1
- package/lib/index.js +94 -26
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -43,7 +43,7 @@ pnpm dsh --profile web
|
|
|
43
43
|
设置 → 飞书通知
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
-
填写飞书机器人 Webhook,点击“保存 Webhook
|
|
46
|
+
填写飞书机器人 Webhook,点击“保存 Webhook”,然后点击“发送测试消息”验证连接。配置会通过插件自己的 Host API 持久化到 DSH 的 `settings.yaml`,通常位于 `C:\Users\<用户名>\.dsh\settings.yaml`。真实 Webhook 不会包含在 npm 包中,每位用户都需要配置自己的地址。
|
|
47
47
|
|
|
48
48
|
## 从 GitHub 安装
|
|
49
49
|
|
package/lib/client.js
CHANGED
|
@@ -8,22 +8,43 @@ window.__ModuleLoader__.load({
|
|
|
8
8
|
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
9
9
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
10
10
|
//#region src/client/FeishuSettingsSection.tsx
|
|
11
|
-
function FeishuSettingsSection({
|
|
12
|
-
const [
|
|
11
|
+
function FeishuSettingsSection({ controller }) {
|
|
12
|
+
const [view, setView] = (0, react.useState)({ status: "loading" });
|
|
13
13
|
const [webhook, setWebhook] = (0, react.useState)("");
|
|
14
14
|
const [busy, setBusy] = (0, react.useState)(false);
|
|
15
15
|
const [status, setStatus] = (0, react.useState)("");
|
|
16
|
-
(0, react.useEffect)(() =>
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
(0, react.useEffect)(() => {
|
|
17
|
+
let active = true;
|
|
18
|
+
controller.load().then((config) => {
|
|
19
|
+
if (active) setView({
|
|
20
|
+
status: "ready",
|
|
21
|
+
config
|
|
22
|
+
});
|
|
23
|
+
}).catch((error) => {
|
|
24
|
+
if (active) setStatus(error instanceof Error ? error.message : String(error));
|
|
25
|
+
});
|
|
26
|
+
return () => {
|
|
27
|
+
active = false;
|
|
28
|
+
};
|
|
29
|
+
}, [controller]);
|
|
30
|
+
const config = view.status === "ready" ? view.config : {
|
|
31
|
+
enabled: true,
|
|
32
|
+
webhookConfigured: false
|
|
33
|
+
};
|
|
34
|
+
const save = async (patch, message) => {
|
|
35
|
+
setBusy(true);
|
|
21
36
|
setStatus("正在保存…");
|
|
22
37
|
try {
|
|
23
|
-
await
|
|
24
|
-
|
|
38
|
+
const next = await controller.update(patch);
|
|
39
|
+
setView({
|
|
40
|
+
status: "ready",
|
|
41
|
+
config: next
|
|
42
|
+
});
|
|
43
|
+
setStatus(message);
|
|
25
44
|
} catch (error) {
|
|
26
45
|
setStatus(error instanceof Error ? error.message : String(error));
|
|
46
|
+
} finally {
|
|
47
|
+
setBusy(false);
|
|
27
48
|
}
|
|
28
49
|
};
|
|
29
50
|
const saveWebhook = async () => {
|
|
@@ -32,26 +53,14 @@ window.__ModuleLoader__.load({
|
|
|
32
53
|
setStatus("请输入 Webhook 地址");
|
|
33
54
|
return;
|
|
34
55
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
try {
|
|
38
|
-
await scope.set("webhook", value);
|
|
39
|
-
setWebhook("");
|
|
40
|
-
setStatus("Webhook 已保存");
|
|
41
|
-
} catch (error) {
|
|
42
|
-
setStatus(error instanceof Error ? error.message : String(error));
|
|
43
|
-
} finally {
|
|
44
|
-
setBusy(false);
|
|
45
|
-
}
|
|
56
|
+
await save({ webhook: value }, "Webhook 已保存到 DSH 设置文件");
|
|
57
|
+
setWebhook("");
|
|
46
58
|
};
|
|
47
59
|
const sendTest = async () => {
|
|
48
60
|
setBusy(true);
|
|
49
61
|
setStatus("正在发送测试消息…");
|
|
50
62
|
try {
|
|
51
|
-
|
|
52
|
-
const result = await response.json();
|
|
53
|
-
if (!response.ok || result.ok !== true) throw new Error(result.message ?? `HTTP ${String(response.status)}`);
|
|
54
|
-
setStatus(result.message ?? "测试消息已发送");
|
|
63
|
+
setStatus(await controller.test());
|
|
55
64
|
} catch (error) {
|
|
56
65
|
setStatus(error instanceof Error ? error.message : String(error));
|
|
57
66
|
} finally {
|
|
@@ -101,9 +110,10 @@ window.__ModuleLoader__.load({
|
|
|
101
110
|
})] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
102
111
|
"aria-label": "启用通知",
|
|
103
112
|
type: "checkbox",
|
|
104
|
-
checked: enabled,
|
|
113
|
+
checked: config.enabled,
|
|
114
|
+
disabled: busy || view.status === "loading",
|
|
105
115
|
onChange: (event) => {
|
|
106
|
-
|
|
116
|
+
save({ enabled: event.target.checked }, event.target.checked ? "通知已开启" : "通知已关闭");
|
|
107
117
|
}
|
|
108
118
|
})]
|
|
109
119
|
}),
|
|
@@ -116,7 +126,8 @@ window.__ModuleLoader__.load({
|
|
|
116
126
|
"aria-label": "Webhook 地址",
|
|
117
127
|
type: "url",
|
|
118
128
|
value: webhook,
|
|
119
|
-
placeholder: "
|
|
129
|
+
placeholder: config.webhookConfigured ? "已配置地址;输入新地址以替换" : "请输入飞书机器人 Webhook",
|
|
130
|
+
disabled: busy || view.status === "loading",
|
|
120
131
|
onChange: (event) => {
|
|
121
132
|
setWebhook(event.target.value);
|
|
122
133
|
setStatus("");
|
|
@@ -140,7 +151,7 @@ window.__ModuleLoader__.load({
|
|
|
140
151
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
141
152
|
variant: "primary",
|
|
142
153
|
size: "sm",
|
|
143
|
-
disabled: busy ||
|
|
154
|
+
disabled: busy || view.status === "loading",
|
|
144
155
|
onClick: () => {
|
|
145
156
|
saveWebhook();
|
|
146
157
|
},
|
|
@@ -148,7 +159,7 @@ window.__ModuleLoader__.load({
|
|
|
148
159
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
149
160
|
variant: "outline",
|
|
150
161
|
size: "sm",
|
|
151
|
-
disabled: busy || !
|
|
162
|
+
disabled: busy || view.status === "loading" || !config.webhookConfigured,
|
|
152
163
|
onClick: () => {
|
|
153
164
|
sendTest();
|
|
154
165
|
},
|
|
@@ -162,10 +173,6 @@ window.__ModuleLoader__.load({
|
|
|
162
173
|
color: "var(--dsw-alias-label-secondary)"
|
|
163
174
|
},
|
|
164
175
|
children: status
|
|
165
|
-
}),
|
|
166
|
-
snapshot.status === "unavailable" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
167
|
-
role: "alert",
|
|
168
|
-
children: "当前连接不支持持久化设置。"
|
|
169
176
|
})
|
|
170
177
|
]
|
|
171
178
|
})]
|
|
@@ -173,15 +180,38 @@ window.__ModuleLoader__.load({
|
|
|
173
180
|
}
|
|
174
181
|
//#endregion
|
|
175
182
|
//#region src/client/index.ts
|
|
176
|
-
const inject = ["slots"
|
|
183
|
+
const inject = ["slots"];
|
|
177
184
|
function apply(ctx) {
|
|
178
|
-
const
|
|
185
|
+
const controller = {
|
|
186
|
+
load: async () => {
|
|
187
|
+
const response = await fetch("/api/feishu-notifier/config");
|
|
188
|
+
const result = await response.json();
|
|
189
|
+
if (!response.ok || result.ok !== true || result.config === void 0) throw new Error(result.message ?? `HTTP ${String(response.status)}`);
|
|
190
|
+
return result.config;
|
|
191
|
+
},
|
|
192
|
+
update: async (patch) => {
|
|
193
|
+
const response = await fetch("/api/feishu-notifier/config", {
|
|
194
|
+
method: "POST",
|
|
195
|
+
headers: { "content-type": "application/json" },
|
|
196
|
+
body: JSON.stringify(patch)
|
|
197
|
+
});
|
|
198
|
+
const result = await response.json();
|
|
199
|
+
if (!response.ok || result.ok !== true || result.config === void 0) throw new Error(result.message ?? `HTTP ${String(response.status)}`);
|
|
200
|
+
return result.config;
|
|
201
|
+
},
|
|
202
|
+
test: async () => {
|
|
203
|
+
const response = await fetch("/api/feishu-notifier/test", { method: "POST" });
|
|
204
|
+
const result = await response.json();
|
|
205
|
+
if (!response.ok || result.ok !== true) throw new Error(result.message ?? `HTTP ${String(response.status)}`);
|
|
206
|
+
return result.message ?? "测试消息已发送";
|
|
207
|
+
}
|
|
208
|
+
};
|
|
179
209
|
ctx.slots.inject("settings.section", () => ctx.slots.register({
|
|
180
210
|
name: "settings.section",
|
|
181
211
|
id: "feishu-notifier",
|
|
182
212
|
order: 25,
|
|
183
213
|
label: () => "飞书通知",
|
|
184
|
-
inject: () => ({
|
|
214
|
+
inject: () => ({ controller })
|
|
185
215
|
}, FeishuSettingsSection));
|
|
186
216
|
}
|
|
187
217
|
//#endregion
|
package/lib/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","names":["Button"],"sources":["../src/client/FeishuSettingsSection.tsx","../src/client/index.ts"],"sourcesContent":["import { useEffect, useState } from 'react'\nimport type { PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { SettingsScope } from '@deepseek-ai/dsh-client-runtime/client'\nimport { Button } from '@deepseek-ai/dsh-client-ui-primitives'\n\nexport interface FeishuSettings {\n enabled: boolean\n webhook?: string\n}\n\ninterface Injected {\n scope: SettingsScope<FeishuSettings>\n}\n\ntype Props = PropsRuntime<'settings.section'> & Injected\n\nexport function FeishuSettingsSection({ scope }: Props) {\n const [snapshot, setSnapshot] = useState(scope.getSnapshot())\n const [webhook, setWebhook] = useState('')\n const [busy, setBusy] = useState(false)\n const [status, setStatus] = useState('')\n\n useEffect(() => scope.subscribe(() => { setSnapshot(scope.getSnapshot()) }), [scope])\n\n const enabled = snapshot.value?.enabled ?? true\n const saveEnabled = async (value: boolean) => {\n setStatus('正在保存…')\n try {\n await scope.set('enabled', value)\n setStatus(value ? '通知已开启' : '通知已关闭')\n } catch (error) {\n setStatus(error instanceof Error ? error.message : String(error))\n }\n }\n\n const saveWebhook = async () => {\n const value = webhook.trim()\n if (value === '') {\n setStatus('请输入 Webhook 地址')\n return\n }\n setBusy(true)\n setStatus('正在保存…')\n try {\n await scope.set('webhook', value)\n setWebhook('')\n setStatus('Webhook 已保存')\n } catch (error) {\n setStatus(error instanceof Error ? error.message : String(error))\n } finally {\n setBusy(false)\n }\n }\n\n const sendTest = async () => {\n setBusy(true)\n setStatus('正在发送测试消息…')\n try {\n const response = await fetch('/api/feishu-notifier/test', { method: 'POST' })\n const result = await response.json() as { ok?: boolean; message?: string }\n if (!response.ok || result.ok !== true) throw new Error(result.message ?? `HTTP ${String(response.status)}`)\n setStatus(result.message ?? '测试消息已发送')\n } catch (error) {\n setStatus(error instanceof Error ? error.message : String(error))\n } finally {\n setBusy(false)\n }\n }\n\n return (\n <section style={{ maxWidth: 680, display: 'grid', gap: 24 }}>\n <div>\n <h2 style={{ margin: 0, fontSize: 24 }}>飞书通知</h2>\n <p style={{ color: 'var(--dsw-alias-label-secondary)', lineHeight: 1.6 }}>\n 当智能体等待你的批准、需要回答问题,或一轮对话结束时,向飞书机器人发送提醒。\n </p>\n </div>\n <div style={{ display: 'grid', gap: 16, padding: 20, border: '1px solid var(--dsw-alias-border-l1)', borderRadius: 16 }}>\n <label style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 16 }}>\n <span>\n <strong style={{ display: 'block' }}>启用通知</strong>\n <small style={{ color: 'var(--dsw-alias-label-secondary)' }}>关闭后不会发送任何飞书消息。</small>\n </span>\n <input aria-label=\"启用通知\" type=\"checkbox\" checked={enabled} onChange={event => { void saveEnabled(event.target.checked) }} />\n </label>\n <label style={{ display: 'grid', gap: 8 }}>\n <span><strong>Webhook 地址</strong></span>\n <input\n aria-label=\"Webhook 地址\"\n type=\"url\"\n value={webhook}\n placeholder=\"已保存的地址不会回显;输入新地址以替换\"\n onChange={event => { setWebhook(event.target.value); setStatus('') }}\n style={{ minHeight: 40, padding: '0 12px', borderRadius: 10, border: '1px solid var(--dsw-alias-border-l1)', background: 'var(--dsw-alias-bg-layer-1)', color: 'inherit' }}\n />\n </label>\n <div style={{ display: 'flex', flexWrap: 'wrap', gap: 10 }}>\n <Button variant=\"primary\" size=\"sm\" disabled={busy || !snapshot.writable} onClick={() => { void saveWebhook() }}>保存 Webhook</Button>\n <Button variant=\"outline\" size=\"sm\" disabled={busy || !snapshot.writable} onClick={() => { void sendTest() }}>发送测试消息</Button>\n </div>\n {status !== '' && <p role=\"status\" style={{ margin: 0, color: 'var(--dsw-alias-label-secondary)' }}>{status}</p>}\n {snapshot.status === 'unavailable' && <p role=\"alert\">当前连接不支持持久化设置。</p>}\n </div>\n </section>\n )\n}\n","import type { ClientContext, SettingsScope } from '@deepseek-ai/dsh-client-runtime/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-settings/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-slots'\nimport { FeishuSettingsSection, type FeishuSettings } from './FeishuSettingsSection.tsx'\n\ninterface Injected {\n scope: SettingsScope<FeishuSettings>\n}\n\nexport const inject = ['slots', 'settingsScope']\n\nexport function apply(ctx: ClientContext): void {\n const scope = ctx.settingsScope.bind<FeishuSettings>({ namespace: 'feishu-notifier' })\n ctx.slots.inject('settings.section', () => ctx.slots.register({\n name: 'settings.section',\n id: 'feishu-notifier',\n order: 25,\n label: () => '飞书通知',\n inject: (): Injected => ({ scope }),\n }, FeishuSettingsSection))\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"client.js","names":["useState","Button"],"sources":["../src/client/FeishuSettingsSection.tsx","../src/client/index.ts"],"sourcesContent":["import { useEffect, useState } from 'react'\nimport type { PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'\nimport { Button } from '@deepseek-ai/dsh-client-ui-primitives'\n\nexport interface FeishuConfigView {\n enabled: boolean\n webhookConfigured: boolean\n}\n\nexport interface FeishuController {\n config?: FeishuConfigView\n load: () => Promise<FeishuConfigView>\n update: (patch: { enabled?: boolean; webhook?: string }) => Promise<FeishuConfigView>\n test: () => Promise<string>\n}\n\ntype Props = PropsRuntime<'settings.section'> & { controller: FeishuController }\n\ntype ViewState =\n | { status: 'loading' }\n | { status: 'ready'; config: FeishuConfigView }\n\nexport function FeishuSettingsSection({ controller }: Props) {\n const [view, setView] = useState<ViewState>({ status: 'loading' })\n const [webhook, setWebhook] = useState('')\n const [busy, setBusy] = useState(false)\n const [status, setStatus] = useState('')\n\n useEffect(() => {\n let active = true\n void controller.load()\n .then(config => {\n if (active) setView({ status: 'ready', config })\n })\n .catch(error => {\n if (active) setStatus(error instanceof Error ? error.message : String(error))\n })\n return () => { active = false }\n }, [controller])\n\n const config = view.status === 'ready' ? view.config : { enabled: true, webhookConfigured: false }\n\n const save = async (patch: { enabled?: boolean; webhook?: string }, message: string) => {\n setBusy(true)\n setStatus('正在保存…')\n try {\n const next = await controller.update(patch)\n setView({ status: 'ready', config: next })\n setStatus(message)\n } catch (error) {\n setStatus(error instanceof Error ? error.message : String(error))\n } finally {\n setBusy(false)\n }\n }\n\n const saveWebhook = async () => {\n const value = webhook.trim()\n if (value === '') {\n setStatus('请输入 Webhook 地址')\n return\n }\n await save({ webhook: value }, 'Webhook 已保存到 DSH 设置文件')\n setWebhook('')\n }\n\n const sendTest = async () => {\n setBusy(true)\n setStatus('正在发送测试消息…')\n try {\n setStatus(await controller.test())\n } catch (error) {\n setStatus(error instanceof Error ? error.message : String(error))\n } finally {\n setBusy(false)\n }\n }\n\n return (\n <section style={{ maxWidth: 680, display: 'grid', gap: 24 }}>\n <div>\n <h2 style={{ margin: 0, fontSize: 24 }}>飞书通知</h2>\n <p style={{ color: 'var(--dsw-alias-label-secondary)', lineHeight: 1.6 }}>\n 当智能体等待你的批准、需要回答问题,或一轮对话结束时,向飞书机器人发送提醒。\n </p>\n </div>\n <div style={{ display: 'grid', gap: 16, padding: 20, border: '1px solid var(--dsw-alias-border-l1)', borderRadius: 16 }}>\n <label style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 16 }}>\n <span>\n <strong style={{ display: 'block' }}>启用通知</strong>\n <small style={{ color: 'var(--dsw-alias-label-secondary)' }}>关闭后不会发送任何飞书消息。</small>\n </span>\n <input\n aria-label=\"启用通知\"\n type=\"checkbox\"\n checked={config.enabled}\n disabled={busy || view.status === 'loading'}\n onChange={event => { void save({ enabled: event.target.checked }, event.target.checked ? '通知已开启' : '通知已关闭') }}\n />\n </label>\n <label style={{ display: 'grid', gap: 8 }}>\n <span><strong>Webhook 地址</strong></span>\n <input\n aria-label=\"Webhook 地址\"\n type=\"url\"\n value={webhook}\n placeholder={config.webhookConfigured ? '已配置地址;输入新地址以替换' : '请输入飞书机器人 Webhook'}\n disabled={busy || view.status === 'loading'}\n onChange={event => { setWebhook(event.target.value); setStatus('') }}\n style={{ minHeight: 40, padding: '0 12px', borderRadius: 10, border: '1px solid var(--dsw-alias-border-l1)', background: 'var(--dsw-alias-bg-layer-1)', color: 'inherit' }}\n />\n </label>\n <div style={{ display: 'flex', flexWrap: 'wrap', gap: 10 }}>\n <Button variant=\"primary\" size=\"sm\" disabled={busy || view.status === 'loading'} onClick={() => { void saveWebhook() }}>保存 Webhook</Button>\n <Button variant=\"outline\" size=\"sm\" disabled={busy || view.status === 'loading' || !config.webhookConfigured} onClick={() => { void sendTest() }}>发送测试消息</Button>\n </div>\n {status !== '' && <p role=\"status\" style={{ margin: 0, color: 'var(--dsw-alias-label-secondary)' }}>{status}</p>}\n </div>\n </section>\n )\n}\n","import type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-settings/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-slots'\nimport { FeishuSettingsSection, type FeishuController } from './FeishuSettingsSection.tsx'\n\nexport const inject = ['slots']\n\nexport function apply(ctx: ClientContext): void {\n const controller: FeishuController = {\n load: async () => {\n const response = await fetch('/api/feishu-notifier/config')\n const result = await response.json() as { ok?: boolean; message?: string; config?: FeishuController['config'] }\n if (!response.ok || result.ok !== true || result.config === undefined) {\n throw new Error(result.message ?? `HTTP ${String(response.status)}`)\n }\n return result.config\n },\n update: async (patch) => {\n const response = await fetch('/api/feishu-notifier/config', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(patch),\n })\n const result = await response.json() as { ok?: boolean; message?: string; config?: FeishuController['config'] }\n if (!response.ok || result.ok !== true || result.config === undefined) {\n throw new Error(result.message ?? `HTTP ${String(response.status)}`)\n }\n return result.config\n },\n test: async () => {\n const response = await fetch('/api/feishu-notifier/test', { method: 'POST' })\n const result = await response.json() as { ok?: boolean; message?: string }\n if (!response.ok || result.ok !== true) throw new Error(result.message ?? `HTTP ${String(response.status)}`)\n return result.message ?? '测试消息已发送'\n },\n }\n\n ctx.slots.inject('settings.section', () => ctx.slots.register({\n name: 'settings.section',\n id: 'feishu-notifier',\n order: 25,\n label: () => '飞书通知',\n inject: (): { controller: FeishuController } => ({ controller }),\n }, FeishuSettingsSection))\n}\n"],"mappings":"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"}
|
package/lib/index.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import z from "@deepseek-ai/schemastery";
|
|
2
|
-
import {
|
|
2
|
+
import { settingsNamespace } from "@deepseek-ai/dsh-settings";
|
|
3
3
|
//#region src/index.ts
|
|
4
4
|
const name = "dsh-feishu-notifier";
|
|
5
|
+
const inject = ["settings", "webServer"];
|
|
5
6
|
const Config = z.object({
|
|
6
7
|
enabled: z.boolean().default(true),
|
|
7
8
|
webhook: z.string().role("secret").default("")
|
|
8
9
|
});
|
|
9
10
|
const SETTINGS_NAMESPACE = settingsNamespace("feishu-notifier");
|
|
11
|
+
const CONFIG_PATH = "/api/feishu-notifier/config";
|
|
10
12
|
const TEST_PATH = "/api/feishu-notifier/test";
|
|
11
13
|
function textFor(kind, detail) {
|
|
12
14
|
return kind === "approval" ? `DeepSeek Harness 需要你的操作\n${detail}` : `DeepSeek Harness 对话已结束\n${detail}`;
|
|
@@ -34,10 +36,35 @@ function turnReasonText(reason) {
|
|
|
34
36
|
default: return typeof reason === "string" ? reason : "结束原因未知";
|
|
35
37
|
}
|
|
36
38
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
function configView(config) {
|
|
40
|
+
return {
|
|
41
|
+
enabled: config.enabled,
|
|
42
|
+
webhookConfigured: config.webhook.trim() !== ""
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function writeJson(res, status, value) {
|
|
46
|
+
const body = JSON.stringify(value);
|
|
47
|
+
res.writeHead(status, { "content-type": "application/json; charset=utf-8" });
|
|
48
|
+
res.end(body);
|
|
49
|
+
}
|
|
50
|
+
async function readJson(req) {
|
|
51
|
+
const chunks = [];
|
|
52
|
+
for await (const chunk of req) chunks.push(Buffer.from(chunk));
|
|
53
|
+
if (chunks.length === 0) return {};
|
|
54
|
+
try {
|
|
55
|
+
return JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
56
|
+
} catch {
|
|
57
|
+
throw new Error("请求体不是有效 JSON");
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function webhookOf(value) {
|
|
61
|
+
if (typeof value !== "string" || value.trim() === "") throw new Error("请提供 Webhook 地址");
|
|
62
|
+
const webhook = value.trim();
|
|
63
|
+
if (new URL(webhook).protocol !== "https:") throw new Error("Webhook 必须使用 HTTPS 地址");
|
|
64
|
+
return webhook;
|
|
65
|
+
}
|
|
66
|
+
async function sendText(config, text) {
|
|
67
|
+
const webhook = webhookOf(config.webhook);
|
|
41
68
|
const response = await fetch(webhook, {
|
|
42
69
|
method: "POST",
|
|
43
70
|
headers: { "content-type": "application/json" },
|
|
@@ -57,19 +84,57 @@ async function sendText(current, text) {
|
|
|
57
84
|
throw error;
|
|
58
85
|
}
|
|
59
86
|
}
|
|
60
|
-
function
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
87
|
+
async function handleConfig(req, res, scope) {
|
|
88
|
+
if (req.method === "GET") {
|
|
89
|
+
writeJson(res, 200, {
|
|
90
|
+
ok: true,
|
|
91
|
+
config: configView(scope.get())
|
|
92
|
+
});
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
if (req.method !== "POST") {
|
|
96
|
+
res.writeHead(405, { allow: "GET, POST" });
|
|
97
|
+
res.end();
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
try {
|
|
101
|
+
const body = recordOf(await readJson(req));
|
|
102
|
+
if (body === void 0) throw new Error("请求体必须是 JSON 对象");
|
|
103
|
+
const patch = {};
|
|
104
|
+
if (body.enabled !== void 0) {
|
|
105
|
+
if (typeof body.enabled !== "boolean") throw new Error("enabled 必须是布尔值");
|
|
106
|
+
patch.enabled = body.enabled;
|
|
107
|
+
}
|
|
108
|
+
if (body.webhook !== void 0) patch.webhook = webhookOf(body.webhook);
|
|
109
|
+
if (Object.keys(patch).length === 0) throw new Error("没有可保存的配置");
|
|
110
|
+
await scope.update(patch);
|
|
111
|
+
writeJson(res, 200, {
|
|
112
|
+
ok: true,
|
|
113
|
+
config: configView(scope.get())
|
|
114
|
+
});
|
|
115
|
+
} catch (error) {
|
|
116
|
+
writeJson(res, 400, {
|
|
117
|
+
ok: false,
|
|
118
|
+
message: error instanceof Error ? error.message : String(error)
|
|
119
|
+
});
|
|
120
|
+
}
|
|
64
121
|
}
|
|
65
|
-
async function handleTest(req, res,
|
|
122
|
+
async function handleTest(req, res, scope) {
|
|
66
123
|
if (req.method !== "POST") {
|
|
67
124
|
res.writeHead(405, { allow: "POST" });
|
|
68
125
|
res.end();
|
|
69
126
|
return;
|
|
70
127
|
}
|
|
128
|
+
const config = scope.get();
|
|
129
|
+
if (!config.enabled) {
|
|
130
|
+
writeJson(res, 409, {
|
|
131
|
+
ok: false,
|
|
132
|
+
message: "飞书通知当前已关闭"
|
|
133
|
+
});
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
71
136
|
try {
|
|
72
|
-
await sendText(
|
|
137
|
+
await sendText(config, "这是一条来自 DeepSeek Harness 的飞书通知测试消息。");
|
|
73
138
|
writeJson(res, 200, {
|
|
74
139
|
ok: true,
|
|
75
140
|
message: "测试消息已发送"
|
|
@@ -82,17 +147,17 @@ async function handleTest(req, res, current) {
|
|
|
82
147
|
}
|
|
83
148
|
}
|
|
84
149
|
function notify(current, kind, detail) {
|
|
85
|
-
|
|
150
|
+
const config = current();
|
|
151
|
+
if (!config.enabled || config.webhook.trim() === "") return;
|
|
152
|
+
sendText(config, textFor(kind, detail)).catch((error) => {
|
|
86
153
|
console.warn(`[feishu-notifier] notification failed: ${String(error)}`);
|
|
87
154
|
});
|
|
88
155
|
}
|
|
89
156
|
function apply(ctx, config) {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
},
|
|
95
|
-
onChange: () => {}
|
|
157
|
+
const scope = ctx.settings.register(SETTINGS_NAMESPACE, Config, { base: config });
|
|
158
|
+
let current = () => scope.get();
|
|
159
|
+
scope.watch(() => {
|
|
160
|
+
current = () => scope.get();
|
|
96
161
|
});
|
|
97
162
|
ctx.on("approval/request", (request, next) => {
|
|
98
163
|
notify(current, "approval", request.reason ?? `工具 ${request.toolName} 正在等待批准。`);
|
|
@@ -102,13 +167,16 @@ function apply(ctx, config) {
|
|
|
102
167
|
if (event.type === "turn/end") notify(current, "turn-end", `第 ${String(event.data.turn)} 轮:${turnReasonText(event.data.reason)}`);
|
|
103
168
|
if (event.type === "tool/call" && (event.data.name === "ask_user_question" || event.data.name === "exit_plan_mode")) notify(current, "approval", event.data.name === "exit_plan_mode" ? "智能体正在等待你确认计划。" : "智能体正在等待你回答问题。");
|
|
104
169
|
});
|
|
105
|
-
ctx.
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
170
|
+
ctx.effect(() => ctx.webServer.register({
|
|
171
|
+
kind: "exact",
|
|
172
|
+
path: CONFIG_PATH,
|
|
173
|
+
handler: (req, res) => handleConfig(req, res, scope)
|
|
174
|
+
}), "feishu-notifier: config route");
|
|
175
|
+
ctx.effect(() => ctx.webServer.register({
|
|
176
|
+
kind: "exact",
|
|
177
|
+
path: TEST_PATH,
|
|
178
|
+
handler: (req, res) => handleTest(req, res, scope)
|
|
179
|
+
}), "feishu-notifier: test route");
|
|
112
180
|
}
|
|
113
181
|
//#endregion
|
|
114
|
-
export { Config, apply, name, turnReasonText };
|
|
182
|
+
export { Config, apply, inject, name, turnReasonText };
|