dsh-yolo-mode 0.6.0 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +52 -0
- package/lib/client/index.js +159 -162
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,58 @@
|
|
|
2
2
|
|
|
3
3
|
本项目遵循 [Semantic Versioning](https://semver.org/)。
|
|
4
4
|
|
|
5
|
+
## [0.6.2] - 2026-09-27
|
|
6
|
+
|
|
7
|
+
### 修复:插件详情页的配置卡在引言、表单永不出现
|
|
8
|
+
|
|
9
|
+
现象:0.6.1 起配置区块已出现在插件详情页,但只画出引言一句,表单始终不渲染。
|
|
10
|
+
|
|
11
|
+
根因(浏览器控制台实锤):
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
Uncaught (in promise) Error: cannot get property "remote.session" without inject
|
|
15
|
+
at YoloStore._fetchRemoteDirectory (store.js)
|
|
16
|
+
at async Promise.all (index 2)
|
|
17
|
+
at async YoloStore.load (store.js)
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
- `_fetchRemoteDirectory()` 中的 `this.remote.session` 在 **try 块之外**,而客户端的 Remote
|
|
21
|
+
代理是**按 inject 声明做门禁**的:读取未声明的命名空间会**抛错**,而不是返回 undefined。
|
|
22
|
+
插件当时只声明了 `remote.llm`,**漏了 `remote.session`**。
|
|
23
|
+
- 而 `load()` 的 `Promise.all` **没有 try/catch**,这一抛就把 `status` 永久钉在 `loading`:
|
|
24
|
+
界面既不显示表单、也不显示错误,只剩引言那一句(这个静默失败本身也是缺陷)。
|
|
25
|
+
|
|
26
|
+
修复:
|
|
27
|
+
|
|
28
|
+
- `dsh.client.inject` 与源码的 `inject` 导出都补上 **`remote.session`**(`remote.llm` 同理,
|
|
29
|
+
两个命名空间都必须声明,缺一即抛)。
|
|
30
|
+
- `store.js`:两处 Remote 命名空间属性读取移入 `try/catch`,缺失时**降级为「无目录」**,
|
|
31
|
+
不再 reject `load()`。
|
|
32
|
+
- `store.js`:`load()` 整体包 `try/catch`,桥/传输 reject 时置 `status:'error'` 并保留原因,
|
|
33
|
+
不再静默停在 `loading`。
|
|
34
|
+
- 测试 +3(**141/141**):inject 必须含两个 Remote 命名空间;`remote.session` /
|
|
35
|
+
`remote.llm` 属性读取抛错时目录降级且 `status` 仍 `ready`;桥 reject 时 `status='error'`
|
|
36
|
+
且带原始原因。
|
|
37
|
+
|
|
38
|
+
## [0.6.1] - 2026-09-27
|
|
39
|
+
|
|
40
|
+
### 修复:配置位置改到插件详情页(0.6.0 放错了地方)
|
|
41
|
+
|
|
42
|
+
0.6.0 把配置放进了输入框旁的 `YOLO <预设>` 胶囊浮层(`shell.overlay`)——那是**状态与日志**
|
|
43
|
+
的位置,不是「插件的配置面板」。本次改用 DSH 插件管理页声明的专用槽位:
|
|
44
|
+
|
|
45
|
+
- **新增 `plugins.bundle.config` 注册**(key = npm 包名 `dsh-yolo-mode`):配置渲染在
|
|
46
|
+
「插件列表 → dsh-yolo-mode」页面的**描述与组件行之间**。该槽位是 keyed slot,页面仅在
|
|
47
|
+
有注册者(且 key 等于包名)时才渲染配置区块
|
|
48
|
+
(`dsh-client-ui-plugin-manager`:`configured = ledger.bundles.has(pkg.name)`)——
|
|
49
|
+
这正是此前该页面完全没有配置区的原因。
|
|
50
|
+
- bundle 页可容纳多个条目,因此页面**不传宿主 `form`**,由本插件自持草稿、校验与保存
|
|
51
|
+
(仍走 `/yolo-mode` 设置桥 + 乐观修订锁)。`view === 'summary'` 时不渲染
|
|
52
|
+
(bundle 页只请求 `page`)。
|
|
53
|
+
- **胶囊浮层回归纯状态/日志**:撤销 0.6.0 给 `shell.overlay` 加的「配置 / 状态与日志」双标签;
|
|
54
|
+
点胶囊仍是原来的统计卡 + 裁决表分页 + 打开日志 + 刷新。
|
|
55
|
+
- 文案 `tabConfig` / `tabStatus` 退役(不再有标签页);`settings.section` 保持退役。
|
|
56
|
+
|
|
5
57
|
## [0.6.0] - 2026-09-27
|
|
6
58
|
|
|
7
59
|
### 变更:全部配置移入插件自身 UI 面板(设置页退役)
|
package/lib/client/index.js
CHANGED
|
@@ -15,8 +15,6 @@ window.__ModuleLoader__.load({
|
|
|
15
15
|
const NS = "settings.yoloMode";
|
|
16
16
|
/** English dictionary. */
|
|
17
17
|
const en = {
|
|
18
|
-
tabConfig: "Configuration",
|
|
19
|
-
tabStatus: "Status & log",
|
|
20
18
|
chip: "YOLO status",
|
|
21
19
|
sectionIntro: "Configure how YOLO mode auto-judges sandbox escalation approval requests.",
|
|
22
20
|
loadError: "Could not load YOLO mode settings.",
|
|
@@ -74,8 +72,6 @@ window.__ModuleLoader__.load({
|
|
|
74
72
|
};
|
|
75
73
|
/** Chinese dictionary. */
|
|
76
74
|
const zh = {
|
|
77
|
-
tabConfig: "配置",
|
|
78
|
-
tabStatus: "状态与日志",
|
|
79
75
|
chip: "YOLO 状态",
|
|
80
76
|
sectionIntro: "配置 YOLO 模式如何自动裁决沙箱升权审批申请。",
|
|
81
77
|
loadError: "无法加载 YOLO 模式设置。",
|
|
@@ -398,7 +394,12 @@ window.__ModuleLoader__.load({
|
|
|
398
394
|
* no `remote.llm` namespace is present, so the caller can fall back.
|
|
399
395
|
*/
|
|
400
396
|
async _fetchRemoteDirectory() {
|
|
401
|
-
|
|
397
|
+
let llm = null;
|
|
398
|
+
try {
|
|
399
|
+
llm = this.remote ? this.remote.llm : null;
|
|
400
|
+
} catch {
|
|
401
|
+
llm = null;
|
|
402
|
+
}
|
|
402
403
|
if (llm === null || llm === void 0) return void 0;
|
|
403
404
|
let providers = [];
|
|
404
405
|
let models = [];
|
|
@@ -408,7 +409,12 @@ window.__ModuleLoader__.load({
|
|
|
408
409
|
} catch {
|
|
409
410
|
providers = [];
|
|
410
411
|
}
|
|
411
|
-
|
|
412
|
+
let session = null;
|
|
413
|
+
try {
|
|
414
|
+
session = this.remote ? this.remote.session : null;
|
|
415
|
+
} catch {
|
|
416
|
+
session = null;
|
|
417
|
+
}
|
|
412
418
|
if (session && typeof session.modelCatalog === "function") try {
|
|
413
419
|
const response = await session.modelCatalog();
|
|
414
420
|
const value = response && response.ok === true ? response.value : void 0;
|
|
@@ -460,11 +466,24 @@ window.__ModuleLoader__.load({
|
|
|
460
466
|
status: "loading",
|
|
461
467
|
error: void 0
|
|
462
468
|
});
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
469
|
+
let viewResult;
|
|
470
|
+
let statusResult;
|
|
471
|
+
let directory;
|
|
472
|
+
try {
|
|
473
|
+
[viewResult, statusResult, directory] = await Promise.all([
|
|
474
|
+
this._call(YOLO_RPC_VIEW, {}),
|
|
475
|
+
this._call(YOLO_RPC_STATUS, {}),
|
|
476
|
+
this._fetchLlmDirectory()
|
|
477
|
+
]);
|
|
478
|
+
} catch (error) {
|
|
479
|
+
if (generation !== this._generation) return;
|
|
480
|
+
this.set({
|
|
481
|
+
status: "error",
|
|
482
|
+
error: error !== null && typeof error === "object" && typeof error.message === "string" ? error.message : true,
|
|
483
|
+
conflicted: false
|
|
484
|
+
});
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
468
487
|
if (generation !== this._generation) return;
|
|
469
488
|
if (!viewResult.ok || !statusResult.ok) {
|
|
470
489
|
const code = this._errorCode(!viewResult.ok ? viewResult.error : statusResult.error);
|
|
@@ -558,60 +577,6 @@ window.__ModuleLoader__.load({
|
|
|
558
577
|
}
|
|
559
578
|
};
|
|
560
579
|
//#endregion
|
|
561
|
-
//#region src/client/ui/Chip.js
|
|
562
|
-
/**
|
|
563
|
-
* YOLO mode status chip (slot `conversation.input.left`). Shows "YOLO <preset>"
|
|
564
|
-
* and toggles the shared popup via store.togglePopup().
|
|
565
|
-
*/
|
|
566
|
-
const chipStyle = {
|
|
567
|
-
display: "inline-flex",
|
|
568
|
-
alignItems: "center",
|
|
569
|
-
gap: 6,
|
|
570
|
-
padding: "4px 10px",
|
|
571
|
-
borderRadius: 999,
|
|
572
|
-
backgroundColor: "#ffffff",
|
|
573
|
-
color: "#1d4ed8",
|
|
574
|
-
border: "1px solid #93c5fd",
|
|
575
|
-
fontSize: 12,
|
|
576
|
-
cursor: "pointer",
|
|
577
|
-
whiteSpace: "nowrap",
|
|
578
|
-
userSelect: "none"
|
|
579
|
-
};
|
|
580
|
-
/** Status colour for the leading dot based on judge readiness. */
|
|
581
|
-
function dotColor(statusInfo) {
|
|
582
|
-
return statusInfo && statusInfo.judgeConfigured ? "#16a34a" : "#d97706";
|
|
583
|
-
}
|
|
584
|
-
/**
|
|
585
|
-
* @param {object} props - slot-delivered { store, useSnapshot, t } (inject face).
|
|
586
|
-
*/
|
|
587
|
-
function Chip(props) {
|
|
588
|
-
const store = props.store;
|
|
589
|
-
const useSnapshot = props.useSnapshot;
|
|
590
|
-
if (store === void 0 || useSnapshot === void 0) return null;
|
|
591
|
-
return ChipLoaded({
|
|
592
|
-
store,
|
|
593
|
-
useSnapshot
|
|
594
|
-
});
|
|
595
|
-
}
|
|
596
|
-
function ChipLoaded({ store, useSnapshot }) {
|
|
597
|
-
(0, react.useEffect)(() => {
|
|
598
|
-
store.load();
|
|
599
|
-
}, [store]);
|
|
600
|
-
const statusInfo = useSnapshot((s) => s).statusInfo;
|
|
601
|
-
const preset = statusInfo && typeof statusInfo.preset === "string" ? statusInfo.preset : "…";
|
|
602
|
-
return (0, react.createElement)("button", {
|
|
603
|
-
style: chipStyle,
|
|
604
|
-
title: "YOLO",
|
|
605
|
-
onClick: () => store.togglePopup()
|
|
606
|
-
}, (0, react.createElement)("span", { style: {
|
|
607
|
-
width: 8,
|
|
608
|
-
height: 8,
|
|
609
|
-
borderRadius: 4,
|
|
610
|
-
backgroundColor: dotColor(statusInfo),
|
|
611
|
-
display: "inline-block"
|
|
612
|
-
} }), "YOLO " + preset);
|
|
613
|
-
}
|
|
614
|
-
//#endregion
|
|
615
580
|
//#region src/client/presets.js
|
|
616
581
|
/**
|
|
617
582
|
* Static per-preset display data for the YOLO settings page: the level table
|
|
@@ -701,8 +666,10 @@ window.__ModuleLoader__.load({
|
|
|
701
666
|
//#endregion
|
|
702
667
|
//#region src/client/ui/ConfigForm.js
|
|
703
668
|
/**
|
|
704
|
-
* YOLO mode configuration form. Rendered
|
|
705
|
-
* (
|
|
669
|
+
* YOLO mode configuration form. Rendered on this plugin's own page in the DSH
|
|
670
|
+
* plugin manager (插件列表 → dsh-yolo-mode), via the `plugins.bundle.config`
|
|
671
|
+
* slot keyed by the package name (see ../index.js) — between the package
|
|
672
|
+
* description and the component rows. The `settings.section` page was retired,
|
|
706
673
|
* so this form is the single configuration surface for the plugin. It renders
|
|
707
674
|
* the resolved configuration (view.value) and saves via path ops through
|
|
708
675
|
* store.mutate with an optimistic-revision lock. Everything is React.createElement
|
|
@@ -889,14 +856,21 @@ window.__ModuleLoader__.load({
|
|
|
889
856
|
}
|
|
890
857
|
};
|
|
891
858
|
/**
|
|
892
|
-
* The YOLO configuration form
|
|
859
|
+
* The YOLO configuration form.
|
|
860
|
+
*
|
|
861
|
+
* Hosted by this plugin's own page in the plugin manager: registered into
|
|
862
|
+
* `plugins.bundle.config` under the package name (see ../index.js). The page
|
|
863
|
+
* dispatches `view`; a bundle page only ever asks for `page`, and a bundle may
|
|
864
|
+
* hold several configuration entries, so the page passes NO host `form` — this
|
|
865
|
+
* component owns its draft, validation and save path (the /yolo-mode bridge).
|
|
893
866
|
*
|
|
894
|
-
* @param {object} props -
|
|
895
|
-
*
|
|
867
|
+
* @param {object} props - slot props `{ view? }` merged with the inject face
|
|
868
|
+
* `{ store, useSnapshot, t }`; an optional `close()` adds a dismiss control.
|
|
896
869
|
*/
|
|
897
870
|
function ConfigForm(props) {
|
|
898
|
-
const { store, useSnapshot, t, close } = props;
|
|
871
|
+
const { view, store, useSnapshot, t, close } = props;
|
|
899
872
|
if (store === void 0 || useSnapshot === void 0 || t === void 0) return null;
|
|
873
|
+
if (view === "summary") return null;
|
|
900
874
|
return ReactSection({
|
|
901
875
|
store,
|
|
902
876
|
useSnapshot,
|
|
@@ -1154,20 +1128,69 @@ window.__ModuleLoader__.load({
|
|
|
1154
1128
|
return ops;
|
|
1155
1129
|
}
|
|
1156
1130
|
//#endregion
|
|
1131
|
+
//#region src/client/ui/Chip.js
|
|
1132
|
+
/**
|
|
1133
|
+
* YOLO mode status chip (slot `conversation.input.left`). Shows "YOLO <preset>"
|
|
1134
|
+
* and toggles the shared popup via store.togglePopup().
|
|
1135
|
+
*/
|
|
1136
|
+
const chipStyle = {
|
|
1137
|
+
display: "inline-flex",
|
|
1138
|
+
alignItems: "center",
|
|
1139
|
+
gap: 6,
|
|
1140
|
+
padding: "4px 10px",
|
|
1141
|
+
borderRadius: 999,
|
|
1142
|
+
backgroundColor: "#ffffff",
|
|
1143
|
+
color: "#1d4ed8",
|
|
1144
|
+
border: "1px solid #93c5fd",
|
|
1145
|
+
fontSize: 12,
|
|
1146
|
+
cursor: "pointer",
|
|
1147
|
+
whiteSpace: "nowrap",
|
|
1148
|
+
userSelect: "none"
|
|
1149
|
+
};
|
|
1150
|
+
/** Status colour for the leading dot based on judge readiness. */
|
|
1151
|
+
function dotColor(statusInfo) {
|
|
1152
|
+
return statusInfo && statusInfo.judgeConfigured ? "#16a34a" : "#d97706";
|
|
1153
|
+
}
|
|
1154
|
+
/**
|
|
1155
|
+
* @param {object} props - slot-delivered { store, useSnapshot, t } (inject face).
|
|
1156
|
+
*/
|
|
1157
|
+
function Chip(props) {
|
|
1158
|
+
const store = props.store;
|
|
1159
|
+
const useSnapshot = props.useSnapshot;
|
|
1160
|
+
if (store === void 0 || useSnapshot === void 0) return null;
|
|
1161
|
+
return ChipLoaded({
|
|
1162
|
+
store,
|
|
1163
|
+
useSnapshot
|
|
1164
|
+
});
|
|
1165
|
+
}
|
|
1166
|
+
function ChipLoaded({ store, useSnapshot }) {
|
|
1167
|
+
(0, react.useEffect)(() => {
|
|
1168
|
+
store.load();
|
|
1169
|
+
}, [store]);
|
|
1170
|
+
const statusInfo = useSnapshot((s) => s).statusInfo;
|
|
1171
|
+
const preset = statusInfo && typeof statusInfo.preset === "string" ? statusInfo.preset : "…";
|
|
1172
|
+
return (0, react.createElement)("button", {
|
|
1173
|
+
style: chipStyle,
|
|
1174
|
+
title: "YOLO",
|
|
1175
|
+
onClick: () => store.togglePopup()
|
|
1176
|
+
}, (0, react.createElement)("span", { style: {
|
|
1177
|
+
width: 8,
|
|
1178
|
+
height: 8,
|
|
1179
|
+
borderRadius: 4,
|
|
1180
|
+
backgroundColor: dotColor(statusInfo),
|
|
1181
|
+
display: "inline-block"
|
|
1182
|
+
} }), "YOLO " + preset);
|
|
1183
|
+
}
|
|
1184
|
+
//#endregion
|
|
1157
1185
|
//#region src/client/ui/Popup.js
|
|
1158
1186
|
/**
|
|
1159
|
-
* YOLO mode
|
|
1160
|
-
* flag is false
|
|
1161
|
-
*
|
|
1162
|
-
*
|
|
1163
|
-
* - `config` (default): the full configuration form (preset / modes / judge
|
|
1164
|
-
* provider+model / timeout / maxTokens / concurrency / system prompt /
|
|
1165
|
-
* levels), reusing ./ConfigForm.js.
|
|
1166
|
-
* - `status`: the stats card, the recent decisions table (paged, PAGE_SIZE per
|
|
1167
|
-
* page, newest first), the "open audit log" button and a refresh button.
|
|
1187
|
+
* YOLO mode status popup (slot `shell.overlay`). Renders null while the store's
|
|
1188
|
+
* open flag is false; otherwise a stats card plus the recent decisions table
|
|
1189
|
+
* (paged, PAGE_SIZE per page, newest first), an "open audit log" button, and a
|
|
1190
|
+
* refresh button.
|
|
1168
1191
|
*
|
|
1169
|
-
* Guard/hook split mirrors ConfigForm: the outer component validates the
|
|
1170
|
-
* inject face, the inner component owns all hooks unconditionally.
|
|
1192
|
+
* Guard/hook split mirrors ConfigForm: the outer component validates the
|
|
1193
|
+
* slot inject face, the inner component owns all hooks unconditionally.
|
|
1171
1194
|
*/
|
|
1172
1195
|
/** Rows per page of the recent-decisions table. */
|
|
1173
1196
|
const PAGE_SIZE = 5;
|
|
@@ -1284,35 +1307,6 @@ window.__ModuleLoader__.load({
|
|
|
1284
1307
|
};
|
|
1285
1308
|
const logResultOk = { color: "#16a34a" };
|
|
1286
1309
|
const logResultErr = { color: "#dc2626" };
|
|
1287
|
-
/** Body wrapper for the status tab (the config tab renders ConfigForm directly). */
|
|
1288
|
-
const statusBodyStyle = {
|
|
1289
|
-
display: "flex",
|
|
1290
|
-
flexDirection: "column",
|
|
1291
|
-
gap: 12
|
|
1292
|
-
};
|
|
1293
|
-
const tabStyle = {
|
|
1294
|
-
backgroundColor: "#ffffff",
|
|
1295
|
-
color: "#374151",
|
|
1296
|
-
border: "1px solid #cbd5e1",
|
|
1297
|
-
borderRadius: 6,
|
|
1298
|
-
padding: "5px 12px",
|
|
1299
|
-
fontSize: 12,
|
|
1300
|
-
cursor: "pointer"
|
|
1301
|
-
};
|
|
1302
|
-
const tabActiveStyle = {
|
|
1303
|
-
backgroundColor: "#2563eb",
|
|
1304
|
-
color: "#ffffff",
|
|
1305
|
-
borderColor: "#2563eb"
|
|
1306
|
-
};
|
|
1307
|
-
/** One panel tab switch; the active tab is highlighted and non-submit. */
|
|
1308
|
-
function tabButton(t, id, current, setTab) {
|
|
1309
|
-
return (0, react.createElement)("button", {
|
|
1310
|
-
key: id,
|
|
1311
|
-
type: "button",
|
|
1312
|
-
style: Object.assign({}, tabStyle, id === current ? tabActiveStyle : {}),
|
|
1313
|
-
onClick: () => setTab(id)
|
|
1314
|
-
}, t(id === "config" ? "tabConfig" : "tabStatus"));
|
|
1315
|
-
}
|
|
1316
1310
|
/** Truncate long reason text for display. */
|
|
1317
1311
|
function truncate(text, max) {
|
|
1318
1312
|
if (typeof text !== "string") return "";
|
|
@@ -1334,7 +1328,6 @@ window.__ModuleLoader__.load({
|
|
|
1334
1328
|
}
|
|
1335
1329
|
function ReactPopup({ store, useSnapshot, t }) {
|
|
1336
1330
|
const state = useSnapshot((s) => s);
|
|
1337
|
-
const [tab, setTab] = (0, react.useState)("config");
|
|
1338
1331
|
const [page, setPage] = (0, react.useState)(0);
|
|
1339
1332
|
const [logResult, setLogResult] = (0, react.useState)(void 0);
|
|
1340
1333
|
const [logBusy, setLogBusy] = (0, react.useState)(false);
|
|
@@ -1372,55 +1365,43 @@ window.__ModuleLoader__.load({
|
|
|
1372
1365
|
setLogBusy(false);
|
|
1373
1366
|
}
|
|
1374
1367
|
};
|
|
1375
|
-
|
|
1376
|
-
logResult !== void 0 ? (0, react.createElement)("p", { style: Object.assign({}, captionStyle, logResult.ok ? logResultOk : logResultErr) }, logResult.ok ? t("openLogOk") + (logResult.value && logResult.value.path ? ": " + logResult.value.path : "") : logMessage(t, logResult.error)) : auditFile !== void 0 ? (0, react.createElement)("p", { style: captionStyle }, t("logPath") + ": " + auditFile) : null,
|
|
1377
|
-
(0, react.createElement)("div", { style: statGrid }, statCellOf(t, "statsTotal", stats.total), statCellOf(t, "statsAllowed", stats.allowed), statCellOf(t, "statsRejected", stats.rejected), statCellOf(t, "statsDelegated", stats.delegated)),
|
|
1378
|
-
recent.length === 0 ? (0, react.createElement)("p", { style: {
|
|
1379
|
-
color: "#6b7280",
|
|
1380
|
-
fontSize: 12,
|
|
1381
|
-
margin: 0
|
|
1382
|
-
} }, t("recentEmpty")) : (0, react.createElement)("table", { style: tableStyle }, (0, react.createElement)("thead", {}, (0, react.createElement)("tr", {}, (0, react.createElement)("th", { style: thStyle }, t("tableTime")), (0, react.createElement)("th", { style: thStyle }, t("tableTool")), (0, react.createElement)("th", { style: thStyle }, t("tableTarget")), (0, react.createElement)("th", { style: thStyle }, t("tableDecision")), (0, react.createElement)("th", { style: thStyle }, t("tableOutcome")), (0, react.createElement)("th", { style: thStyle }, t("tableReason")))), (0, react.createElement)("tbody", {}, pageRows.map((row, idx) => tableRow(t, row, safePage * PAGE_SIZE + idx)))),
|
|
1383
|
-
totalPages > 1 ? (0, react.createElement)("div", { style: {
|
|
1384
|
-
display: "flex",
|
|
1385
|
-
alignItems: "center",
|
|
1386
|
-
justifyContent: "flex-end",
|
|
1387
|
-
gap: 8
|
|
1388
|
-
} }, (0, react.createElement)("button", {
|
|
1389
|
-
style: Object.assign({}, pagerButton, safePage === 0 ? pagerButtonDisabled : {}),
|
|
1390
|
-
disabled: safePage === 0,
|
|
1391
|
-
onClick: () => setPage(safePage - 1)
|
|
1392
|
-
}, t("pagePrev")), (0, react.createElement)("span", { style: {
|
|
1393
|
-
fontSize: 12,
|
|
1394
|
-
color: "#374151"
|
|
1395
|
-
} }, t("pageOf", {
|
|
1396
|
-
current: String(safePage + 1),
|
|
1397
|
-
total: String(totalPages)
|
|
1398
|
-
})), (0, react.createElement)("button", {
|
|
1399
|
-
style: Object.assign({}, pagerButton, safePage >= totalPages - 1 ? pagerButtonDisabled : {}),
|
|
1400
|
-
disabled: safePage >= totalPages - 1,
|
|
1401
|
-
onClick: () => setPage(safePage + 1)
|
|
1402
|
-
}, t("pageNext"))) : null,
|
|
1403
|
-
(0, react.createElement)("div", { style: {
|
|
1404
|
-
display: "flex",
|
|
1405
|
-
justifyContent: "flex-end"
|
|
1406
|
-
} }, (0, react.createElement)("button", {
|
|
1407
|
-
style: primaryButton,
|
|
1408
|
-
onClick: () => store.togglePopup()
|
|
1409
|
-
}, t("close")))
|
|
1410
|
-
];
|
|
1411
|
-
return (0, react.createElement)("div", { style: popupStyle }, (0, react.createElement)("div", { style: headerStyle }, (0, react.createElement)("strong", {}, t("chip")), (0, react.createElement)("div", { style: headerButtons }, tabButton(t, "config", tab, setTab), tabButton(t, "status", tab, setTab), tab === "status" ? (0, react.createElement)("button", {
|
|
1368
|
+
return (0, react.createElement)("div", { style: popupStyle }, (0, react.createElement)("div", { style: headerStyle }, (0, react.createElement)("strong", {}, t("chip")), (0, react.createElement)("div", { style: headerButtons }, (0, react.createElement)("button", {
|
|
1412
1369
|
style: logBusy ? Object.assign({}, secondaryButton, { opacity: .6 }) : secondaryButton,
|
|
1413
1370
|
onClick: () => void openLog(),
|
|
1414
1371
|
title: auditFile !== void 0 ? auditFile : void 0
|
|
1415
|
-
}, logBusy ? t("openLogBusy") : t("openLog"))
|
|
1372
|
+
}, logBusy ? t("openLogBusy") : t("openLog")), (0, react.createElement)("button", {
|
|
1416
1373
|
style: primaryButton,
|
|
1417
1374
|
onClick: () => void store.load()
|
|
1418
|
-
}, t("refresh")))),
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1375
|
+
}, t("refresh")))), logResult !== void 0 ? (0, react.createElement)("p", { style: Object.assign({}, captionStyle, logResult.ok ? logResultOk : logResultErr) }, logResult.ok ? t("openLogOk") + (logResult.value && logResult.value.path ? ": " + logResult.value.path : "") : logMessage(t, logResult.error)) : auditFile !== void 0 ? (0, react.createElement)("p", { style: captionStyle }, t("logPath") + ": " + auditFile) : null, (0, react.createElement)("div", { style: statGrid }, statCellOf(t, "statsTotal", stats.total), statCellOf(t, "statsAllowed", stats.allowed), statCellOf(t, "statsRejected", stats.rejected), statCellOf(t, "statsDelegated", stats.delegated)), recent.length === 0 ? (0, react.createElement)("p", { style: {
|
|
1376
|
+
color: "#6b7280",
|
|
1377
|
+
fontSize: 12,
|
|
1378
|
+
margin: 0
|
|
1379
|
+
} }, t("recentEmpty")) : (0, react.createElement)("table", { style: tableStyle }, (0, react.createElement)("thead", {}, (0, react.createElement)("tr", {}, (0, react.createElement)("th", { style: thStyle }, t("tableTime")), (0, react.createElement)("th", { style: thStyle }, t("tableTool")), (0, react.createElement)("th", { style: thStyle }, t("tableTarget")), (0, react.createElement)("th", { style: thStyle }, t("tableDecision")), (0, react.createElement)("th", { style: thStyle }, t("tableOutcome")), (0, react.createElement)("th", { style: thStyle }, t("tableReason")))), (0, react.createElement)("tbody", {}, pageRows.map((row, idx) => tableRow(t, row, safePage * PAGE_SIZE + idx)))), totalPages > 1 ? (0, react.createElement)("div", { style: {
|
|
1380
|
+
display: "flex",
|
|
1381
|
+
alignItems: "center",
|
|
1382
|
+
justifyContent: "flex-end",
|
|
1383
|
+
gap: 8
|
|
1384
|
+
} }, (0, react.createElement)("button", {
|
|
1385
|
+
style: Object.assign({}, pagerButton, safePage === 0 ? pagerButtonDisabled : {}),
|
|
1386
|
+
disabled: safePage === 0,
|
|
1387
|
+
onClick: () => setPage(safePage - 1)
|
|
1388
|
+
}, t("pagePrev")), (0, react.createElement)("span", { style: {
|
|
1389
|
+
fontSize: 12,
|
|
1390
|
+
color: "#374151"
|
|
1391
|
+
} }, t("pageOf", {
|
|
1392
|
+
current: String(safePage + 1),
|
|
1393
|
+
total: String(totalPages)
|
|
1394
|
+
})), (0, react.createElement)("button", {
|
|
1395
|
+
style: Object.assign({}, pagerButton, safePage >= totalPages - 1 ? pagerButtonDisabled : {}),
|
|
1396
|
+
disabled: safePage >= totalPages - 1,
|
|
1397
|
+
onClick: () => setPage(safePage + 1)
|
|
1398
|
+
}, t("pageNext"))) : null, (0, react.createElement)("div", { style: {
|
|
1399
|
+
display: "flex",
|
|
1400
|
+
justifyContent: "flex-end"
|
|
1401
|
+
} }, (0, react.createElement)("button", {
|
|
1402
|
+
style: primaryButton,
|
|
1403
|
+
onClick: () => store.togglePopup()
|
|
1404
|
+
}, t("close"))));
|
|
1424
1405
|
}
|
|
1425
1406
|
/** User-facing message for a failed open-log attempt (by error code). */
|
|
1426
1407
|
function logMessage(t, error) {
|
|
@@ -1476,24 +1457,34 @@ window.__ModuleLoader__.load({
|
|
|
1476
1457
|
/**
|
|
1477
1458
|
* YOLO mode — browser half (DSH client plugin).
|
|
1478
1459
|
*
|
|
1479
|
-
* Registers
|
|
1480
|
-
*
|
|
1481
|
-
*
|
|
1460
|
+
* Registers three slots:
|
|
1461
|
+
* - `plugins.bundle.config` (key `dsh-yolo-mode`): ALL configuration, rendered
|
|
1462
|
+
* on this plugin's own page in the plugin manager (插件列表 → dsh-yolo-mode),
|
|
1463
|
+
* between the package description and the component rows;
|
|
1464
|
+
* - `conversation.input.left`: the `YOLO <preset>` status chip;
|
|
1465
|
+
* - `shell.overlay`: the stats / recent-decisions panel that chip opens.
|
|
1466
|
+
*
|
|
1467
|
+
* The `settings.section` page stays retired: nothing is configured from the DSH
|
|
1482
1468
|
* Settings surface any more. Data flows through the connection's generic RPC
|
|
1483
1469
|
* channel (/yolo-mode) into a snapshot store; writes travel as path ops through
|
|
1484
1470
|
* settingsMutate with an optimistic-revision lock.
|
|
1485
1471
|
*/
|
|
1486
1472
|
/**
|
|
1487
1473
|
* Services required by these slot registrations (dsh.client.inject is the same
|
|
1488
|
-
* short-name set): slots, locale, connection, remote, remote.llm.
|
|
1489
|
-
*
|
|
1474
|
+
* short-name set): slots, locale, connection, remote, remote.llm, remote.session.
|
|
1475
|
+
* Both dotted Remote namespaces are REQUIRED, not optional: the client Remote
|
|
1476
|
+
* proxy is inject-gated and throws `cannot get property "remote.X" without
|
|
1477
|
+
* inject` on an undeclared read — it does not return undefined. The store reads
|
|
1478
|
+
* `remote.llm` for the provider directory and `remote.session` for the model
|
|
1479
|
+
* catalog, so both must be declared here (and in package.json's dsh.client.inject).
|
|
1490
1480
|
*/
|
|
1491
1481
|
const inject = [
|
|
1492
1482
|
"slots",
|
|
1493
1483
|
"locale",
|
|
1494
1484
|
"connection",
|
|
1495
1485
|
"remote",
|
|
1496
|
-
"remote.llm"
|
|
1486
|
+
"remote.llm",
|
|
1487
|
+
"remote.session"
|
|
1497
1488
|
];
|
|
1498
1489
|
/**
|
|
1499
1490
|
* Register the YOLO chip/panel once the slot declarations are on the ledger,
|
|
@@ -1535,6 +1526,12 @@ window.__ModuleLoader__.load({
|
|
|
1535
1526
|
useSnapshot,
|
|
1536
1527
|
t
|
|
1537
1528
|
});
|
|
1529
|
+
ctx.slots.inject("plugins.bundle.config", () => ctx.slots.register({
|
|
1530
|
+
name: "plugins.bundle.config",
|
|
1531
|
+
key: "dsh-yolo-mode",
|
|
1532
|
+
locale: NS,
|
|
1533
|
+
inject: injected
|
|
1534
|
+
}, ConfigForm));
|
|
1538
1535
|
ctx.slots.inject("conversation.input.left", () => ctx.slots.register({
|
|
1539
1536
|
name: "conversation.input.left",
|
|
1540
1537
|
id: "yolo-mode-chip",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-yolo-mode",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.2",
|
|
4
4
|
"description": "dsh-yolo-mode —— DeepSeek Harness 双面包插件:当会话处于可写沙箱模式且审批策略为 ask 时,用大模型自动裁决沙箱升权申请,支持内置预设与自定义权限层级,并提供宿主 settings + 自发布设置桥(/yolo-mode)与 Web 客户端 UI。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -73,7 +73,8 @@
|
|
|
73
73
|
"locale",
|
|
74
74
|
"connection",
|
|
75
75
|
"remote",
|
|
76
|
-
"remote.llm"
|
|
76
|
+
"remote.llm",
|
|
77
|
+
"remote.session"
|
|
77
78
|
]
|
|
78
79
|
},
|
|
79
80
|
"compatibility": {
|