@openvulcan/dsh-vulcancode 0.4.0 → 0.4.4
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 +103 -38
- package/cordis.patch.yml +4 -0
- package/node_modules/@openvulcan/dsh-decision-guard/lib/client.js +133 -38
- package/node_modules/@openvulcan/dsh-decision-guard/package.json +2 -2
- package/node_modules/@openvulcan/dsh-vulcan-account/lib/client.js +55 -47
- package/node_modules/@openvulcan/dsh-vulcan-account/package.json +2 -2
- package/node_modules/@openvulcan/dsh-vulcan-answers/lib/client.js +30 -30
- package/node_modules/@openvulcan/dsh-vulcan-answers/lib/committed-fiber.d.ts +18 -0
- package/node_modules/@openvulcan/dsh-vulcan-answers/lib/committed-fiber.js +62 -0
- package/node_modules/@openvulcan/dsh-vulcan-answers/package.json +1 -1
- package/node_modules/@openvulcan/dsh-vulcan-luaskills/lib/client.js +14 -5
- package/node_modules/@openvulcan/dsh-vulcan-luaskills/package.json +1 -1
- package/node_modules/@openvulcan/dsh-vulcan-memory/assets/markdown-memory-guide.md +1 -0
- package/node_modules/@openvulcan/dsh-vulcan-memory/assets/memory-curator/SKILL.md +1 -1
- package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/errors.d.ts +7 -0
- package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/errors.js +11 -0
- package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/index.d.ts +1 -1
- package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/index.js +1 -1
- package/node_modules/@openvulcan/dsh-vulcan-memory/lib/markdown/operations.js +22 -3
- package/node_modules/@openvulcan/dsh-vulcan-memory/package.json +1 -1
- package/node_modules/@openvulcan/dsh-vulcan-reasoning/LICENSE +21 -0
- package/node_modules/@openvulcan/dsh-vulcan-reasoning/lib/client.js +509 -0
- package/node_modules/@openvulcan/dsh-vulcan-reasoning/lib/index.d.ts +6 -0
- package/node_modules/@openvulcan/dsh-vulcan-reasoning/lib/index.js +8 -0
- package/node_modules/@openvulcan/dsh-vulcan-reasoning/package.json +42 -0
- package/node_modules/@openvulcan/dsh-vulcan-style/lib/client.js +67 -3
- package/node_modules/@openvulcan/dsh-vulcan-style/package.json +1 -1
- package/node_modules/@openvulcan/dsh-vulcan-voice-input/LICENSE +21 -0
- package/node_modules/@openvulcan/dsh-vulcan-voice-input/lib/client.js +1567 -0
- package/node_modules/@openvulcan/dsh-vulcan-voice-input/lib/cloud.d.ts +39 -0
- package/node_modules/@openvulcan/dsh-vulcan-voice-input/lib/cloud.js +187 -0
- package/node_modules/@openvulcan/dsh-vulcan-voice-input/lib/contract.d.ts +58 -0
- package/node_modules/@openvulcan/dsh-vulcan-voice-input/lib/contract.js +1 -0
- package/node_modules/@openvulcan/dsh-vulcan-voice-input/lib/index.d.ts +49 -0
- package/node_modules/@openvulcan/dsh-vulcan-voice-input/lib/index.js +204 -0
- package/node_modules/@openvulcan/dsh-vulcan-voice-input/package.json +46 -0
- package/node_modules/@openvulcan/openvulcan-account/package.json +1 -1
- package/node_modules/@openvulcan/vulcan-decision/package.json +2 -2
- package/package.json +14 -10
package/README.md
CHANGED
|
@@ -10,62 +10,127 @@
|
|
|
10
10
|
**把 VulcanCode 的体验带进 DeepSeek Harness(dsh)**
|
|
11
11
|
|
|
12
12
|
[](LICENSE)
|
|
13
|
+
[](https://www.npmjs.com/package/@openvulcan/dsh-vulcancode)
|
|
13
14
|
|
|
14
15
|
**简体中文** | [English](README.en.md)
|
|
15
16
|
|
|
17
|
+
[特色功能](#特色功能) · [界面截图](#界面截图) · [架构与包结构](#架构与包结构) · [快速开始](#快速开始) · [开发](#开发)
|
|
18
|
+
|
|
16
19
|
</div>
|
|
17
20
|
|
|
18
21
|
---
|
|
19
22
|
|
|
20
23
|
## 关于项目
|
|
21
24
|
|
|
22
|
-
dsh-vulcancode 是 DeepSeek Harness(dsh
|
|
23
|
-
适配 dsh `0.2.1-rc.2` 及以上、`0.3.0` 以下:
|
|
24
|
-
|
|
25
|
-
```sh
|
|
26
|
-
dsh plugin add @openvulcan/dsh-vulcancode
|
|
27
|
-
```
|
|
25
|
+
dsh-vulcancode 是 DeepSeek Harness(dsh)的社区插件套件。安装一个包,即可在 dsh 中使用 VulcanCode 的界面风格、交互式回答、自动审批、LuaSkills 和长期记忆,并为 pi-ai 模型配置思考级别。
|
|
28
26
|
|
|
29
27
|
本项目与 DeepSeek 无关联,dsh 是 DeepSeek 的产品。
|
|
30
28
|
|
|
31
|
-
|
|
32
|
-
| --- | --- |
|
|
33
|
-
| OpenVulcan 账号 | 登录一次,套件内各插件共用;凭据保存在 dsh 的凭据存储中。设置位于「设置 › OpenVulcan 账号」 |
|
|
34
|
-
| 自动审批(Vulcan Decision) | 每次工具调用前由专用判定模型判断放行、需确认或拦下;未放行的可交复核 LLM,再询问你或拒绝 |
|
|
35
|
-
| 交互式回答 | 模型可以用 VulcanCode 的 `ui-*` 组件作答,在对话中渲染;启用后默认开启,可按会话关闭 |
|
|
36
|
-
| LuaSkills | 技能以工具形式提供给 AI,默认技能 codekit(代码导航);运行库约 190 MB,在「设置 › LuaSkills」中按需安装,装好即自动接入。侧栏的 LuaSkills 页面管理已装技能并连接 LuaSkills Hub |
|
|
37
|
-
| Vulcan 界面风格 | VulcanCode 的主题、直角与配色 |
|
|
38
|
-
| 长期记忆 | VulcanCode 的 Markdown 长期记忆:AI 用 `memory_outline`、`memory_search`、`memory_write` 维护用户画像、空间记忆与项目记忆,每次会话开始时注入一份快照。侧栏的「长期记忆」页面可编辑、调整注入预算,并导入导出 |
|
|
29
|
+
## 特色功能
|
|
39
30
|
|
|
40
|
-
|
|
31
|
+
- **桌面工作台**采用 VulcanCode 配色、可独立开关的直角外观,以及加宽后能充分利用内容区的设置页面。
|
|
32
|
+
- **结果可交互**,模型可在对话中用图表、流程图和决策卡片呈现结果。卡片里的选择可以带回输入框继续讨论。
|
|
33
|
+
- **工具调用可审查**,Vulcan Decision 在执行前使用云端或本机模型判定,并支持配置复核和人工确认。
|
|
34
|
+
- **技能与经验可复用**,LuaSkills 把技能发布为 AI 可调用的工具,Markdown 长期记忆保留用户、空间和项目经验。
|
|
35
|
+
- **模型可单独配置思考级别**,pi-ai 模型的可选级别和接口传输值可按供应商、按模型设置。
|
|
36
|
+
- **语音输入**使用 OpenVulcan 账号所属站点的免费转写服务,把口述内容插入对话草稿。
|
|
41
37
|
|
|
42
|
-
##
|
|
38
|
+
## 界面截图
|
|
43
39
|
|
|
44
|
-
|
|
45
|
-
2. 打开「设置 › 自动审批」,在权限选择器中选择“自动审查”,或把接入方式改为“独立开关”。不登录也可以改用本机
|
|
46
|
-
模型判定。
|
|
47
|
-
3. 需要 LuaSkills 时,打开「设置 › LuaSkills」点击安装运行库,进度显示在页面上,完成后技能工具自动出现。
|
|
48
|
-
4. 交互式回答、界面风格和长期记忆安装即生效,可在各自的设置或侧栏页面中调整。
|
|
40
|
+
### 暗色主题 · 工具分类卡片
|
|
49
41
|
|
|
50
|
-
|
|
42
|
+

|
|
43
|
+
|
|
44
|
+
### 指挥舱主题 · 记忆工具验证
|
|
45
|
+
|
|
46
|
+

|
|
47
|
+
|
|
48
|
+
## 架构与包结构
|
|
49
|
+
|
|
50
|
+
dsh 通过套件的 `cordis.patch.yml` 装载八个插件。自动审批和语音输入通过 `openvulcanAccount` 获取云端服务所需的凭据。账号插件和判定引擎复用同一账号库。发布包随附工作区依赖,用户只需安装套件。
|
|
51
|
+
|
|
52
|
+
### 套件入口
|
|
53
|
+
|
|
54
|
+
#### `@openvulcan/dsh-vulcancode`
|
|
55
|
+
|
|
56
|
+
套件通过 `dsh.bundle.patch` 声明插件组。dsh 加载各子插件的 `apply(ctx)` 入口,账号插件则导出 Cordis 服务类。安装后可在插件管理页查看套件及其组件。源码在 [packages/dsh-vulcancode](packages/dsh-vulcancode)。
|
|
57
|
+
|
|
58
|
+
### dsh 插件
|
|
59
|
+
|
|
60
|
+
#### `@openvulcan/dsh-vulcan-account`
|
|
61
|
+
|
|
62
|
+
在「设置 › OpenVulcan 账号」选择国际站或中国站,浏览器授权后即可在 dsh 内查看账号状态或退出登录。插件将凭据保存到 dsh 的凭据存储,并通过 `openvulcanAccount` 向云端判定等集成提供账号服务。源码在 [packages/dsh-vulcan-account](packages/dsh-vulcan-account)。
|
|
63
|
+
|
|
64
|
+
#### `@openvulcan/dsh-decision-guard`
|
|
65
|
+
|
|
66
|
+
在「设置 › 自动审批」选择与权限预设的接入方式,配置云端或本机判定、复核 LLM 和询问规则,并查看审批记录。判定模型给出放行、需确认或拦截三类结果。未放行的调用按配置进入复核、询问或拒绝。详见[自动审批的边界](#自动审批的边界)和[插件说明](packages/dsh-decision-guard/README.md)。
|
|
67
|
+
|
|
68
|
+
#### `@openvulcan/dsh-vulcan-answers`
|
|
69
|
+
|
|
70
|
+
在对话中渲染模型输出的 `ui-*` 代码围栏,例如对比表、图表、流程图、时序图和决策卡片。决策选项可以插入输入框或发送。对话默认使用交互模式,输入框里的「回答」可以按会话切换为标准 Markdown,从下次请求起生效。源码在 [packages/dsh-vulcan-answers](packages/dsh-vulcan-answers)。
|
|
71
|
+
|
|
72
|
+
#### `@openvulcan/dsh-vulcan-luaskills`
|
|
73
|
+
|
|
74
|
+
在「设置 › LuaSkills」按需安装约 190 MB 的运行库和默认代码导航技能 CodeKit。安装后引擎和工具自动接入,可以调整 VM 池、缓存、受管 Python / Node 和网络设置。侧栏「LuaSkills」可启用、停用、更新或卸载技能,也可从 LuaSkills Hub 搜索和安装新技能。源码在 [packages/dsh-vulcan-luaskills](packages/dsh-vulcan-luaskills)。
|
|
75
|
+
|
|
76
|
+
#### `@openvulcan/dsh-vulcan-memory`
|
|
77
|
+
|
|
78
|
+
以 Markdown 保存用户画像、空间记忆和项目记忆。向模型提供 `memory_outline`、`memory_search`、`memory_write` 与 `memory-curator` 技能,并在会话开始时按预算注入快照。侧栏「长期记忆」支持编辑、调整注入预算和导入导出。当前可用的是 Markdown 模式。源码在 [packages/dsh-vulcan-memory](packages/dsh-vulcan-memory)。
|
|
79
|
+
|
|
80
|
+
#### `@openvulcan/dsh-vulcan-reasoning`
|
|
51
81
|
|
|
52
|
-
|
|
53
|
-
- 任何错误都不会被当作放行;无人值守的会话从不询问你,复核未放行即拒绝。
|
|
54
|
-
- 自动审批只降低事故概率,不保证拒绝一切风险;请同时使用沙箱、Git 与备份。
|
|
82
|
+
在「设置 › 模型」展开已配置的 pi-ai 提供商卡片中的「自定义思考级别」。按模型选择跟随目录、关闭思考,或自定义对话中可选的级别及接口传输值。保存后在对话模型选择器中选用,传输值以对应模型接口支持的参数为准。源码在 [packages/dsh-vulcan-reasoning](packages/dsh-vulcan-reasoning)。
|
|
55
83
|
|
|
56
|
-
|
|
84
|
+
#### `@openvulcan/dsh-vulcan-style`
|
|
85
|
+
|
|
86
|
+
在「设置 › 通用设置」的「Vulcan 界面风格」选择跟随系统、亮色、暗色、指挥舱或 dsh 自带主题。直角开关可与任意主题搭配,覆盖 Windows 桌面内容区等界面边角。设置布局为模型目录、内置插件和 LuaSkills 等页面留出更多可用宽度。源码在 [packages/dsh-vulcan-style](packages/dsh-vulcan-style)。
|
|
87
|
+
|
|
88
|
+
#### `@openvulcan/dsh-vulcan-voice-input`
|
|
89
|
+
|
|
90
|
+
登录 OpenVulcan 后,将鼠标移到对话输入框的麦克风按钮,可选择所属站点发布的免费转写模型、优先语言、麦克风和录音快捷键。可在「设置 › OpenVulcan 语音输入」测试麦克风和查看服务状态。录音快捷键支持单独绑定右 Ctrl 等左右修饰键。按住麦克风按钮或在输入框内按住所设快捷键说话,松开后转写内容会回填当前草稿。连续录满 120 秒会自动停止并转写。源码在 [packages/dsh-vulcan-voice-input](packages/dsh-vulcan-voice-input)。
|
|
91
|
+
|
|
92
|
+
### 共享库与命令行
|
|
93
|
+
|
|
94
|
+
#### `@openvulcan/vulcan-decision`
|
|
95
|
+
|
|
96
|
+
为宿主提供 `allow`、`review` 和 `block` 判定,支持 OpenVulcan 云端端点和按需安装的本机运行包。随附的 `vulcan-decision` CLI 可用于探测云端、检查硬件、管理本机运行包和模型,以及对单次工具调用执行判定。详见[判定引擎与命令行说明](packages/vulcan-decision/README.md)。
|
|
97
|
+
|
|
98
|
+
#### `@openvulcan/openvulcan-account`
|
|
99
|
+
|
|
100
|
+
封装设备授权、凭据刷新、账号资料查询和设备注销,并定义 `CredentialStore` 存储接口。dsh 插件接入宿主凭据存储,独立 CLI 可使用库内的文件存储实现。源码在 [packages/openvulcan-account](packages/openvulcan-account)。
|
|
101
|
+
|
|
102
|
+
## 快速开始
|
|
103
|
+
|
|
104
|
+
套件声明兼容 dsh `>=0.2.1-rc.2 <0.3.0`。
|
|
105
|
+
|
|
106
|
+
### 桌面版
|
|
107
|
+
|
|
108
|
+
在 dsh 侧栏打开「插件」,点击「添加插件」,在安装框中粘贴以下 npm 包名并安装。
|
|
109
|
+
|
|
110
|
+
```text
|
|
111
|
+
@openvulcan/dsh-vulcancode
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### 网页版
|
|
115
|
+
|
|
116
|
+
```sh
|
|
117
|
+
dsh plugin --profile web add @openvulcan/dsh-vulcancode
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### 使用
|
|
121
|
+
|
|
122
|
+
1. 安装后重启 dsh。使用云端判定时,在「设置 › OpenVulcan 账号」完成浏览器授权。选用本机判定可以跳过账号登录。
|
|
123
|
+
2. 打开「设置 › 自动审批」配置判定方式。在权限选择器中选「自动审查」可对当前会话启用,该预设使用完全权限。改用「独立开关」会为所有会话叠加判定,并保留已有权限预设的文件沙箱。
|
|
124
|
+
3. 需要技能工具时,在「设置 › LuaSkills」安装运行库。进度显示在页面上,完成后技能工具自动接入。
|
|
125
|
+
4. 界面风格、交互式回答和 Markdown 长期记忆默认启用。思考级别可以在配置好 pi-ai 提供商后按模型调整。
|
|
126
|
+
|
|
127
|
+
> **我们承诺云端命令判定模型永久免费提供。**(2026 年 10 月 27 日)
|
|
128
|
+
|
|
129
|
+
## 自动审批的边界
|
|
57
130
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
| `@openvulcan/dsh-vulcan-account` | OpenVulcan 账号插件,向其他插件提供 `openvulcanAccount` 服务 |
|
|
62
|
-
| `@openvulcan/dsh-decision-guard` | 自动审批插件 |
|
|
63
|
-
| `@openvulcan/dsh-vulcan-answers` | 交互式回答插件 |
|
|
64
|
-
| `@openvulcan/dsh-vulcan-luaskills` | LuaSkills 插件:运行库安装、技能工具、VM 设置与 Hub |
|
|
65
|
-
| `@openvulcan/dsh-vulcan-style` | 界面风格插件 |
|
|
66
|
-
| `@openvulcan/dsh-vulcan-memory` | 长期记忆插件:记忆工具、会话快照注入、memory-curator 技能与记忆页面 |
|
|
67
|
-
| `@openvulcan/vulcan-decision` | 判定引擎与命令行 `vulcan-decision` |
|
|
68
|
-
| `@openvulcan/openvulcan-account` | OpenVulcan 账号库:登录、刷新与凭据存储接口 |
|
|
131
|
+
- 自动审批可以降低 AI 误执行命令的风险。AI 刻意绕过时,仍可能先写脚本再运行它。
|
|
132
|
+
- 任何错误都不会被当作放行。无人值守时,需要复核的调用只有在复核明确放行后才会执行。
|
|
133
|
+
- 请同时使用沙箱、Git 与备份。
|
|
69
134
|
|
|
70
135
|
## 开发
|
|
71
136
|
|
|
@@ -76,7 +141,7 @@ pnpm run test
|
|
|
76
141
|
pnpm run pack:dsh # 生成 dist/openvulcan-dsh-vulcancode-<版本>.tgz
|
|
77
142
|
```
|
|
78
143
|
|
|
79
|
-
|
|
144
|
+
在网页版配置中安装本地包时,执行 `dsh plugin --profile web add ./dist/openvulcan-dsh-vulcancode-<版本>.tgz`。
|
|
80
145
|
|
|
81
146
|
## 致谢
|
|
82
147
|
|
package/cordis.patch.yml
CHANGED
|
@@ -305,6 +305,10 @@ window.__ModuleLoader__.load({
|
|
|
305
305
|
'conflict.failed': '停用失败:',
|
|
306
306
|
'page.title': '自动审批',
|
|
307
307
|
'page.lead': 'AI 每次调用工具前,先由 Vulcan Decision 判定模型判断:安全的直接放行,拿不准的交给复核 LLM 或由你确认,危险的拦截。任何错误都不会被当作放行。',
|
|
308
|
+
'page.sections': '自动审批设置分区',
|
|
309
|
+
'page.section.engine': '判定引擎',
|
|
310
|
+
'page.section.policy': '复核与规则',
|
|
311
|
+
'page.section.records': '判定记录',
|
|
308
312
|
'loading': '读取中…',
|
|
309
313
|
'saveFailed': '保存失败:{error}',
|
|
310
314
|
'readOnly': '当前配置为只读,无法修改。',
|
|
@@ -319,6 +323,7 @@ window.__ModuleLoader__.load({
|
|
|
319
323
|
'status.auto.standalone': '独立开关方式不使用该预设,所有会话都受判定',
|
|
320
324
|
'status.decision.cloud': 'OpenVulcan 云端 · {model}',
|
|
321
325
|
'status.decision.cloudNoModel': 'OpenVulcan 云端 · 没有可用的模型',
|
|
326
|
+
'status.decision.cloudLoadFailed': 'OpenVulcan 云端 · 无法读取判定模型',
|
|
322
327
|
'status.decision.signedOut': 'OpenVulcan 云端 · 未登录,受管控的调用会询问你',
|
|
323
328
|
'status.decision.local': '本机 · {pack} 运行 {model}',
|
|
324
329
|
'status.decision.localMissing': '本机 · 尚未选择运行包与模型,受管控的调用会询问你',
|
|
@@ -593,8 +598,6 @@ window.__ModuleLoader__.load({
|
|
|
593
598
|
'records.enabled': '记录判定',
|
|
594
599
|
'records.enabledHint': '保存判定模型或复核参与的每次决定,含原始命令与路径,只保存在这台电脑上。关闭后不再写入新记录,已有记录保留到你清除为止。',
|
|
595
600
|
'records.entryHint': '查看判定模型的决定,标记判错的结果并导出为训练样本。',
|
|
596
|
-
'records.open': '查看记录',
|
|
597
|
-
'records.back': '返回自动审批',
|
|
598
601
|
'records.paused': '记录判定已关闭,不会写入新记录。',
|
|
599
602
|
'records.hint': '保留最近 {records} 条、最长 {days} 天的记录。记录包含原始命令与路径,只保存在这台电脑上。',
|
|
600
603
|
'records.count': '{count} 条记录',
|
|
@@ -660,6 +663,10 @@ window.__ModuleLoader__.load({
|
|
|
660
663
|
'conflict.failed': 'Could not disable: ',
|
|
661
664
|
'page.title': 'Auto approval',
|
|
662
665
|
'page.lead': 'Before the AI runs a tool, the Vulcan Decision model judges the call: safe calls run, uncertain ones go to the review LLM or to you, dangerous ones are blocked. No error ever counts as an allow.',
|
|
666
|
+
'page.sections': 'Auto approval settings sections',
|
|
667
|
+
'page.section.engine': 'Decision engine',
|
|
668
|
+
'page.section.policy': 'Review and rules',
|
|
669
|
+
'page.section.records': 'Decision records',
|
|
663
670
|
'loading': 'Loading…',
|
|
664
671
|
'saveFailed': 'Could not save: {error}',
|
|
665
672
|
'readOnly': 'The configuration is read-only.',
|
|
@@ -674,6 +681,7 @@ window.__ModuleLoader__.load({
|
|
|
674
681
|
'status.auto.standalone': 'The standalone switch does not use the preset; every session is judged',
|
|
675
682
|
'status.decision.cloud': 'OpenVulcan cloud · {model}',
|
|
676
683
|
'status.decision.cloudNoModel': 'OpenVulcan cloud · no model available',
|
|
684
|
+
'status.decision.cloudLoadFailed': 'OpenVulcan cloud · could not load decision models',
|
|
677
685
|
'status.decision.signedOut': 'OpenVulcan cloud · not signed in; governed calls ask you',
|
|
678
686
|
'status.decision.local': 'This machine · {pack} runs {model}',
|
|
679
687
|
'status.decision.localMissing': 'This machine · no runtime pack and model chosen; governed calls ask you',
|
|
@@ -948,8 +956,6 @@ window.__ModuleLoader__.load({
|
|
|
948
956
|
'records.enabled': 'Record decisions',
|
|
949
957
|
'records.enabledHint': 'Keep every decision the decision model or the review took part in, with the original commands and paths, on this computer only. Turning it off stops new records; existing ones stay until you clear them.',
|
|
950
958
|
'records.entryHint': 'See the decision model\'s answers, mark wrong ones and export them as training samples.',
|
|
951
|
-
'records.open': 'View records',
|
|
952
|
-
'records.back': 'Back to auto approval',
|
|
953
959
|
'records.paused': 'Recording is off, so no new records are written.',
|
|
954
960
|
'records.hint': 'Keeps the latest {records} records for at most {days} days. Records hold the original commands and paths, on this computer only.',
|
|
955
961
|
'records.count': '{count} records',
|
|
@@ -1072,26 +1078,45 @@ window.__ModuleLoader__.load({
|
|
|
1072
1078
|
ctx.effect(() => ctx.locale.register(i18n_1.NAMESPACE, { zh: i18n_1.zh, en: i18n_1.en }), 'vulcan-decision-guard: dictionaries');
|
|
1073
1079
|
ctx.effect(() => {
|
|
1074
1080
|
let accountKey;
|
|
1081
|
+
// A reconnect can keep the same authorization ID while replacing the backend.
|
|
1082
|
+
// 重连可能更换宿主,但仍保留相同的授权标识。
|
|
1083
|
+
let refreshCloud = true;
|
|
1084
|
+
/**
|
|
1085
|
+
* Invalidate account-scoped data and read the new Host state after reconnect or plugin changes.
|
|
1086
|
+
* 重连或插件变更后清除账号相关数据,并读取新的宿主状态。
|
|
1087
|
+
*/
|
|
1075
1088
|
const refreshAll = () => {
|
|
1089
|
+
refreshCloud = true;
|
|
1090
|
+
services.panel.reset();
|
|
1091
|
+
services.cloudModels.reset();
|
|
1092
|
+
services.endpoints.reset();
|
|
1076
1093
|
services.autoReview.refresh();
|
|
1077
1094
|
services.autoListed.refresh();
|
|
1078
1095
|
services.panel.refresh();
|
|
1079
1096
|
};
|
|
1080
1097
|
const stops = [
|
|
1081
|
-
ctx.remote.$on('plugin-manager/changed',
|
|
1098
|
+
ctx.remote.$on('plugin-manager/changed', refreshAll),
|
|
1082
1099
|
ctx.remote.$on('permission-presets/catalog-changed', () => { services.autoListed.refresh(); }),
|
|
1083
1100
|
ctx.on('connection/reset', refreshAll),
|
|
1084
|
-
//
|
|
1101
|
+
// Load the model list when the first signed-in account arrives, without opening any UI.
|
|
1102
|
+
// 首次确认账号已登录时主动加载模型目录,无需打开界面。
|
|
1085
1103
|
services.panel.subscribe(() => {
|
|
1086
1104
|
const account = services.panel.getSnapshot()?.account;
|
|
1087
1105
|
if (account === undefined)
|
|
1088
1106
|
return;
|
|
1089
|
-
const key = account.signedIn ? account.authorizationId ?? '' : null;
|
|
1090
|
-
if (
|
|
1107
|
+
const key = account.signedIn ? `${account.site ?? ''}:${account.authorizationId ?? ''}` : null;
|
|
1108
|
+
if (!refreshCloud && key === accountKey)
|
|
1109
|
+
return;
|
|
1110
|
+
const hadAccount = accountKey !== undefined;
|
|
1111
|
+
accountKey = key;
|
|
1112
|
+
refreshCloud = false;
|
|
1113
|
+
services.cloudModels.reset();
|
|
1114
|
+
services.endpoints.reset();
|
|
1115
|
+
if (account.signedIn) {
|
|
1091
1116
|
services.cloudModels.refresh();
|
|
1092
|
-
|
|
1117
|
+
if (hadAccount)
|
|
1118
|
+
services.endpoints.refresh();
|
|
1093
1119
|
}
|
|
1094
|
-
accountKey = key;
|
|
1095
1120
|
}),
|
|
1096
1121
|
];
|
|
1097
1122
|
refreshAll();
|
|
@@ -1112,6 +1137,12 @@ window.__ModuleLoader__.load({
|
|
|
1112
1137
|
locale: i18n_1.NAMESPACE,
|
|
1113
1138
|
inject: withServices,
|
|
1114
1139
|
}, ({ t, services: injected }) => (0, jsx_runtime_1.jsx)(settings_1.SettingsPage, { services: injected, t: t })));
|
|
1140
|
+
// dsh 0.2.2 declares this optional slot. The injection waits on older hosts.
|
|
1141
|
+
// dsh 0.2.2 声明了此可选插槽,旧版宿主上的注入会保持等待。
|
|
1142
|
+
ctx.slots.inject('settings.section.icon', () => ctx.slots.register({
|
|
1143
|
+
name: 'settings.section.icon',
|
|
1144
|
+
id: shared_1.SETTINGS_SECTION,
|
|
1145
|
+
}, SettingsIcon));
|
|
1115
1146
|
ctx.slots.inject('plugins.detail.badge', () => ctx.slots.register({
|
|
1116
1147
|
name: 'plugins.detail.badge',
|
|
1117
1148
|
id: 'vulcan-decision-guard-conflict',
|
|
@@ -1158,6 +1189,13 @@ window.__ModuleLoader__.load({
|
|
|
1158
1189
|
},
|
|
1159
1190
|
}, quick_1.QuickSetting));
|
|
1160
1191
|
}
|
|
1192
|
+
/**
|
|
1193
|
+
* Render the approval shield at the square size requested by dsh's settings navigation.
|
|
1194
|
+
* 按 dsh 设置导航请求的方形尺寸渲染审批盾牌图标。
|
|
1195
|
+
*/
|
|
1196
|
+
function SettingsIcon({ size }) {
|
|
1197
|
+
return ((0, jsx_runtime_1.jsxs)("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 1.7, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", focusable: "false", children: [(0, jsx_runtime_1.jsx)("path", { d: "M12 3 20 6v5c0 4.7-2.8 7.9-8 10-5.2-2.1-8-5.3-8-10V6l8-3Z" }), (0, jsx_runtime_1.jsx)("path", { d: "m9 12 2 2 4-4" })] }));
|
|
1198
|
+
}
|
|
1161
1199
|
|
|
1162
1200
|
},
|
|
1163
1201
|
"./local": function (module, exports, require) {
|
|
@@ -1262,8 +1300,8 @@ window.__ModuleLoader__.load({
|
|
|
1262
1300
|
return null;
|
|
1263
1301
|
const governed = value.mode === 'standalone' || permissions?.currentValue === shared_1.AUTO_PRESET;
|
|
1264
1302
|
const unattended = view?.unattended === true;
|
|
1265
|
-
const decision = (0, settings_1.decisionStatus)(t, value, live, models
|
|
1266
|
-
const dot = !governed ? 'off' : decision.tone === 'ok' ? 'ok' : 'warn';
|
|
1303
|
+
const decision = (0, settings_1.decisionStatus)(t, value, live, models);
|
|
1304
|
+
const dot = !governed || decision.tone === 'outline' ? 'off' : decision.tone === 'ok' ? 'ok' : 'warn';
|
|
1267
1305
|
return ((0, jsx_runtime_1.jsxs)("span", { className: "vdg", children: [(0, jsx_runtime_1.jsxs)("button", { ref: anchor, type: "button", className: "vdg-trigger", "aria-haspopup": "dialog", "aria-expanded": open, title: decision.text, onClick: () => {
|
|
1268
1306
|
if (!open) {
|
|
1269
1307
|
services.panel.refresh();
|
|
@@ -1356,7 +1394,7 @@ window.__ModuleLoader__.load({
|
|
|
1356
1394
|
"./records": function (module, exports, require) {
|
|
1357
1395
|
"use strict";
|
|
1358
1396
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1359
|
-
exports.
|
|
1397
|
+
exports.RecordsPanel = RecordsPanel;
|
|
1360
1398
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
1361
1399
|
/**
|
|
1362
1400
|
* Settings › Auto approval › Decision log, after VulcanCode's records page: every decision the
|
|
@@ -1403,7 +1441,11 @@ window.__ModuleLoader__.load({
|
|
|
1403
1441
|
query.set('before', before);
|
|
1404
1442
|
return `records?${query.toString()}`;
|
|
1405
1443
|
}
|
|
1406
|
-
|
|
1444
|
+
/**
|
|
1445
|
+
* Render decision history and its actions using the dictionary and current recording state.
|
|
1446
|
+
* 根据本地化词典和当前记录开关状态渲染判定历史及其操作。
|
|
1447
|
+
*/
|
|
1448
|
+
function RecordsPanel({ t, recording }) {
|
|
1407
1449
|
const [filters, setFilters] = (0, react_1.useState)({ label: undefined, workspace: undefined, markedOnly: false });
|
|
1408
1450
|
const [page, setPage] = (0, react_1.useState)(undefined);
|
|
1409
1451
|
const [loadError, setLoadError] = (0, react_1.useState)(undefined);
|
|
@@ -1467,7 +1509,7 @@ window.__ModuleLoader__.load({
|
|
|
1467
1509
|
}, 'records.clearFailed');
|
|
1468
1510
|
};
|
|
1469
1511
|
const clearButton = ((0, jsx_runtime_1.jsx)("button", { type: "button", className: "vdg-btn ghost sm", disabled: busy !== undefined, onClick: () => { setClearing(true); }, children: t('records.clear') }));
|
|
1470
|
-
return ((0, jsx_runtime_1.jsxs)(
|
|
1512
|
+
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [recording ? null : (0, jsx_runtime_1.jsx)("div", { className: "vdg-alert warn", children: t('records.paused') }), page === undefined
|
|
1471
1513
|
? (loadError === undefined
|
|
1472
1514
|
? (0, jsx_runtime_1.jsx)("p", { className: "vdg-hint", children: t('loading') })
|
|
1473
1515
|
: ((0, jsx_runtime_1.jsx)("div", { className: "vdg-alert bad", children: (0, jsx_runtime_1.jsxs)("div", { className: "vdg-between", children: [(0, jsx_runtime_1.jsx)("span", { children: (0, i18n_1.fill)(t('records.loadFailed'), { error: loadError }) }), clearButton] }) })))
|
|
@@ -1544,16 +1586,28 @@ window.__ModuleLoader__.load({
|
|
|
1544
1586
|
safe: { tool: 'shell_command', args: { command: 'git status', shell: 'bash' } },
|
|
1545
1587
|
risky: { tool: 'shell_command', args: { command: 'git push --force origin main', shell: 'bash' } },
|
|
1546
1588
|
};
|
|
1547
|
-
|
|
1548
|
-
|
|
1589
|
+
// The sections appear in the same order in navigation and in the page.
|
|
1590
|
+
// 分区在导航和正文中按同一顺序排列。
|
|
1591
|
+
const SETTINGS_VIEWS = ['engine', 'policy', 'records'];
|
|
1592
|
+
/**
|
|
1593
|
+
* Describe the configured backend using the latest account and model-list response.
|
|
1594
|
+
* 使用最新账号和模型目录响应描述已配置的判定后端。
|
|
1595
|
+
*/
|
|
1596
|
+
function decisionStatus(t, value, live, list) {
|
|
1549
1597
|
if (live == null)
|
|
1550
1598
|
return { tone: 'outline', text: t('loading') };
|
|
1551
1599
|
if (value.backend === 'cloud') {
|
|
1552
1600
|
if (!live.account.signedIn)
|
|
1553
1601
|
return { tone: 'warn', text: t('status.decision.signedOut') };
|
|
1554
|
-
|
|
1555
|
-
|
|
1602
|
+
if (list === undefined)
|
|
1603
|
+
return { tone: 'outline', text: t('model.loading') };
|
|
1604
|
+
if (list.kind === 'failed')
|
|
1605
|
+
return { tone: 'warn', text: t('status.decision.cloudLoadFailed') };
|
|
1606
|
+
const model = value.cloudModel === '' ? list.models[0] : value.cloudModel;
|
|
1607
|
+
if (list.models.length === 0 || model === undefined)
|
|
1556
1608
|
return { tone: 'warn', text: t('status.decision.cloudNoModel') };
|
|
1609
|
+
if (!list.models.includes(model))
|
|
1610
|
+
return { tone: 'warn', text: (0, i18n_1.fill)(t('model.disabled'), { model }) };
|
|
1557
1611
|
return { tone: 'ok', text: (0, i18n_1.fill)(t('status.decision.cloud'), { model }) };
|
|
1558
1612
|
}
|
|
1559
1613
|
const pack = live.packs.find(item => item.id === value.localPack && item.installed !== null);
|
|
@@ -1566,7 +1620,7 @@ window.__ModuleLoader__.load({
|
|
|
1566
1620
|
const heldByReview = (0, ui_1.useWatch)(services.autoReview);
|
|
1567
1621
|
const listed = (0, ui_1.useWatch)(services.autoListed);
|
|
1568
1622
|
const models = (0, ui_1.useWatch)(services.cloudModels);
|
|
1569
|
-
const decision = decisionStatus(t, value, live, models
|
|
1623
|
+
const decision = decisionStatus(t, value, live, models);
|
|
1570
1624
|
let auto;
|
|
1571
1625
|
if (value.mode === 'standalone')
|
|
1572
1626
|
auto = { tone: 'outline', text: 'status.auto.standalone', badge: 'tone.off' };
|
|
@@ -1576,7 +1630,7 @@ window.__ModuleLoader__.load({
|
|
|
1576
1630
|
auto = { tone: 'ok', text: 'status.auto.provided', badge: 'tone.ok' };
|
|
1577
1631
|
else
|
|
1578
1632
|
auto = { tone: 'outline', text: 'status.auto.pending', badge: 'status.auto.pending' };
|
|
1579
|
-
return ((0, jsx_runtime_1.jsx)(ui_1.Section, { title: t('status.title'), flush: true, children: (0, jsx_runtime_1.jsxs)("div", { className: "vdg-rows", children: [(0, jsx_runtime_1.jsxs)("div", { className: "vdg-row", children: [(0, jsx_runtime_1.jsx)("span", { className: "vdg-row-label", children: t('status.decision') }), (0, jsx_runtime_1.jsxs)("span", { className: "vdg-row-value", children: [decision.text, (0, jsx_runtime_1.jsx)(ui_1.Badge, { tone: decision.tone, children: t(decision.tone === 'ok' ? 'tone.ok' : 'tone.warn') })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "vdg-row", children: [(0, jsx_runtime_1.jsx)("span", { className: "vdg-row-label", children: t('status.auto') }), (0, jsx_runtime_1.jsxs)("span", { className: "vdg-row-value", children: [t(auto.text), (0, jsx_runtime_1.jsx)(ui_1.Badge, { tone: auto.tone, children: t(auto.badge) })] })] })] }) }));
|
|
1633
|
+
return ((0, jsx_runtime_1.jsx)(ui_1.Section, { title: t('status.title'), flush: true, children: (0, jsx_runtime_1.jsxs)("div", { className: "vdg-rows", children: [(0, jsx_runtime_1.jsxs)("div", { className: "vdg-row", children: [(0, jsx_runtime_1.jsx)("span", { className: "vdg-row-label", children: t('status.decision') }), (0, jsx_runtime_1.jsxs)("span", { className: "vdg-row-value", children: [decision.text, decision.tone === 'outline' ? null : (0, jsx_runtime_1.jsx)(ui_1.Badge, { tone: decision.tone, children: t(decision.tone === 'ok' ? 'tone.ok' : 'tone.warn') })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "vdg-row", children: [(0, jsx_runtime_1.jsx)("span", { className: "vdg-row-label", children: t('status.auto') }), (0, jsx_runtime_1.jsxs)("span", { className: "vdg-row-value", children: [t(auto.text), (0, jsx_runtime_1.jsx)(ui_1.Badge, { tone: auto.tone, children: t(auto.badge) })] })] })] }) }));
|
|
1580
1634
|
}
|
|
1581
1635
|
function OptionsSection({ t, value, disabled, save }) {
|
|
1582
1636
|
const row = (field, prefix, values) => ((0, jsx_runtime_1.jsxs)("div", { className: "vdg-row", children: [(0, jsx_runtime_1.jsx)("span", { children: t(`${prefix}.title`) }), (0, jsx_runtime_1.jsx)(ui_1.Segments, { label: t(`${prefix}.title`), value: value[field], options: values.map(item => ({ value: item, title: t(`${prefix}.${item}`) })), disabled: disabled, onChoose: (next) => { save({ [field]: next }); } })] }));
|
|
@@ -1631,8 +1685,12 @@ window.__ModuleLoader__.load({
|
|
|
1631
1685
|
}, children: t('prompts.save') })] })] }));
|
|
1632
1686
|
}
|
|
1633
1687
|
/** The decision log switch and the way into the log. */
|
|
1634
|
-
|
|
1635
|
-
|
|
1688
|
+
/**
|
|
1689
|
+
* Render the recording preference using the dictionary, current settings, disabled state and save callback.
|
|
1690
|
+
* 使用文案、当前设置、禁用状态与保存回调渲染判定记录开关。
|
|
1691
|
+
*/
|
|
1692
|
+
function RecordsSection({ t, value, disabled, save }) {
|
|
1693
|
+
return ((0, jsx_runtime_1.jsx)(ui_1.Section, { title: t('records.title'), lead: t('records.entryHint'), children: (0, jsx_runtime_1.jsxs)("label", { className: "vdg-check", children: [(0, jsx_runtime_1.jsx)("input", { type: "checkbox", checked: value.recordDecisions, disabled: disabled, onChange: (event) => { save({ recordDecisions: event.target.checked }); } }), (0, jsx_runtime_1.jsxs)("span", { children: [(0, jsx_runtime_1.jsx)("span", { className: "vdg-choice-title", children: t('records.enabled') }), (0, jsx_runtime_1.jsx)("span", { className: "vdg-choice-hint", children: t('records.enabledHint') })] })] }) }));
|
|
1636
1694
|
}
|
|
1637
1695
|
function AskSection({ t, value, disabled, save }) {
|
|
1638
1696
|
const [text, setText] = (0, react_1.useState)(String(value.askTimeout));
|
|
@@ -1661,10 +1719,22 @@ window.__ModuleLoader__.load({
|
|
|
1661
1719
|
? null
|
|
1662
1720
|
: ((0, jsx_runtime_1.jsxs)("div", { className: "vdg-inline", children: [(0, jsx_runtime_1.jsx)(ui_1.Badge, { tone: verdict.decision === 'allow' ? 'ok' : verdict.decision === 'review' ? 'warn' : 'bad', children: ['allow', 'review', 'block'].includes(verdict.decision) ? t(`decision.${verdict.decision}`) : verdict.decision }), (0, jsx_runtime_1.jsx)("span", { className: "vdg-hint", children: (0, i18n_1.fill)(t('try.result'), { decision: verdict.decision, ms: verdict.elapsed_ms, backend: verdict.backend, via: verdict.via }) })] })), (0, jsx_runtime_1.jsx)(ui_1.ErrorLine, { text: error === undefined ? undefined : (0, i18n_1.fill)(t('try.failed'), { error }) })] }));
|
|
1663
1721
|
}
|
|
1722
|
+
/**
|
|
1723
|
+
* Render the engine, policy and records views using the guard services and localized dictionary.
|
|
1724
|
+
* 使用审批服务与本地化词典渲染判定引擎、复核规则与记录分区。
|
|
1725
|
+
*/
|
|
1664
1726
|
function SettingsPage({ services, t }) {
|
|
1665
1727
|
const settings = (0, ui_1.useSettings)(services.form, t);
|
|
1666
1728
|
const live = (0, ui_1.useWatch)(services.panel);
|
|
1667
|
-
|
|
1729
|
+
// The selected section controls visibility while keeping settings drafts mounted.
|
|
1730
|
+
// 所选分区控制显示状态,同时保留已挂载的设置草稿。
|
|
1731
|
+
const [view, setView] = (0, react_1.useState)('engine');
|
|
1732
|
+
// The root is the scroll target when the user changes sections from the sticky navigation.
|
|
1733
|
+
// 用户从吸附导航切换分区时,将设置页根节点作为滚动目标。
|
|
1734
|
+
const page = (0, react_1.useRef)(null);
|
|
1735
|
+
// Each navigation control points to its own panel within this settings instance.
|
|
1736
|
+
// 每个分区按钮指向当前设置实例中的对应面板。
|
|
1737
|
+
const viewId = (0, react_1.useId)();
|
|
1668
1738
|
const active = live != null && (0, shared_1.running)(live.job);
|
|
1669
1739
|
(0, ui_1.usePolling)(services.panel, active ? 1000 : undefined);
|
|
1670
1740
|
const value = settings.value;
|
|
@@ -1675,25 +1745,33 @@ window.__ModuleLoader__.load({
|
|
|
1675
1745
|
return (0, jsx_runtime_1.jsx)("div", { className: "vdg vdg-page", children: (0, jsx_runtime_1.jsx)("div", { className: "vdg-alert warn", children: t('unavailable') }) });
|
|
1676
1746
|
}
|
|
1677
1747
|
const { disabled, save, saveAsync } = settings;
|
|
1678
|
-
if (view === 'records')
|
|
1679
|
-
return (0, jsx_runtime_1.jsx)(records_1.RecordsPage, { t: t, recording: value.recordDecisions, onBack: () => { setView('settings'); } });
|
|
1680
1748
|
const refresh = () => {
|
|
1681
1749
|
services.panel.refresh();
|
|
1682
1750
|
services.cloudModels.refresh();
|
|
1683
1751
|
services.endpoints.refresh();
|
|
1684
1752
|
};
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1753
|
+
/**
|
|
1754
|
+
* Activate the requested settings section and return to its start in the host's scroll pane.
|
|
1755
|
+
* 激活指定的设置分区,并在宿主滚动容器内回到分区开头。
|
|
1756
|
+
*/
|
|
1757
|
+
const selectView = (next) => {
|
|
1758
|
+
if (view === next)
|
|
1759
|
+
return;
|
|
1760
|
+
setView(next);
|
|
1761
|
+
page.current?.scrollIntoView({ block: 'start' });
|
|
1762
|
+
};
|
|
1763
|
+
return ((0, jsx_runtime_1.jsxs)("div", { ref: page, className: "vdg vdg-page", "data-vulcan-settings-width": "full", children: [(0, jsx_runtime_1.jsxs)("header", { className: "vdg-page-head", "data-vulcan-settings-header": true, children: [(0, jsx_runtime_1.jsx)("h2", { children: t('page.title') }), (0, jsx_runtime_1.jsx)("p", { className: "vdg-lead", children: t('page.lead') })] }), (0, jsx_runtime_1.jsx)("div", { className: "vdg-settings-navigation", role: "group", "aria-label": t('page.sections'), children: SETTINGS_VIEWS.map(item => ((0, jsx_runtime_1.jsx)("button", { id: `${viewId}-${item}-button`, type: "button", "aria-pressed": view === item, "aria-controls": `${viewId}-${item}`, onClick: () => { selectView(item); }, children: t(`page.section.${item}`) }, item))) }), settings.writable ? null : (0, jsx_runtime_1.jsx)("div", { className: "vdg-alert warn", children: t('readOnly') }), (0, jsx_runtime_1.jsx)(ui_1.ErrorLine, { text: settings.error }), (0, jsx_runtime_1.jsxs)("section", { className: "vdg-settings-group", id: `${viewId}-engine`, "aria-labelledby": `${viewId}-engine-button`, hidden: view !== 'engine', children: [(0, jsx_runtime_1.jsx)(StatusSection, { services: services, t: t, value: value, live: live }), (0, jsx_runtime_1.jsx)(ui_1.Section, { title: t('mode.title'), children: (0, jsx_runtime_1.jsx)(ui_1.Choices, { label: t('mode.title'), value: value.mode, options: ['auto-preset', 'standalone'].map(item => ({ value: item, title: t(`mode.${item}`), hint: t(`mode.${item}.hint`) })), disabled: disabled, onChoose: (next) => { save({ mode: next }); } }) }), (0, jsx_runtime_1.jsx)(ui_1.Section, { title: t('backend.title'), children: (0, jsx_runtime_1.jsx)(ui_1.Choices, { label: t('backend.title'), value: value.backend, options: ['cloud', 'local'].map(item => ({ value: item, title: t(`backend.${item}`), hint: t(`backend.${item}.hint`) })), disabled: disabled, onChoose: (next) => { save({ backend: next }); } }) }), live === undefined
|
|
1764
|
+
? (0, jsx_runtime_1.jsx)("p", { className: "vdg-hint", children: t('loading') })
|
|
1765
|
+
: live === null
|
|
1766
|
+
? (0, jsx_runtime_1.jsx)("div", { className: "vdg-alert bad", children: t('panel.unavailable') })
|
|
1767
|
+
: value.backend === 'cloud'
|
|
1768
|
+
? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(cloud_1.AccountSection, { services: services, t: t, account: live.account }), live.account.signedIn
|
|
1769
|
+
? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(cloud_1.ModelSection, { services: services, t: t, saved: value.cloudModel, disabled: disabled, onChoose: (model) => {
|
|
1770
|
+
// A lock belongs to the model it was chosen for.
|
|
1771
|
+
void saveAsync({ cloudModel: model === '' ? undefined : model, cloudEndpoint: undefined }).then(() => { services.endpoints.refresh(); });
|
|
1772
|
+
} }), (0, jsx_runtime_1.jsx)(cloud_1.EndpointsSection, { services: services, t: t, disabled: disabled, onLock: endpoint => (0, cloud_1.lockEndpoint)(saveAsync, value.cloudModel, endpoint, t('readOnly')) })] }))
|
|
1773
|
+
: null] }))
|
|
1774
|
+
: (0, jsx_runtime_1.jsx)(local_1.LocalSections, { t: t, live: live, value: value, disabled: disabled, save: save, refresh: refresh }), (0, jsx_runtime_1.jsx)(OptionsSection, { t: t, value: value, disabled: disabled, save: save }), (0, jsx_runtime_1.jsx)(TrySection, { t: t })] }), (0, jsx_runtime_1.jsxs)("section", { className: "vdg-settings-group", id: `${viewId}-policy`, "aria-labelledby": `${viewId}-policy-button`, hidden: view !== 'policy', children: [(0, jsx_runtime_1.jsx)(ReviewSection, { services: services, t: t, value: value, disabled: disabled, save: save }), value.review === 'off' ? null : (0, jsx_runtime_1.jsx)(RulesSection, { t: t, value: value, disabled: disabled, saveAsync: saveAsync }), value.review === 'off' ? null : (0, jsx_runtime_1.jsx)(PromptsSection, { t: t, value: value, disabled: disabled, saveAsync: saveAsync }), (0, jsx_runtime_1.jsx)(AskSection, { t: t, value: value, disabled: disabled, save: save })] }), (0, jsx_runtime_1.jsx)("section", { className: "vdg-settings-group", id: `${viewId}-records`, "aria-labelledby": `${viewId}-records-button`, hidden: view !== 'records', children: view === 'records' ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(RecordsSection, { t: t, value: value, disabled: disabled, save: save }), (0, jsx_runtime_1.jsx)(records_1.RecordsPanel, { t: t, recording: value.recordDecisions })] })) : null })] }));
|
|
1697
1775
|
}
|
|
1698
1776
|
|
|
1699
1777
|
},
|
|
@@ -1776,6 +1854,14 @@ window.__ModuleLoader__.load({
|
|
|
1776
1854
|
this.#set(undefined);
|
|
1777
1855
|
});
|
|
1778
1856
|
}
|
|
1857
|
+
/**
|
|
1858
|
+
* Clear the previous Host value and discard reads still in flight, keeping subscribers.
|
|
1859
|
+
* 清除旧宿主值并丢弃未完成的读取,同时保留订阅。
|
|
1860
|
+
*/
|
|
1861
|
+
reset() {
|
|
1862
|
+
++this.#generation;
|
|
1863
|
+
this.#set(undefined);
|
|
1864
|
+
}
|
|
1779
1865
|
/** Stop publishing answers that are still in flight. */
|
|
1780
1866
|
close() {
|
|
1781
1867
|
++this.#generation;
|
|
@@ -1838,6 +1924,15 @@ window.__ModuleLoader__.load({
|
|
|
1838
1924
|
.vdg *, .vdg *::before, .vdg *::after { box-sizing: border-box; }
|
|
1839
1925
|
.vdg-page { display: flex; flex-direction: column; gap: calc(var(--vk-u) * 4); counter-reset: vdg-section; padding-bottom: calc(var(--vk-u) * 2); }
|
|
1840
1926
|
.vdg-page-head h2 { margin: 0; font-size: 16px; font-weight: 600; }
|
|
1927
|
+
|
|
1928
|
+
/* Keep the section switcher reachable inside the host's single scrolling pane. */
|
|
1929
|
+
/* 在宿主唯一的滚动正文内,让分区切换栏始终可触达。 */
|
|
1930
|
+
.vdg-settings-navigation { position: sticky; top: 0; z-index: 1; display: flex; flex-wrap: wrap; gap: 4px; border-bottom: 1px solid var(--vk-line); background: var(--vk-panel); }
|
|
1931
|
+
.vdg-settings-navigation > button { min-height: 36px; padding: 6px 12px; border: 0; border-bottom: 2px solid transparent; background: none; color: var(--vk-ink-2); font: inherit; cursor: pointer; }
|
|
1932
|
+
.vdg-settings-navigation > button:hover { background: var(--vk-hover); color: var(--vk-ink); }
|
|
1933
|
+
.vdg-settings-navigation > button[aria-pressed="true"] { border-bottom-color: var(--vk-ember); color: var(--vk-ink); font-weight: 600; }
|
|
1934
|
+
.vdg-settings-navigation > button:focus-visible { outline: 2px solid var(--vk-signal); outline-offset: -3px; }
|
|
1935
|
+
.vdg-settings-group:not([hidden]) { display: flex; flex-direction: column; gap: calc(var(--vk-u) * 4); min-width: 0; }
|
|
1841
1936
|
.vdg-lead { margin: calc(var(--vk-u) * 1) 0 0; color: var(--vk-ink-2); }
|
|
1842
1937
|
.vdg-hint { margin: 0; color: var(--vk-ink-3); font-size: 12px; }
|
|
1843
1938
|
.vdg-error { margin: 0; color: var(--vk-bad); font-size: 12px; overflow-wrap: anywhere; }
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openvulcan/dsh-decision-guard",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.4",
|
|
4
4
|
"description": "Vulcan Decision permission guard for DeepSeek Harness: every tool call is judged before it runs.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"icon": "./assets/decision-guard-icon.png",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
}
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@openvulcan/vulcan-decision": "0.4.
|
|
47
|
+
"@openvulcan/vulcan-decision": "0.4.4"
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|
|
50
50
|
"@deepseek-ai/cordis": "^4.0.5",
|