@jaychang1989/dsh-webchat 0.3.0 → 0.4.1
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/NOTICE +10 -3
- package/README.md +44 -39
- package/README.zh.md +43 -43
- package/cordis.patch.yml +8 -7
- package/lib/client.js +203 -1703
- package/lib/index.js +246 -3884
- package/package.json +3 -35
- package/lib/client.js.map +0 -1
- package/lib/types/client/api.d.ts +0 -74
- package/lib/types/client/controller.d.ts +0 -19
- package/lib/types/client/index.d.ts +0 -30
- package/lib/types/client/locales.d.ts +0 -169
- package/lib/types/client/mount.d.ts +0 -23
- package/lib/types/client/panel/Markdown.d.ts +0 -15
- package/lib/types/client/panel/WebChatPanel.d.ts +0 -28
- package/lib/types/client/sidebar-entry-core.d.ts +0 -60
- package/lib/types/client/sidebar-entry.d.ts +0 -22
- package/lib/types/engine/engine.d.ts +0 -247
- package/lib/types/engine/html-md.d.ts +0 -23
- package/lib/types/index.d.ts +0 -63
- package/lib/types/protocol.d.ts +0 -107
- package/lib/types/routes.d.ts +0 -22
- package/lib/types/store.d.ts +0 -69
- package/lib/types/tools.d.ts +0 -27
- package/lib/types/transfer.d.ts +0 -138
- package/src/client/api.ts +0 -90
- package/src/client/controller.ts +0 -43
- package/src/client/css-modules.d.ts +0 -5
- package/src/client/index.ts +0 -75
- package/src/client/locales.ts +0 -172
- package/src/client/mount.tsx +0 -125
- package/src/client/panel/Markdown.tsx +0 -251
- package/src/client/panel/WebChatPanel.tsx +0 -760
- package/src/client/panel/panel.module.css +0 -936
- package/src/client/sidebar-entry-core.ts +0 -207
- package/src/client/sidebar-entry.ts +0 -49
- package/src/engine/engine.ts +0 -1385
- package/src/engine/html-md.ts +0 -126
- package/src/index.ts +0 -212
- package/src/protocol.ts +0 -116
- package/src/routes.ts +0 -307
- package/src/store.ts +0 -217
- package/src/tools.ts +0 -257
- package/src/transfer.ts +0 -602
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* dsh-webchat surface copy: zh is the key source, en mirrors every key. The
|
|
3
|
-
* key union (not an object shape) is what the locale machinery wants —
|
|
4
|
-
* LocaleKeysOf<N> intersects the namespace with `string`, so the namespace
|
|
5
|
-
* value must itself be the key union (same pattern as dsh-ssh).
|
|
6
|
-
*/
|
|
7
|
-
export declare const zh: {
|
|
8
|
-
readonly 'entry.label': "网页聊天";
|
|
9
|
-
readonly 'entry.tooltip': "DeepSeek 网页端聊天(deepseek-chat,Codex ChatGPT 模式)";
|
|
10
|
-
readonly 'panel.title': "DeepSeek 网页端聊天";
|
|
11
|
-
readonly 'status.engine.stopped': "浏览器未启动";
|
|
12
|
-
readonly 'status.engine.launching': "浏览器启动中…";
|
|
13
|
-
readonly 'status.engine.ready': "浏览器就绪";
|
|
14
|
-
readonly 'status.engine.error': "浏览器错误";
|
|
15
|
-
readonly 'status.loggedIn': "已登录";
|
|
16
|
-
readonly 'status.notLoggedIn': "未登录";
|
|
17
|
-
readonly 'status.unknown': "状态未知";
|
|
18
|
-
readonly 'action.openLogin': "打开登录窗口";
|
|
19
|
-
readonly 'action.closeBrowser': "关闭浏览器";
|
|
20
|
-
readonly 'action.newChat': "新对话";
|
|
21
|
-
readonly 'action.recover': "从网页恢复";
|
|
22
|
-
readonly 'action.refresh': "刷新";
|
|
23
|
-
readonly 'action.send': "发送";
|
|
24
|
-
readonly 'action.stop': "停止生成";
|
|
25
|
-
readonly 'action.transferToHarness': "转移到 Harness";
|
|
26
|
-
readonly 'action.exportFile': "导出为文件";
|
|
27
|
-
readonly 'transfer.mode.distill': "蒸馏为任务简报";
|
|
28
|
-
readonly 'transfer.mode.raw': "原始记录";
|
|
29
|
-
readonly 'transfer.mode.hint': "蒸馏:用 harness LLM 把对话提炼成可执行任务简报;原始记录:原样放入新会话首条消息";
|
|
30
|
-
readonly 'transfer.workspace.label': "目标工作区";
|
|
31
|
-
readonly 'transfer.workspace.ungrouped': "未分组";
|
|
32
|
-
readonly 'transfer.workspace.new': "新建工作区…";
|
|
33
|
-
readonly 'transfer.workspace.hint': "转移到所选工作区;未分组则不归入任何工作区";
|
|
34
|
-
readonly 'transfer.workspace.created': "已创建并选中工作区 {title}";
|
|
35
|
-
readonly 'transfer.workspace.createFailed': "创建工作区失败:{error}";
|
|
36
|
-
readonly 'transfer.session.label': "延续到会话";
|
|
37
|
-
readonly 'transfer.session.hint': "选择已有 harness 会话以延续(把简报作为新消息追加);默认新建会话";
|
|
38
|
-
readonly 'transfer.session.new': "新建会话";
|
|
39
|
-
readonly 'transfer.attached.failed': "会话已创建({sessionId}),但归入工作区失败,现为未分组";
|
|
40
|
-
readonly 'handoff.eyebrow': "交付到 Harness";
|
|
41
|
-
readonly 'toggle.deepThink': "深度思考";
|
|
42
|
-
readonly 'toggle.search': "智能搜索";
|
|
43
|
-
readonly 'toggle.deepThink.hint': "开启 R1 深度思考";
|
|
44
|
-
readonly 'toggle.search.hint': "开启联网检索";
|
|
45
|
-
readonly 'sidebar.empty': "暂无对话";
|
|
46
|
-
readonly 'chats.count': "{count} 条";
|
|
47
|
-
readonly 'role.you': "你";
|
|
48
|
-
readonly 'chat.rename': "重命名";
|
|
49
|
-
readonly 'chat.delete': "删除";
|
|
50
|
-
readonly 'chat.delete.confirm': "确认删除?";
|
|
51
|
-
readonly 'chat.clearAll': "清空全部";
|
|
52
|
-
readonly 'chat.clearAll.confirm': "确认清空全部?";
|
|
53
|
-
readonly 'chat.rename.placeholder': "输入新标题…";
|
|
54
|
-
readonly 'chat.rename.ok': "确定";
|
|
55
|
-
readonly 'chat.rename.cancel': "取消";
|
|
56
|
-
readonly 'chat.manage.hint': "管理本地保存的聊天记录(不影响网页端)";
|
|
57
|
-
readonly 'toast.rename.done': "已重命名";
|
|
58
|
-
readonly 'toast.rename.failed': "重命名失败:{error}";
|
|
59
|
-
readonly 'toast.delete.done': "已删除该对话";
|
|
60
|
-
readonly 'toast.delete.failed': "删除失败:{error}";
|
|
61
|
-
readonly 'toast.clear.done': "已清空全部对话";
|
|
62
|
-
readonly 'toast.clear.failed': "清空失败:{error}";
|
|
63
|
-
readonly 'model.deepseek-chat': "DeepSeek";
|
|
64
|
-
readonly 'model.deepseek-reasoner': "DeepSeek";
|
|
65
|
-
readonly 'composer.placeholder': "给 DeepSeek 发送消息…(将发送到网页端)";
|
|
66
|
-
readonly 'composer.hint': "Enter 发送 · Shift+Enter 换行";
|
|
67
|
-
readonly 'composer.busy': "正在生成回复…";
|
|
68
|
-
readonly 'composer.notLoggedIn': "请先登录 DeepSeek 网页端";
|
|
69
|
-
readonly 'composer.attach': "图片";
|
|
70
|
-
readonly 'composer.attach.hint': "添加本地图片(绝对路径,逗号/换行分隔)";
|
|
71
|
-
readonly 'composer.attach.placeholder': "图片绝对路径,逗号或换行分隔";
|
|
72
|
-
readonly 'empty.eyebrow': "DeepSeek 网页端 · 桥接开发";
|
|
73
|
-
readonly 'empty.title': "先聊,再交付";
|
|
74
|
-
readonly 'empty.body': "登录网页端即可开始对话;任何对话都能蒸馏成任务简报,转入 Harness 会话继续开发。";
|
|
75
|
-
readonly 'transfer.done': "已创建 Harness 会话 {sessionId},正在为你打开…";
|
|
76
|
-
readonly 'transfer.continued': "已延续 Harness 会话 {sessionId},正在为你打开…";
|
|
77
|
-
readonly 'transfer.failed': "转移到 Harness 失败:{error}";
|
|
78
|
-
readonly 'export.done': "已导出到工作区文件:{filePath}";
|
|
79
|
-
readonly 'export.failed': "导出失败:{error}";
|
|
80
|
-
readonly 'recover.hint': "列出网页端尚未导入本地的会话";
|
|
81
|
-
readonly 'recover.empty': "网页端没有未导入的会话";
|
|
82
|
-
readonly 'recover.done': "已恢复「{title}」";
|
|
83
|
-
readonly 'msg.empty': "(空)";
|
|
84
|
-
readonly 'msg.error': "(该回复可能不完整:{error})";
|
|
85
|
-
readonly streaming: "生成中";
|
|
86
|
-
};
|
|
87
|
-
/** Bilingual balance enforced: en must cover every zh key. */
|
|
88
|
-
export declare const en: {
|
|
89
|
-
'entry.label': string;
|
|
90
|
-
'entry.tooltip': string;
|
|
91
|
-
'panel.title': string;
|
|
92
|
-
'status.engine.stopped': string;
|
|
93
|
-
'status.engine.launching': string;
|
|
94
|
-
'status.engine.ready': string;
|
|
95
|
-
'status.engine.error': string;
|
|
96
|
-
'status.loggedIn': string;
|
|
97
|
-
'status.notLoggedIn': string;
|
|
98
|
-
'status.unknown': string;
|
|
99
|
-
'action.openLogin': string;
|
|
100
|
-
'action.closeBrowser': string;
|
|
101
|
-
'action.newChat': string;
|
|
102
|
-
'action.recover': string;
|
|
103
|
-
'action.refresh': string;
|
|
104
|
-
'action.send': string;
|
|
105
|
-
'action.stop': string;
|
|
106
|
-
'action.transferToHarness': string;
|
|
107
|
-
'action.exportFile': string;
|
|
108
|
-
'transfer.mode.distill': string;
|
|
109
|
-
'transfer.mode.raw': string;
|
|
110
|
-
'transfer.mode.hint': string;
|
|
111
|
-
'transfer.workspace.label': string;
|
|
112
|
-
'transfer.workspace.ungrouped': string;
|
|
113
|
-
'transfer.workspace.new': string;
|
|
114
|
-
'transfer.workspace.hint': string;
|
|
115
|
-
'transfer.workspace.created': string;
|
|
116
|
-
'transfer.workspace.createFailed': string;
|
|
117
|
-
'transfer.session.label': string;
|
|
118
|
-
'transfer.session.hint': string;
|
|
119
|
-
'transfer.session.new': string;
|
|
120
|
-
'transfer.attached.failed': string;
|
|
121
|
-
'handoff.eyebrow': string;
|
|
122
|
-
'toggle.deepThink': string;
|
|
123
|
-
'toggle.search': string;
|
|
124
|
-
'toggle.deepThink.hint': string;
|
|
125
|
-
'toggle.search.hint': string;
|
|
126
|
-
'sidebar.empty': string;
|
|
127
|
-
'chats.count': string;
|
|
128
|
-
'role.you': string;
|
|
129
|
-
'chat.rename': string;
|
|
130
|
-
'chat.delete': string;
|
|
131
|
-
'chat.delete.confirm': string;
|
|
132
|
-
'chat.clearAll': string;
|
|
133
|
-
'chat.clearAll.confirm': string;
|
|
134
|
-
'chat.rename.placeholder': string;
|
|
135
|
-
'chat.rename.ok': string;
|
|
136
|
-
'chat.rename.cancel': string;
|
|
137
|
-
'chat.manage.hint': string;
|
|
138
|
-
'toast.rename.done': string;
|
|
139
|
-
'toast.rename.failed': string;
|
|
140
|
-
'toast.delete.done': string;
|
|
141
|
-
'toast.delete.failed': string;
|
|
142
|
-
'toast.clear.done': string;
|
|
143
|
-
'toast.clear.failed': string;
|
|
144
|
-
'model.deepseek-chat': string;
|
|
145
|
-
'model.deepseek-reasoner': string;
|
|
146
|
-
'composer.placeholder': string;
|
|
147
|
-
'composer.hint': string;
|
|
148
|
-
'composer.busy': string;
|
|
149
|
-
'composer.notLoggedIn': string;
|
|
150
|
-
'composer.attach': string;
|
|
151
|
-
'composer.attach.hint': string;
|
|
152
|
-
'composer.attach.placeholder': string;
|
|
153
|
-
'empty.eyebrow': string;
|
|
154
|
-
'empty.title': string;
|
|
155
|
-
'empty.body': string;
|
|
156
|
-
'transfer.done': string;
|
|
157
|
-
'transfer.continued': string;
|
|
158
|
-
'transfer.failed': string;
|
|
159
|
-
'export.done': string;
|
|
160
|
-
'export.failed': string;
|
|
161
|
-
'recover.hint': string;
|
|
162
|
-
'recover.empty': string;
|
|
163
|
-
'recover.done': string;
|
|
164
|
-
'msg.empty': string;
|
|
165
|
-
'msg.error': string;
|
|
166
|
-
streaming: string;
|
|
167
|
-
};
|
|
168
|
-
/** Key union of the web-chat surface copy. */
|
|
169
|
-
export type WebChatKey = keyof typeof zh;
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import type { ISessions, IWorkspaces } from '@deepseek-ai/dsh-client-runtime/client';
|
|
2
|
-
import type { WebChatApi } from './api.ts';
|
|
3
|
-
import type { PanelController } from './controller.ts';
|
|
4
|
-
import type { WebChatKey } from './locales.ts';
|
|
5
|
-
/** The injected panel container (kept in the DOM, hidden when inactive). */
|
|
6
|
-
export declare const PANEL_VIEW_SELECTOR = "[data-dsh-webchat-view]";
|
|
7
|
-
export interface MountPanelDeps {
|
|
8
|
-
sessions: ISessions;
|
|
9
|
-
workspaces: IWorkspaces;
|
|
10
|
-
api: WebChatApi;
|
|
11
|
-
controller: PanelController;
|
|
12
|
-
/** Locale accessor bound to the plugin's registered dictionaries. */
|
|
13
|
-
tt: (key: WebChatKey) => string;
|
|
14
|
-
/** Resolve the current session's cwd (for transfers / exports). */
|
|
15
|
-
currentCwd: () => string | undefined;
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* Mount the panel React tree into the center column and bind its visibility
|
|
19
|
-
* to the controller's panelOpen state.
|
|
20
|
-
* @param deps - sessions, api, controller, locale accessor.
|
|
21
|
-
* @returns disposer unmounting the tree and restoring the column.
|
|
22
|
-
*/
|
|
23
|
-
export declare function mountPanel(deps: MountPanelDeps): () => void;
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Minimal markdown renderer for the web-chat panel. Deliberately small and
|
|
3
|
-
* dependency-free: headings, paragraphs, bold/italic, inline + fenced code,
|
|
4
|
-
* links, DeepSeek `[citation:N]` markers (rendered as superscript badges),
|
|
5
|
-
* unordered/ordered lists, blockquotes, GFM tables, horizontal rules, and
|
|
6
|
-
* <details> passthrough (used for DeepSeek R1 thinking blocks). Anything
|
|
7
|
-
* unrecognized renders as plain text — never raw HTML.
|
|
8
|
-
*/
|
|
9
|
-
import { type ReactNode } from 'react';
|
|
10
|
-
/** Render one block of markdown text into React nodes. */
|
|
11
|
-
export declare function renderMarkdown(source: string): ReactNode[];
|
|
12
|
-
/** The markdown renderer component. */
|
|
13
|
-
export declare function Markdown({ source }: {
|
|
14
|
-
source: string;
|
|
15
|
-
}): ReactNode;
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The web-chat panel: engine/login status, a conversation sidebar (the local
|
|
3
|
-
* transcript list + management), the chat transcript with markdown rendering,
|
|
4
|
-
* streaming updates (polled), the composer with deep-think / internet-search
|
|
5
|
-
* toggles, and the transfer-to-Harness handoff. All data flows through the
|
|
6
|
-
* /api/dsh-webchat routes; the panel is a plain React root in the center column.
|
|
7
|
-
*
|
|
8
|
-
* Layout mirrors chat.deepseek.com: a left conversation sidebar (new chat +
|
|
9
|
-
* history + per-item rename/delete + clear-all) beside the main chat column
|
|
10
|
-
* (messages → composer with 深度思考 / 智能搜索 toggles → handoff dock). The
|
|
11
|
-
* mono register stays reserved for machine things — timestamps, counts,
|
|
12
|
-
* status, and the handoff eyebrow.
|
|
13
|
-
*/
|
|
14
|
-
import type { ISessions, IWorkspaces } from '@deepseek-ai/dsh-client-runtime/client';
|
|
15
|
-
import type { WebChatApi } from '../api.ts';
|
|
16
|
-
import type { WebChatKey } from '../locales.ts';
|
|
17
|
-
export interface WebChatPanelProps {
|
|
18
|
-
api: WebChatApi;
|
|
19
|
-
/** Locale accessor (the plugin registers its dictionaries on ctx.locale). */
|
|
20
|
-
tt: (key: WebChatKey) => string;
|
|
21
|
-
/** Client sessions service (ctx.sessions) for opening transferred sessions. */
|
|
22
|
-
sessions: ISessions;
|
|
23
|
-
/** Client workspaces service (ctx.workspaces) for the transfer target picker. */
|
|
24
|
-
workspaces: IWorkspaces;
|
|
25
|
-
/** Resolve the current workspace directory (cwd for new sessions / exports). */
|
|
26
|
-
currentCwd: () => string | undefined;
|
|
27
|
-
}
|
|
28
|
-
export declare function WebChatPanel({ api, tt, sessions, workspaces, currentCwd }: WebChatPanelProps): import("react").JSX.Element;
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared sidebar entry injection core.
|
|
3
|
-
*
|
|
4
|
-
* dsh's sidebar shell exposes no slot an external plugin can register into,
|
|
5
|
-
* so the entry row is injected between the shell's New Session button and the
|
|
6
|
-
* workspace browser. The injection self-heals: a MutationObserver watches the
|
|
7
|
-
* sidebar root and re-inserts the row whenever a React re-render displaces it
|
|
8
|
-
* (re-insertion happens in the same frame, before paint, so no flicker).
|
|
9
|
-
*
|
|
10
|
-
* The row is plain DOM (no React tree) so it can never disturb the shell's
|
|
11
|
-
* reconciliation; the view it toggles is a separate root owned by the caller.
|
|
12
|
-
*
|
|
13
|
-
* Packages receive this file as a generated copy via scripts/sync-shared.mjs;
|
|
14
|
-
* edit the shared source and re-run the sync instead of editing a copy.
|
|
15
|
-
*/
|
|
16
|
-
/** Per-package configuration for one sidebar entry row. */
|
|
17
|
-
export interface SidebarEntryOptions {
|
|
18
|
-
/** Full attribute name identifying the injected row (idempotency key), e.g. 'data-dsh-ssh-entry'. */
|
|
19
|
-
rowAttribute: string;
|
|
20
|
-
/** CSS selector matching the injected row, e.g. '[data-dsh-ssh-entry]'. */
|
|
21
|
-
rowSelector: string;
|
|
22
|
-
/**
|
|
23
|
-
* L2 semantic-attribute plugin id (issue #506, enum table:
|
|
24
|
-
* skins/skin-center/contracts/semantic-attrs-v1.md). When set, the row also
|
|
25
|
-
* outputs data-dsh-plugin="<id>" and data-dsh-part="sidebar-entry"; unset
|
|
26
|
-
* leaves the row without semantic attributes.
|
|
27
|
-
*/
|
|
28
|
-
plugin?: string;
|
|
29
|
-
/** Inline icon markup (matches the shell's 16px nav-icon look). */
|
|
30
|
-
icon: string;
|
|
31
|
-
/** CSS module class names for the row and its two spans (entry / entryIcon / entryLabel). */
|
|
32
|
-
css: Record<string, string>;
|
|
33
|
-
/** Localized row label (aria-label + visible text). */
|
|
34
|
-
label(): string;
|
|
35
|
-
/** Optional localized tooltip (title attribute). */
|
|
36
|
-
tooltip?(): string;
|
|
37
|
-
/** Click action (open/toggle the owning panel). */
|
|
38
|
-
onToggle(): void;
|
|
39
|
-
/** Family-block position: 'before' inserts ahead of sibling plugin rows, 'after' behind them. */
|
|
40
|
-
position: 'before' | 'after';
|
|
41
|
-
/**
|
|
42
|
-
* Selectors of the sibling plugin entry rows this package orders against
|
|
43
|
-
* (its own row included — the placement guard excludes a row that is
|
|
44
|
-
* already inside the root). Each package passes the same list it used
|
|
45
|
-
* before the consolidation so the rendered order stays stable.
|
|
46
|
-
*/
|
|
47
|
-
familySelectors: readonly string[];
|
|
48
|
-
/** Optional active-state bridge; highlights the row while the panel is open. */
|
|
49
|
-
active?: {
|
|
50
|
-
subscribe(listener: () => void): () => void;
|
|
51
|
-
isOpen(): boolean;
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* Mount the sidebar entry, waiting for the shell to render and self-healing
|
|
56
|
-
* on later React re-renders.
|
|
57
|
-
* @param options - the row's attribute/icon/copy/action/ordering configuration.
|
|
58
|
-
* @returns disposer removing the entry and its observers.
|
|
59
|
-
*/
|
|
60
|
-
export declare function mountSidebarEntry(options: SidebarEntryOptions): () => void;
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Sidebar entry injection — package-specific wiring over the shared core
|
|
3
|
-
* (sidebar-entry-core.ts). The row is plain DOM so it can never disturb the
|
|
4
|
-
* shell's reconciliation; the panel view it toggles is a separate React root
|
|
5
|
-
* mounted in the center column (see mount.tsx).
|
|
6
|
-
*/
|
|
7
|
-
import type { PanelController } from './controller.ts';
|
|
8
|
-
import type { WebChatKey } from './locales.ts';
|
|
9
|
-
/** Stable data attribute identifying the injected entry row. */
|
|
10
|
-
export declare const ENTRY_SELECTOR = "[data-dsh-webchat-entry]";
|
|
11
|
-
export interface SidebarEntryDeps {
|
|
12
|
-
controller: PanelController;
|
|
13
|
-
/** Locale accessor (spelled here to keep the core dependency-free). */
|
|
14
|
-
tt: (key: WebChatKey) => string;
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* Mount the sidebar entry, waiting for the shell to render and self-healing
|
|
18
|
-
* on later React re-renders.
|
|
19
|
-
* @param deps - controller and copy accessor.
|
|
20
|
-
* @returns disposer removing the entry and its observers.
|
|
21
|
-
*/
|
|
22
|
-
export declare function mountSidebarEntry(deps: SidebarEntryDeps): () => void;
|
|
@@ -1,247 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* DeepSeek web engine — the "Codex ChatGPT mode" analog for DeepSeek Harness.
|
|
3
|
-
*
|
|
4
|
-
* Drives a real browser (system Chrome/Edge via playwright-core) against
|
|
5
|
-
* chat.deepseek.com with a dedicated persistent profile, so the user logs in
|
|
6
|
-
* once with their own DeepSeek account (phone / password / Apple / WeChat QR)
|
|
7
|
-
* and the session persists. Chatting happens THROUGH the real web page —
|
|
8
|
-
* messages are typed into the real composer — so the plugin needs no API key,
|
|
9
|
-
* no billing, and stays immune to DeepSeek's private-API PoW challenge.
|
|
10
|
-
*
|
|
11
|
-
* Replies are read by teeing the page's own SSE stream: an injected init
|
|
12
|
-
* script wraps XMLHttpRequest and captures the `/api/v0/chat/completion`
|
|
13
|
-
* response as it streams (the page has already solved PoW + auth, so we get
|
|
14
|
-
* the model's raw markdown for free). DOM scraping of `.ds-markdown` is kept
|
|
15
|
-
* only as a fallback for when the capture cannot install. The web chat runs
|
|
16
|
-
* the `deepseek-chat` model by default (switchable to deepseek-reasoner).
|
|
17
|
-
*
|
|
18
|
-
* All page interactions are best-effort and selector-defensive: failures
|
|
19
|
-
* produce readable errors (never crashes) and the caller decides how to
|
|
20
|
-
* degrade.
|
|
21
|
-
*/
|
|
22
|
-
import { type Page } from 'playwright-core';
|
|
23
|
-
import type { TranscriptStore } from '../store.ts';
|
|
24
|
-
import type { EngineState, SendResult, WebChatErrorCode } from '../protocol.ts';
|
|
25
|
-
/** Engine configuration (resolved from the plugin settings surface). */
|
|
26
|
-
export interface WebChatEngineConfig {
|
|
27
|
-
/** Data dir root (browser profile lives under it). */
|
|
28
|
-
dataDir: string;
|
|
29
|
-
/** Browser channel hint: 'chrome' | 'msedge' | 'chromium' | undefined (auto-detect). */
|
|
30
|
-
channel?: string;
|
|
31
|
-
/** Explicit browser executable path (overrides channel detection). */
|
|
32
|
-
executablePath?: string;
|
|
33
|
-
/** Proxy mode: 'direct' (--no-proxy-server), 'system' (browser default), or a proxy URL. */
|
|
34
|
-
proxy?: string;
|
|
35
|
-
/** Visible browser window (required for the one-time login; defaults true). */
|
|
36
|
-
headless?: boolean;
|
|
37
|
-
/** Max seconds to wait for a reply before returning the partial. */
|
|
38
|
-
replyTimeoutMs?: number;
|
|
39
|
-
/** DeepSeek web origin. */
|
|
40
|
-
baseUrl?: string;
|
|
41
|
-
}
|
|
42
|
-
/** Parsed reply from the accumulated SSE text. */
|
|
43
|
-
export interface ParsedStreamReply {
|
|
44
|
-
/** Markdown body (thinking wrapped in a <details> block). */
|
|
45
|
-
markdown: string;
|
|
46
|
-
/** Raw thinking text (empty when the model has none). */
|
|
47
|
-
thinking: string;
|
|
48
|
-
/** True once the stream reported FINISHED. */
|
|
49
|
-
finished: boolean;
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Parse the accumulated `/api/v0/chat/completion` SSE body into reply text.
|
|
53
|
-
* The stream is `event:` / `data:` lines; each `data:` payload is JSON. The
|
|
54
|
-
* protocol distinguishes the R1 reasoning fragment (type `THINK`) from the
|
|
55
|
-
* answer fragment (type `RESPONSE`), and carries search steps as `TOOL_SEARCH`
|
|
56
|
-
* / `TOOL_OPEN` fragments:
|
|
57
|
-
* - {"v":{"response":{"fragments":[{"type":"THINK","content":"…"}]}}}
|
|
58
|
-
* a snapshot carrying the fragment list and their types.
|
|
59
|
-
* - {"p":"response/fragments","o":"APPEND","v":[{"type":"RESPONSE",…}]}
|
|
60
|
-
* appends a NEW fragment (reasoning / search / answer); `-1/content`
|
|
61
|
-
* deltas after this belong to that new fragment.
|
|
62
|
-
* - {"p":"response/fragments/-1/content","o":"APPEND","v":"是一座"} — appends
|
|
63
|
-
* a text delta to the CURRENT fragment's content.
|
|
64
|
-
* - {"p":"response/fragments/-1/results","o":"SET","v":[…]} — search results
|
|
65
|
-
* for a TOOL_SEARCH step (rendered as "搜索到 N 个网页").
|
|
66
|
-
* - {"v":"将"} — a bare delta continuing the current fragment; a bare
|
|
67
|
-
* `{"v":[{p:"content",o:"APPEND",v:"[reference:N]"},…]}` carries citation
|
|
68
|
-
* markers.
|
|
69
|
-
* - {"p":"response/status","o":"SET","v":"FINISHED"} — generation complete.
|
|
70
|
-
*/
|
|
71
|
-
export declare function parseStreamReply(raw: string): ParsedStreamReply;
|
|
72
|
-
export declare class DeepSeekWebEngine {
|
|
73
|
-
private readonly store;
|
|
74
|
-
private readonly config;
|
|
75
|
-
private readonly profileDir;
|
|
76
|
-
private context;
|
|
77
|
-
private page;
|
|
78
|
-
private readonly queue;
|
|
79
|
-
private state;
|
|
80
|
-
private engineError;
|
|
81
|
-
private busy;
|
|
82
|
-
private lastError;
|
|
83
|
-
private lastErrorCode;
|
|
84
|
-
private launchedOnce;
|
|
85
|
-
/** True while a headed one-time login window is open (auto-closes on login). */
|
|
86
|
-
private loginMode;
|
|
87
|
-
/** Remembered login state — survives the auto-close so the panel stays "已登录". */
|
|
88
|
-
private loggedInOnce;
|
|
89
|
-
/** Commanded toggle state (best-effort read-back overrides on status). */
|
|
90
|
-
private deepThink;
|
|
91
|
-
private search;
|
|
92
|
-
constructor(store: TranscriptStore, config: WebChatEngineConfig);
|
|
93
|
-
/** Coarse state for status snapshots. */
|
|
94
|
-
getState(): EngineState;
|
|
95
|
-
getEngineError(): string | undefined;
|
|
96
|
-
getBusy(): boolean;
|
|
97
|
-
getLastError(): string | undefined;
|
|
98
|
-
getLastErrorCode(): WebChatErrorCode | undefined;
|
|
99
|
-
/** Set the last error + its structured code together (keeps them in sync). */
|
|
100
|
-
private setLastError;
|
|
101
|
-
private setState;
|
|
102
|
-
/** Resolve a browser launch descriptor (executable + args). */
|
|
103
|
-
private launchOptions;
|
|
104
|
-
/**
|
|
105
|
-
* Ensure the browser + chat.deepseek.com page exist. Launches the persistent
|
|
106
|
-
* context on first call; subsequent calls reuse the page.
|
|
107
|
-
*/
|
|
108
|
-
/** True when the cached page/context are still connected (not closed by the user). */
|
|
109
|
-
private isPageAlive;
|
|
110
|
-
ensureBrowser(): Promise<Page>;
|
|
111
|
-
/** The candidate list used during auto-detection. */
|
|
112
|
-
private launchOptionsCandidates;
|
|
113
|
-
/** Navigate to the DeepSeek chat root. */
|
|
114
|
-
private openDeepSeekPage;
|
|
115
|
-
/** True when the page shows the chat UI (not the login page). */
|
|
116
|
-
isLoggedIn(): Promise<boolean | null>;
|
|
117
|
-
/**
|
|
118
|
-
* Open a visible browser window for the one-time login. The window is forced
|
|
119
|
-
* headed (login needs a user) and auto-closes as soon as the page reaches the
|
|
120
|
-
* chat UI; normal chatting then runs headless on the persisted profile.
|
|
121
|
-
*/
|
|
122
|
-
openLoginWindow(): Promise<{
|
|
123
|
-
ok: boolean;
|
|
124
|
-
error?: string;
|
|
125
|
-
}>;
|
|
126
|
-
/** Poll the login window and close it once the user has logged in. */
|
|
127
|
-
private watchLoginAndClose;
|
|
128
|
-
/** Current page URL (for status/debug). */
|
|
129
|
-
pageUrl(): string | undefined;
|
|
130
|
-
/**
|
|
131
|
-
* Best-effort read of the deep-think (R1) and search toggle state from the
|
|
132
|
-
* page. The toggles are `div.ds-toggle-button` elements (NOT `<button>`)
|
|
133
|
-
* carrying `aria-pressed` plus a `ds-toggle-button--selected` class when on;
|
|
134
|
-
* the search toggle is labeled 智能搜索. Falls back to the last commanded
|
|
135
|
-
* state when the page gives no clear signal.
|
|
136
|
-
*/
|
|
137
|
-
private readToggles;
|
|
138
|
-
/** Serialized page evaluation guarded against a dead page. */
|
|
139
|
-
private evalPage;
|
|
140
|
-
/**
|
|
141
|
-
* In-page scraper: returns the ordered rendered messages currently in the
|
|
142
|
-
* DOM. Uses the virtual-list item keys as message boundaries and the
|
|
143
|
-
* assistant-main-content class to split roles. Fallback only — the primary
|
|
144
|
-
* reply source is the teed SSE stream.
|
|
145
|
-
*/
|
|
146
|
-
private scrapeConversation;
|
|
147
|
-
/** Convert scraped DOM messages into transcript messages (markdown content). */
|
|
148
|
-
private scrapedToMessages;
|
|
149
|
-
/**
|
|
150
|
-
* Scrape the DeepSeek sidebar conversation titles (best effort). The web
|
|
151
|
-
* conversation list has no stable contract, so several candidate selectors
|
|
152
|
-
* are probed and short non-menu texts are returned.
|
|
153
|
-
*/
|
|
154
|
-
listWebConversations(): Promise<Array<{
|
|
155
|
-
title: string;
|
|
156
|
-
}>>;
|
|
157
|
-
/** Click a sidebar conversation whose text contains the given title. */
|
|
158
|
-
private clickConversationByTitle;
|
|
159
|
-
/**
|
|
160
|
-
* Recover a web conversation into the local transcript store: open it in the
|
|
161
|
-
* sidebar, scrape its history, and import it (idempotent by title).
|
|
162
|
-
*/
|
|
163
|
-
recoverWebConversation(title: string): Promise<{
|
|
164
|
-
ok: boolean;
|
|
165
|
-
chatId?: string;
|
|
166
|
-
title?: string;
|
|
167
|
-
created?: boolean;
|
|
168
|
-
error?: string;
|
|
169
|
-
}>;
|
|
170
|
-
/** Detect whether the page is currently generating (stop affordance visible). */
|
|
171
|
-
private isGenerating;
|
|
172
|
-
/** Click the stop-generation affordance, best effort. */
|
|
173
|
-
stop(): Promise<void>;
|
|
174
|
-
/** Find the composer textarea (defensive selector list). */
|
|
175
|
-
private composerLocator;
|
|
176
|
-
/**
|
|
177
|
-
* Upload local image files into the composer through the page's (usually
|
|
178
|
-
* hidden) file input. `setInputFiles` fires the input's change event, which
|
|
179
|
-
* is how DeepSeek picks up attachments without clicking its native dialog.
|
|
180
|
-
*/
|
|
181
|
-
private attachImages;
|
|
182
|
-
/**
|
|
183
|
-
* Send a message through the real web page.
|
|
184
|
-
* @param text - message text.
|
|
185
|
-
* @param wait - when true (agent tools), resolve with the final reply after
|
|
186
|
-
* streaming completes; when false (GUI), resolve right after the message
|
|
187
|
-
* is submitted — the reply streams in the background into the transcript
|
|
188
|
-
* and the panel polls it live.
|
|
189
|
-
*/
|
|
190
|
-
send(text: string, wait?: boolean, images?: string[]): Promise<SendResult>;
|
|
191
|
-
private sendImpl;
|
|
192
|
-
/**
|
|
193
|
-
* Background reply loop. Primary source is the teed SSE stream (raw model
|
|
194
|
-
* markdown, no selectors); if the capture never installs, falls back to
|
|
195
|
-
* scraping the rendered DOM. Writes the growing reply into the transcript
|
|
196
|
-
* until the stream reports done/FINISHED or the timeout hits.
|
|
197
|
-
*/
|
|
198
|
-
private streamReply;
|
|
199
|
-
/** Start a new chat on the web page (best effort) + a fresh local transcript. */
|
|
200
|
-
newChat(): Promise<{
|
|
201
|
-
ok: boolean;
|
|
202
|
-
chatId?: string;
|
|
203
|
-
error?: string;
|
|
204
|
-
}>;
|
|
205
|
-
/**
|
|
206
|
-
* Click a toggle on the page by label candidates (best effort — the DeepSeek
|
|
207
|
-
* web UI has no stable contract, so a miss is not an error). The toggles are
|
|
208
|
-
* `div.ds-toggle-button` elements (not `<button>`), so those selectors come
|
|
209
|
-
* first; `<button>` variants remain as fallbacks for older page versions.
|
|
210
|
-
* @returns true when a candidate was clicked.
|
|
211
|
-
*/
|
|
212
|
-
private clickToggle;
|
|
213
|
-
/** Toggle deep-think (R1) mode on the web page. */
|
|
214
|
-
setDeepThink(enabled: boolean): Promise<{
|
|
215
|
-
ok: boolean;
|
|
216
|
-
error?: string;
|
|
217
|
-
}>;
|
|
218
|
-
/** Toggle internet search on the web page (web label: 智能搜索). */
|
|
219
|
-
setSearch(enabled: boolean): Promise<{
|
|
220
|
-
ok: boolean;
|
|
221
|
-
error?: string;
|
|
222
|
-
}>;
|
|
223
|
-
/** Close the browser (releases the profile lock). */
|
|
224
|
-
disposeBrowser(): Promise<void>;
|
|
225
|
-
/** Engine snapshot for status routes and agent tools. */
|
|
226
|
-
status(): Promise<{
|
|
227
|
-
engine: EngineState;
|
|
228
|
-
engineError?: string;
|
|
229
|
-
loggedIn: boolean | null;
|
|
230
|
-
pageUrl?: string;
|
|
231
|
-
deepThink: boolean;
|
|
232
|
-
search: boolean;
|
|
233
|
-
busy: boolean;
|
|
234
|
-
lastError?: string;
|
|
235
|
-
lastErrorCode?: WebChatErrorCode;
|
|
236
|
-
}>;
|
|
237
|
-
}
|
|
238
|
-
export interface MarkupNode {
|
|
239
|
-
readonly tagName?: string;
|
|
240
|
-
readonly nodeType: number;
|
|
241
|
-
readonly textContent?: string;
|
|
242
|
-
readonly children: MarkupNode[];
|
|
243
|
-
readonly attributes: Record<string, string | undefined>;
|
|
244
|
-
readonly parent?: MarkupNode;
|
|
245
|
-
}
|
|
246
|
-
/** Convert a scraped markdown-HTML string to markdown (used by send). */
|
|
247
|
-
export declare function scrapedHtmlToMarkdown(html: string): string;
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Minimal HTML → Markdown converter used to scrape DeepSeek's rendered
|
|
3
|
-
* `.ds-markdown` replies. Covers the shapes DeepSeek renders (headings,
|
|
4
|
-
* paragraphs, code blocks with syntax classes, inline code, bold/italic,
|
|
5
|
-
* links, images, lists, tables, blockquotes, math placeholders). Anything
|
|
6
|
-
* unrecognized falls back to its text content — fidelity loss degrades
|
|
7
|
-
* gracefully instead of dropping text.
|
|
8
|
-
*/
|
|
9
|
-
interface NodeLike {
|
|
10
|
-
readonly tagName?: string;
|
|
11
|
-
readonly nodeType?: number;
|
|
12
|
-
readonly textContent?: string | null;
|
|
13
|
-
readonly children?: readonly NodeLike[];
|
|
14
|
-
readonly attributes?: Record<string, string | undefined>;
|
|
15
|
-
readonly innerHTML?: string;
|
|
16
|
-
readonly className?: string;
|
|
17
|
-
readonly parent?: NodeLike;
|
|
18
|
-
}
|
|
19
|
-
/** Convert one DOM subtree (element or text) to markdown. */
|
|
20
|
-
export declare function htmlToMarkdown(root: NodeLike): string;
|
|
21
|
-
/** Convert serialized DOM (from the in-page scraper) to markdown. */
|
|
22
|
-
export declare function serializeToMarkdown(root: NodeLike): string;
|
|
23
|
-
export {};
|
package/lib/types/index.d.ts
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* dsh-webchat — host half. Mounts the DeepSeek web engine (a real browser at
|
|
3
|
-
* chat.deepseek.com driven through its own page, persistent login profile),
|
|
4
|
-
* the /api/dsh-webchat route family, the agent tools (webchat_status,
|
|
5
|
-
* webchat_send, webchat_import, webchat_transfer), the harness transfer
|
|
6
|
-
* (seed a new session with a web transcript) and a system-prompt
|
|
7
|
-
* announcement. The browser half (./client) renders the chat panel. All
|
|
8
|
-
* transport rides the official NPM SDK packages — no dsh source changes.
|
|
9
|
-
*/
|
|
10
|
-
import type { Context } from '@deepseek-ai/cordis';
|
|
11
|
-
import z from 'schemastery';
|
|
12
|
-
/** Stable cordis plugin name. */
|
|
13
|
-
export declare const name = "webchat";
|
|
14
|
-
/** Services required before the web-chat surfaces can mount. */
|
|
15
|
-
export declare const inject: string[];
|
|
16
|
-
/**
|
|
17
|
-
* Settings namespace of the web-chat capability — the section the web
|
|
18
|
-
* settings surface edits. Spelled here rather than imported: the browser half
|
|
19
|
-
* spells the same value and must not depend on a Host package.
|
|
20
|
-
*/
|
|
21
|
-
export declare const WEBCHAT_SETTINGS_NAMESPACE: import("@deepseek-ai/dsh-settings").SettingsNamespace;
|
|
22
|
-
/** Plugin config, validated by the same-named schemastery schema. */
|
|
23
|
-
export interface Config {
|
|
24
|
-
/** When true (default), a system-prompt section announces the plugin to every agent. */
|
|
25
|
-
announceToAgent?: boolean;
|
|
26
|
-
/** Master switch for the plugin (routes, tools, prompt section). */
|
|
27
|
-
enabled?: boolean;
|
|
28
|
-
/** Browser channel hint ('chrome' | 'msedge' | 'chromium' | 'auto'). */
|
|
29
|
-
browserChannel?: string;
|
|
30
|
-
/** Explicit browser executable path. */
|
|
31
|
-
browserExecutablePath?: string;
|
|
32
|
-
/** Proxy mode: 'direct' | 'system' | 'http://host:port'. */
|
|
33
|
-
browserProxy?: string;
|
|
34
|
-
/**
|
|
35
|
-
* Run the chat browser headless (invisible). Default true: only the one-time
|
|
36
|
-
* login window is visible, and it auto-closes once logged in. Set false to
|
|
37
|
-
* keep a visible browser window during chatting too.
|
|
38
|
-
*/
|
|
39
|
-
browserHeadless?: boolean;
|
|
40
|
-
/** Max ms to wait for a web reply. */
|
|
41
|
-
replyTimeoutMs?: number;
|
|
42
|
-
/** Data directory override (tests). */
|
|
43
|
-
dataDir?: string;
|
|
44
|
-
/** When true (default), distill the transcript into an executable task brief before transfer. */
|
|
45
|
-
transferDistill?: boolean;
|
|
46
|
-
/** Provider route for the transfer distillation call (empty = auto-detect). */
|
|
47
|
-
transferProvider?: string;
|
|
48
|
-
/** Model id for the transfer distillation call (empty = auto-detect). */
|
|
49
|
-
transferModel?: string;
|
|
50
|
-
/** Output-token cap for the final distillation brief (default 4096). */
|
|
51
|
-
transferMaxTokens?: number;
|
|
52
|
-
/** Output-token cap per chunk summary in long-conversation map-reduce (default 1024). */
|
|
53
|
-
transferChunkTokens?: number;
|
|
54
|
-
}
|
|
55
|
-
export declare const Config: z<Config>;
|
|
56
|
-
/** Model-facing announcement: plugin presence, capabilities, and limits. */
|
|
57
|
-
export declare const WEBCHAT_GUIDANCE = "\u672C\u673A\u5DF2\u5B89\u88C5 dsh-webchat \u63D2\u4EF6\uFF08Codex ChatGPT \u6A21\u5F0F \u00B7 DeepSeek \u7F51\u9875\u7AEF\u804A\u5929\uFF09\uFF1A\u4FA7\u8FB9\u680F\u300C\u7F51\u9875\u804A\u5929\u300D\u5165\u53E3\uFF1B\u901A\u8FC7\u771F\u5B9E\u6D4F\u89C8\u5668\u9A71\u52A8 chat.deepseek.com\uFF08DeepSeek \u7F51\u9875\u6A21\u578B\uFF0C\u6DF1\u5EA6\u601D\u8003/\u667A\u80FD\u641C\u7D22\u5F00\u5173\uFF0C\u7F51\u9875\u767B\u5F55\u4F1A\u8BDD\uFF0C\u65E0\u9700 API \u989D\u5EA6\uFF09\u3002\u80FD\u529B\uFF1Awebchat_status \u67E5\u770B\u767B\u5F55/\u4F1A\u8BDD\u72B6\u6001\u3001webchat_send \u901A\u8FC7\u7F51\u9875\u7AEF\u53D1\u9001\u6D88\u606F\u5E76\u6D41\u5F0F\u83B7\u53D6\u56DE\u590D\uFF08\u53EF\u9644\u5E26\u672C\u5730\u56FE\u7247\u8DEF\u5F84\u505A\u591A\u6A21\u6001\u63D0\u95EE\uFF09\u3001webchat_recover \u628A\u7F51\u9875\u7AEF\u5DF2\u6709\u4F1A\u8BDD\u540C\u6B65/\u6062\u590D\u5230\u672C\u5730\u3001webchat_import \u628A\u5B58\u50A8\u7684\u7F51\u9875\u5BF9\u8BDD\u5BFC\u5165\u4E3A markdown \u4E0A\u4E0B\u6587\u3001webchat_transfer \u628A\u7F51\u9875\u5BF9\u8BDD\u84B8\u998F\u6210\u53EF\u6267\u884C\u4EFB\u52A1\u7B80\u62A5\u5E76\u521B\u5EFA\u65B0 harness \u4F1A\u8BDD\uFF08\u9996\u6761\u6D88\u606F\u5373\u4EFB\u52A1\u7B80\u62A5\uFF0C\u800C\u975E\u539F\u59CB\u804A\u5929\u8BB0\u5F55\uFF09\uFF0C\u6216\u7ECF targetSessionId \u628A\u7B80\u62A5\u4F5C\u4E3A\u65B0\u6D88\u606F\u8FFD\u52A0\u5230\u5DF2\u6709\u4F1A\u8BDD\u5EF6\u7EED\u540C\u4E00\u4EFB\u52A1\uFF1BGUI \u9762\u677F\u53EF\u5C06\u5BF9\u8BDD\u968F\u65F6\u5BFC\u51FA\u4E3A\u5DE5\u4F5C\u533A\u6587\u4EF6\u6216\u8F6C\u5165 harness \u4F1A\u8BDD\u3002\u9650\u5236\uFF1A\u9996\u6B21\u4F7F\u7528\u9700\u7528\u6237\u5728\u5F39\u51FA\u7684\u6D4F\u89C8\u5668\u7A97\u53E3\u5B8C\u6210 DeepSeek \u7F51\u9875\u767B\u5F55\uFF1B\u7F51\u9875\u7AEF\u53D7 DeepSeek \u5B98\u65B9\u98CE\u63A7\uFF0C\u64CD\u4F5C\u5931\u8D25\u6216\u9875\u9762\u6539\u7248\u65F6\u8FD4\u56DE\u9519\u8BEF\u800C\u975E\u5D29\u6E83\uFF1B\u9762\u677F\u63D0\u4F9B\u300C\u6DF1\u5EA6\u601D\u8003\uFF08R1\uFF09\u300D\u4E0E\u300C\u667A\u80FD\u641C\u7D22\u300D\u5F00\u5173\uFF08\u7F51\u9875\u7AEF\u65E0\u6A21\u578B\u9009\u62E9\u5668\uFF09\u3002\u7528\u6237\u63D0\u5230\u300C\u7F51\u9875\u804A\u5929 / \u7F51\u9875\u7AEF / ChatGPT \u6A21\u5F0F / deepseek web / \u8F6C\u79FB\u5230 harness\u300D\u65F6\u5373\u6307\u672C\u63D2\u4EF6\uFF0C\u8BF7\u636E\u6B64\u534F\u4F5C\u3002";
|
|
58
|
-
/**
|
|
59
|
-
* Mount the engine, routes, tools, and announcement.
|
|
60
|
-
* @param ctx - host plugin context carrying webServer/tools/systemPrompt.
|
|
61
|
-
* @param config - resolved plugin config (schema defaults applied by the loader).
|
|
62
|
-
*/
|
|
63
|
-
export declare function apply(ctx: Context, config?: Config): void;
|