dsh-yolo-mode 0.6.0 → 0.6.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/CHANGELOG.md CHANGED
@@ -2,6 +2,25 @@
2
2
 
3
3
  本项目遵循 [Semantic Versioning](https://semver.org/)。
4
4
 
5
+ ## [0.6.1] - 2026-09-27
6
+
7
+ ### 修复:配置位置改到插件详情页(0.6.0 放错了地方)
8
+
9
+ 0.6.0 把配置放进了输入框旁的 `YOLO <预设>` 胶囊浮层(`shell.overlay`)——那是**状态与日志**
10
+ 的位置,不是「插件的配置面板」。本次改用 DSH 插件管理页声明的专用槽位:
11
+
12
+ - **新增 `plugins.bundle.config` 注册**(key = npm 包名 `dsh-yolo-mode`):配置渲染在
13
+ 「插件列表 → dsh-yolo-mode」页面的**描述与组件行之间**。该槽位是 keyed slot,页面仅在
14
+ 有注册者(且 key 等于包名)时才渲染配置区块
15
+ (`dsh-client-ui-plugin-manager`:`configured = ledger.bundles.has(pkg.name)`)——
16
+ 这正是此前该页面完全没有配置区的原因。
17
+ - bundle 页可容纳多个条目,因此页面**不传宿主 `form`**,由本插件自持草稿、校验与保存
18
+ (仍走 `/yolo-mode` 设置桥 + 乐观修订锁)。`view === 'summary'` 时不渲染
19
+ (bundle 页只请求 `page`)。
20
+ - **胶囊浮层回归纯状态/日志**:撤销 0.6.0 给 `shell.overlay` 加的「配置 / 状态与日志」双标签;
21
+ 点胶囊仍是原来的统计卡 + 裁决表分页 + 打开日志 + 刷新。
22
+ - 文案 `tabConfig` / `tabStatus` 退役(不再有标签页);`settings.section` 保持退役。
23
+
5
24
  ## [0.6.0] - 2026-09-27
6
25
 
7
26
  ### 变更:全部配置移入插件自身 UI 面板(设置页退役)
@@ -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 模式设置。",
@@ -558,60 +554,6 @@ window.__ModuleLoader__.load({
558
554
  }
559
555
  };
560
556
  //#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
557
  //#region src/client/presets.js
616
558
  /**
617
559
  * Static per-preset display data for the YOLO settings page: the level table
@@ -701,8 +643,10 @@ window.__ModuleLoader__.load({
701
643
  //#endregion
702
644
  //#region src/client/ui/ConfigForm.js
703
645
  /**
704
- * YOLO mode configuration form. Rendered inside the plugin's own UI panel
705
- * (`shell.overlay`, see ./Popup.js): the DSH `settings.section` page was retired
646
+ * YOLO mode configuration form. Rendered on this plugin's own page in the DSH
647
+ * plugin manager (插件列表 → dsh-yolo-mode), via the `plugins.bundle.config`
648
+ * slot keyed by the package name (see ../index.js) — between the package
649
+ * description and the component rows. The `settings.section` page was retired,
706
650
  * so this form is the single configuration surface for the plugin. It renders
707
651
  * the resolved configuration (view.value) and saves via path ops through
708
652
  * store.mutate with an optimistic-revision lock. Everything is React.createElement
@@ -889,14 +833,21 @@ window.__ModuleLoader__.load({
889
833
  }
890
834
  };
891
835
  /**
892
- * The YOLO configuration form (hosted by the plugin's own UI panel).
836
+ * The YOLO configuration form.
893
837
  *
894
- * @param {object} props - panel-delivered inject face: { store, useSnapshot, t }
895
- * plus optional close() to dismiss the hosting panel.
838
+ * Hosted by this plugin's own page in the plugin manager: registered into
839
+ * `plugins.bundle.config` under the package name (see ../index.js). The page
840
+ * dispatches `view`; a bundle page only ever asks for `page`, and a bundle may
841
+ * hold several configuration entries, so the page passes NO host `form` — this
842
+ * component owns its draft, validation and save path (the /yolo-mode bridge).
843
+ *
844
+ * @param {object} props - slot props `{ view? }` merged with the inject face
845
+ * `{ store, useSnapshot, t }`; an optional `close()` adds a dismiss control.
896
846
  */
897
847
  function ConfigForm(props) {
898
- const { store, useSnapshot, t, close } = props;
848
+ const { view, store, useSnapshot, t, close } = props;
899
849
  if (store === void 0 || useSnapshot === void 0 || t === void 0) return null;
850
+ if (view === "summary") return null;
900
851
  return ReactSection({
901
852
  store,
902
853
  useSnapshot,
@@ -1154,20 +1105,69 @@ window.__ModuleLoader__.load({
1154
1105
  return ops;
1155
1106
  }
1156
1107
  //#endregion
1108
+ //#region src/client/ui/Chip.js
1109
+ /**
1110
+ * YOLO mode status chip (slot `conversation.input.left`). Shows "YOLO <preset>"
1111
+ * and toggles the shared popup via store.togglePopup().
1112
+ */
1113
+ const chipStyle = {
1114
+ display: "inline-flex",
1115
+ alignItems: "center",
1116
+ gap: 6,
1117
+ padding: "4px 10px",
1118
+ borderRadius: 999,
1119
+ backgroundColor: "#ffffff",
1120
+ color: "#1d4ed8",
1121
+ border: "1px solid #93c5fd",
1122
+ fontSize: 12,
1123
+ cursor: "pointer",
1124
+ whiteSpace: "nowrap",
1125
+ userSelect: "none"
1126
+ };
1127
+ /** Status colour for the leading dot based on judge readiness. */
1128
+ function dotColor(statusInfo) {
1129
+ return statusInfo && statusInfo.judgeConfigured ? "#16a34a" : "#d97706";
1130
+ }
1131
+ /**
1132
+ * @param {object} props - slot-delivered { store, useSnapshot, t } (inject face).
1133
+ */
1134
+ function Chip(props) {
1135
+ const store = props.store;
1136
+ const useSnapshot = props.useSnapshot;
1137
+ if (store === void 0 || useSnapshot === void 0) return null;
1138
+ return ChipLoaded({
1139
+ store,
1140
+ useSnapshot
1141
+ });
1142
+ }
1143
+ function ChipLoaded({ store, useSnapshot }) {
1144
+ (0, react.useEffect)(() => {
1145
+ store.load();
1146
+ }, [store]);
1147
+ const statusInfo = useSnapshot((s) => s).statusInfo;
1148
+ const preset = statusInfo && typeof statusInfo.preset === "string" ? statusInfo.preset : "…";
1149
+ return (0, react.createElement)("button", {
1150
+ style: chipStyle,
1151
+ title: "YOLO",
1152
+ onClick: () => store.togglePopup()
1153
+ }, (0, react.createElement)("span", { style: {
1154
+ width: 8,
1155
+ height: 8,
1156
+ borderRadius: 4,
1157
+ backgroundColor: dotColor(statusInfo),
1158
+ display: "inline-block"
1159
+ } }), "YOLO " + preset);
1160
+ }
1161
+ //#endregion
1157
1162
  //#region src/client/ui/Popup.js
1158
1163
  /**
1159
- * YOLO mode panel (slot `shell.overlay`). Renders null while the store's open
1160
- * flag is false. When open it hosts TWO tabs and is the plugin's single
1161
- * configuration surface (the DSH `settings.section` page was retired):
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.
1164
+ * YOLO mode status popup (slot `shell.overlay`). Renders null while the store's
1165
+ * open flag is false; otherwise a stats card plus the recent decisions table
1166
+ * (paged, PAGE_SIZE per page, newest first), an "open audit log" button, and a
1167
+ * refresh button.
1168
1168
  *
1169
- * Guard/hook split mirrors ConfigForm: the outer component validates the slot
1170
- * inject face, the inner component owns all hooks unconditionally.
1169
+ * Guard/hook split mirrors ConfigForm: the outer component validates the
1170
+ * slot inject face, the inner component owns all hooks unconditionally.
1171
1171
  */
1172
1172
  /** Rows per page of the recent-decisions table. */
1173
1173
  const PAGE_SIZE = 5;
@@ -1284,35 +1284,6 @@ window.__ModuleLoader__.load({
1284
1284
  };
1285
1285
  const logResultOk = { color: "#16a34a" };
1286
1286
  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
1287
  /** Truncate long reason text for display. */
1317
1288
  function truncate(text, max) {
1318
1289
  if (typeof text !== "string") return "";
@@ -1334,7 +1305,6 @@ window.__ModuleLoader__.load({
1334
1305
  }
1335
1306
  function ReactPopup({ store, useSnapshot, t }) {
1336
1307
  const state = useSnapshot((s) => s);
1337
- const [tab, setTab] = (0, react.useState)("config");
1338
1308
  const [page, setPage] = (0, react.useState)(0);
1339
1309
  const [logResult, setLogResult] = (0, react.useState)(void 0);
1340
1310
  const [logBusy, setLogBusy] = (0, react.useState)(false);
@@ -1372,55 +1342,43 @@ window.__ModuleLoader__.load({
1372
1342
  setLogBusy(false);
1373
1343
  }
1374
1344
  };
1375
- const statusBody = [
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", {
1345
+ 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
1346
  style: logBusy ? Object.assign({}, secondaryButton, { opacity: .6 }) : secondaryButton,
1413
1347
  onClick: () => void openLog(),
1414
1348
  title: auditFile !== void 0 ? auditFile : void 0
1415
- }, logBusy ? t("openLogBusy") : t("openLog")) : null, (0, react.createElement)("button", {
1349
+ }, logBusy ? t("openLogBusy") : t("openLog")), (0, react.createElement)("button", {
1416
1350
  style: primaryButton,
1417
1351
  onClick: () => void store.load()
1418
- }, t("refresh")))), tab === "config" ? (0, react.createElement)(ConfigForm, {
1419
- store,
1420
- useSnapshot,
1421
- t,
1422
- close: () => store.togglePopup()
1423
- }) : (0, react.createElement)("div", { style: statusBodyStyle }, ...statusBody));
1352
+ }, 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: {
1353
+ color: "#6b7280",
1354
+ fontSize: 12,
1355
+ margin: 0
1356
+ } }, 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: {
1357
+ display: "flex",
1358
+ alignItems: "center",
1359
+ justifyContent: "flex-end",
1360
+ gap: 8
1361
+ } }, (0, react.createElement)("button", {
1362
+ style: Object.assign({}, pagerButton, safePage === 0 ? pagerButtonDisabled : {}),
1363
+ disabled: safePage === 0,
1364
+ onClick: () => setPage(safePage - 1)
1365
+ }, t("pagePrev")), (0, react.createElement)("span", { style: {
1366
+ fontSize: 12,
1367
+ color: "#374151"
1368
+ } }, t("pageOf", {
1369
+ current: String(safePage + 1),
1370
+ total: String(totalPages)
1371
+ })), (0, react.createElement)("button", {
1372
+ style: Object.assign({}, pagerButton, safePage >= totalPages - 1 ? pagerButtonDisabled : {}),
1373
+ disabled: safePage >= totalPages - 1,
1374
+ onClick: () => setPage(safePage + 1)
1375
+ }, t("pageNext"))) : null, (0, react.createElement)("div", { style: {
1376
+ display: "flex",
1377
+ justifyContent: "flex-end"
1378
+ } }, (0, react.createElement)("button", {
1379
+ style: primaryButton,
1380
+ onClick: () => store.togglePopup()
1381
+ }, t("close"))));
1424
1382
  }
1425
1383
  /** User-facing message for a failed open-log attempt (by error code). */
1426
1384
  function logMessage(t, error) {
@@ -1476,9 +1434,14 @@ window.__ModuleLoader__.load({
1476
1434
  /**
1477
1435
  * YOLO mode — browser half (DSH client plugin).
1478
1436
  *
1479
- * Registers the `conversation.input.left` status chip and the `shell.overlay`
1480
- * panel. ALL configuration (preset/modes/judge/levels) lives in that panel: the
1481
- * `settings.section` page was retired, so nothing is configured from the DSH
1437
+ * Registers three slots:
1438
+ * - `plugins.bundle.config` (key `dsh-yolo-mode`): ALL configuration, rendered
1439
+ * on this plugin's own page in the plugin manager (插件列表 → dsh-yolo-mode),
1440
+ * between the package description and the component rows;
1441
+ * - `conversation.input.left`: the `YOLO <preset>` status chip;
1442
+ * - `shell.overlay`: the stats / recent-decisions panel that chip opens.
1443
+ *
1444
+ * The `settings.section` page stays retired: nothing is configured from the DSH
1482
1445
  * Settings surface any more. Data flows through the connection's generic RPC
1483
1446
  * channel (/yolo-mode) into a snapshot store; writes travel as path ops through
1484
1447
  * settingsMutate with an optimistic-revision lock.
@@ -1535,6 +1498,12 @@ window.__ModuleLoader__.load({
1535
1498
  useSnapshot,
1536
1499
  t
1537
1500
  });
1501
+ ctx.slots.inject("plugins.bundle.config", () => ctx.slots.register({
1502
+ name: "plugins.bundle.config",
1503
+ key: "dsh-yolo-mode",
1504
+ locale: NS,
1505
+ inject: injected
1506
+ }, ConfigForm));
1538
1507
  ctx.slots.inject("conversation.input.left", () => ctx.slots.register({
1539
1508
  name: "conversation.input.left",
1540
1509
  id: "yolo-mode-chip",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-yolo-mode",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "description": "dsh-yolo-mode —— DeepSeek Harness 双面包插件:当会话处于可写沙箱模式且审批策略为 ask 时,用大模型自动裁决沙箱升权申请,支持内置预设与自定义权限层级,并提供宿主 settings + 自发布设置桥(/yolo-mode)与 Web 客户端 UI。",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",