@rezti/dsh-rez-suite 0.1.48 → 0.1.50

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.
Files changed (64) hide show
  1. package/CHANGELOG.md +12 -2
  2. package/README.md +3 -3
  3. package/README.zh.md +2 -2
  4. package/cordis.patch.yml +1 -1
  5. package/lib/client.d.ts +37 -3
  6. package/lib/client.js +533 -8
  7. package/lib/index.js +247 -19
  8. package/lib/style.css +18 -0
  9. package/package.json +4 -3
  10. package/src/boss/mount.ts +4 -3
  11. package/src/boss/register.ts +1 -1
  12. package/src/boss/seed.ts +29 -0
  13. package/src/changelog.ts +48 -0
  14. package/src/channel-board.ts +55 -0
  15. package/src/client/api.ts +15 -0
  16. package/src/client/locales.ts +74 -6
  17. package/src/client/panel/BoardTab.tsx +136 -0
  18. package/src/client/panel/ConfigTab.tsx +137 -2
  19. package/src/client/panel/StatusTab.tsx +31 -0
  20. package/src/client/panel/panel.module.css +18 -0
  21. package/src/client/settings-card.tsx +4 -1
  22. package/src/esphome-lab.ts +4 -3
  23. package/src/index.ts +3 -2
  24. package/src/observe-mcp.ts +1 -1
  25. package/src/protocol.ts +37 -0
  26. package/src/routes.ts +7 -1
  27. package/src/tools.ts +49 -6
  28. package/src/wecom-cli.ts +125 -0
  29. package/templates/boss/ops/AGENTS.md +2 -0
  30. package/templates/shared/wecom-cli.SOURCE.md +5 -0
  31. package/templates/shared/wecom-office/SKILL.md +31 -0
  32. package/templates/shared/wecomcli-calendar/SKILL.md +303 -0
  33. package/templates/shared/wecomcli-contact/SKILL.md +58 -0
  34. package/templates/shared/wecomcli-disk/SKILL.md +389 -0
  35. package/templates/shared/wecomcli-doc/SKILL.md +137 -0
  36. package/templates/shared/wecomcli-doc-manage/SKILL.md +132 -0
  37. package/templates/shared/wecomcli-email/SKILL.md +218 -0
  38. package/templates/shared/wecomcli-media/SKILL.md +98 -0
  39. package/templates/shared/wecomcli-meeting/SKILL.md +373 -0
  40. package/templates/shared/wecomcli-message/SKILL.md +200 -0
  41. package/templates/shared/wecomcli-shared/SKILL.md +73 -0
  42. package/templates/shared/wecomcli-sheet/SKILL.md +172 -0
  43. package/templates/shared/wecomcli-smartpage/SKILL.md +170 -0
  44. package/templates/shared/wecomcli-smartsheet/SKILL.md +154 -0
  45. package/templates/shared/wecomcli-todo/SKILL.md +76 -0
  46. package/templates/staff/design/AGENTS.md +2 -1
  47. package/templates/staff/ecommerce/.agents/skills/ops-ecommerce/SKILL.md +6 -0
  48. package/templates/staff/ecommerce/AGENTS.md +48 -0
  49. package/templates/staff/ecommerce/BOOTSTRAP.md +23 -0
  50. package/templates/staff/ecommerce/IDENTITY.md +8 -0
  51. package/templates/staff/ecommerce/MEMORY.md +9 -0
  52. package/templates/staff/ecommerce/PRIORITIES.md +3 -0
  53. package/templates/staff/ecommerce/SOUL.md +5 -0
  54. package/templates/staff/ecommerce/USER.md +8 -0
  55. package/templates/staff/hr/.agents/skills/staff-onboard-keys/SKILL.md +9 -7
  56. package/templates/staff/lab/AGENTS.md +1 -1
  57. package/templates/staff/publish/.agents/skills/ops-publish/SKILL.md +6 -0
  58. package/templates/staff/publish/AGENTS.md +56 -0
  59. package/templates/staff/publish/BOOTSTRAP.md +23 -0
  60. package/templates/staff/publish/IDENTITY.md +8 -0
  61. package/templates/staff/publish/MEMORY.md +9 -0
  62. package/templates/staff/publish/PRIORITIES.md +3 -0
  63. package/templates/staff/publish/SOUL.md +6 -0
  64. package/templates/staff/publish/USER.md +8 -0
package/lib/client.js CHANGED
@@ -4,7 +4,7 @@ window.__ModuleLoader__.load({
4
4
  var module = { exports: {} };
5
5
  var exports = module.exports;
6
6
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
- (function(){var id="dsh-rez-suite-css";if(typeof document==="undefined"||document.getElementById(id))return;var s=document.createElement("style");s.id=id;s.textContent=".r9dlUa_wordmark {\n min-width: 0;\n color: inherit;\n align-items: center;\n gap: 8px;\n display: inline-flex;\n overflow: hidden;\n}\n\n.r9dlUa_mark {\n object-fit: contain;\n flex: none;\n width: 28px;\n height: 28px;\n display: block;\n}\n\n.r9dlUa_markOnly {\n object-fit: contain;\n width: 100%;\n max-width: 28px;\n height: 100%;\n max-height: 28px;\n display: block;\n}\n\n.r9dlUa_name {\n letter-spacing: .02em;\n white-space: nowrap;\n text-overflow: ellipsis;\n font-size: 15px;\n font-weight: 650;\n overflow: hidden;\n}\n\n.r9dlUa_badge {\n letter-spacing: .06em;\n opacity: .72;\n white-space: nowrap;\n border: 1px solid;\n border-radius: 4px;\n flex: none;\n padding: 2px 6px;\n font-size: 10px;\n font-weight: 700;\n line-height: 1.2;\n}\n.LOjPTW_shell {\n flex-direction: column;\n gap: 12px;\n min-width: 0;\n display: flex;\n}\n\n.LOjPTW_card {\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-bg-layer-3);\n border-radius: 12px;\n list-style: none;\n transition: border-color .16s, background .16s;\n}\n\n.LOjPTW_card:hover {\n border-color: var(--dsw-alias-label-dimmed);\n}\n\n.LOjPTW_cardOpen {\n background: var(--dsw-alias-bg-layer-2);\n border-color: var(--dsw-alias-label-dimmed);\n}\n\n.LOjPTW_header {\n appearance: none;\n width: 100%;\n font: inherit;\n color: inherit;\n text-align: left;\n cursor: pointer;\n background: none;\n border: 0;\n border-radius: 12px;\n align-items: center;\n gap: 12px;\n padding: 14px 16px;\n display: flex;\n}\n\n.LOjPTW_header:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: -2px;\n}\n\n.LOjPTW_headText {\n flex-direction: column;\n flex: 1;\n gap: 4px;\n min-width: 0;\n display: flex;\n}\n\n.LOjPTW_name {\n color: var(--dsw-alias-label-primary);\n font-size: 15px;\n font-weight: 600;\n line-height: 1.4;\n}\n\n.LOjPTW_description {\n color: var(--dsw-alias-label-tertiary);\n font-size: 13px;\n line-height: 1.5;\n}\n\n.LOjPTW_chevron {\n color: var(--dsw-alias-label-tertiary);\n flex: none;\n transition: transform .16s;\n}\n\n.LOjPTW_chevronOpen {\n transform: rotate(180deg);\n}\n\n.LOjPTW_body {\n border-top: 1px solid var(--dsw-alias-border-l2);\n margin: 0 16px 12px;\n padding-top: 8px;\n}\n\n.LOjPTW_tabBar {\n border-bottom: 1px solid var(--dsw-alias-border-l1);\n flex: none;\n gap: 2px;\n display: flex;\n}\n\n.LOjPTW_tab {\n color: var(--dsw-alias-label-secondary);\n cursor: pointer;\n background: none;\n border: none;\n border-bottom: 2px solid #0000;\n border-radius: 6px 6px 0 0;\n padding: 7px 14px;\n font-size: 13px;\n}\n\n.LOjPTW_tab:hover {\n color: var(--dsw-alias-label-primary);\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.LOjPTW_tab[data-active] {\n color: var(--dsw-alias-label-primary);\n border-bottom-color: var(--dsw-alias-state-business-primary);\n font-weight: 600;\n}\n\n.LOjPTW_tabBody {\n flex-direction: column;\n gap: 12px;\n min-width: 0;\n padding: 12px 0 4px;\n display: flex;\n}\n\n.LOjPTW_section {\n border: 1px solid var(--dsw-alias-border-l1);\n background: var(--dsw-alias-bg-layer-1, var(--dsw-alias-bg-base));\n border-radius: 10px;\n flex-direction: column;\n gap: 8px;\n padding: 12px;\n display: flex;\n}\n\n.LOjPTW_sectionTitle {\n color: var(--dsw-alias-label-primary);\n margin: 0;\n font-size: 13px;\n font-weight: 600;\n}\n\n.LOjPTW_field {\n flex-direction: column;\n align-items: stretch;\n gap: 4px;\n display: flex;\n}\n\n.LOjPTW_label {\n color: var(--dsw-alias-label-secondary);\n font-size: 12.5px;\n}\n\n.LOjPTW_input, .LOjPTW_select {\n color: var(--dsw-alias-label-primary);\n background: var(--dsw-specific-input-major, var(--dsw-alias-bg-layer-3));\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 8px;\n outline: none;\n padding: 6px 8px;\n font-size: 13px;\n}\n\n.LOjPTW_checkbox {\n justify-self: start;\n width: 15px;\n height: 15px;\n}\n\n.LOjPTW_toolbar {\n flex-wrap: wrap;\n flex: none;\n align-items: center;\n gap: 8px;\n display: flex;\n}\n\n.LOjPTW_button {\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-specific-input-major, var(--dsw-alias-bg-layer-3));\n color: var(--dsw-alias-label-primary);\n cursor: pointer;\n border-radius: 8px;\n padding: 7px 12px;\n font-size: 13px;\n}\n\n.LOjPTW_button:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.LOjPTW_button:disabled {\n opacity: .4;\n cursor: default;\n}\n\n.LOjPTW_primaryButton {\n background: var(--dsw-alias-label-primary);\n border-color: var(--dsw-alias-label-primary);\n color: var(--dsw-alias-bg-layer-3);\n}\n\n.LOjPTW_dangerButton {\n background: var(--dsw-alias-state-danger, #c62828);\n border-color: var(--dsw-alias-state-danger, #c62828);\n color: #fff;\n}\n\n.LOjPTW_qr {\n background: #fff;\n border-radius: 8px;\n width: 200px;\n height: 200px;\n margin: 8px 0;\n display: block;\n}\n\n.LOjPTW_message {\n color: var(--dsw-alias-label-secondary);\n margin: 0;\n font-size: 12.5px;\n line-height: 1.5;\n}\n\n.LOjPTW_error {\n color: var(--dsw-alias-state-danger, #c62828);\n}\n\n.LOjPTW_tableWrap {\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 10px;\n overflow: auto;\n}\n\n.LOjPTW_table {\n border-collapse: collapse;\n width: 100%;\n font-size: 12.5px;\n}\n\n.LOjPTW_table th {\n z-index: 1;\n text-align: left;\n background: var(--dsw-alias-bg-layer-2, var(--dsw-alias-bg-layer-1));\n padding: 8px 10px;\n position: sticky;\n top: 0;\n}\n\n.LOjPTW_table td {\n border-top: 1px solid var(--dsw-alias-border-l1);\n vertical-align: top;\n padding: 8px 10px;\n}\n\n.LOjPTW_badge {\n border-radius: 999px;\n padding: 2px 8px;\n font-size: 11px;\n display: inline-block;\n}\n\n.LOjPTW_badgeOk {\n color: #229a55;\n background: #229a5524;\n}\n\n.LOjPTW_badgeFail {\n color: #c62828;\n background: #c6282824;\n}\n\n.LOjPTW_cards {\n grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));\n gap: 8px;\n display: grid;\n}\n\n.LOjPTW_stat {\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 10px;\n padding: 10px;\n}\n\n.LOjPTW_cardLabel {\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n}\n\n.LOjPTW_cardValue {\n color: var(--dsw-alias-label-primary);\n font-size: 15px;\n font-weight: 600;\n}\n\n.LOjPTW_upgradeLayer {\n flex: none;\n align-items: center;\n width: 100%;\n height: 42px;\n margin: 8px 0 0;\n display: flex;\n position: relative;\n}\n\n.LOjPTW_upgradeLayerRail {\n width: 36px;\n height: 36px;\n margin: 0;\n}\n\n.LOjPTW_upgradeButton {\n width: calc(100% + 4px);\n height: 42px;\n color: var(--dsw-alias-label-primary);\n cursor: pointer;\n background: none;\n border: none;\n border-radius: 12px;\n align-items: center;\n gap: 8px;\n margin: 0 -2px;\n padding: 0 10px 0 8px;\n font-family: inherit;\n font-size: 14px;\n display: inline-flex;\n overflow: hidden;\n}\n\n.LOjPTW_upgradeButton:hover, .LOjPTW_upgradeButton[data-outdated] {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.LOjPTW_upgradeButton:disabled {\n cursor: progress;\n opacity: .72;\n}\n\n.LOjPTW_upgradeButton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: -2px;\n}\n\n.LOjPTW_upgradeLayerRail .LOjPTW_upgradeButton {\n border-radius: 50%;\n justify-content: center;\n gap: 0;\n width: 36px;\n height: 36px;\n padding: 0;\n}\n\n.LOjPTW_upgradeLabel {\n text-overflow: ellipsis;\n white-space: nowrap;\n min-width: 0;\n overflow: hidden;\n}\n\n.LOjPTW_upgradeMeta {\n color: var(--dsw-alias-label-tertiary);\n font-variant-numeric: tabular-nums;\n flex: none;\n margin-left: auto;\n font-size: 12px;\n line-height: 16px;\n}\n\n.LOjPTW_upgradeDot {\n background: var(--dsw-alias-brand-primary);\n border-radius: 50%;\n flex: none;\n width: 7px;\n height: 7px;\n}\n\n.LOjPTW_changelogDate {\n font-variant-numeric: tabular-nums;\n color: var(--dsw-alias-label-tertiary);\n margin-left: 8px;\n font-weight: 500;\n}\n\n.LOjPTW_changelogBlock {\n flex-direction: column;\n gap: 6px;\n display: flex;\n}\n\n.LOjPTW_changelogHeading {\n color: var(--dsw-alias-label-secondary);\n font-size: 12.5px;\n font-weight: 600;\n}\n\n.LOjPTW_changelogList {\n color: var(--dsw-alias-label-primary);\n flex-direction: column;\n gap: 6px;\n margin: 0;\n padding-left: 18px;\n font-size: 13px;\n line-height: 1.55;\n display: flex;\n}\n\n.LOjPTW_changelogList li + li {\n margin: 0;\n}\n\n.LOjPTW_changelogCode {\n background: var(--dsw-alias-bg-layer-3);\n border: 1px solid var(--dsw-alias-border-l2);\n word-break: break-all;\n border-radius: 6px;\n padding: 1px 6px;\n font-size: 12px;\n}\n";document.head.appendChild(s);})();
7
+ (function(){var id="dsh-rez-suite-css";if(typeof document==="undefined"||document.getElementById(id))return;var s=document.createElement("style");s.id=id;s.textContent=".r9dlUa_wordmark {\n min-width: 0;\n color: inherit;\n align-items: center;\n gap: 8px;\n display: inline-flex;\n overflow: hidden;\n}\n\n.r9dlUa_mark {\n object-fit: contain;\n flex: none;\n width: 28px;\n height: 28px;\n display: block;\n}\n\n.r9dlUa_markOnly {\n object-fit: contain;\n width: 100%;\n max-width: 28px;\n height: 100%;\n max-height: 28px;\n display: block;\n}\n\n.r9dlUa_name {\n letter-spacing: .02em;\n white-space: nowrap;\n text-overflow: ellipsis;\n font-size: 15px;\n font-weight: 650;\n overflow: hidden;\n}\n\n.r9dlUa_badge {\n letter-spacing: .06em;\n opacity: .72;\n white-space: nowrap;\n border: 1px solid;\n border-radius: 4px;\n flex: none;\n padding: 2px 6px;\n font-size: 10px;\n font-weight: 700;\n line-height: 1.2;\n}\n.LOjPTW_shell {\n flex-direction: column;\n gap: 12px;\n min-width: 0;\n display: flex;\n}\n\n.LOjPTW_card {\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-bg-layer-3);\n border-radius: 12px;\n list-style: none;\n transition: border-color .16s, background .16s;\n}\n\n.LOjPTW_card:hover {\n border-color: var(--dsw-alias-label-dimmed);\n}\n\n.LOjPTW_cardOpen {\n background: var(--dsw-alias-bg-layer-2);\n border-color: var(--dsw-alias-label-dimmed);\n}\n\n.LOjPTW_header {\n appearance: none;\n width: 100%;\n font: inherit;\n color: inherit;\n text-align: left;\n cursor: pointer;\n background: none;\n border: 0;\n border-radius: 12px;\n align-items: center;\n gap: 12px;\n padding: 14px 16px;\n display: flex;\n}\n\n.LOjPTW_header:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: -2px;\n}\n\n.LOjPTW_headText {\n flex-direction: column;\n flex: 1;\n gap: 4px;\n min-width: 0;\n display: flex;\n}\n\n.LOjPTW_name {\n color: var(--dsw-alias-label-primary);\n font-size: 15px;\n font-weight: 600;\n line-height: 1.4;\n}\n\n.LOjPTW_description {\n color: var(--dsw-alias-label-tertiary);\n font-size: 13px;\n line-height: 1.5;\n}\n\n.LOjPTW_chevron {\n color: var(--dsw-alias-label-tertiary);\n flex: none;\n transition: transform .16s;\n}\n\n.LOjPTW_chevronOpen {\n transform: rotate(180deg);\n}\n\n.LOjPTW_body {\n border-top: 1px solid var(--dsw-alias-border-l2);\n margin: 0 16px 12px;\n padding-top: 8px;\n}\n\n.LOjPTW_tabBar {\n border-bottom: 1px solid var(--dsw-alias-border-l1);\n flex: none;\n gap: 2px;\n display: flex;\n}\n\n.LOjPTW_tab {\n color: var(--dsw-alias-label-secondary);\n cursor: pointer;\n background: none;\n border: none;\n border-bottom: 2px solid #0000;\n border-radius: 6px 6px 0 0;\n padding: 7px 14px;\n font-size: 13px;\n}\n\n.LOjPTW_tab:hover {\n color: var(--dsw-alias-label-primary);\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.LOjPTW_tab[data-active] {\n color: var(--dsw-alias-label-primary);\n border-bottom-color: var(--dsw-alias-state-business-primary);\n font-weight: 600;\n}\n\n.LOjPTW_tabBody {\n flex-direction: column;\n gap: 12px;\n min-width: 0;\n padding: 12px 0 4px;\n display: flex;\n}\n\n.LOjPTW_section {\n border: 1px solid var(--dsw-alias-border-l1);\n background: var(--dsw-alias-bg-layer-1, var(--dsw-alias-bg-base));\n border-radius: 10px;\n flex-direction: column;\n gap: 8px;\n padding: 12px;\n display: flex;\n}\n\n.LOjPTW_sectionTitle {\n color: var(--dsw-alias-label-primary);\n margin: 0;\n font-size: 13px;\n font-weight: 600;\n}\n\n.LOjPTW_field {\n flex-direction: column;\n align-items: stretch;\n gap: 4px;\n display: flex;\n}\n\n.LOjPTW_label {\n color: var(--dsw-alias-label-secondary);\n font-size: 12.5px;\n}\n\n.LOjPTW_input, .LOjPTW_select {\n color: var(--dsw-alias-label-primary);\n background: var(--dsw-specific-input-major, var(--dsw-alias-bg-layer-3));\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 8px;\n outline: none;\n padding: 6px 8px;\n font-size: 13px;\n}\n\n.LOjPTW_checkbox {\n justify-self: start;\n width: 15px;\n height: 15px;\n}\n\n.LOjPTW_wecomRow {\n grid-template-columns: minmax(7rem, .8fr) minmax(8rem, 1fr) minmax(8rem, 1fr) auto;\n align-items: end;\n gap: 8px;\n display: grid;\n}\n\n.LOjPTW_wecomRow .LOjPTW_message {\n grid-column: 1 / -1;\n}\n\n.LOjPTW_toolbar {\n flex-wrap: wrap;\n flex: none;\n align-items: center;\n gap: 8px;\n display: flex;\n}\n\n.LOjPTW_button {\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-specific-input-major, var(--dsw-alias-bg-layer-3));\n color: var(--dsw-alias-label-primary);\n cursor: pointer;\n border-radius: 8px;\n padding: 7px 12px;\n font-size: 13px;\n}\n\n.LOjPTW_button:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.LOjPTW_button:disabled {\n opacity: .4;\n cursor: default;\n}\n\n.LOjPTW_primaryButton {\n background: var(--dsw-alias-label-primary);\n border-color: var(--dsw-alias-label-primary);\n color: var(--dsw-alias-bg-layer-3);\n}\n\n.LOjPTW_dangerButton {\n background: var(--dsw-alias-state-danger, #c62828);\n border-color: var(--dsw-alias-state-danger, #c62828);\n color: #fff;\n}\n\n.LOjPTW_qr {\n background: #fff;\n border-radius: 8px;\n width: 200px;\n height: 200px;\n margin: 8px 0;\n display: block;\n}\n\n.LOjPTW_message {\n color: var(--dsw-alias-label-secondary);\n margin: 0;\n font-size: 12.5px;\n line-height: 1.5;\n}\n\n.LOjPTW_error {\n color: var(--dsw-alias-state-danger, #c62828);\n}\n\n.LOjPTW_tableWrap {\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 10px;\n overflow: auto;\n}\n\n.LOjPTW_table {\n border-collapse: collapse;\n width: 100%;\n font-size: 12.5px;\n}\n\n.LOjPTW_table th {\n z-index: 1;\n text-align: left;\n background: var(--dsw-alias-bg-layer-2, var(--dsw-alias-bg-layer-1));\n padding: 8px 10px;\n position: sticky;\n top: 0;\n}\n\n.LOjPTW_table td {\n border-top: 1px solid var(--dsw-alias-border-l1);\n vertical-align: top;\n padding: 8px 10px;\n}\n\n.LOjPTW_boardTitle {\n color: var(--dsw-alias-label-primary);\n margin: 16px 0 4px;\n font-size: 13px;\n font-weight: 600;\n}\n\n.LOjPTW_badge {\n border-radius: 999px;\n padding: 2px 8px;\n font-size: 11px;\n display: inline-block;\n}\n\n.LOjPTW_badgeOk {\n color: #229a55;\n background: #229a5524;\n}\n\n.LOjPTW_badgeFail {\n color: #c62828;\n background: #c6282824;\n}\n\n.LOjPTW_cards {\n grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));\n gap: 8px;\n display: grid;\n}\n\n.LOjPTW_stat {\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 10px;\n padding: 10px;\n}\n\n.LOjPTW_cardLabel {\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n}\n\n.LOjPTW_cardValue {\n color: var(--dsw-alias-label-primary);\n font-size: 15px;\n font-weight: 600;\n}\n\n.LOjPTW_upgradeLayer {\n flex: none;\n align-items: center;\n width: 100%;\n height: 42px;\n margin: 8px 0 0;\n display: flex;\n position: relative;\n}\n\n.LOjPTW_upgradeLayerRail {\n width: 36px;\n height: 36px;\n margin: 0;\n}\n\n.LOjPTW_upgradeButton {\n width: calc(100% + 4px);\n height: 42px;\n color: var(--dsw-alias-label-primary);\n cursor: pointer;\n background: none;\n border: none;\n border-radius: 12px;\n align-items: center;\n gap: 8px;\n margin: 0 -2px;\n padding: 0 10px 0 8px;\n font-family: inherit;\n font-size: 14px;\n display: inline-flex;\n overflow: hidden;\n}\n\n.LOjPTW_upgradeButton:hover, .LOjPTW_upgradeButton[data-outdated] {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.LOjPTW_upgradeButton:disabled {\n cursor: progress;\n opacity: .72;\n}\n\n.LOjPTW_upgradeButton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: -2px;\n}\n\n.LOjPTW_upgradeLayerRail .LOjPTW_upgradeButton {\n border-radius: 50%;\n justify-content: center;\n gap: 0;\n width: 36px;\n height: 36px;\n padding: 0;\n}\n\n.LOjPTW_upgradeLabel {\n text-overflow: ellipsis;\n white-space: nowrap;\n min-width: 0;\n overflow: hidden;\n}\n\n.LOjPTW_upgradeMeta {\n color: var(--dsw-alias-label-tertiary);\n font-variant-numeric: tabular-nums;\n flex: none;\n margin-left: auto;\n font-size: 12px;\n line-height: 16px;\n}\n\n.LOjPTW_upgradeDot {\n background: var(--dsw-alias-brand-primary);\n border-radius: 50%;\n flex: none;\n width: 7px;\n height: 7px;\n}\n\n.LOjPTW_changelogDate {\n font-variant-numeric: tabular-nums;\n color: var(--dsw-alias-label-tertiary);\n margin-left: 8px;\n font-weight: 500;\n}\n\n.LOjPTW_changelogBlock {\n flex-direction: column;\n gap: 6px;\n display: flex;\n}\n\n.LOjPTW_changelogHeading {\n color: var(--dsw-alias-label-secondary);\n font-size: 12.5px;\n font-weight: 600;\n}\n\n.LOjPTW_changelogList {\n color: var(--dsw-alias-label-primary);\n flex-direction: column;\n gap: 6px;\n margin: 0;\n padding-left: 18px;\n font-size: 13px;\n line-height: 1.55;\n display: flex;\n}\n\n.LOjPTW_changelogList li + li {\n margin: 0;\n}\n\n.LOjPTW_changelogCode {\n background: var(--dsw-alias-bg-layer-3);\n border: 1px solid var(--dsw-alias-border-l2);\n word-break: break-all;\n border-radius: 6px;\n padding: 1px 6px;\n font-size: 12px;\n}\n";document.head.appendChild(s);})();
8
8
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
9
9
  let react = require("react");
10
10
  let react_jsx_runtime = require("react/jsx-runtime");
@@ -139,15 +139,16 @@ const zh = {
139
139
  "entry.label": "Rez",
140
140
  "entry.tooltip": "Rez Suite 配置与审计面板",
141
141
  "settings.title": "ReZ-TI",
142
- "settings.description": "公司 MCP(Odoo / Nextcloud / HA / TAPD / GSC)。企业微信栏暂隐。个人微信在「微信」页签扫码。",
142
+ "settings.description": "公司 MCP(Odoo / Nextcloud / HA / TAPD / GSC)和企业微信智能机器人。个人微信在「微信」页签扫码。",
143
143
  "panel.title": "Rez Suite",
144
144
  "tab.config": "配置",
145
+ "tab.board": "看板",
145
146
  "tab.weixin": "微信",
146
147
  "tab.status": "状态",
147
148
  "tab.audit": "用量",
148
149
  "tab.changelog": "更新日志",
149
150
  "config.role": "角色",
150
- "config.roleHint": "选身份后左侧自动出现工作区。人事/财务/法务助理全员可见;设计房间仅「运营」;实验室仅「工程师」;搞钱/产品/运营仅「老板」。",
151
+ "config.roleHint": "选身份后左侧自动出现工作区。人事/财务/法务助理全员可见;设计、发布、电商房间仅「运营」;实验室仅「工程师」;搞钱/产品/运营仅「老板」。",
151
152
  "role.all": "全部",
152
153
  "role.boss": "老板",
153
154
  "role.engineer": "工程师",
@@ -175,6 +176,12 @@ const zh = {
175
176
  "config.wecom.botId": "Bot ID",
176
177
  "config.wecom.secret": "Secret",
177
178
  "config.wecom.webhook": "群机器人 Webhook",
179
+ "config.wecom.hint": "Accio 同款:一个房间绑一个智能机器人。同事在企微跟哪个 bot 说话,就进网页左侧那个房间的同一条会话。密钥只存本机 ~/.dsh/wecom-channels.json,不要写进 compose。BotID / Secret 在企业微信管理后台 → 智能机器人。",
180
+ "config.wecom.room": "房间",
181
+ "config.wecom.add": "添加绑定",
182
+ "config.wecom.remove": "删除",
183
+ "config.wecom.empty": "还没有绑定。给人事/财务/法务等房间各建一个企微智能机器人,就能变成不同角色。",
184
+ "config.wecom.status": "通道",
178
185
  "config.ha": "Home Assistant",
179
186
  "config.ha.url": "HA MCP URL",
180
187
  "config.ha.token": "长期访问令牌",
@@ -209,7 +216,7 @@ const zh = {
209
216
  "config.testResult": "{server}: {status}",
210
217
  "config.testOk": "成功({tools} 个工具, {latency}ms)",
211
218
  "config.testFail": "失败:{error}",
212
- "status.hint": "绿标表示本机已注册 mcp__<系统>__* 工具。远端 MCP 通了但这里仍红,说明 dsh-mcp-client 还没挂上。企查查只在法务助理房间可用;其它房间不显示或显示「房间未启用」。",
219
+ "status.hint": "绿标表示本机已注册 mcp__<系统>__* 工具。远端 MCP 通了但这里仍红,说明 dsh-mcp-client 还没挂上。企微办公能力看下方 wecom-cli,不是 MCP 表。企查查只在法务助理房间可用;其它房间不显示或显示「房间未启用」。",
213
220
  "status.total": "当前角色 {role},共注册 {count} 个 MCP 工具",
214
221
  "status.col.server": "Server",
215
222
  "status.col.state": "状态",
@@ -217,6 +224,12 @@ const zh = {
217
224
  "status.col.registered": "已注册",
218
225
  "status.col.error": "错误",
219
226
  "status.none": "暂无状态",
227
+ "status.wecomCli": "企业微信办公(官方 wecom-cli,不是 MCP)",
228
+ "status.wecomCli.installed": "CLI",
229
+ "status.wecomCli.authorized": "授权",
230
+ "status.wecomCli.yes": "已就绪",
231
+ "status.wecomCli.no": "未就绪",
232
+ "status.wecomCli.modules": "品类",
220
233
  "audit.totalCalls": "调用次数",
221
234
  "audit.inputTokens": "输入 token",
222
235
  "audit.outputTokens": "输出 token",
@@ -238,6 +251,27 @@ const zh = {
238
251
  "audit.empty": "暂无记录",
239
252
  "changelog.current": "当前 {version}",
240
253
  "changelog.upgrade": "升级:",
254
+ "board.hint": "合同 Account 拆两室:媒体走「发布」,电商走「电商」(一期只阿里巴巴国际站;Amazon 等有号)。不是独立后台。密钥不在这里。",
255
+ "board.people": "People / 权限",
256
+ "board.peopleHint": "继续 Odoo 员工帐号(公司/记录规则)+ 企业微信一房间一 bot。不新建权限库,不改 Odoo 权限组。",
257
+ "board.role": "当前 Harness 身份:{role}(只决定侧栏房间)",
258
+ "board.media": "媒体连接",
259
+ "board.mediaHint": "内容/社媒在「发布」工作间(Post editor)。不建图库。",
260
+ "board.ecommerce": "电商平台",
261
+ "board.ecommerceHint": "Listing 在「电商」工作间。一期只有阿里巴巴国际站。渠道商品 ID 写回 Odoo,库存从 Odoo 推到阿里。Amazon 没有号,标后续。",
262
+ "board.rag": "RAG",
263
+ "board.ragHint": "基于已有 Dify,知识库对接 Nextcloud 锁死路径(Standards + Marketing/Publish)。Harness 不做推理。",
264
+ "board.inbox": "Message inbox",
265
+ "board.inboxHint": "一期只做 WhatsApp Business,会话落到 Odoo(浙江雷泽,Business 号已有)。不用官方收费模块,走社区 add-on(OCA mail_gateway_whatsapp)。禁止自写 Meta 客户端。生产安装另开窗口。",
266
+ "board.col.source": "来源",
267
+ "board.col.state": "状态",
268
+ "board.col.channel": "渠道",
269
+ "board.col.room": "房间",
270
+ "board.col.phase": "阶段",
271
+ "board.bound": "已绑 bot",
272
+ "board.unbound": "未绑定",
273
+ "board.phase1": "一期",
274
+ "board.phaseLater": "后续",
241
275
  "common.close": "关闭",
242
276
  "common.loading": "加载中…",
243
277
  "common.error": "出错:{error}",
@@ -268,15 +302,16 @@ const en = {
268
302
  "entry.label": "Rez",
269
303
  "entry.tooltip": "Rez Suite configuration and audit panel",
270
304
  "settings.title": "ReZ-TI",
271
- "settings.description": "Company MCP (Odoo / Nextcloud / HA / TAPD / GSC). WeCom settings are hidden for now. Personal WeChat is the WeChat tab.",
305
+ "settings.description": "Company MCP (Odoo / Nextcloud / HA / TAPD / GSC) and WeCom AI bots. Personal WeChat is the WeChat tab.",
272
306
  "panel.title": "Rez Suite",
273
307
  "tab.config": "Config",
308
+ "tab.board": "Board",
274
309
  "tab.weixin": "WeChat",
275
310
  "tab.status": "Status",
276
311
  "tab.audit": "Usage",
277
312
  "tab.changelog": "Changelog",
278
313
  "config.role": "Role",
279
- "config.roleHint": "Identity registers workspaces in the sidebar. Staff assistants for every role; 设计 for Operations only; 实验室 for Engineer only; 搞钱 / 产品 / 运营 for Boss only.",
314
+ "config.roleHint": "Identity registers workspaces in the sidebar. Staff assistants for every role; 设计 / 发布 / 电商 for Operations only; 实验室 for Engineer only; 搞钱 / 产品 / 运营 for Boss only.",
280
315
  "role.all": "All",
281
316
  "role.boss": "Boss",
282
317
  "role.engineer": "Engineer",
@@ -304,6 +339,12 @@ const en = {
304
339
  "config.wecom.botId": "Bot ID",
305
340
  "config.wecom.secret": "Secret",
306
341
  "config.wecom.webhook": "Group bot webhook",
342
+ "config.wecom.hint": "Accio-style: one WeCom AI bot per Harness room. Whoever talks to that bot lands in the same sidebar session. Secrets stay in ~/.dsh/wecom-channels.json, not compose. BotID / Secret come from WeCom admin → AI bot.",
343
+ "config.wecom.room": "Room",
344
+ "config.wecom.add": "Add binding",
345
+ "config.wecom.remove": "Remove",
346
+ "config.wecom.empty": "No bots yet. Bind a WeCom AI bot to HR / finance / legal / … so each room is a different role.",
347
+ "config.wecom.status": "Channel",
307
348
  "config.ha": "Home Assistant",
308
349
  "config.ha.url": "HA MCP URL",
309
350
  "config.ha.token": "Long-lived access token",
@@ -338,7 +379,7 @@ const en = {
338
379
  "config.testResult": "{server}: {status}",
339
380
  "config.testOk": "OK ({tools} tools, {latency}ms)",
340
381
  "config.testFail": "Failed: {error}",
341
- "status.hint": "Green means this host registered mcp__<system>__* tools. A live remote MCP with a red row means dsh-mcp-client has not attached yet. Qichacha is only for the Legal assistant room; other rooms hide the row or show “room not enabled”.",
382
+ "status.hint": "Green means this host registered mcp__<system>__* tools. A live remote MCP with a red row means dsh-mcp-client has not attached yet. WeCom office APIs are the wecom-cli row below, not the MCP table. Qichacha is only for the Legal assistant room; other rooms hide the row or show “room not enabled”.",
342
383
  "status.total": "Role {role}, {count} MCP tools registered",
343
384
  "status.col.server": "Server",
344
385
  "status.col.state": "State",
@@ -346,6 +387,12 @@ const en = {
346
387
  "status.col.registered": "Registered",
347
388
  "status.col.error": "Error",
348
389
  "status.none": "No status yet",
390
+ "status.wecomCli": "WeCom office APIs (official wecom-cli, not MCP)",
391
+ "status.wecomCli.installed": "CLI",
392
+ "status.wecomCli.authorized": "Auth",
393
+ "status.wecomCli.yes": "Ready",
394
+ "status.wecomCli.no": "Not ready",
395
+ "status.wecomCli.modules": "Modules",
349
396
  "audit.totalCalls": "Calls",
350
397
  "audit.inputTokens": "Input tokens",
351
398
  "audit.outputTokens": "Output tokens",
@@ -367,6 +414,27 @@ const en = {
367
414
  "audit.empty": "No records",
368
415
  "changelog.current": "Now {version}",
369
416
  "changelog.upgrade": "Upgrade:",
417
+ "board.hint": "Account connections split into two rooms: media in 发布, ecommerce in 电商 (phase 1: Alibaba.com only; Amazon waits for an account). Not a separate admin site. Secrets stay off this board.",
418
+ "board.people": "People / permissions",
419
+ "board.peopleHint": "Keep Odoo employee accounts (company / record rules) plus one WeCom AI bot per room. No new ACL database. Do not change Odoo groups.",
420
+ "board.role": "Current Harness identity: {role} (sidebar rooms only)",
421
+ "board.media": "Media connections",
422
+ "board.mediaHint": "Content / social lives in the 发布 room (Post editor). No media library.",
423
+ "board.ecommerce": "Ecommerce platforms",
424
+ "board.ecommerceHint": "Listings live in the 电商 room. Phase 1 is Alibaba.com. Write channel product IDs back to Odoo and push stock Odoo → Alibaba. Amazon is later until we have an account.",
425
+ "board.rag": "RAG",
426
+ "board.ragHint": "Reuse Dify. Index Nextcloud locked paths (Standards + Marketing/Publish). Harness does not run embeddings.",
427
+ "board.inbox": "Message inbox",
428
+ "board.inboxHint": "Phase 1 is WhatsApp Business into Odoo (Zhejiang ReZ-TI; WABA already exists). Skip the paid official module; reuse OCA mail_gateway_whatsapp. No homemade Meta client. Production install needs a separate window.",
429
+ "board.col.source": "Source",
430
+ "board.col.state": "State",
431
+ "board.col.channel": "Channel",
432
+ "board.col.room": "Room",
433
+ "board.col.phase": "Phase",
434
+ "board.bound": "Bot bound",
435
+ "board.unbound": "Unbound",
436
+ "board.phase1": "Phase 1",
437
+ "board.phaseLater": "Later",
370
438
  "common.close": "Close",
371
439
  "common.loading": "Loading…",
372
440
  "common.error": "Error: {error}",
@@ -409,6 +477,7 @@ const REZ_API = {
409
477
  auditReset: "/api/dsh-rez-suite/audit/reset",
410
478
  weixin: "/api/dsh-rez-suite/weixin",
411
479
  weixinVerify: "/api/dsh-rez-suite/weixin/verify",
480
+ wecomChannels: "/api/dsh-rez-suite/wecom/channels",
412
481
  update: "/api/dsh-rez-suite/update"
413
482
  };
414
483
  //#endregion
@@ -471,6 +540,16 @@ var RezApi = class {
471
540
  async weixinLogout() {
472
541
  return readJson(await fetch(REZ_API.weixin, { method: "DELETE" }));
473
542
  }
543
+ async wecomChannels() {
544
+ return readJson(await fetch(REZ_API.wecomChannels));
545
+ }
546
+ async saveWecomChannels(bots) {
547
+ return readJson(await fetch(REZ_API.wecomChannels, {
548
+ method: "PUT",
549
+ headers: { "content-type": "application/json" },
550
+ body: JSON.stringify({ bots })
551
+ }));
552
+ }
474
553
  async updateStatus() {
475
554
  return readJson(await fetch(REZ_API.update));
476
555
  }
@@ -507,6 +586,7 @@ var panel_module_default = {
507
586
  "badge": "LOjPTW_badge",
508
587
  "badgeFail": "LOjPTW_badgeFail",
509
588
  "badgeOk": "LOjPTW_badgeOk",
589
+ "boardTitle": "LOjPTW_boardTitle",
510
590
  "body": "LOjPTW_body",
511
591
  "button": "LOjPTW_button",
512
592
  "card": "LOjPTW_card",
@@ -550,7 +630,8 @@ var panel_module_default = {
550
630
  "upgradeLabel": "LOjPTW_upgradeLabel",
551
631
  "upgradeLayer": "LOjPTW_upgradeLayer",
552
632
  "upgradeLayerRail": "LOjPTW_upgradeLayerRail",
553
- "upgradeMeta": "LOjPTW_upgradeMeta"
633
+ "upgradeMeta": "LOjPTW_upgradeMeta",
634
+ "wecomRow": "LOjPTW_wecomRow"
554
635
  };
555
636
  //#endregion
556
637
  //#region src/client/panel/AuditTab.tsx
@@ -717,8 +798,252 @@ function AuditTab({ api }) {
717
798
  });
718
799
  }
719
800
  //#endregion
801
+ //#region src/channel-board.ts
802
+ const MEDIA_ROOM = "发布";
803
+ const ECOMMERCE_ROOM = "电商";
804
+ const CHANNEL_BOARD = [
805
+ {
806
+ id: "media_via_publish",
807
+ nameZh: "内容 / 社媒分发",
808
+ nameEn: "Content / social",
809
+ kind: "media",
810
+ room: MEDIA_ROOM,
811
+ phase: "1"
812
+ },
813
+ {
814
+ id: "alibaba_icbu",
815
+ nameZh: "阿里巴巴国际站",
816
+ nameEn: "Alibaba.com",
817
+ kind: "ecommerce",
818
+ room: ECOMMERCE_ROOM,
819
+ phase: "1"
820
+ },
821
+ {
822
+ id: "amazon",
823
+ nameZh: "Amazon",
824
+ nameEn: "Amazon",
825
+ kind: "ecommerce",
826
+ room: ECOMMERCE_ROOM,
827
+ phase: "later"
828
+ }
829
+ ];
830
+ function channelsFor(kind) {
831
+ return CHANNEL_BOARD.filter((row) => row.kind === kind);
832
+ }
833
+ function wecomBound(room, bots) {
834
+ return bots.some((bot) => bot.room === room);
835
+ }
836
+ //#endregion
837
+ //#region src/client/panel/BoardTab.tsx
838
+ /**
839
+ * Account board: media (发布) vs ecommerce (电商; phase 1 Alibaba, Amazon later).
840
+ * People = Odoo user + WeCom room bots. No new ACL store.
841
+ */
842
+ function BoardTab({ api }) {
843
+ const t = useT();
844
+ const [config, setConfig] = (0, react.useState)(null);
845
+ const [bots, setBots] = (0, react.useState)([]);
846
+ const [error, setError] = (0, react.useState)("");
847
+ const [loading, setLoading] = (0, react.useState)(true);
848
+ const load = async () => {
849
+ setLoading(true);
850
+ setError("");
851
+ try {
852
+ const next = await api.getConfig();
853
+ setConfig(next);
854
+ try {
855
+ const wecom = await api.wecomChannels();
856
+ setBots(wecom.bots);
857
+ } catch {
858
+ setBots([]);
859
+ }
860
+ } catch (err) {
861
+ setError(errorMessage(err));
862
+ } finally {
863
+ setLoading(false);
864
+ }
865
+ };
866
+ (0, react.useEffect)(() => {
867
+ load();
868
+ }, []);
869
+ if (loading) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
870
+ className: panel_module_default.message,
871
+ children: t("common.loading")
872
+ });
873
+ if (config === null) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
874
+ className: panel_module_default.error,
875
+ children: error
876
+ });
877
+ const odooOk = config.connections.odoo.secretConfigured;
878
+ const lang = t("tab.config") === "配置" ? "zh" : "en";
879
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
880
+ className: panel_module_default.tabBody,
881
+ children: [
882
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
883
+ className: panel_module_default.toolbar,
884
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
885
+ className: panel_module_default.message,
886
+ children: t("board.hint")
887
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
888
+ type: "button",
889
+ className: panel_module_default.button,
890
+ onClick: () => {
891
+ load();
892
+ },
893
+ children: t("common.refresh")
894
+ })]
895
+ }),
896
+ error !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
897
+ className: panel_module_default.error,
898
+ children: error
899
+ }),
900
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
901
+ className: panel_module_default.boardTitle,
902
+ children: t("board.people")
903
+ }),
904
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
905
+ className: panel_module_default.message,
906
+ children: t("board.peopleHint")
907
+ }),
908
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
909
+ className: panel_module_default.tableWrap,
910
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("table", {
911
+ className: panel_module_default.table,
912
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("tr", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: t("board.col.source") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: t("board.col.state") })] }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("tbody", { children: [
913
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("tr", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: "Odoo" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
914
+ className: panel_module_default.badge + " " + (odooOk ? panel_module_default.badgeOk : panel_module_default.badgeFail),
915
+ children: odooOk ? t("config.secretConfigured") : t("config.secretMissing")
916
+ }) })] }),
917
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("tr", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("td", { children: ["WeCom · ", MEDIA_ROOM] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
918
+ className: panel_module_default.badge + " " + (wecomBound("发布", bots) ? panel_module_default.badgeOk : panel_module_default.badgeFail),
919
+ children: wecomBound("发布", bots) ? t("board.bound") : t("board.unbound")
920
+ }) })] }),
921
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("tr", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("td", { children: ["WeCom · ", ECOMMERCE_ROOM] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
922
+ className: panel_module_default.badge + " " + (wecomBound("电商", bots) ? panel_module_default.badgeOk : panel_module_default.badgeFail),
923
+ children: wecomBound("电商", bots) ? t("board.bound") : t("board.unbound")
924
+ }) })] })
925
+ ] })]
926
+ })
927
+ }),
928
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
929
+ className: panel_module_default.message,
930
+ children: t("board.role", { role: config.role })
931
+ }),
932
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
933
+ className: panel_module_default.boardTitle,
934
+ children: t("board.media")
935
+ }),
936
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
937
+ className: panel_module_default.message,
938
+ children: t("board.mediaHint")
939
+ }),
940
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChannelTable, {
941
+ kind: "media",
942
+ lang
943
+ }),
944
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
945
+ className: panel_module_default.boardTitle,
946
+ children: t("board.ecommerce")
947
+ }),
948
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
949
+ className: panel_module_default.message,
950
+ children: t("board.ecommerceHint")
951
+ }),
952
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChannelTable, {
953
+ kind: "ecommerce",
954
+ lang
955
+ }),
956
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
957
+ className: panel_module_default.boardTitle,
958
+ children: t("board.rag")
959
+ }),
960
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
961
+ className: panel_module_default.message,
962
+ children: t("board.ragHint")
963
+ }),
964
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
965
+ className: panel_module_default.boardTitle,
966
+ children: t("board.inbox")
967
+ }),
968
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
969
+ className: panel_module_default.message,
970
+ children: t("board.inboxHint")
971
+ })
972
+ ]
973
+ });
974
+ }
975
+ function ChannelTable({ kind, lang }) {
976
+ const t = useT();
977
+ const rows = channelsFor(kind);
978
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
979
+ className: panel_module_default.tableWrap,
980
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("table", {
981
+ className: panel_module_default.table,
982
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("tr", { children: [
983
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: t("board.col.channel") }),
984
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: t("board.col.room") }),
985
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: t("board.col.phase") })
986
+ ] }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("tbody", { children: rows.map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("tr", { children: [
987
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: lang === "zh" ? row.nameZh : row.nameEn }),
988
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: row.room }),
989
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: row.phase === "1" ? t("board.phase1") : t("board.phaseLater") })
990
+ ] }, row.id)) })]
991
+ })
992
+ });
993
+ }
994
+ //#endregion
720
995
  //#region src/changelog.ts
721
996
  const SUITE_CHANGELOG = [
997
+ {
998
+ version: "0.1.50",
999
+ date: "2026-08-20",
1000
+ sections: [
1001
+ {
1002
+ title: {
1003
+ zh: "运营发布工作间",
1004
+ en: "Operations publish room"
1005
+ },
1006
+ items: [{
1007
+ zh: "运营身份侧栏多了「发布」房间(合同 Post editor / 媒体连接)和「电商」房间(一期只阿里巴巴国际站;Amazon 等有号)。设置页「看板」看两室状态与 Odoo/企微权限。不建图库,排期写企微共享日历。员工机安装包仍钉已发布版本,不要改审批镜像。",
1008
+ en: "Operations now gets 发布 (media / Post editor) and 电商 (phase 1: Alibaba.com; Amazon waits for an account). The settings Board tab shows channel status plus Odoo/WeCom identity. No media library; schedule goes to one shared WeCom calendar. Do not bump the installer SUITE_VERSION."
1009
+ }]
1010
+ },
1011
+ {
1012
+ title: {
1013
+ zh: "企微固定对话框",
1014
+ en: "Sticky WeCom sessions"
1015
+ },
1016
+ items: [{
1017
+ zh: "同一房间连续说话复用网页左侧同一条会话。只有「重启对话」才新开。",
1018
+ en: "WeCom turns in one room reuse the same sidebar session. Only “重启对话” opens a new one."
1019
+ }]
1020
+ },
1021
+ {
1022
+ title: {
1023
+ zh: "企微办公能力",
1024
+ en: "WeCom office APIs"
1025
+ },
1026
+ items: [{
1027
+ zh: "rez_status / 状态页报官方 wecom-cli >=1.1 品类(通讯录/在线文档/表格/待办/日程/会议/邮件/微盘等)。房间种入官方 wecomcli-* 技能。不要把「没有 wechat MCP」或旧的 10 人限制当成没有企微能力。BotID 只是房间消息通道。不改员工机安装包。",
1028
+ en: "rez_status lists official wecom-cli >=1.1 modules (contacts/docs/sheets/todos/calendar/meetings/mail/disk). Rooms get upstream wecomcli-* skills. A missing wechat MCP row is not “no WeCom”. BotID is only the room chat channel. Do not bump the installer."
1029
+ }]
1030
+ }
1031
+ ]
1032
+ },
1033
+ {
1034
+ version: "0.1.49",
1035
+ date: "2026-08-19",
1036
+ sections: [{
1037
+ title: {
1038
+ zh: "企业微信智能机器人",
1039
+ en: "WeCom AI bots"
1040
+ },
1041
+ items: [{
1042
+ zh: "设置页按房间绑定企微智能机器人(BotID + Secret)。同事在企微跟哪个 bot 说话,就进网页左侧那个房间。密钥只存本机,不进 compose。",
1043
+ en: "Bind a WeCom AI bot (BotID + Secret) per Harness room. Talk to that bot in WeCom and the thread lands in that sidebar folder. Secrets stay on disk, not in compose."
1044
+ }]
1045
+ }]
1046
+ },
722
1047
  {
723
1048
  version: "0.1.48",
724
1049
  date: "2026-08-19",
@@ -910,6 +1235,128 @@ const EMPTY_SECRETS = {
910
1235
  tianyancha: "",
911
1236
  esphome: ""
912
1237
  };
1238
+ const WECOM_SECRET_MASK = "********";
1239
+ function WecomSection({ rooms, drafts, status, onChange }) {
1240
+ const t = useT();
1241
+ const used = new Set(drafts.map((row) => row.room));
1242
+ const unused = rooms.filter((room) => !used.has(room));
1243
+ const hintFor = (room) => status.find((row) => row.room === room)?.hint ?? "";
1244
+ const add = () => {
1245
+ const room = unused[0];
1246
+ if (room === void 0) return;
1247
+ onChange([...drafts, {
1248
+ key: `new-${room}-${Date.now()}`,
1249
+ room,
1250
+ botId: "",
1251
+ secret: "",
1252
+ secretConfigured: false
1253
+ }]);
1254
+ };
1255
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Section, {
1256
+ title: t("config.wecom"),
1257
+ children: [
1258
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1259
+ className: panel_module_default.message,
1260
+ children: t("config.wecom.hint")
1261
+ }),
1262
+ drafts.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1263
+ className: panel_module_default.message,
1264
+ children: t("config.wecom.empty")
1265
+ }),
1266
+ drafts.map((row, index) => {
1267
+ const options = rooms.filter((room) => room === row.room || !used.has(room));
1268
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1269
+ className: panel_module_default.wecomRow,
1270
+ children: [
1271
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
1272
+ label: t("config.wecom.room"),
1273
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
1274
+ className: panel_module_default.select,
1275
+ value: row.room,
1276
+ onChange: (event) => {
1277
+ const next = drafts.slice();
1278
+ next[index] = {
1279
+ ...row,
1280
+ room: event.target.value
1281
+ };
1282
+ onChange(next);
1283
+ },
1284
+ children: options.map((room) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1285
+ value: room,
1286
+ children: room
1287
+ }, room))
1288
+ })
1289
+ }),
1290
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
1291
+ label: t("config.wecom.botId"),
1292
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1293
+ className: panel_module_default.input,
1294
+ autoComplete: "off",
1295
+ value: row.botId,
1296
+ onChange: (event) => {
1297
+ const next = drafts.slice();
1298
+ next[index] = {
1299
+ ...row,
1300
+ botId: event.target.value
1301
+ };
1302
+ onChange(next);
1303
+ }
1304
+ })
1305
+ }),
1306
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
1307
+ label: t("config.wecom.secret"),
1308
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1309
+ className: panel_module_default.input,
1310
+ type: "password",
1311
+ autoComplete: "off",
1312
+ value: row.secret,
1313
+ placeholder: row.secretConfigured ? t("config.secretConfigured") : t("config.secretMissing"),
1314
+ onChange: (event) => {
1315
+ const next = drafts.slice();
1316
+ next[index] = {
1317
+ ...row,
1318
+ secret: event.target.value
1319
+ };
1320
+ onChange(next);
1321
+ }
1322
+ })
1323
+ }),
1324
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1325
+ type: "button",
1326
+ className: panel_module_default.button,
1327
+ onClick: () => {
1328
+ onChange(drafts.filter((_, i) => i !== index));
1329
+ },
1330
+ children: t("config.wecom.remove")
1331
+ }),
1332
+ hintFor(row.room) !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1333
+ className: panel_module_default.message,
1334
+ children: hintFor(row.room)
1335
+ })
1336
+ ]
1337
+ }, row.key);
1338
+ }),
1339
+ unused.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1340
+ className: panel_module_default.toolbar,
1341
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1342
+ type: "button",
1343
+ className: panel_module_default.button,
1344
+ onClick: add,
1345
+ children: t("config.wecom.add")
1346
+ })
1347
+ })
1348
+ ]
1349
+ });
1350
+ }
1351
+ function toWecomDrafts(bots) {
1352
+ return bots.map((bot, index) => ({
1353
+ key: `${bot.room}-${index}`,
1354
+ room: bot.room,
1355
+ botId: bot.botId,
1356
+ secret: "",
1357
+ secretConfigured: bot.secretConfigured
1358
+ }));
1359
+ }
913
1360
  function Field({ label, children }) {
914
1361
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
915
1362
  className: panel_module_default.field,
@@ -1003,9 +1450,42 @@ function ConfigTab({ api }) {
1003
1450
  const [message, setMessage] = (0, react.useState)("");
1004
1451
  const [error, setError] = (0, react.useState)("");
1005
1452
  const [results, setResults] = (0, react.useState)([]);
1453
+ const [wecomRooms, setWecomRooms] = (0, react.useState)([]);
1454
+ const [wecomDrafts, setWecomDrafts] = (0, react.useState)([]);
1455
+ const [wecomStatus, setWecomStatus] = (0, react.useState)([]);
1006
1456
  const load = async () => {
1007
1457
  try {
1008
1458
  setConfig(await api.getConfig());
1459
+ try {
1460
+ const wecom = await api.wecomChannels();
1461
+ setWecomRooms(wecom.rooms.length > 0 ? wecom.rooms : [
1462
+ "搞钱",
1463
+ "产品",
1464
+ "运营",
1465
+ "人事助理",
1466
+ "财务助理",
1467
+ "法务助理",
1468
+ "设计",
1469
+ "发布",
1470
+ "电商",
1471
+ "实验室"
1472
+ ]);
1473
+ setWecomDrafts(toWecomDrafts(wecom.bots));
1474
+ setWecomStatus(wecom.status);
1475
+ } catch {
1476
+ setWecomRooms([
1477
+ "搞钱",
1478
+ "产品",
1479
+ "运营",
1480
+ "人事助理",
1481
+ "财务助理",
1482
+ "法务助理",
1483
+ "设计",
1484
+ "发布",
1485
+ "电商",
1486
+ "实验室"
1487
+ ]);
1488
+ }
1009
1489
  } catch (err) {
1010
1490
  setError(errorMessage(err));
1011
1491
  } finally {
@@ -1092,6 +1572,14 @@ function ConfigTab({ api }) {
1092
1572
  const saved = await api.saveConfig(payload);
1093
1573
  setConfig(saved);
1094
1574
  setSecrets(EMPTY_SECRETS);
1575
+ const wecomSaved = await api.saveWecomChannels(wecomDrafts.map((row) => ({
1576
+ room: row.room,
1577
+ botId: row.botId.trim(),
1578
+ secret: row.secret.trim().length > 0 ? row.secret.trim() : row.secretConfigured ? WECOM_SECRET_MASK : ""
1579
+ })));
1580
+ setWecomRooms(wecomSaved.rooms);
1581
+ setWecomDrafts(toWecomDrafts(wecomSaved.bots));
1582
+ setWecomStatus(wecomSaved.status);
1095
1583
  setMessage(t("config.saved"));
1096
1584
  } catch (err) {
1097
1585
  setError(errorMessage(err));
@@ -1205,6 +1693,12 @@ function ConfigTab({ api }) {
1205
1693
  usernameLabel: t("config.nextcloud.username"),
1206
1694
  secretLabel: t("config.nextcloud.appPassword")
1207
1695
  }),
1696
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(WecomSection, {
1697
+ rooms: wecomRooms,
1698
+ drafts: wecomDrafts,
1699
+ status: wecomStatus,
1700
+ onChange: setWecomDrafts
1701
+ }),
1208
1702
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(McpSection, {
1209
1703
  title: t("config.ha"),
1210
1704
  row: config.connections.homeassistant,
@@ -1518,6 +2012,32 @@ function StatusTab({ api }) {
1518
2012
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: server.lastError ?? "" })
1519
2013
  ] }, server.server)) })]
1520
2014
  })
2015
+ }),
2016
+ status.wecomCli !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2017
+ className: panel_module_default.tableWrap,
2018
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2019
+ className: panel_module_default.message,
2020
+ children: t("status.wecomCli")
2021
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("table", {
2022
+ className: panel_module_default.table,
2023
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("tr", { children: [
2024
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: t("status.wecomCli.installed") }),
2025
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: t("status.wecomCli.authorized") }),
2026
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: t("status.wecomCli.modules") }),
2027
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: t("status.col.error") })
2028
+ ] }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("tbody", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("tr", { children: [
2029
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2030
+ className: panel_module_default.badge + " " + (status.wecomCli.installed ? panel_module_default.badgeOk : panel_module_default.badgeFail),
2031
+ children: status.wecomCli.installed ? t("status.wecomCli.yes") : t("status.wecomCli.no")
2032
+ }) }),
2033
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2034
+ className: panel_module_default.badge + " " + (status.wecomCli.authorized ? panel_module_default.badgeOk : panel_module_default.badgeFail),
2035
+ children: status.wecomCli.authorized ? t("status.wecomCli.yes") : t("status.wecomCli.no")
2036
+ }) }),
2037
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: (status.wecomCli.modules ?? []).map((row) => row.label).join("、") }),
2038
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: status.wecomCli.hint })
2039
+ ] }) })]
2040
+ })]
1521
2041
  })
1522
2042
  ]
1523
2043
  });
@@ -1675,6 +2195,10 @@ function RezSettingsPage({ t }) {
1675
2195
  id: "config",
1676
2196
  label: t("tab.config")
1677
2197
  },
2198
+ {
2199
+ id: "board",
2200
+ label: t("tab.board")
2201
+ },
1678
2202
  {
1679
2203
  id: "weixin",
1680
2204
  label: t("tab.weixin")
@@ -1713,6 +2237,7 @@ function RezSettingsPage({ t }) {
1713
2237
  }, tab.id))
1714
2238
  }),
1715
2239
  activeTab === "config" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ConfigTab, { api }),
2240
+ activeTab === "board" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BoardTab, { api }),
1716
2241
  activeTab === "weixin" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(WeixinTab, { api }),
1717
2242
  activeTab === "status" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatusTab, { api }),
1718
2243
  activeTab === "audit" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AuditTab, { api }),