@michengai/dsh-codex-ui 0.2.99 → 0.2.100

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
@@ -4,6 +4,24 @@
4
4
 
5
5
  This changelog records recent releases of DSH Codex UI and its one-click installer. Earlier changes remain available in the [Git history](https://github.com/MichengAI/dsh-codex-ui/commits/main).
6
6
 
7
+ ## 0.2.100 — 2026-09-01
8
+
9
+ Companion release: `@michengai/dsh-codex-suite-installer@0.1.17`.
10
+
11
+ ### Fixed
12
+
13
+ - Added pending-interaction indicators to workspace, channel, and scheduled-task trees for questions, approvals, and plan reviews, with the warning state taking precedence over unread and running indicators.
14
+ - Aligned all three trees with the official `SessionSummary.pendingInteraction` contract, removed the unused host-store abstraction, and matched the official English status labels.
15
+ - Removed the package release-age guard from About-page companion installs and updates so explicitly requested versions can be installed immediately.
16
+
17
+ ### Tests
18
+
19
+ - Added DOM rendering coverage for pending-interaction states in all three trees using fully typed `SessionListState` and `SessionSummary` snapshots.
20
+
21
+ ### Dependencies
22
+
23
+ - The Suite Installer release workflow will resolve Codex UI `0.2.100` after this release is published.
24
+
7
25
  ## 0.2.99 — 2026-09-01
8
26
 
9
27
  Companion release: `@michengai/dsh-codex-suite-installer@0.1.16`.
@@ -4,6 +4,24 @@
4
4
 
5
5
  本日志记录 DSH Codex UI 及其一键安装器的最近发布;更早的变更可查看 [Git 提交历史](https://github.com/MichengAI/dsh-codex-ui/commits/main)。
6
6
 
7
+ ## 0.2.100 — 2026-09-01
8
+
9
+ 配套版本:`@michengai/dsh-codex-suite-installer@0.1.17`。
10
+
11
+ ### 修复
12
+
13
+ - 在任务、频道和定时任务树中显示等待回答、等待审批和计划待审状态,并让待处理警告优先于未读和运行中标记。
14
+ - 三棵树统一使用官方 `SessionSummary.pendingInteraction` 契约,删除未使用的宿主状态抽象,并对齐官方英文状态文案。
15
+ - 移除关于页配套插件安装与更新的发布时间保护,让用户明确请求的版本可以立即安装。
16
+
17
+ ### 测试
18
+
19
+ - 使用完整类型约束的 `SessionListState` 和 `SessionSummary` 快照,为三棵树补充待处理状态 DOM 渲染测试。
20
+
21
+ ### 依赖
22
+
23
+ - Suite Installer 发布 workflow 会在本版本发布后解析 Codex UI `0.2.100`。
24
+
7
25
  ## 0.2.99 — 2026-09-01
8
26
 
9
27
  配套版本:`@michengai/dsh-codex-suite-installer@0.1.16`。
package/dist/client.js CHANGED
@@ -501,7 +501,41 @@ window.__ModuleLoader__.load({
501
501
  function pointerMenuRect(x, y) {
502
502
  return new DOMRect(x, y, 0, 0);
503
503
  }
504
- function SessionRow({ id, title, selected, menuOpen, pinned, unread, running, t, menuItems, onOpen, onMenuChange, onSelectAction, onPin, onArchive, onHover, onLeave, onContextMenu, menuPoint, subtitle, flat, draggable, dropActive, onDragStart, onDragEnd, onDragOver, onDrop }) {
504
+ function pendingLabel(kind, t) {
505
+ switch (kind) {
506
+ case "approval": return t("sessions.waitingApproval");
507
+ case "plan-review": return t("sessions.planReview");
508
+ case "question": return t("sessions.waitingAnswer");
509
+ }
510
+ }
511
+ function SessionState({ pendingInteraction, unread, running, t }) {
512
+ if (pendingInteraction !== void 0) {
513
+ const label = pendingLabel(pendingInteraction, t);
514
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
515
+ className: "dcu-wb-pending",
516
+ "data-state": "warning",
517
+ "data-pending-kind": pendingInteraction,
518
+ "aria-label": label,
519
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
520
+ className: "dcu-wb-pending-dot",
521
+ "aria-hidden": "true"
522
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
523
+ className: "dcu-wb-pending-label",
524
+ children: label
525
+ })]
526
+ });
527
+ }
528
+ if (unread) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
529
+ className: "dcu-wb-unread",
530
+ "aria-label": t("sessions.unread")
531
+ });
532
+ if (running) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
533
+ className: "dcu-wb-running",
534
+ "aria-hidden": "true"
535
+ });
536
+ return null;
537
+ }
538
+ function SessionRow({ id, title, selected, menuOpen, pinned, unread, running, pendingInteraction, t, menuItems, onOpen, onMenuChange, onSelectAction, onPin, onArchive, onHover, onLeave, onContextMenu, menuPoint, subtitle, flat, draggable, dropActive, onDragStart, onDragEnd, onDragOver, onDrop }) {
505
539
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
506
540
  className: `dcu-wb-session${selected ? " dcu-wb-selected" : ""}${menuOpen ? " dcu-wb-menu-open" : ""}${dropActive === true ? " dcu-wb-drop" : ""}${flat === true ? " dcu-wb-session-flat" : ""}`,
507
541
  role: "treeitem",
@@ -534,13 +568,12 @@ window.__ModuleLoader__.load({
534
568
  "aria-label": t("sessions.pinned"),
535
569
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PinMark, {})
536
570
  }),
537
- unread ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
538
- className: "dcu-wb-unread",
539
- "aria-label": t("sessions.unread")
540
- }) : running ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
541
- className: "dcu-wb-running",
542
- "aria-hidden": "true"
543
- }) : null,
571
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SessionState, {
572
+ pendingInteraction,
573
+ unread,
574
+ running,
575
+ t
576
+ }),
544
577
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
545
578
  className: "dcu-wb-quick-actions",
546
579
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
@@ -621,6 +654,16 @@ window.__ModuleLoader__.load({
621
654
  });
622
655
  }
623
656
  //#endregion
657
+ //#region src/client/session-pending.ts
658
+ function visiblePendingKind(kind) {
659
+ switch (kind) {
660
+ case "approval":
661
+ case "plan-review":
662
+ case "question": return kind;
663
+ default: return;
664
+ }
665
+ }
666
+ //#endregion
624
667
  //#region src/client/pinned-workspaces.ts
625
668
  /** 浏览器本地持久化键;置顶只影响本插件中的工作区分区。 */
626
669
  const PINNED_WORKSPACES_STORAGE_KEY = "dsh-codex-ui.pinned-workspace-ids";
@@ -1361,6 +1404,7 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
1361
1404
  .dcu-wb-context-anchor{opacity:0;pointer-events:none}
1362
1405
  .dcu-wb-tip{position:fixed;z-index:10050;min-width:220px;max-width:280px;padding:10px 12px;border:1px solid var(--dcu-sidebar-border);border-radius:12px;background:var(--dcu-tip-bg,#fff);box-shadow:var(--dcu-tip-shadow,0 8px 28px rgba(31,39,36,.16));color:var(--dcu-sidebar-primary);animation:dcu-tip-in 180ms cubic-bezier(.16,1,.3,1)}@keyframes dcu-tip-in{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}@media (prefers-reduced-motion:reduce){.dcu-wb-tip{animation:none}}.dcu-wb-tip-title{display:flex;align-items:center;justify-content:space-between;gap:12px;min-width:0;font-size:14px;line-height:20px;font-weight:500}.dcu-wb-tip-time{flex:none;color:var(--dcu-sidebar-tertiary);font-size:12px;line-height:18px;font-weight:400}.dcu-wb-tip-title>span{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dcu-wb-tip-meta{margin-top:6px;color:var(--dcu-sidebar-secondary);font-size:12px;line-height:18px}.dcu-wb-tip-row{display:flex;align-items:center;gap:6px;min-width:0;margin-top:4px;color:var(--dcu-sidebar-secondary);font-size:12px;line-height:18px}.dcu-wb-tip-row>span{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dcu-wb-tip-sep{height:1px;margin:8px 0;background:var(--dcu-sidebar-border)}.dcu-wb-tip-edit{display:flex;align-items:center;gap:8px;width:100%;border:0;padding:4px 0;background:transparent;color:var(--dcu-sidebar-primary);font:12px/18px var(--dcu-font,inherit);cursor:pointer}
1363
1406
  .dcu-wb-unread{flex:none;width:7px;height:7px;margin-left:8px;margin-right:8px;border-radius:50%;background:var(--dsw-alias-state-business-primary)}
1407
+ .dcu-wb-pending{display:flex;align-items:center;gap:4px;flex:none;max-width:88px;margin-left:8px;margin-right:8px;color:var(--dsw-alias-state-warn-label,#b45309);font-size:11px;line-height:16px;white-space:nowrap}.dcu-wb-pending-dot{flex:none;width:7px;height:7px;border-radius:50%;background:var(--dsw-alias-state-warn-primary,#f59e0b)}.dcu-wb-pending-label{min-width:0;overflow:hidden;text-overflow:ellipsis}
1364
1408
  .dcu-wb-empty{padding:14px 8px;color:var(--dcu-sidebar-tertiary);font-size:13px}
1365
1409
  .dcu-wb-error{margin:4px 0;padding:6px 8px;border-radius:6px;background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 12%,transparent);color:var(--dsw-alias-state-error-primary);font-size:12px}
1366
1410
  .dcu-wb-rail{display:none}
@@ -1905,6 +1949,7 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
1905
1949
  isExpanded && shownIds.map((id) => {
1906
1950
  const session = sessions.byId[id];
1907
1951
  if (session === void 0) return null;
1952
+ const pendingInteraction = visiblePendingKind(session.pendingInteraction);
1908
1953
  const path = session.cwd ?? workspace.path;
1909
1954
  const selected = sessions.current === id;
1910
1955
  const sessionMenuOpen = menu?.type === "session" && menu.id === id;
@@ -2013,13 +2058,12 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
2013
2058
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M5 2.5h6M5 2.5v2L3.75 6v1h8.5V6L11 4.5v-2M8 7v6" })
2014
2059
  })
2015
2060
  }),
2016
- unreadSessionIds.includes(id) ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2017
- className: "dcu-wb-unread",
2018
- "aria-label": t("sessions.unread")
2019
- }) : session.running ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2020
- className: "dcu-wb-running",
2021
- "aria-hidden": "true"
2022
- }) : null,
2061
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SessionState, {
2062
+ pendingInteraction,
2063
+ unread: unreadSessionIds.includes(id),
2064
+ running: session.running === true,
2065
+ t
2066
+ }),
2023
2067
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2024
2068
  className: "dcu-wb-quick-actions",
2025
2069
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
@@ -2318,6 +2362,7 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
2318
2362
  const title = session.displayTitle;
2319
2363
  const pinned = pinnedSessionIds.includes(id);
2320
2364
  const unread = unreadSessionIds.includes(id);
2365
+ const pendingInteraction = visiblePendingKind(session.pendingInteraction);
2321
2366
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SessionRow, {
2322
2367
  id,
2323
2368
  title,
@@ -2326,6 +2371,7 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
2326
2371
  pinned,
2327
2372
  unread,
2328
2373
  running: session.running === true,
2374
+ pendingInteraction,
2329
2375
  t,
2330
2376
  menuItems: sessionMenu(id, title, session.cwd),
2331
2377
  draggable: true,
@@ -2922,6 +2968,7 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
2922
2968
  const updatedAt = session.updatedAt ?? sessions.byId[id]?.updatedAt;
2923
2969
  const pinned = flags.pinnedSessionIds.includes(id);
2924
2970
  const unread = flags.unreadSessionIds.includes(id);
2971
+ const pendingInteraction = visiblePendingKind(sessions.byId[id]?.pendingInteraction);
2925
2972
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SessionRow, {
2926
2973
  id,
2927
2974
  title,
@@ -2930,6 +2977,7 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
2930
2977
  pinned,
2931
2978
  unread,
2932
2979
  running,
2980
+ pendingInteraction,
2933
2981
  t,
2934
2982
  menuItems: sessionMenuItems(t, {
2935
2983
  pinned,
@@ -3211,6 +3259,7 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
3211
3259
  const title = session.title;
3212
3260
  const pinned = flags.pinnedSessionIds.includes(id);
3213
3261
  const unread = flags.unreadSessionIds.includes(id);
3262
+ const pendingInteraction = visiblePendingKind(sessions.byId[id]?.pendingInteraction);
3214
3263
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SessionRow, {
3215
3264
  id,
3216
3265
  title,
@@ -3219,6 +3268,7 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
3219
3268
  pinned,
3220
3269
  unread,
3221
3270
  running: session.running,
3271
+ pendingInteraction,
3222
3272
  t,
3223
3273
  menuItems: sessionMenuItems(t, {
3224
3274
  pinned,
@@ -4836,6 +4886,9 @@ html[data-dcu-official-turn-navigator-supported=true] .dcu-turn-navigator,html:h
4836
4886
  "sessions.markUnread": "标记为未读",
4837
4887
  "sessions.markRead": "标记为已读",
4838
4888
  "sessions.unread": "未读",
4889
+ "sessions.waitingAnswer": "等待回答",
4890
+ "sessions.waitingApproval": "等待审批",
4891
+ "sessions.planReview": "计划待审",
4839
4892
  "sessions.rename": "重命名会话",
4840
4893
  "sessions.renameDescription": "保持简短且易于识别",
4841
4894
  "sessions.archive": "归档会话",
@@ -4976,6 +5029,9 @@ html[data-dcu-official-turn-navigator-supported=true] .dcu-turn-navigator,html:h
4976
5029
  "sessions.markUnread": "Mark as unread",
4977
5030
  "sessions.markRead": "Mark as read",
4978
5031
  "sessions.unread": "Unread",
5032
+ "sessions.waitingAnswer": "Waiting for answer",
5033
+ "sessions.waitingApproval": "Waiting for approval",
5034
+ "sessions.planReview": "Plan awaiting review",
4979
5035
  "sessions.rename": "Rename conversation",
4980
5036
  "sessions.renameDescription": "Keep it short and easy to recognize.",
4981
5037
  "sessions.archive": "Archive conversation",
package/dist/index.mjs CHANGED
@@ -226,29 +226,6 @@ async function npmLatestVersion(packageName) {
226
226
  if (isOfficialRuntimePackage(packageName)) return await npmTaggedVersion(packageName, "next") ?? await npmTaggedVersion(packageName, "latest");
227
227
  return npmTaggedVersion(packageName, "latest");
228
228
  }
229
- function escapeRegExp(value) {
230
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
231
- }
232
- /** 允许写入 YAML 单引号白名单的版本:semver 及常见预发布后缀,禁止引号与空白。 */
233
- function isSafeReleaseVersion(version) {
234
- return /^v?\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/.test(version);
235
- }
236
- /** 将用户确认的精确版本合并进 Profile 的 pnpm 发布时间保护例外。 */
237
- function applyReleaseExclude(source, packageName, version) {
238
- if (!isSafeReleaseVersion(version)) throw new Error("npm 返回了无法识别的最新版本。");
239
- const eol = source.includes("\r\n") ? "\r\n" : "\n";
240
- const existing = new RegExp(`^ - '${escapeRegExp(packageName)}@([^']*)'\\s*$`, "m").exec(source);
241
- if (existing !== null) {
242
- const versions = existing[1].split(/\s*\|\|\s*/).map((item) => item.trim()).filter((item) => item !== "");
243
- if (versions.includes(version)) return source;
244
- const next = ` - '${packageName}@${[...versions, version].join(" || ")}'`;
245
- return `${source.slice(0, existing.index)}${next}${source.slice(existing.index + existing[0].length)}`;
246
- }
247
- const entry = ` - '${packageName}@${version}'`;
248
- const section = /^minimumReleaseAgeExclude:\r?\n(?:(?: |\t).*(?:\r?\n|$))*/m;
249
- if (section.test(source)) return source.replace(section, (match) => `${match.endsWith("\n") ? match : `${match}${eol}`}${entry}${eol}`);
250
- return `${source}${source === "" || source.endsWith("\n") ? "" : eol}minimumReleaseAgeExclude:${eol}${entry}${eol}`;
251
- }
252
229
  /**
253
230
  * pnpm 11 会在首次解析带 install script 的传递依赖时中止并写入占位值。
254
231
  * 这些依赖的运行时不需要 postinstall,因此在调用 DSH plugin add 之前显式
@@ -277,20 +254,6 @@ async function ensureRequiredBuildPolicies(runtime) {
277
254
  const next = applyRequiredBuildPolicies(source);
278
255
  if (next !== source) await writeFile(path, next, "utf8");
279
256
  }
280
- /** 将用户本次确认的精确版本加入 Profile 的 pnpm 发布时间保护例外。 */
281
- async function ensureLatestReleaseAllowed(packageName, version, runtime) {
282
- if (parseSemver(version) === void 0) throw new Error("npm 返回了无法识别的最新版本。");
283
- const path = resolve(profileDirectory(runtime), "pnpm-workspace.yaml");
284
- let source;
285
- try {
286
- source = await readFile(path, "utf8");
287
- } catch (error) {
288
- if (error.code !== "ENOENT") throw error;
289
- source = "";
290
- }
291
- const next = applyReleaseExclude(source, packageName, version);
292
- if (next !== source) await writeFile(path, next, "utf8");
293
- }
294
257
  function parseSemver(version) {
295
258
  const match = /^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/.exec(version);
296
259
  if (match === null) return void 0;
@@ -542,7 +505,6 @@ async function waitUntilPluginMounted(packageName, version, runtime, timeoutMs =
542
505
  }
543
506
  function pluginCommandError(stderr) {
544
507
  const detail = stderr.replace(/\s+/g, " ").trim();
545
- if (detail.includes("minimumReleaseAge") || detail.includes("Release age")) return /* @__PURE__ */ new Error("更新被 pnpm 发布时间保护拦截。请确认已写入当前版本白名单后重试。");
546
508
  if (/EPERM|EBUSY|EACCES|unable to unlink|ERR_PNPM_LOCKED|Lock/i.test(detail)) return /* @__PURE__ */ new Error("无法覆盖正在运行的插件文件。请先完全退出桌面端,再重新打开后更新。");
547
509
  if (/UNEXPECTED_STORE|Unexpected store location/i.test(detail)) return /* @__PURE__ */ new Error("插件目录和 pnpm 仓库不一致。请完全退出桌面端后再更新。");
548
510
  if (/ERR_PNPM_IGNORED_BUILDS|Ignored build scripts/i.test(detail)) return /* @__PURE__ */ new Error("插件依赖的 pnpm 构建脚本策略尚未确认。请更新 Profile 的 allowBuilds 配置后重试。");
@@ -798,7 +760,6 @@ async function installDependenciesLocked(ids, requestHotUpdate, runtime) {
798
760
  const remove = [...new Set(targets.flatMap((target) => pluginsToRemoveBeforeInstall(declared, target.packageName)))];
799
761
  await ensureRequiredBuildPolicies(runtime);
800
762
  for (const target of targets) {
801
- await ensureLatestReleaseAllowed(target.packageName, target.version, runtime);
802
763
  if (!isOfficialRuntimePackage(target.packageName)) await removeUnmountedPackagePath(target.packageName, runtime);
803
764
  await recordPendingUpdate(target.packageName, target.version, runtime);
804
765
  }
@@ -813,6 +774,7 @@ async function installDependenciesLocked(ids, requestHotUpdate, runtime) {
813
774
  for (const batch of batches) {
814
775
  await runDshPlugin([
815
776
  "add",
777
+ "--config.minimumReleaseAge=0",
816
778
  ...batch.map((target) => `${target.packageName}@${target.version}`),
817
779
  "--registry=https://registry.npmjs.org/"
818
780
  ], runtime);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@michengai/dsh-codex-ui",
3
- "version": "0.2.99",
3
+ "version": "0.2.100",
4
4
  "description": "以 Codex 风格重构 DSH Web 侧栏的独立客户端插件",
5
5
  "license": "Apache-2.0",
6
6
  "publishConfig": {