dsh-chat-embed 0.6.3 → 0.6.5
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 +20 -11
- package/client.js +123 -77
- package/docs/architecture.md +3 -1
- package/docs/publishing.md +4 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -179,20 +179,28 @@ node tools/shell-patch/patch-asar.cjs --help # 查看参数
|
|
|
179
179
|
|
|
180
180
|
### 补丁状态与自检(0.6.0 起)
|
|
181
181
|
|
|
182
|
-
|
|
182
|
+
设置菜单里的「跨重启保持登录」有四种状态,**风险提示与「复制补丁说明」只在未生效时出现**:
|
|
183
183
|
|
|
184
|
-
| 状态 | 含义 |
|
|
184
|
+
| 状态 | 含义 | 菜单里额外提供 |
|
|
185
185
|
| --- | --- | --- |
|
|
186
|
-
| 已开启(`persist:` 分区) | 补丁生效,重启后仍登录 |
|
|
187
|
-
| 未开启(内存分区) | 从没打过补丁 | 风险提示 + 复制补丁说明
|
|
186
|
+
| 已开启(`persist:` 分区) | 补丁生效,重启后仍登录 | —(已是最优,只留「检查补丁配置」) |
|
|
187
|
+
| 未开启(内存分区) | 从没打过补丁 | 风险提示 + 复制补丁说明 |
|
|
188
188
|
| **已失效**(升级覆盖) | **打过补丁,但桌面壳升级覆盖了 `app.asar`** | 同上;这一状态专门告诉你"该重打了" |
|
|
189
189
|
| 尚未检测 | 还没打开过聊天页 | 同上 |
|
|
190
190
|
|
|
191
|
-
|
|
191
|
+
「检查补丁配置」是一行**开关样式**的控件,任何状态下都在。打开它不会在菜单里铺开报告——那会把面板撑宽——而是:
|
|
192
192
|
|
|
193
|
-
|
|
193
|
+
1. 读取一次 Host 侧只读探针(见下);
|
|
194
|
+
2. **打开一个新会话**,把已知事实(安装目录、可写性、补丁点、是否已打补丁、确切命令)预先写进输入框;
|
|
195
|
+
3. 你点「发送」即可。agent 会调用 `dsh_chat_embed_patch_check` 工具核对,然后说明现状和该执行的命令。
|
|
194
196
|
|
|
195
|
-
|
|
197
|
+
这条路径用的就是 DSH 自己「创建你的插件」入口的同一个 API(`uiWorkspace.startSession`),所以行为与原生一致:已有草稿时追加而不是覆盖。没有 `uiWorkspace` 的宿主会退化成把说明复制到剪贴板。
|
|
198
|
+
|
|
199
|
+
背后是 Host 侧只读探针 `GET /dsh-chat-embed/patch-check`:安装目录、`app.asar` 是否可写、是否已打补丁、补丁点是否还在(壳版本是否兼容)、备份与待替换文件、大小与内容哈希。
|
|
200
|
+
|
|
201
|
+
**不需要自己装 Node.js**:探针会找出桌面版自带的 Node 运行时(`~/.dsh/dsh-runtimes/*/dependencies/node/`),并把一条完整可复制的命令写进草稿——含正确的 Node 路径、补丁脚本路径和 `--resources` 安装目录(自定义/便携安装也能用)。
|
|
202
|
+
|
|
203
|
+
Host 侧还注册了只读工具 **`dsh_chat_embed_patch_check`**:可以直接问 agent「我的聊天为什么重启后要重新登录」,它会自己去查这些事实,而不是让你跑命令再贴输出——上面第 2 步的草稿就是让它这么做的。
|
|
196
204
|
|
|
197
205
|
补丁做两件事:`persist:` 固定分区(登录落盘)+ `cache:false`(不落 Chromium 缓存)。
|
|
198
206
|
没有补丁也能用,只是每次重启要重新登录。细节与风险见 [已知限制](#已知限制)。
|
|
@@ -214,7 +222,7 @@ dsh-chat-embed/
|
|
|
214
222
|
├── package.json # 包名/版本、dsh.bundle.patch、dsh.client、exports/files
|
|
215
223
|
├── cordis.patch.yml # 组合补丁:插入 id=chat-embed 的 loader 行
|
|
216
224
|
├── index.js # Host 半侧:补丁探针(HTTP + agent 工具)
|
|
217
|
-
├── client.js # Client 半侧(约
|
|
225
|
+
├── client.js # Client 半侧(约 3985 行,手写产物)
|
|
218
226
|
├── locale/{en,zh}.json # 插件管理器显示用 meta
|
|
219
227
|
├── icon.svg # 插件图标(插件管理器显示)
|
|
220
228
|
├── assets/
|
|
@@ -264,7 +272,7 @@ git clone <repo-url> dsh-chat-embed
|
|
|
264
272
|
cd dsh-chat-embed
|
|
265
273
|
|
|
266
274
|
# 无需安装依赖;直接跑门禁与测试
|
|
267
|
-
npm run check # 语法 + 清单 + 显示资源 + 服务注册表 +
|
|
275
|
+
npm run check # 语法 + 清单 + 显示资源 + 服务注册表 + 85 项测试
|
|
268
276
|
npm test # 只跑测试
|
|
269
277
|
```
|
|
270
278
|
|
|
@@ -285,7 +293,7 @@ plugin_manager { "action": "install_bundle", "target": "<本仓库克隆路径>"
|
|
|
285
293
|
|
|
286
294
|
## 已知限制
|
|
287
295
|
|
|
288
|
-
1. **跨重启登录需要壳补丁**:壳默认给内存分区(`dsh-sidebar-browser-<uuid>`,无 `persist:`,每次启动随机),登录态不落盘。`tools/shell-patch/` 可修;客户端升级会覆盖 `app.asar
|
|
296
|
+
1. **跨重启登录需要壳补丁**:壳默认给内存分区(`dsh-sidebar-browser-<uuid>`,无 `persist:`,每次启动随机),登录态不落盘。`tools/shell-patch/` 可修;客户端升级会覆盖 `app.asar`,需要重打——菜单里的「检查补丁配置」会告诉你现在是哪种情况、命令是什么。
|
|
289
297
|
2. **左右互斥**:右栏聊天依赖会话视图;中央打开聊天面板时右栏不可用。同一时刻只有一处显示聊天页,切换会重建一次(自动回到上次会话,不用重新登录)。
|
|
290
298
|
3. **中心面板会被切走**:选另一个主面板时中央聊天页随 DOM 卸载,回来要重建;右栏 tab 带 `keepMounted`,切标签/切会话不卸载。
|
|
291
299
|
4. **内存**:每点亮一个标签 = 一个渲染进程;7 个全开会明显吃内存。
|
|
@@ -305,7 +313,7 @@ plugin_manager { "action": "install_bundle", "target": "<本仓库克隆路径>"
|
|
|
305
313
|
| 标签变红(`data-failed`) | 该标签租约或导航失败:点它看状态行 `detail`,再「重新加载」 |
|
|
306
314
|
| 显示「已连接」但页面空白 | 网络或站点风控;「重新加载」或「外开」 |
|
|
307
315
|
| 出现「使用环境异常」 | UA 归一化失效(壳升级后 UA 结构变了),查 `normalizedUserAgent` |
|
|
308
|
-
| 重启后要重新登录 |
|
|
316
|
+
| 重启后要重新登录 | 未打壳补丁,或补丁被客户端升级覆盖:菜单「检查补丁配置」→ 发送,让 agent 核对并给出命令 |
|
|
309
317
|
| 磁盘占用变大 | 执行 `清理聊天缓存.cmd`(只删缓存,保留登录);或确认补丁含 `cache:false` |
|
|
310
318
|
| 标签文字/顺序要改 | 只改 `PROVIDERS`,`npm run check` + 重装 |
|
|
311
319
|
|
|
@@ -315,6 +323,7 @@ plugin_manager { "action": "install_bundle", "target": "<本仓库克隆路径>"
|
|
|
315
323
|
|
|
316
324
|
| 版本 | 变化 |
|
|
317
325
|
| --- | --- |
|
|
326
|
+
| `0.6.5` | 设置菜单标题与诊断卡片显示自身版本号;「检查补丁配置」改为一次性开关:不再内联渲染报告,而是打开新会话并把已知事实预填进输入框(借用 DSH「创建你的插件」的 `uiWorkspace.startSession` 用法) |
|
|
318
327
|
| `0.6.3` | 自检报告改为一次性:关闭设置菜单即清除,重新打开不再显示上次的报告 |
|
|
319
328
|
| `0.6.2` | 修复设置菜单被长路径撑宽:菜单加 `max-width`、报告文本 `overflow-wrap:anywhere`、补丁命令单独占一行(便于整条复制) |
|
|
320
329
|
| `0.6.1` | 探针找出桌面版自带的 Node 运行时,自检报告与「复制补丁说明」直接给出可执行命令(含 `--resources`,支持自定义安装目录);无需用户另装 Node |
|
package/client.js
CHANGED
|
@@ -103,7 +103,7 @@ window.__ModuleLoader__.load({
|
|
|
103
103
|
let openRightTabImpl = () => {}
|
|
104
104
|
|
|
105
105
|
/** Build identity, surfaced in the panel so a packaged app can be diagnosed. */
|
|
106
|
-
const BUILD = '0.6.
|
|
106
|
+
const BUILD = '0.6.5'
|
|
107
107
|
|
|
108
108
|
/** Where a re-applied instance finds the one it must take over from. */
|
|
109
109
|
const INSTANCE_KEY = '__DSH_CHAT_EMBED_INSTANCE__'
|
|
@@ -264,9 +264,20 @@ window.__ModuleLoader__.load({
|
|
|
264
264
|
'Experimental: the patch rewrites the app.asar of the desktop shell and needs write access to its install folder. A shell update replaces it, so the patch has to be applied again.',
|
|
265
265
|
persistCopy: 'Copy the patch instructions',
|
|
266
266
|
persistCheck: 'Check the patch setup',
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
267
|
+
persistCheckHint: 'Opens a new session and drafts the check for you',
|
|
268
|
+
patchPromptPlugin: 'Plugin',
|
|
269
|
+
patchPrompt:
|
|
270
|
+
'Check the DSH desktop shell patch that keeps the embedded chat logins across restarts, and tell me what to do.\n\nKnown so far:\n{facts}\n\nCall the dsh_chat_embed_patch_check tool to confirm the facts, then:\n1. say in a sentence or two what the situation is;\n2. if the patch is missing or was lost, give me the exact command to run;\n3. do not modify any file.',
|
|
271
|
+
patchYes: 'yes',
|
|
272
|
+
patchNo: 'no',
|
|
273
|
+
patchPresent: 'present',
|
|
274
|
+
patchMissing: 'missing (shell version mismatch)',
|
|
275
|
+
patchUnknown: 'unknown',
|
|
276
|
+
patchWritable: 'writable',
|
|
277
|
+
patchSiteLabel: 'patch site',
|
|
278
|
+
patchApplied: 'already patched',
|
|
279
|
+
patchCommandLabel: 'Patch command:',
|
|
280
|
+
patchDetail: 'Note',
|
|
270
281
|
copyDiag: 'Copy diagnostics',
|
|
271
282
|
copied: 'Copied',
|
|
272
283
|
copyFailed: 'Copy failed',
|
|
@@ -345,9 +356,20 @@ window.__ModuleLoader__.load({
|
|
|
345
356
|
persistRisk: '实验性功能:补丁会改写桌面壳的 app.asar,需要安装目录的写权限;桌面壳升级会覆盖补丁,需要重新打一次。',
|
|
346
357
|
persistCopy: '复制补丁说明',
|
|
347
358
|
persistCheck: '检查补丁配置',
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
359
|
+
persistCheckHint: '打开新会话,把检查内容写好,点发送即可',
|
|
360
|
+
patchPromptPlugin: '插件',
|
|
361
|
+
patchPrompt:
|
|
362
|
+
'帮我检查 DSH 桌面版「跨重启保持登录」的补丁状态,并告诉我该做什么。\n\n已知信息:\n{facts}\n\n请先调用 dsh_chat_embed_patch_check 工具核对上面的信息,然后:\n1. 用一两句话说明现在是什么情况;\n2. 如果补丁没打或已失效,给出我可以直接执行的命令;\n3. 不要修改任何文件。',
|
|
363
|
+
patchYes: '是',
|
|
364
|
+
patchNo: '否',
|
|
365
|
+
patchPresent: '存在',
|
|
366
|
+
patchMissing: '不存在(壳版本不匹配)',
|
|
367
|
+
patchUnknown: '未知',
|
|
368
|
+
patchWritable: '可写',
|
|
369
|
+
patchSiteLabel: '补丁点',
|
|
370
|
+
patchApplied: '已打补丁',
|
|
371
|
+
patchCommandLabel: '补丁命令:',
|
|
372
|
+
patchDetail: '说明',
|
|
351
373
|
copyDiag: '复制诊断信息',
|
|
352
374
|
copied: '已复制',
|
|
353
375
|
copyFailed: '复制失败',
|
|
@@ -2472,7 +2494,7 @@ window.__ModuleLoader__.load({
|
|
|
2472
2494
|
*
|
|
2473
2495
|
* @type {{ running: boolean, report: string }}
|
|
2474
2496
|
*/
|
|
2475
|
-
const patchCheck = { running: false
|
|
2497
|
+
const patchCheck = { running: false }
|
|
2476
2498
|
|
|
2477
2499
|
/** One-shot cache of the Host probe, so both actions read the same facts. */
|
|
2478
2500
|
const patchProbe = { loaded: false, value: undefined }
|
|
@@ -2510,69 +2532,77 @@ window.__ModuleLoader__.load({
|
|
|
2510
2532
|
return patchProbe.value
|
|
2511
2533
|
}
|
|
2512
2534
|
|
|
2513
|
-
|
|
2514
|
-
|
|
2535
|
+
/**
|
|
2536
|
+
* Hand the self-check to a Session instead of rendering it into the menu.
|
|
2537
|
+
*
|
|
2538
|
+
* A report inside the settings menu made the panel unusable: absolute paths
|
|
2539
|
+
* and a full command line stretched it across the surface, and the result was
|
|
2540
|
+
* a one-shot answer kept in panel state. A Session is the right place — the
|
|
2541
|
+
* agent can call the patch-check tool, read the reply and explain it, and the
|
|
2542
|
+
* user confirms by pressing send.
|
|
2543
|
+
*
|
|
2544
|
+
* @param {(key: string) => string} t
|
|
2545
|
+
* @returns {Promise<void>}
|
|
2546
|
+
*/
|
|
2547
|
+
async function openPatchCheckSession(t) {
|
|
2548
|
+
const workspace = typeof ctx.get === 'function' ? ctx.get('uiWorkspace') : undefined
|
|
2549
|
+
if (workspace === undefined || workspace === null || typeof workspace.startSession !== 'function') {
|
|
2550
|
+
// No Session service: fall back to the clipboard so the click still does
|
|
2551
|
+
// something useful instead of failing silently.
|
|
2552
|
+
void copyText(patchInstructions(t, patchProbe.value))
|
|
2553
|
+
return
|
|
2554
|
+
}
|
|
2515
2555
|
patchCheck.running = true
|
|
2516
2556
|
notify()
|
|
2517
|
-
let report = ''
|
|
2518
2557
|
try {
|
|
2519
|
-
patchProbe.loaded = false
|
|
2520
2558
|
const probe = await fetchProbe()
|
|
2521
|
-
|
|
2559
|
+
const prompt = buildPatchPrompt(t, probe)
|
|
2560
|
+
patchProbe.loaded = false
|
|
2561
|
+
workspace.startSession(undefined, { prompt, whenDraftExists: 'append' })
|
|
2522
2562
|
} catch (error) {
|
|
2523
|
-
|
|
2563
|
+
void copyText(patchInstructions(t, patchProbe.value) + '\n' + t('copyFailed') + ': ' + messageOf(error))
|
|
2524
2564
|
}
|
|
2525
2565
|
patchCheck.running = false
|
|
2526
|
-
patchCheck.report = report
|
|
2527
2566
|
notify()
|
|
2528
|
-
void copyText(report)
|
|
2529
2567
|
}
|
|
2530
2568
|
|
|
2531
2569
|
/**
|
|
2570
|
+
* The text dropped into the new Session's draft: what is already known, so
|
|
2571
|
+
* the agent starts from facts instead of asking the user to run something.
|
|
2572
|
+
*
|
|
2532
2573
|
* @param {(key: string) => string} t
|
|
2533
2574
|
* @param {any} probe
|
|
2534
2575
|
* @returns {string}
|
|
2535
2576
|
*/
|
|
2536
|
-
function
|
|
2537
|
-
const
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
lines.push('补丁命令:')
|
|
2564
|
-
lines.push(probe.patchCommand)
|
|
2577
|
+
function buildPatchPrompt(t, probe) {
|
|
2578
|
+
const facts = []
|
|
2579
|
+
facts.push(t('patchPromptPlugin') + ': dsh-chat-embed ' + BUILD)
|
|
2580
|
+
facts.push(t('persistLabel') + ': ' + persistenceText(t, persistenceState()))
|
|
2581
|
+
if (probe !== undefined && probe !== null) {
|
|
2582
|
+
const row = (label, value) => {
|
|
2583
|
+
if (value === undefined || value === null || value === '') return
|
|
2584
|
+
facts.push(label + ': ' + String(value))
|
|
2585
|
+
}
|
|
2586
|
+
row('resources', probe.resources)
|
|
2587
|
+
row('app.asar', probe.asar)
|
|
2588
|
+
row('app.asar.orig', probe.backup === true ? t('patchYes') : t('patchNo'))
|
|
2589
|
+
row(t('patchWritable'), probe.writable === true ? t('patchYes') : t('patchNo'))
|
|
2590
|
+
row(
|
|
2591
|
+
t('patchSiteLabel'),
|
|
2592
|
+
probe.patchSite === true ? t('patchPresent') : probe.patchSite === false ? t('patchMissing') : t('patchUnknown'),
|
|
2593
|
+
)
|
|
2594
|
+
row(
|
|
2595
|
+
t('patchApplied'),
|
|
2596
|
+
probe.patched === true ? t('patchYes') : probe.patched === false ? t('patchNo') : t('patchUnknown'),
|
|
2597
|
+
)
|
|
2598
|
+
if (typeof probe.patchCommand === 'string' && probe.patchCommand !== '') {
|
|
2599
|
+
facts.push('')
|
|
2600
|
+
facts.push(t('patchCommandLabel'))
|
|
2601
|
+
facts.push(probe.patchCommand)
|
|
2602
|
+
}
|
|
2603
|
+
if (typeof probe.detail === 'string' && probe.detail !== '') facts.push(t('patchDetail') + ': ' + probe.detail)
|
|
2565
2604
|
}
|
|
2566
|
-
return
|
|
2567
|
-
}
|
|
2568
|
-
|
|
2569
|
-
/**
|
|
2570
|
-
* Drop the one-shot self-check answer. Closing the settings menu calls this:
|
|
2571
|
-
* a report belongs to the click that asked for it, not to the panel state.
|
|
2572
|
-
*/
|
|
2573
|
-
function resetPatchCheck() {
|
|
2574
|
-
patchCheck.report = ''
|
|
2575
|
-
patchCheck.running = false
|
|
2605
|
+
return fill(t('patchPrompt'), { facts: facts.join('\n') })
|
|
2576
2606
|
}
|
|
2577
2607
|
|
|
2578
2608
|
/** @returns {{ state: string, detail: string, seats: any[] }} */
|
|
@@ -2785,13 +2815,15 @@ window.__ModuleLoader__.load({
|
|
|
2785
2815
|
'.dce-guest-layer[data-active="true"]{display:flex}',
|
|
2786
2816
|
'.dce-guest{display:inline-flex;flex:1 1 auto;width:100%;height:100%;border:0;background:transparent}',
|
|
2787
2817
|
'.dce-card{position:absolute;inset:0;z-index:2;display:flex;flex-direction:column;gap:10px;box-sizing:border-box;width:auto;max-width:460px;height:max-content;margin:auto;padding:20px 22px;border:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,.24));border-radius:12px;background:var(--dsw-alias-bg-layer-2,#fff);box-shadow:0 12px 32px rgba(0,0,0,.12);font-size:13px}',
|
|
2788
|
-
'.dce-card-title{font-size:14px;font-weight:600}',
|
|
2818
|
+
'.dce-card-title{display:flex;align-items:baseline;gap:8px;font-size:14px;font-weight:600}',
|
|
2789
2819
|
'.dce-card-body{color:var(--dsw-alias-label-secondary,#5f6368);line-height:1.6}',
|
|
2790
2820
|
'.dce-card-detail{font-family:var(--ds-font-family-code,ui-monospace,monospace);font-size:11px;color:var(--dsw-alias-label-tertiary,#9aa0a6);word-break:break-all}',
|
|
2791
2821
|
'.dce-card-actions{display:flex;gap:8px;justify-content:flex-end}',
|
|
2792
2822
|
'.dce-menu-scrim{position:absolute;inset:0;z-index:5}',
|
|
2793
2823
|
'.dce-menu{position:absolute;top:34px;right:6px;z-index:6;min-width:196px;max-width:min(420px,calc(100% - 12px));box-sizing:border-box;padding:6px;border:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,.24));border-radius:10px;background:var(--dsw-alias-bg-layer-2,#fff);box-shadow:0 12px 32px rgba(0,0,0,.18);display:flex;flex-direction:column;gap:1px}',
|
|
2794
|
-
'.dce-menu-title{font-size:11px;color:var(--dsw-alias-label-tertiary,#9aa0a6);padding:6px 8px 4px;letter-spacing:.04em}',
|
|
2824
|
+
'.dce-menu-title{display:flex;align-items:baseline;justify-content:space-between;gap:10px;font-size:11px;color:var(--dsw-alias-label-tertiary,#9aa0a6);padding:6px 8px 4px;letter-spacing:.04em}',
|
|
2825
|
+
'.dce-menu-version{flex:0 0 auto;font-size:10.5px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;letter-spacing:0;opacity:.8}',
|
|
2826
|
+
'.dce-card-version{margin-left:auto;font-size:11px;font-weight:400;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;color:var(--dsw-alias-label-tertiary,#9aa0a6);opacity:.8}',
|
|
2795
2827
|
'.dce-menu-row{display:flex;align-items:center;justify-content:space-between;gap:14px;padding:5px 8px;border-radius:6px;font-size:13px;color:var(--dsw-alias-label-primary,#1f2329)}',
|
|
2796
2828
|
'.dce-menu-row:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(127,127,127,.12))}',
|
|
2797
2829
|
'.dce-menu-label{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}',
|
|
@@ -2799,7 +2831,6 @@ window.__ModuleLoader__.load({
|
|
|
2799
2831
|
'.dce-menu-sep{margin:5px 0 3px;border-top:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,.24))}',
|
|
2800
2832
|
'.dce-menu-note{margin:4px 8px 6px;font-size:11.5px;line-height:1.5;color:var(--dsw-alias-label-tertiary,#9aa0a6);white-space:pre-wrap;overflow-wrap:anywhere;word-break:break-word;max-width:100%;box-sizing:border-box}',
|
|
2801
2833
|
'.dce-menu-note[data-persist-risk="true"]{color:var(--dsw-alias-label-warning,#c98b16)}',
|
|
2802
|
-
'.dce-menu-note[data-patch-report="true"]{max-height:180px;overflow:auto;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px}',
|
|
2803
2834
|
'.dce-menu-item{display:flex;align-items:center;justify-content:flex-start;gap:8px;width:100%;box-sizing:border-box;padding:6px 8px;border:0;border-radius:6px;background:transparent;color:inherit;font-size:13px;text-align:left;cursor:pointer}',
|
|
2804
2835
|
'.dce-menu-item:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(127,127,127,.12))}',
|
|
2805
2836
|
'.dce-right-title{display:inline-flex;align-items:center;gap:6px;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}',
|
|
@@ -3229,7 +3260,6 @@ window.__ModuleLoader__.load({
|
|
|
3229
3260
|
*/
|
|
3230
3261
|
const closeMenu = () => {
|
|
3231
3262
|
setMenuOpen(false)
|
|
3232
|
-
resetPatchCheck()
|
|
3233
3263
|
}
|
|
3234
3264
|
const [copyState, setCopyState] = React.useState('')
|
|
3235
3265
|
const barTimerRef = React.useRef(0)
|
|
@@ -3436,7 +3466,12 @@ window.__ModuleLoader__.load({
|
|
|
3436
3466
|
? h(
|
|
3437
3467
|
'div',
|
|
3438
3468
|
{ className: 'dce-menu', role: 'menu' },
|
|
3439
|
-
h(
|
|
3469
|
+
h(
|
|
3470
|
+
'div',
|
|
3471
|
+
{ className: 'dce-menu-title' },
|
|
3472
|
+
h('span', null, t('menuTitle')),
|
|
3473
|
+
h('span', { className: 'dce-menu-version', 'data-build': BUILD }, 'v' + BUILD),
|
|
3474
|
+
),
|
|
3440
3475
|
providersInOrder().map((entry) => {
|
|
3441
3476
|
const on = isEnabled(entry.id)
|
|
3442
3477
|
const lastOne = on && enabledIds.length <= 1
|
|
@@ -3488,21 +3523,28 @@ window.__ModuleLoader__.load({
|
|
|
3488
3523
|
h('span', { className: 'dce-menu-label' }, t('persistCopy')),
|
|
3489
3524
|
),
|
|
3490
3525
|
h(
|
|
3491
|
-
'
|
|
3492
|
-
{
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
'
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3526
|
+
'div',
|
|
3527
|
+
{ className: 'dce-menu-row' },
|
|
3528
|
+
h('span', { className: 'dce-menu-label', title: t('persistCheckHint') }, t('persistCheck')),
|
|
3529
|
+
h(
|
|
3530
|
+
'button',
|
|
3531
|
+
{
|
|
3532
|
+
type: 'button',
|
|
3533
|
+
role: 'switch',
|
|
3534
|
+
className: 'dce-switch',
|
|
3535
|
+
'data-on': patchCheck.running ? 'true' : 'false',
|
|
3536
|
+
'aria-checked': patchCheck.running ? 'true' : 'false',
|
|
3537
|
+
'data-patch-check': 'true',
|
|
3538
|
+
'aria-label': t('persistCheck'),
|
|
3539
|
+
title: t('persistCheckHint'),
|
|
3540
|
+
disabled: patchCheck.running,
|
|
3541
|
+
onClick: () => {
|
|
3542
|
+
closeMenu()
|
|
3543
|
+
void openPatchCheckSession(t)
|
|
3544
|
+
},
|
|
3499
3545
|
},
|
|
3500
|
-
|
|
3501
|
-
h('span', { className: 'dce-menu-label' }, patchCheck.running ? t('persistCheckRunning') : t('persistCheck')),
|
|
3546
|
+
),
|
|
3502
3547
|
),
|
|
3503
|
-
patchCheck.report === ''
|
|
3504
|
-
? null
|
|
3505
|
-
: h('div', { className: 'dce-menu-note', 'data-patch-report': 'true' }, patchCheck.report),
|
|
3506
3548
|
h(
|
|
3507
3549
|
'button',
|
|
3508
3550
|
{
|
|
@@ -3599,7 +3641,12 @@ window.__ModuleLoader__.load({
|
|
|
3599
3641
|
? h(
|
|
3600
3642
|
'div',
|
|
3601
3643
|
{ className: 'dce-card', role: 'dialog' },
|
|
3602
|
-
h(
|
|
3644
|
+
h(
|
|
3645
|
+
'div',
|
|
3646
|
+
{ className: 'dce-card-title' },
|
|
3647
|
+
h('span', null, t('diagTitle')),
|
|
3648
|
+
h('span', { className: 'dce-card-version', 'data-build': BUILD }, 'v' + BUILD),
|
|
3649
|
+
),
|
|
3603
3650
|
h(
|
|
3604
3651
|
'div',
|
|
3605
3652
|
{ className: 'dce-card-detail' },
|
|
@@ -3779,10 +3826,9 @@ window.__ModuleLoader__.load({
|
|
|
3779
3826
|
patchInstructions,
|
|
3780
3827
|
fetchProbe,
|
|
3781
3828
|
patchProbe,
|
|
3782
|
-
formatPatchReport,
|
|
3783
|
-
runPatchCheck,
|
|
3784
3829
|
patchCheck,
|
|
3785
|
-
|
|
3830
|
+
openPatchCheckSession,
|
|
3831
|
+
buildPatchPrompt,
|
|
3786
3832
|
setLogging,
|
|
3787
3833
|
clearLog,
|
|
3788
3834
|
readLogEntries,
|
package/docs/architecture.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
本文记录 `client.js` 里每一条**反直觉**的实现决策、支撑它的证据,以及升级桌面壳时必须重新验证的清单。
|
|
4
4
|
|
|
5
5
|
目标运行环境:**`@deepseek-ai/dsh-desktop 0.2.2-rc.1`(Electron / Chromium)**。
|
|
6
|
-
当前实现:**v0.6.
|
|
6
|
+
当前实现:**v0.6.5**(`client.js` 约 3985 行,85 项契约测试)。
|
|
7
7
|
|
|
8
8
|
> **架构一句话**:聊天页直接渲染在**席位的布局盒**里(`.dce-panel.dce-inline`,`width/height:100%`),几何完全交给 CSS;席位只有两个 —— 中央主面板(`main`)和右栏聊天 tab —— 二者互斥,**哪边点哪边接管**,切换时 guest 重建一次并回到上次会话。
|
|
9
9
|
|
|
@@ -331,6 +331,8 @@ Host 半侧另有两条只读通道,专门用来回答"为什么重启后要
|
|
|
331
331
|
| 通道 | 内容 |
|
|
332
332
|
| --- | --- |
|
|
333
333
|
| `GET /dsh-chat-embed/patch-check` | 安装目录、`app.asar` 是否可写、是否已打补丁、补丁点是否还存在、备份与待替换文件、大小与内容哈希、**可执行的补丁命令**(含自带 Node 运行时路径与 `--resources`) |
|
|
334
|
+
|
|
335
|
+
客户端拿到探针结果后不再内联渲染,而是通过 `uiWorkspace.startSession(undefined, { prompt, whenDraftExists: 'append' })` 打开一个新会话,把事实与授权边界写进草稿;用户点发送即可。这与 DSH 自身「创建你的插件」入口用的是同一个 API,`whenDraftExists` 的取值语义在 `dsh-client-ui-conversation` 的 `requestDraftInitialization` 里定义(`replace` / `keep` / `append`)。
|
|
334
336
|
| agent 工具 `dsh_chat_embed_patch_check` | 同一份读数,供模型直接查询(只读,无需审批) |
|
|
335
337
|
|
|
336
338
|
> 读取 `app.asar` 必须走 Electron 的 `original-fs`:被补丁过的 `node:fs` 会把以 `.asar` 结尾的路径当成目录,`readFileSync` 直接 ENOENT。探针优先用 `original-fs`,普通 Node 下回落到 `node:fs`。
|
package/docs/publishing.md
CHANGED
|
@@ -5,13 +5,13 @@
|
|
|
5
5
|
## 版本与提交约定
|
|
6
6
|
|
|
7
7
|
- 同时改 `package.json` 的 `version` 与 `client.js` 的 `BUILD`,两者必须一致;
|
|
8
|
-
- 门禁必须绿:`npm run check`(`verify-bundle` +
|
|
8
|
+
- 门禁必须绿:`npm run check`(`verify-bundle` + 85 项契约测试);
|
|
9
9
|
- 代码注释/提交信息英文,文档中文。
|
|
10
10
|
|
|
11
11
|
## 发版步骤
|
|
12
12
|
|
|
13
13
|
```powershell
|
|
14
|
-
npm run check # 门禁:verify-bundle +
|
|
14
|
+
npm run check # 门禁:verify-bundle + 85 项测试(prepublishOnly 也会跑)
|
|
15
15
|
npm run pack:dist # 产出 dist\dsh-chat-embed-<版本>.tgz
|
|
16
16
|
gh release create v0.5.0 dist\dsh-chat-embed-0.5.0.tgz --title "v0.5.0" --notes-file dist\release-notes.md
|
|
17
17
|
```
|
|
@@ -31,8 +31,8 @@ Get-FileHash $env:TEMP\check\dsh-chat-embed-0.5.0.tgz -Algorithm SHA256
|
|
|
31
31
|
| --- | --- |
|
|
32
32
|
| 仓库 | <https://github.com/poluce/dsh-chat-embed> |
|
|
33
33
|
| Release | <https://github.com/poluce/dsh-chat-embed/releases> |
|
|
34
|
-
| 分发包 | `dsh-chat-embed-0.6.
|
|
35
|
-
| npm | `dsh-chat-embed@0.6.
|
|
34
|
+
| 分发包 | `dsh-chat-embed-0.6.5.tgz`(16 个文件,约 70 kB) |
|
|
35
|
+
| npm | `dsh-chat-embed@0.6.5`(<https://www.npmjs.com/package/dsh-chat-embed>,维护者 `poluce_auto`) |
|
|
36
36
|
| 商店图标 | `assets/icon-180.png`(180×180 PNG) |
|
|
37
37
|
| 商店截图 | `assets/screenshots/` + 仓库根 `screenshots.json`(6 张) |
|
|
38
38
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-chat-embed",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.5",
|
|
4
4
|
"author": "poluce <fishsummer@126.com>",
|
|
5
5
|
"description": "Multi-tab AI chat embed for the DeepSeek Harness desktop app: DeepSeek, 豆包, ChatGPT, MiniMax, Kimi, Gemini and Grok, each signed in separately.",
|
|
6
6
|
"type": "module",
|