@lanbaolu/dsh-wechat-bridge 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +152 -0
  3. package/cordis.patch.yml +12 -0
  4. package/docs/feasibility-plan.md +62 -0
  5. package/lib/bridge/commands/handlers.js +155 -0
  6. package/lib/bridge/commands/handlers.js.map +1 -0
  7. package/lib/bridge/commands/router.js +52 -0
  8. package/lib/bridge/commands/router.js.map +1 -0
  9. package/lib/bridge/config.js +40 -0
  10. package/lib/bridge/config.js.map +1 -0
  11. package/lib/bridge/constants.js +14 -0
  12. package/lib/bridge/constants.js.map +1 -0
  13. package/lib/bridge/dsh-client.js +100 -0
  14. package/lib/bridge/dsh-client.js.map +1 -0
  15. package/lib/bridge/logger.js +75 -0
  16. package/lib/bridge/logger.js.map +1 -0
  17. package/lib/bridge/main.js +546 -0
  18. package/lib/bridge/main.js.map +1 -0
  19. package/lib/bridge/pending-queue.js +55 -0
  20. package/lib/bridge/pending-queue.js.map +1 -0
  21. package/lib/bridge/session.js +84 -0
  22. package/lib/bridge/session.js.map +1 -0
  23. package/lib/bridge/store.js +38 -0
  24. package/lib/bridge/store.js.map +1 -0
  25. package/lib/bridge/wechat/accounts.js +50 -0
  26. package/lib/bridge/wechat/accounts.js.map +1 -0
  27. package/lib/bridge/wechat/api.js +197 -0
  28. package/lib/bridge/wechat/api.js.map +1 -0
  29. package/lib/bridge/wechat/cdn.js +45 -0
  30. package/lib/bridge/wechat/cdn.js.map +1 -0
  31. package/lib/bridge/wechat/crypto.js +17 -0
  32. package/lib/bridge/wechat/crypto.js.map +1 -0
  33. package/lib/bridge/wechat/login.js +139 -0
  34. package/lib/bridge/wechat/login.js.map +1 -0
  35. package/lib/bridge/wechat/media.js +138 -0
  36. package/lib/bridge/wechat/media.js.map +1 -0
  37. package/lib/bridge/wechat/monitor.js +104 -0
  38. package/lib/bridge/wechat/monitor.js.map +1 -0
  39. package/lib/bridge/wechat/send.js +172 -0
  40. package/lib/bridge/wechat/send.js.map +1 -0
  41. package/lib/bridge/wechat/sync-buf.js +11 -0
  42. package/lib/bridge/wechat/sync-buf.js.map +1 -0
  43. package/lib/bridge/wechat/types.js +35 -0
  44. package/lib/bridge/wechat/types.js.map +1 -0
  45. package/lib/bridge/wechat/upload.js +111 -0
  46. package/lib/bridge/wechat/upload.js.map +1 -0
  47. package/lib/client/Panel.js +205 -0
  48. package/lib/client/Panel.js.map +1 -0
  49. package/lib/client/index.js +11 -0
  50. package/lib/client/index.js.map +1 -0
  51. package/lib/client.js +376 -0
  52. package/lib/client.js.map +1 -0
  53. package/lib/index.js +828 -0
  54. package/lib/index.js.map +1 -0
  55. package/lib/types/bridge/commands/handlers.d.ts +18 -0
  56. package/lib/types/bridge/commands/router.d.ts +26 -0
  57. package/lib/types/bridge/config.d.ts +7 -0
  58. package/lib/types/bridge/constants.d.ts +10 -0
  59. package/lib/types/bridge/dsh-client.d.ts +39 -0
  60. package/lib/types/bridge/logger.d.ts +13 -0
  61. package/lib/types/bridge/main.d.ts +1 -0
  62. package/lib/types/bridge/pending-queue.d.ts +10 -0
  63. package/lib/types/bridge/session.d.ts +22 -0
  64. package/lib/types/bridge/store.d.ts +11 -0
  65. package/lib/types/bridge/wechat/accounts.d.ts +14 -0
  66. package/lib/types/bridge/wechat/api.d.ts +32 -0
  67. package/lib/types/bridge/wechat/cdn.d.ts +2 -0
  68. package/lib/types/bridge/wechat/crypto.d.ts +4 -0
  69. package/lib/types/bridge/wechat/login.d.ts +31 -0
  70. package/lib/types/bridge/wechat/media.d.ts +20 -0
  71. package/lib/types/bridge/wechat/monitor.d.ts +10 -0
  72. package/lib/types/bridge/wechat/send.d.ts +6 -0
  73. package/lib/types/bridge/wechat/sync-buf.d.ts +2 -0
  74. package/lib/types/bridge/wechat/types.d.ts +135 -0
  75. package/lib/types/bridge/wechat/upload.d.ts +11 -0
  76. package/lib/types/client/Panel.d.ts +3 -0
  77. package/lib/types/client/index.d.ts +9 -0
  78. package/lib/types/index.d.ts +39 -0
  79. package/package.json +92 -0
package/lib/client.js ADDED
@@ -0,0 +1,376 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "@lanbaolu/dsh-wechat-bridge",
3
+ factory: (require) => {
4
+ var module = { exports: {} };
5
+ var exports = module.exports;
6
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
+ let react = require("react");
8
+ let react_jsx_runtime = require("react/jsx-runtime");
9
+ //#region src/client/Panel.tsx
10
+ const API_BASE = "/@lanbaolu/dsh-wechat-bridge";
11
+ const panelStyle = {
12
+ padding: "14px 16px",
13
+ fontFamily: "ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", sans-serif",
14
+ fontSize: 13,
15
+ lineHeight: 1.6,
16
+ color: "var(--text-1, #1f2328)",
17
+ background: "var(--surface-2, rgba(127,127,127,.06))",
18
+ border: "1px solid var(--border-color, rgba(127,127,127,.18))",
19
+ borderRadius: 10,
20
+ margin: "8px 0"
21
+ };
22
+ const titleStyle = {
23
+ fontWeight: 600,
24
+ fontSize: 14,
25
+ marginBottom: 8
26
+ };
27
+ const outputStyle = {
28
+ margin: "4px 0 8px",
29
+ padding: "8px 10px",
30
+ background: "var(--surface-3, rgba(0,0,0,.04))",
31
+ border: "1px solid var(--border-color, rgba(127,127,127,.15))",
32
+ borderRadius: 6,
33
+ whiteSpace: "pre-wrap",
34
+ wordBreak: "break-all",
35
+ fontFamily: "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
36
+ fontSize: 12,
37
+ maxHeight: 360,
38
+ overflow: "auto"
39
+ };
40
+ const buttonRowStyle = {
41
+ display: "flex",
42
+ gap: 8,
43
+ flexWrap: "wrap",
44
+ alignItems: "center"
45
+ };
46
+ const buttonStyle = {
47
+ padding: "4px 10px",
48
+ border: "1px solid var(--border-color, rgba(127,127,127,.4))",
49
+ borderRadius: 6,
50
+ background: "var(--button-bg, #fff)",
51
+ color: "var(--text-1, inherit)",
52
+ cursor: "pointer",
53
+ fontSize: 12
54
+ };
55
+ const inputStyle = {
56
+ padding: "4px 8px",
57
+ border: "1px solid var(--border-color, rgba(127,127,127,.4))",
58
+ borderRadius: 6,
59
+ background: "var(--surface-3, rgba(127,127,127,.06))",
60
+ color: "var(--text-1, inherit)",
61
+ fontSize: 12,
62
+ minWidth: 220
63
+ };
64
+ const hintStyle = {
65
+ marginTop: 8,
66
+ fontSize: 12,
67
+ opacity: .7
68
+ };
69
+ function WechatBridgePanel(_props) {
70
+ const [output, setOutput] = (0, react.useState)("加载中…");
71
+ const [error, setError] = (0, react.useState)(null);
72
+ const [busy, setBusy] = (0, react.useState)(null);
73
+ const [workingDir, setWorkingDir] = (0, react.useState)("");
74
+ const [setupPhase, setSetupPhase] = (0, react.useState)("idle");
75
+ const [qrcodeDataUrl, setQrcodeDataUrl] = (0, react.useState)("");
76
+ const [qrcodeId, setQrcodeId] = (0, react.useState)("");
77
+ const [setupError, setSetupError] = (0, react.useState)("");
78
+ const [boundAccountId, setBoundAccountId] = (0, react.useState)("");
79
+ const refresh = (0, react.useCallback)(async () => {
80
+ try {
81
+ const res = await fetch(`${API_BASE}/status`, { cache: "no-store" });
82
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
83
+ const data = await res.json();
84
+ setOutput(JSON.stringify(data, null, 2));
85
+ if (typeof data.workingDirectory === "string") setWorkingDir((prev) => prev || data.workingDirectory);
86
+ setError(null);
87
+ } catch (err) {
88
+ setError(err instanceof Error ? err.message : String(err));
89
+ }
90
+ }, []);
91
+ (0, react.useEffect)(() => {
92
+ refresh();
93
+ }, [refresh]);
94
+ async function run(path) {
95
+ setBusy(path);
96
+ setError(null);
97
+ try {
98
+ const res = await fetch(`${API_BASE}/${path}`, { method: "POST" });
99
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
100
+ const data = await res.json();
101
+ setOutput(JSON.stringify(data, null, 2));
102
+ await refresh();
103
+ } catch (err) {
104
+ setError(err instanceof Error ? err.message : String(err));
105
+ } finally {
106
+ setBusy(null);
107
+ }
108
+ }
109
+ async function showLogs() {
110
+ setBusy("logs");
111
+ setError(null);
112
+ try {
113
+ const res = await fetch(`${API_BASE}/logs`, { cache: "no-store" });
114
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
115
+ setOutput(await res.text());
116
+ } catch (err) {
117
+ setError(err instanceof Error ? err.message : String(err));
118
+ } finally {
119
+ setBusy(null);
120
+ }
121
+ }
122
+ async function startSetup() {
123
+ setSetupPhase("starting");
124
+ setSetupError("");
125
+ try {
126
+ const res = await fetch(`${API_BASE}/setup/start`, {
127
+ method: "POST",
128
+ headers: { "Content-Type": "application/json" },
129
+ body: JSON.stringify({ workingDirectory: workingDir || void 0 })
130
+ });
131
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
132
+ const data = await res.json();
133
+ if (!data.ok) throw new Error(data.message || data.error || "启动扫码绑定失败");
134
+ setQrcodeDataUrl(data.qrcodeDataUrl);
135
+ setQrcodeId(data.qrcodeId);
136
+ setWorkingDir(data.workingDirectory || workingDir);
137
+ setSetupPhase("qr");
138
+ } catch (err) {
139
+ setSetupPhase("error");
140
+ setSetupError(err instanceof Error ? err.message : String(err));
141
+ }
142
+ }
143
+ (0, react.useEffect)(() => {
144
+ if (setupPhase !== "qr" || !qrcodeId) return;
145
+ let cancelled = false;
146
+ let timer;
147
+ async function poll() {
148
+ try {
149
+ const res = await fetch(`${API_BASE}/setup/status?qrcodeId=${encodeURIComponent(qrcodeId)}`, { cache: "no-store" });
150
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
151
+ const data = await res.json();
152
+ if (cancelled) return;
153
+ if (data.status === "confirmed") {
154
+ setSetupPhase("confirmed");
155
+ setBoundAccountId(data.accountId || "");
156
+ refresh();
157
+ return;
158
+ }
159
+ if (data.status === "expired" || data.status === "error" && !data.retryable) {
160
+ setSetupPhase("error");
161
+ setSetupError(data.message || "绑定失败,请重试");
162
+ return;
163
+ }
164
+ timer = window.setTimeout(() => void poll(), 3e3);
165
+ } catch (err) {
166
+ if (!cancelled) {
167
+ setSetupPhase("error");
168
+ setSetupError(err instanceof Error ? err.message : String(err));
169
+ }
170
+ }
171
+ }
172
+ poll();
173
+ return () => {
174
+ cancelled = true;
175
+ if (timer) window.clearTimeout(timer);
176
+ };
177
+ }, [
178
+ setupPhase,
179
+ qrcodeId,
180
+ refresh
181
+ ]);
182
+ function cancelSetup() {
183
+ setSetupPhase("idle");
184
+ setQrcodeDataUrl("");
185
+ setQrcodeId("");
186
+ setSetupError("");
187
+ }
188
+ const actions = [
189
+ {
190
+ label: "刷新状态",
191
+ run: () => void refresh()
192
+ },
193
+ {
194
+ label: "扫码绑定",
195
+ run: () => void startSetup(),
196
+ disabled: setupPhase === "starting" || setupPhase === "qr"
197
+ },
198
+ {
199
+ label: "启动",
200
+ run: () => void run("start"),
201
+ disabled: busy !== null
202
+ },
203
+ {
204
+ label: "停止",
205
+ run: () => void run("stop"),
206
+ disabled: busy !== null
207
+ },
208
+ {
209
+ label: "重启",
210
+ run: () => void run("restart"),
211
+ disabled: busy !== null
212
+ },
213
+ {
214
+ label: "查看日志",
215
+ run: () => void showLogs(),
216
+ disabled: busy !== null
217
+ }
218
+ ];
219
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
220
+ style: panelStyle,
221
+ role: "region",
222
+ "aria-label": "DSH 微信桥接管理面板",
223
+ children: [
224
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
225
+ style: titleStyle,
226
+ children: "📱 DSH 微信桥接"
227
+ }),
228
+ error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
229
+ role: "alert",
230
+ style: {
231
+ color: "#e5484d",
232
+ marginBottom: 8
233
+ },
234
+ children: error
235
+ }),
236
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
237
+ style: {
238
+ display: "block",
239
+ marginBottom: 8,
240
+ fontSize: 12
241
+ },
242
+ children: ["DSH 工作目录", /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
243
+ type: "text",
244
+ value: workingDir,
245
+ onChange: (e) => setWorkingDir(e.target.value),
246
+ placeholder: "例如 ~/Documents/DSH",
247
+ style: {
248
+ ...inputStyle,
249
+ marginLeft: 8
250
+ }
251
+ })]
252
+ }),
253
+ setupPhase === "starting" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
254
+ role: "status",
255
+ style: { marginBottom: 8 },
256
+ children: "正在生成二维码…"
257
+ }),
258
+ setupPhase === "qr" && qrcodeDataUrl && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
259
+ style: { marginBottom: 8 },
260
+ children: [
261
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
262
+ style: {
263
+ fontWeight: 600,
264
+ marginBottom: 4
265
+ },
266
+ children: "请用微信扫描下方二维码完成绑定"
267
+ }),
268
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
269
+ src: qrcodeDataUrl,
270
+ alt: "微信扫码绑定二维码",
271
+ width: 240,
272
+ height: 240,
273
+ style: {
274
+ display: "block",
275
+ maxWidth: "100%",
276
+ height: "auto",
277
+ borderRadius: 8,
278
+ background: "#fff"
279
+ }
280
+ }),
281
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
282
+ style: {
283
+ fontSize: 12,
284
+ opacity: .7,
285
+ marginTop: 4
286
+ },
287
+ children: [
288
+ "工作目录:",
289
+ workingDir || "未设置",
290
+ "。等待扫码确认中…"
291
+ ]
292
+ }),
293
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
294
+ type: "button",
295
+ style: {
296
+ ...buttonStyle,
297
+ marginTop: 8
298
+ },
299
+ onClick: cancelSetup,
300
+ children: "取消绑定"
301
+ })
302
+ ]
303
+ }),
304
+ setupPhase === "confirmed" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
305
+ role: "status",
306
+ style: {
307
+ marginBottom: 8,
308
+ color: "#2f9e44"
309
+ },
310
+ children: [
311
+ "✅ 绑定成功",
312
+ boundAccountId ? `:${boundAccountId}` : "",
313
+ ",可以点击“启动”开始使用。"
314
+ ]
315
+ }),
316
+ setupPhase === "error" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
317
+ role: "alert",
318
+ style: {
319
+ marginBottom: 8,
320
+ color: "#e5484d"
321
+ },
322
+ children: [
323
+ "❌ ",
324
+ setupError,
325
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
326
+ type: "button",
327
+ style: {
328
+ ...buttonStyle,
329
+ marginLeft: 8
330
+ },
331
+ onClick: () => setSetupPhase("idle"),
332
+ children: "重新扫码"
333
+ })
334
+ ]
335
+ }),
336
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
337
+ style: outputStyle,
338
+ "aria-live": "polite",
339
+ children: output
340
+ }),
341
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
342
+ style: buttonRowStyle,
343
+ children: actions.map((action) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
344
+ type: "button",
345
+ style: buttonStyle,
346
+ disabled: action.disabled ?? false,
347
+ onClick: action.run,
348
+ children: action.label
349
+ }, action.label))
350
+ }),
351
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
352
+ style: hintStyle,
353
+ children: "也可在 DSH 对话中直接使用 wechat_bridge_setup / wechat_bridge_status / wechat_bridge_start / wechat_bridge_stop / wechat_bridge_logs 等工具。"
354
+ })
355
+ ]
356
+ });
357
+ }
358
+ //#endregion
359
+ //#region src/client/index.ts
360
+ const inject = ["slots"];
361
+ function apply(ctx) {
362
+ ctx.effect(() => ctx.slots.inject("settings.section", () => ctx.slots.register({
363
+ name: "settings.section",
364
+ id: "@lanbaolu/dsh-wechat-bridge-panel",
365
+ order: 55,
366
+ label: () => "📱 微信桥接"
367
+ }, WechatBridgePanel)), "@lanbaolu/dsh-wechat-bridge: panel");
368
+ }
369
+ //#endregion
370
+ exports.apply = apply;
371
+ exports.inject = inject;
372
+ return module.exports;
373
+ }
374
+ });
375
+
376
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","names":["useState","useCallback"],"sources":["../src/client/Panel.tsx","../src/client/index.ts"],"sourcesContent":["import type * as React from 'react'\nimport { useCallback, useEffect, useState } from 'react'\nimport type { SettingsSectionOwnerProps } from '@deepseek-ai/dsh-client-ui-settings/client'\n\nconst API_BASE = '/@lanbaolu/dsh-wechat-bridge'\n\nconst panelStyle: React.CSSProperties = {\n padding: '14px 16px',\n fontFamily: 'ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", sans-serif',\n fontSize: 13,\n lineHeight: 1.6,\n color: 'var(--text-1, #1f2328)',\n background: 'var(--surface-2, rgba(127,127,127,.06))',\n border: '1px solid var(--border-color, rgba(127,127,127,.18))',\n borderRadius: 10,\n margin: '8px 0',\n}\n\nconst titleStyle: React.CSSProperties = {\n fontWeight: 600,\n fontSize: 14,\n marginBottom: 8,\n}\n\nconst outputStyle: React.CSSProperties = {\n margin: '4px 0 8px',\n padding: '8px 10px',\n background: 'var(--surface-3, rgba(0,0,0,.04))',\n border: '1px solid var(--border-color, rgba(127,127,127,.15))',\n borderRadius: 6,\n whiteSpace: 'pre-wrap',\n wordBreak: 'break-all',\n fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Consolas, monospace',\n fontSize: 12,\n maxHeight: 360,\n overflow: 'auto',\n}\n\nconst buttonRowStyle: React.CSSProperties = {\n display: 'flex',\n gap: 8,\n flexWrap: 'wrap',\n alignItems: 'center',\n}\n\nconst buttonStyle: React.CSSProperties = {\n padding: '4px 10px',\n border: '1px solid var(--border-color, rgba(127,127,127,.4))',\n borderRadius: 6,\n background: 'var(--button-bg, #fff)',\n color: 'var(--text-1, inherit)',\n cursor: 'pointer',\n fontSize: 12,\n}\n\nconst inputStyle: React.CSSProperties = {\n padding: '4px 8px',\n border: '1px solid var(--border-color, rgba(127,127,127,.4))',\n borderRadius: 6,\n background: 'var(--surface-3, rgba(127,127,127,.06))',\n color: 'var(--text-1, inherit)',\n fontSize: 12,\n minWidth: 220,\n}\n\nconst hintStyle: React.CSSProperties = {\n marginTop: 8,\n fontSize: 12,\n opacity: 0.7,\n}\n\ntype SetupPhase = 'idle' | 'starting' | 'qr' | 'confirmed' | 'error'\n\nexport function WechatBridgePanel(_props: SettingsSectionOwnerProps): React.JSX.Element {\n const [output, setOutput] = useState('加载中…')\n const [error, setError] = useState<string | null>(null)\n const [busy, setBusy] = useState<string | null>(null)\n const [workingDir, setWorkingDir] = useState('')\n const [setupPhase, setSetupPhase] = useState<SetupPhase>('idle')\n const [qrcodeDataUrl, setQrcodeDataUrl] = useState('')\n const [qrcodeId, setQrcodeId] = useState('')\n const [setupError, setSetupError] = useState('')\n const [boundAccountId, setBoundAccountId] = useState('')\n\n const refresh = useCallback(async () => {\n try {\n const res = await fetch(`${API_BASE}/status`, { cache: 'no-store' })\n if (!res.ok) throw new Error(`HTTP ${res.status}`)\n const data = await res.json()\n setOutput(JSON.stringify(data, null, 2))\n if (typeof data.workingDirectory === 'string') {\n setWorkingDir((prev) => prev || data.workingDirectory)\n }\n setError(null)\n } catch (err) {\n setError(err instanceof Error ? err.message : String(err))\n }\n }, [])\n\n useEffect(() => {\n void refresh()\n }, [refresh])\n\n async function run(path: string): Promise<void> {\n setBusy(path)\n setError(null)\n try {\n const res = await fetch(`${API_BASE}/${path}`, { method: 'POST' })\n if (!res.ok) throw new Error(`HTTP ${res.status}`)\n const data = await res.json()\n setOutput(JSON.stringify(data, null, 2))\n await refresh()\n } catch (err) {\n setError(err instanceof Error ? err.message : String(err))\n } finally {\n setBusy(null)\n }\n }\n\n async function showLogs(): Promise<void> {\n setBusy('logs')\n setError(null)\n try {\n const res = await fetch(`${API_BASE}/logs`, { cache: 'no-store' })\n if (!res.ok) throw new Error(`HTTP ${res.status}`)\n setOutput(await res.text())\n } catch (err) {\n setError(err instanceof Error ? err.message : String(err))\n } finally {\n setBusy(null)\n }\n }\n\n async function startSetup(): Promise<void> {\n setSetupPhase('starting')\n setSetupError('')\n try {\n const res = await fetch(`${API_BASE}/setup/start`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ workingDirectory: workingDir || undefined }),\n })\n if (!res.ok) throw new Error(`HTTP ${res.status}`)\n const data = await res.json()\n if (!data.ok) throw new Error(data.message || data.error || '启动扫码绑定失败')\n setQrcodeDataUrl(data.qrcodeDataUrl)\n setQrcodeId(data.qrcodeId)\n setWorkingDir(data.workingDirectory || workingDir)\n setSetupPhase('qr')\n } catch (err) {\n setSetupPhase('error')\n setSetupError(err instanceof Error ? err.message : String(err))\n }\n }\n\n useEffect(() => {\n if (setupPhase !== 'qr' || !qrcodeId) return\n\n let cancelled = false\n let timer: number | undefined\n\n async function poll(): Promise<void> {\n try {\n const res = await fetch(`${API_BASE}/setup/status?qrcodeId=${encodeURIComponent(qrcodeId)}`, { cache: 'no-store' })\n if (!res.ok) throw new Error(`HTTP ${res.status}`)\n const data = await res.json()\n if (cancelled) return\n\n if (data.status === 'confirmed') {\n setSetupPhase('confirmed')\n setBoundAccountId(data.accountId || '')\n void refresh()\n return\n }\n\n if (data.status === 'expired' || (data.status === 'error' && !data.retryable)) {\n setSetupPhase('error')\n setSetupError(data.message || '绑定失败,请重试')\n return\n }\n\n timer = window.setTimeout(() => void poll(), 3000)\n } catch (err) {\n if (!cancelled) {\n setSetupPhase('error')\n setSetupError(err instanceof Error ? err.message : String(err))\n }\n }\n }\n\n void poll()\n return () => {\n cancelled = true\n if (timer) window.clearTimeout(timer)\n }\n }, [setupPhase, qrcodeId, refresh])\n\n function cancelSetup(): void {\n setSetupPhase('idle')\n setQrcodeDataUrl('')\n setQrcodeId('')\n setSetupError('')\n }\n\n const actions: Array<{ label: string; run: () => void; disabled?: boolean }> = [\n { label: '刷新状态', run: () => void refresh() },\n { label: '扫码绑定', run: () => void startSetup(), disabled: setupPhase === 'starting' || setupPhase === 'qr' },\n { label: '启动', run: () => void run('start'), disabled: busy !== null },\n { label: '停止', run: () => void run('stop'), disabled: busy !== null },\n { label: '重启', run: () => void run('restart'), disabled: busy !== null },\n { label: '查看日志', run: () => void showLogs(), disabled: busy !== null },\n ]\n\n return (\n <div style={panelStyle} role=\"region\" aria-label=\"DSH 微信桥接管理面板\">\n <div style={titleStyle}>📱 DSH 微信桥接</div>\n\n {error && <div role=\"alert\" style={{ color: '#e5484d', marginBottom: 8 }}>{error}</div>}\n\n <label style={{ display: 'block', marginBottom: 8, fontSize: 12 }}>\n DSH 工作目录\n <input\n type=\"text\"\n value={workingDir}\n onChange={(e) => setWorkingDir(e.target.value)}\n placeholder=\"例如 ~/Documents/DSH\"\n style={{ ...inputStyle, marginLeft: 8 }}\n />\n </label>\n\n {setupPhase === 'starting' && (\n <div role=\"status\" style={{ marginBottom: 8 }}>正在生成二维码…</div>\n )}\n\n {setupPhase === 'qr' && qrcodeDataUrl && (\n <div style={{ marginBottom: 8 }}>\n <div style={{ fontWeight: 600, marginBottom: 4 }}>请用微信扫描下方二维码完成绑定</div>\n <img\n src={qrcodeDataUrl}\n alt=\"微信扫码绑定二维码\"\n width={240}\n height={240}\n style={{ display: 'block', maxWidth: '100%', height: 'auto', borderRadius: 8, background: '#fff' }}\n />\n <div style={{ fontSize: 12, opacity: 0.7, marginTop: 4 }}>\n 工作目录:{workingDir || '未设置'}。等待扫码确认中…\n </div>\n <button type=\"button\" style={{ ...buttonStyle, marginTop: 8 }} onClick={cancelSetup}>\n 取消绑定\n </button>\n </div>\n )}\n\n {setupPhase === 'confirmed' && (\n <div role=\"status\" style={{ marginBottom: 8, color: '#2f9e44' }}>\n ✅ 绑定成功{boundAccountId ? `:${boundAccountId}` : ''},可以点击“启动”开始使用。\n </div>\n )}\n\n {setupPhase === 'error' && (\n <div role=\"alert\" style={{ marginBottom: 8, color: '#e5484d' }}>\n ❌ {setupError}\n <button type=\"button\" style={{ ...buttonStyle, marginLeft: 8 }} onClick={() => setSetupPhase('idle')}>\n 重新扫码\n </button>\n </div>\n )}\n\n <pre style={outputStyle} aria-live=\"polite\">{output}</pre>\n\n <div style={buttonRowStyle}>\n {actions.map((action) => (\n <button\n key={action.label}\n type=\"button\"\n style={buttonStyle}\n disabled={action.disabled ?? false}\n onClick={action.run}\n >\n {action.label}\n </button>\n ))}\n </div>\n\n <div style={hintStyle}>\n 也可在 DSH 对话中直接使用 wechat_bridge_setup / wechat_bridge_status / wechat_bridge_start / wechat_bridge_stop / wechat_bridge_logs 等工具。\n </div>\n </div>\n )\n}\n","/**\n * @lanbaolu/dsh-wechat-bridge — Web 管理面板。\n *\n * 渲染在 settings.section 槽位(设置页),不绑定具体会话,打开任何任务前都可通过\n * 侧边栏 Settings 进入;通过 DSH Web 同源路由读取/控制桥接状态。\n */\nimport type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client'\nimport { WechatBridgePanel } from './Panel.js'\n\nexport const inject = ['slots']\n\nexport function apply(ctx: ClientContext): void {\n ctx.effect(() => ctx.slots.inject('settings.section', () =>\n ctx.slots.register({\n name: 'settings.section',\n id: '@lanbaolu/dsh-wechat-bridge-panel',\n order: 55,\n label: () => '📱 微信桥接',\n }, WechatBridgePanel),\n ), '@lanbaolu/dsh-wechat-bridge: panel')\n}\n"],"mappings":"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"}