@rezti/dsh-rez-suite 0.1.4 → 0.1.6

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/lib/index.d.ts CHANGED
@@ -1702,12 +1702,32 @@ interface RezMcpServerSpec {
1702
1702
  /** Per-tool-call timeout in milliseconds. */
1703
1703
  toolCallTimeoutMs?: number;
1704
1704
  }
1705
+ /** Company MCP endpoints persisted under ~/.dsh/dsh-rez-suite.json. Secrets are not stored here. */
1706
+ interface RezConnections {
1707
+ odoo: {
1708
+ enabled: boolean;
1709
+ url: string;
1710
+ };
1711
+ nextcloud: {
1712
+ enabled: boolean;
1713
+ url: string;
1714
+ username: string;
1715
+ };
1716
+ wechat: {
1717
+ enabled: boolean;
1718
+ };
1719
+ homeassistant: {
1720
+ enabled: boolean;
1721
+ url: string;
1722
+ };
1723
+ }
1705
1724
  /** Full plugin configuration persisted under ~/.dsh/dsh-rez-suite.json. */
1706
1725
  interface RezConfig {
1707
1726
  enabled: boolean;
1708
1727
  announceToAgent: boolean;
1709
1728
  role: RezRoleId;
1710
1729
  servers: Record<string, RezMcpServerSpec>;
1730
+ connections: RezConnections;
1711
1731
  billing: {
1712
1732
  inputCostPer1k: number;
1713
1733
  outputCostPer1k: number;
package/lib/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { createRequire } from "node:module";
2
2
  import { homedir } from "node:os";
3
3
  import { dirname, join } from "node:path";
4
+ import { REZ_CREDENTIAL_REFS, REZ_DEFAULT_CONNECTIONS, REZ_SYSTEMS, mergeConnections, secretConfigured, writeManagedCredential } from "@rezti/dsh-rez-sso";
4
5
  import { mkdirSync, renameSync, writeFileSync } from "node:fs";
5
6
  import { DatabaseSync } from "node:sqlite";
6
7
  //#region ../../node_modules/.pnpm/@deepseek-ai+cosmokit@1.8.2/node_modules/@deepseek-ai/cosmokit/lib/index.js
@@ -2865,13 +2866,14 @@ const FORMAT_VERSION = 1;
2865
2866
  function rezStorePath() {
2866
2867
  return join(homedir(), ".dsh", "dsh-rez-suite.json");
2867
2868
  }
2868
- /** Defaults: panel-only. Company MCP rows live in cordis.patch.yml. */
2869
+ /** Defaults: company MCP endpoints. Secrets live in credentials.yaml / env. */
2869
2870
  function defaultConfig() {
2870
2871
  return {
2871
2872
  enabled: true,
2872
2873
  announceToAgent: true,
2873
2874
  role: "all",
2874
2875
  servers: {},
2876
+ connections: JSON.parse(JSON.stringify(REZ_DEFAULT_CONNECTIONS)),
2875
2877
  billing: {
2876
2878
  inputCostPer1k: .001,
2877
2879
  outputCostPer1k: .002,
@@ -2896,10 +2898,37 @@ function maskServer(server) {
2896
2898
  function publicConfig(config) {
2897
2899
  const servers = {};
2898
2900
  for (const [id, server] of Object.entries(config.servers)) servers[id] = maskServer(server);
2901
+ const connections = mergeConnections(config.connections);
2899
2902
  return {
2900
2903
  enabled: config.enabled,
2901
2904
  role: config.role,
2902
2905
  servers,
2906
+ connections: {
2907
+ odoo: {
2908
+ enabled: connections.odoo.enabled,
2909
+ url: connections.odoo.url,
2910
+ secretConfigured: secretConfigured("odoo"),
2911
+ secretRef: REZ_CREDENTIAL_REFS.odoo
2912
+ },
2913
+ nextcloud: {
2914
+ enabled: connections.nextcloud.enabled,
2915
+ url: connections.nextcloud.url,
2916
+ username: connections.nextcloud.username,
2917
+ secretConfigured: secretConfigured("nextcloud"),
2918
+ secretRef: REZ_CREDENTIAL_REFS.nextcloud
2919
+ },
2920
+ wechat: {
2921
+ enabled: connections.wechat.enabled,
2922
+ secretConfigured: secretConfigured("wechat"),
2923
+ secretRef: REZ_CREDENTIAL_REFS.wechat
2924
+ },
2925
+ homeassistant: {
2926
+ enabled: connections.homeassistant.enabled,
2927
+ url: connections.homeassistant.url,
2928
+ secretConfigured: secretConfigured("homeassistant"),
2929
+ secretRef: REZ_CREDENTIAL_REFS.homeassistant
2930
+ }
2931
+ },
2903
2932
  billing: { ...config.billing }
2904
2933
  };
2905
2934
  }
@@ -2911,6 +2940,7 @@ function mergeConfig(base, override) {
2911
2940
  if (typeof value.enabled === "boolean") result.enabled = value.enabled;
2912
2941
  if (typeof value.announceToAgent === "boolean") result.announceToAgent = value.announceToAgent;
2913
2942
  if (value.role === "engineer" || value.role === "sales" || value.role === "operations" || value.role === "all") result.role = value.role;
2943
+ result.connections = mergeConnections(value.connections ?? result.connections);
2914
2944
  if (typeof value.servers === "object" && value.servers !== null) for (const [id, raw] of Object.entries(value.servers)) {
2915
2945
  if (typeof raw !== "object" || raw === null) continue;
2916
2946
  const incoming = raw;
@@ -3019,8 +3049,25 @@ function mergeMaskedRecord(base, incoming) {
3019
3049
  /** Apply a browser-safe public patch without overwriting masked secrets. */
3020
3050
  function applyPublicPatch(current, patch) {
3021
3051
  const next = JSON.parse(JSON.stringify(current));
3052
+ const secrets = [];
3022
3053
  if (typeof patch.enabled === "boolean") next.enabled = patch.enabled;
3023
3054
  if (patch.role === "engineer" || patch.role === "sales" || patch.role === "operations" || patch.role === "all") next.role = patch.role;
3055
+ if (typeof patch.connections === "object" && patch.connections !== null) {
3056
+ const incoming = patch.connections;
3057
+ next.connections = mergeConnections({
3058
+ ...next.connections,
3059
+ ...incoming
3060
+ });
3061
+ for (const system of Object.keys(REZ_CREDENTIAL_REFS)) {
3062
+ const row = incoming[system];
3063
+ if (typeof row !== "object" || row === null) continue;
3064
+ const secret = row.secret;
3065
+ if (typeof secret === "string" && secret.length > 0 && secret !== SECRET_MASK) secrets.push({
3066
+ system,
3067
+ value: secret
3068
+ });
3069
+ }
3070
+ }
3024
3071
  if (typeof patch.servers === "object" && patch.servers !== null) for (const [id, raw] of Object.entries(patch.servers)) {
3025
3072
  if (typeof raw !== "object" || raw === null) continue;
3026
3073
  const incoming = raw;
@@ -3046,11 +3093,14 @@ function applyPublicPatch(current, patch) {
3046
3093
  if (typeof value.outputCostPer1k === "number") next.billing.outputCostPer1k = value.outputCostPer1k;
3047
3094
  if (typeof value.monthlyBudget === "number") next.billing.monthlyBudget = value.monthlyBudget;
3048
3095
  }
3049
- return next;
3096
+ return {
3097
+ next,
3098
+ secrets
3099
+ };
3050
3100
  }
3051
3101
  /** Build every /api/dsh-rez-suite route (exact paths). */
3052
3102
  function makeRoutes(deps) {
3053
- const { getConfig, updateConfig, host, tokens } = deps;
3103
+ const { getConfig, updateConfig, host, tokens, onCredentialWritten } = deps;
3054
3104
  const guard = (req, res, method) => {
3055
3105
  if (!isLoopbackRequest(req)) {
3056
3106
  writeJson(res, 403, { error: "forbidden: loopback-only" });
@@ -3077,8 +3127,11 @@ function makeRoutes(deps) {
3077
3127
  writeJson(res, 400, { error: "invalid JSON body" });
3078
3128
  return;
3079
3129
  }
3080
- const next = applyPublicPatch(getConfig(), body);
3081
- writeJson(res, 200, { config: publicConfig(await updateConfig(next)) });
3130
+ const { next, secrets } = applyPublicPatch(getConfig(), body);
3131
+ const saved = await updateConfig(next);
3132
+ for (const item of secrets) writeManagedCredential(REZ_CREDENTIAL_REFS[item.system], item.value);
3133
+ for (const system of REZ_SYSTEMS) onCredentialWritten?.(REZ_CREDENTIAL_REFS[system]);
3134
+ writeJson(res, 200, { config: publicConfig(saved) });
3082
3135
  }
3083
3136
  },
3084
3137
  {
@@ -7537,6 +7590,22 @@ const Config = Schema.object({
7537
7590
  "all"
7538
7591
  ]).default("all"),
7539
7592
  servers: Schema.dict(serverSchema).default({}),
7593
+ connections: Schema.object({
7594
+ odoo: Schema.object({
7595
+ enabled: Schema.boolean().default(true),
7596
+ url: Schema.string().default(REZ_DEFAULT_CONNECTIONS.odoo.url)
7597
+ }).default(REZ_DEFAULT_CONNECTIONS.odoo),
7598
+ nextcloud: Schema.object({
7599
+ enabled: Schema.boolean().default(true),
7600
+ url: Schema.string().default(REZ_DEFAULT_CONNECTIONS.nextcloud.url),
7601
+ username: Schema.string().default(REZ_DEFAULT_CONNECTIONS.nextcloud.username)
7602
+ }).default(REZ_DEFAULT_CONNECTIONS.nextcloud),
7603
+ wechat: Schema.object({ enabled: Schema.boolean().default(true) }).default(REZ_DEFAULT_CONNECTIONS.wechat),
7604
+ homeassistant: Schema.object({
7605
+ enabled: Schema.boolean().default(true),
7606
+ url: Schema.string().default(REZ_DEFAULT_CONNECTIONS.homeassistant.url)
7607
+ }).default(REZ_DEFAULT_CONNECTIONS.homeassistant)
7608
+ }).default(REZ_DEFAULT_CONNECTIONS),
7540
7609
  billing: Schema.object({
7541
7610
  inputCostPer1k: Schema.number().default(.001),
7542
7611
  outputCostPer1k: Schema.number().default(.002),
@@ -7558,7 +7627,7 @@ const REZ_GUIDANCE = [
7558
7627
  "本机已安装 ReZ-TI 套件(一次安装):工作身份由 dsh-rez-sso 提供,MCP 由官方 dsh-mcp-client 接入 Odoo / Nextcloud / 企业微信 / Home Assistant。",
7559
7628
  "工具名 mcp__odoo__*、mcp__nextcloud__*、mcp__wechat__*、mcp__homeassistant__*。",
7560
7629
  "密钥:REZ_ODOO_TOKEN、REZ_NEXTCLOUD_PASSWORD、REZ_WECHAT_WEBHOOK、REZ_HA_TOKEN。",
7561
- "个人微信(QClaw/ClawBot):打开 Rez 面板「微信」页扫码,然后直接在微信里说话,不必再跑命令、也不要调用微信 MCP 工具。",
7630
+ "个人微信(QClaw/ClawBot):打开 设置 插件 → ReZ-TI → 微信 扫码,然后直接在微信里说话,不必再跑命令、也不要调用微信 MCP 工具。",
7562
7631
  "意图用 rez_set_intent(erp/files/chat/home/auto)。审计用 rez_audit。",
7563
7632
  "涉及资金、对外发送、设备控制、生产写入时先征得用户批准。"
7564
7633
  ].join("");
@@ -7572,13 +7641,19 @@ function delegatedHost() {
7572
7641
  state: "disconnected",
7573
7642
  toolCount: 0,
7574
7643
  registeredTools: 0,
7575
- lastError: "owned by @deepseek-ai/dsh-mcp-client"
7576
- }));
7577
- const test = async () => COMPOSED_SERVERS.map((server) => ({
7578
- server,
7579
- ok: false,
7580
- error: "MCP is composed by dsh-mcp-client; set REZ_* credentials and check dsh logs"
7644
+ lastError: secretConfigured(server) ? "owned by @deepseek-ai/dsh-mcp-client" : "missing " + REZ_CREDENTIAL_REFS[server]
7581
7645
  }));
7646
+ const test = async () => COMPOSED_SERVERS.map((server) => {
7647
+ return secretConfigured(server) ? {
7648
+ server,
7649
+ ok: true,
7650
+ serverInfo: REZ_CREDENTIAL_REFS[server] + " configured"
7651
+ } : {
7652
+ server,
7653
+ ok: false,
7654
+ error: "missing " + REZ_CREDENTIAL_REFS[server]
7655
+ };
7656
+ });
7582
7657
  return {
7583
7658
  status,
7584
7659
  test,
@@ -7609,7 +7684,11 @@ function apply(ctx, config) {
7609
7684
  return value;
7610
7685
  },
7611
7686
  host,
7612
- tokens
7687
+ tokens,
7688
+ onCredentialWritten: (ref) => {
7689
+ const emit = ctx.emit;
7690
+ emit("credentials/updated", ref);
7691
+ }
7613
7692
  });
7614
7693
  let disposeRoutes;
7615
7694
  let disposeTools;
package/lib/style.css CHANGED
@@ -1,110 +1,83 @@
1
- [data-pane="conversation"] {
2
- position: relative;
1
+ .WRGRRW_shell {
2
+ flex-direction: column;
3
+ gap: 12px;
4
+ min-width: 0;
5
+ display: flex;
3
6
  }
4
7
 
5
- [data-dsh-rez-view] {
6
- z-index: 60;
7
- background: var(--dsw-alias-bg-base);
8
- display: none;
9
- position: absolute;
10
- inset: 0;
8
+ .WRGRRW_card {
9
+ border: 1px solid var(--dsw-alias-border-l2);
10
+ background: var(--dsw-alias-bg-layer-3);
11
+ border-radius: 12px;
12
+ list-style: none;
13
+ transition: border-color .16s, background .16s;
11
14
  }
12
15
 
13
- html[data-dsh-rez-active]:not([data-dsh-taskboard-active]):not([data-dsh-ssh-active]) [data-dsh-rez-view] {
14
- display: block;
16
+ .WRGRRW_card:hover {
17
+ border-color: var(--dsw-alias-label-dimmed);
15
18
  }
16
19
 
17
- html[data-dsh-rez-active]:not([data-dsh-taskboard-active]):not([data-dsh-ssh-active]) [data-pane="conversation"] > :not([data-dsh-rez-view]), html[data-dsh-rez-active]:not([data-dsh-taskboard-active]):not([data-dsh-ssh-active]) [class*="centerCol"] > :not([data-dsh-rez-view]) {
18
- display: none !important;
20
+ .WRGRRW_cardOpen {
21
+ background: var(--dsw-alias-bg-layer-2);
22
+ border-color: var(--dsw-alias-label-dimmed);
19
23
  }
20
24
 
21
- .WRGRRW_entry {
25
+ .WRGRRW_header {
26
+ appearance: none;
22
27
  width: 100%;
23
- height: 32px;
24
- color: var(--dsw-alias-label-secondary);
28
+ font: inherit;
29
+ color: inherit;
30
+ text-align: left;
25
31
  cursor: pointer;
26
- white-space: nowrap;
27
32
  background: none;
28
- border: none;
29
- border-radius: 8px;
33
+ border: 0;
34
+ border-radius: 12px;
30
35
  align-items: center;
31
- gap: 8px;
32
- padding: 0 12px;
33
- font-size: 13px;
36
+ gap: 12px;
37
+ padding: 14px 16px;
34
38
  display: flex;
35
39
  }
36
40
 
37
- .WRGRRW_entry:hover {
38
- background: var(--dsw-specific-sidebar-nav-item-hover);
39
- color: var(--dsw-alias-label-primary);
40
- }
41
-
42
- .WRGRRW_entryIcon {
43
- flex: none;
44
- justify-content: center;
45
- align-items: center;
46
- display: inline-flex;
47
- }
48
-
49
- .WRGRRW_entryLabel {
50
- text-overflow: ellipsis;
51
- overflow: hidden;
52
- }
53
-
54
- [data-dsh-frame][data-sidebar-collapsed] .WRGRRW_entry {
55
- justify-content: center;
56
- width: 100%;
57
- padding: 0;
58
- }
59
-
60
- [data-dsh-frame][data-sidebar-collapsed] .WRGRRW_entryLabel {
61
- display: none;
41
+ .WRGRRW_header:focus-visible {
42
+ outline: 2px solid var(--dsw-alias-brand-primary);
43
+ outline-offset: -2px;
62
44
  }
63
45
 
64
- .WRGRRW_view {
65
- overflow: hidden;
46
+ .WRGRRW_headText {
47
+ flex-direction: column;
48
+ flex: 1;
49
+ gap: 4px;
50
+ min-width: 0;
51
+ display: flex;
66
52
  }
67
53
 
68
- .WRGRRW_panel {
69
- background: var(--dsw-alias-bg-base);
70
- min-width: 0;
71
- height: 100%;
72
- min-height: 0;
54
+ .WRGRRW_name {
73
55
  color: var(--dsw-alias-label-primary);
74
- font-family: var(--dsw-font-family);
75
- flex-direction: column;
76
- gap: 10px;
77
- padding: 14px 16px 16px;
78
- display: flex;
56
+ font-size: 15px;
57
+ font-weight: 600;
58
+ line-height: 1.4;
79
59
  }
80
60
 
81
- .WRGRRW_panelHeader {
82
- flex: none;
83
- align-items: center;
84
- gap: 10px;
85
- display: flex;
61
+ .WRGRRW_description {
62
+ color: var(--dsw-alias-label-tertiary);
63
+ font-size: 13px;
64
+ line-height: 1.5;
86
65
  }
87
66
 
88
- .WRGRRW_panelTitle {
89
- flex: 1;
90
- margin: 0;
91
- font-size: 16px;
92
- font-weight: 700;
67
+ .WRGRRW_chevron {
68
+ color: var(--dsw-alias-label-tertiary);
69
+ flex: none;
70
+ transition: transform .16s;
93
71
  }
94
72
 
95
- .WRGRRW_iconButton {
96
- color: var(--dsw-alias-label-secondary);
97
- cursor: pointer;
98
- background: none;
99
- border: none;
100
- border-radius: 6px;
101
- padding: 4px 8px;
102
- font-size: 14px;
73
+ .WRGRRW_chevronOpen {
74
+ transform: rotate(180deg);
103
75
  }
104
76
 
105
- .WRGRRW_iconButton:hover {
106
- background: var(--dsw-alias-interactive-bg-hover);
107
- color: var(--dsw-alias-label-primary);
77
+ .WRGRRW_body {
78
+ border-top: 1px solid var(--dsw-alias-border-l2);
79
+ margin: 0 16px 12px;
80
+ padding-top: 8px;
108
81
  }
109
82
 
110
83
  .WRGRRW_tabBar {
@@ -136,21 +109,12 @@ html[data-dsh-rez-active]:not([data-dsh-taskboard-active]):not([data-dsh-ssh-act
136
109
  font-weight: 600;
137
110
  }
138
111
 
139
- .WRGRRW_panelContent {
140
- flex-direction: column;
141
- flex: 1;
142
- min-height: 0;
143
- display: flex;
144
- overflow: hidden;
145
- }
146
-
147
112
  .WRGRRW_tabBody {
148
113
  flex-direction: column;
149
- flex: 1;
150
114
  gap: 12px;
151
- min-height: 0;
115
+ min-width: 0;
116
+ padding: 12px 0 4px;
152
117
  display: flex;
153
- overflow-y: auto;
154
118
  }
155
119
 
156
120
  .WRGRRW_section {
@@ -164,16 +128,17 @@ html[data-dsh-rez-active]:not([data-dsh-taskboard-active]):not([data-dsh-ssh-act
164
128
  }
165
129
 
166
130
  .WRGRRW_sectionTitle {
131
+ color: var(--dsw-alias-label-primary);
167
132
  margin: 0;
168
133
  font-size: 13px;
169
134
  font-weight: 600;
170
135
  }
171
136
 
172
137
  .WRGRRW_field {
173
- grid-template-columns: 140px 1fr;
174
- align-items: center;
175
- gap: 8px;
176
- display: grid;
138
+ flex-direction: column;
139
+ align-items: stretch;
140
+ gap: 4px;
141
+ display: flex;
177
142
  }
178
143
 
179
144
  .WRGRRW_label {
@@ -183,7 +148,7 @@ html[data-dsh-rez-active]:not([data-dsh-taskboard-active]):not([data-dsh-ssh-act
183
148
 
184
149
  .WRGRRW_input, .WRGRRW_select {
185
150
  color: var(--dsw-alias-label-primary);
186
- background: var(--dsw-specific-input-major);
151
+ background: var(--dsw-specific-input-major, var(--dsw-alias-bg-layer-3));
187
152
  border: 1px solid var(--dsw-alias-border-l2);
188
153
  border-radius: 8px;
189
154
  outline: none;
@@ -207,7 +172,7 @@ html[data-dsh-rez-active]:not([data-dsh-taskboard-active]):not([data-dsh-ssh-act
207
172
 
208
173
  .WRGRRW_button {
209
174
  border: 1px solid var(--dsw-alias-border-l2);
210
- background: var(--dsw-specific-input-major);
175
+ background: var(--dsw-specific-input-major, var(--dsw-alias-bg-layer-3));
211
176
  color: var(--dsw-alias-label-primary);
212
177
  cursor: pointer;
213
178
  border-radius: 8px;
@@ -215,23 +180,19 @@ html[data-dsh-rez-active]:not([data-dsh-taskboard-active]):not([data-dsh-ssh-act
215
180
  font-size: 13px;
216
181
  }
217
182
 
218
- .WRGRRW_button:hover {
183
+ .WRGRRW_button:hover:not(:disabled) {
219
184
  background: var(--dsw-alias-interactive-bg-hover);
220
185
  }
221
186
 
222
- .WRGRRW_qr {
223
- background: #fff;
224
- border-radius: 8px;
225
- width: 240px;
226
- height: 240px;
227
- margin: 8px 0;
228
- display: block;
187
+ .WRGRRW_button:disabled {
188
+ opacity: .4;
189
+ cursor: default;
229
190
  }
230
191
 
231
192
  .WRGRRW_primaryButton {
232
- background: var(--dsw-alias-state-business-primary);
233
- border-color: var(--dsw-alias-state-business-primary);
234
- color: #fff;
193
+ background: var(--dsw-alias-label-primary);
194
+ border-color: var(--dsw-alias-label-primary);
195
+ color: var(--dsw-alias-bg-layer-3);
235
196
  }
236
197
 
237
198
  .WRGRRW_dangerButton {
@@ -240,9 +201,20 @@ html[data-dsh-rez-active]:not([data-dsh-taskboard-active]):not([data-dsh-ssh-act
240
201
  color: #fff;
241
202
  }
242
203
 
204
+ .WRGRRW_qr {
205
+ background: #fff;
206
+ border-radius: 8px;
207
+ width: 200px;
208
+ height: 200px;
209
+ margin: 8px 0;
210
+ display: block;
211
+ }
212
+
243
213
  .WRGRRW_message {
244
214
  color: var(--dsw-alias-label-secondary);
215
+ margin: 0;
245
216
  font-size: 12.5px;
217
+ line-height: 1.5;
246
218
  }
247
219
 
248
220
  .WRGRRW_error {
@@ -252,8 +224,6 @@ html[data-dsh-rez-active]:not([data-dsh-taskboard-active]):not([data-dsh-ssh-act
252
224
  .WRGRRW_tableWrap {
253
225
  border: 1px solid var(--dsw-alias-border-l1);
254
226
  border-radius: 10px;
255
- flex: 1;
256
- min-height: 0;
257
227
  overflow: auto;
258
228
  }
259
229
 
@@ -296,12 +266,12 @@ html[data-dsh-rez-active]:not([data-dsh-taskboard-active]):not([data-dsh-ssh-act
296
266
  }
297
267
 
298
268
  .WRGRRW_cards {
299
- grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
269
+ grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
300
270
  gap: 8px;
301
271
  display: grid;
302
272
  }
303
273
 
304
- .WRGRRW_card {
274
+ .WRGRRW_stat {
305
275
  border: 1px solid var(--dsw-alias-border-l1);
306
276
  border-radius: 10px;
307
277
  padding: 10px;
@@ -313,6 +283,7 @@ html[data-dsh-rez-active]:not([data-dsh-taskboard-active]):not([data-dsh-ssh-act
313
283
  }
314
284
 
315
285
  .WRGRRW_cardValue {
286
+ color: var(--dsw-alias-label-primary);
316
287
  font-size: 15px;
317
288
  font-weight: 600;
318
289
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@rezti/dsh-rez-suite",
3
3
  "description": "ReZ-TI employee plugin for DeepSeek Harness: one install enables company MCP (Odoo / Nextcloud / WeCom / Weixin-ClawBot / Home Assistant) and work identity.",
4
- "version": "0.1.4",
4
+ "version": "0.1.6",
5
5
  "type": "module",
6
6
  "engines": {
7
7
  "node": "^22.19.0 || >=24.0.0"
@@ -28,7 +28,9 @@
28
28
  "inject": [
29
29
  "@deepseek-ai/dsh-client-runtime",
30
30
  "@deepseek-ai/dsh-client-connection",
31
- "@deepseek-ai/dsh-client-ui-settings"
31
+ "@deepseek-ai/dsh-client-locale",
32
+ "@deepseek-ai/dsh-client-ui-settings",
33
+ "@deepseek-ai/dsh-client-ui-settings-plugins"
32
34
  ],
33
35
  "platform": "web"
34
36
  }
@@ -36,12 +38,12 @@
36
38
  "dependencies": {
37
39
  "@modelcontextprotocol/sdk": "^1.30.0",
38
40
  "zod": "^4.4.3",
39
- "@rezti/dsh-rez-ha-bridge": "0.1.4",
40
- "@rezti/dsh-rez-odoo": "0.1.4",
41
- "@rezti/dsh-rez-nextcloud": "0.1.4",
41
+ "@rezti/dsh-rez-nextcloud": "0.1.5",
42
+ "@rezti/dsh-rez-odoo": "0.1.5",
43
+ "@rezti/dsh-rez-sso": "0.1.5",
44
+ "@rezti/dsh-rez-ha-bridge": "0.1.5",
45
+ "@rezti/dsh-rez-wechat": "0.1.5",
42
46
  "@rezti/dsh-rez-token-manager": "0.1.3",
43
- "@rezti/dsh-rez-sso": "0.1.4",
44
- "@rezti/dsh-rez-wechat": "0.1.4",
45
47
  "@rezti/dsh-rez-intent": "0.1.3"
46
48
  },
47
49
  "peerDependencies": {
@@ -1,17 +1,16 @@
1
1
  /**
2
- * Browser-half entry for dsh-rez-suite — runs inside the dsh web GUI.
3
- * Registers locale dictionaries and mounts the Rez Suite panel (sidebar entry
4
- * plus center-column view). DOM mounting problems are logged, never thrown.
2
+ * Browser-half entry for dsh-rez-suite.
3
+ * Registers locale dictionaries and contributes a native Settings card
4
+ * (设置 插件 → 插件配置) plus a Plugins tab. Do not mount a sidebar
5
+ * takeover over the conversation column.
5
6
  */
6
7
 
7
8
  import type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client'
8
9
  import type {} from '@deepseek-ai/dsh-client-locale/client'
10
+ import type {} from '@deepseek-ai/dsh-client-ui-settings/client'
9
11
  import type {} from '@deepseek-ai/dsh-client-ui-slots'
10
- import { RezApi } from './api.ts'
11
12
  import { en, zh, type RezKey } from './locales.ts'
12
- import { mountPanel } from './mount.tsx'
13
- import { PanelController } from './panel/controller.ts'
14
- import { mountSidebarEntry } from './sidebar-entry.ts'
13
+ import { RezPluginCard, RezSettingsPage } from './settings-card.tsx'
15
14
 
16
15
  const NS = 'dsh-rez-suite'
17
16
 
@@ -19,27 +18,40 @@ declare module '@deepseek-ai/dsh-client-ui-slots' {
19
18
  interface LocaleNamespaceMap {
20
19
  'dsh-rez-suite': RezKey
21
20
  }
21
+ interface SlotMap {
22
+ 'settings.plugin.item': {
23
+ kind: 'list'
24
+ scope: 'root'
25
+ owner: { children?: never }
26
+ }
27
+ 'settings.plugins.tab': {
28
+ kind: 'list'
29
+ scope: 'root'
30
+ owner: { children?: never }
31
+ }
32
+ }
22
33
  }
23
34
 
24
35
  export const inject = ['slots', 'locale']
25
36
 
26
- export type { PanelControllerSnapshot } from './panel/controller.ts'
27
- export type { RezPanelProps } from './panel/RezPanel.tsx'
28
37
  export type { RezKey } from './locales.ts'
29
38
 
30
39
  export function apply(ctx: ClientContext): void {
40
+ const t = ctx.locale.bind(NS)
31
41
  ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'dsh-rez-suite: dictionaries')
32
42
 
33
- const controller = new PanelController()
34
- const api = new RezApi()
35
- const disposers: Array<() => void> = []
36
- try {
37
- disposers.push(mountSidebarEntry(controller))
38
- disposers.push(mountPanel(controller, api))
39
- } catch (error) {
40
- console.warn('[dsh-rez-suite] mount failed:', error)
41
- }
42
- ctx.effect(() => () => {
43
- for (const dispose of disposers.splice(0)) dispose()
44
- }, 'dsh-rez-suite: ui mounts')
43
+ ctx.slots.inject('settings.plugin.item', () => ctx.slots.register({
44
+ name: 'settings.plugin.item',
45
+ id: 'rez-suite',
46
+ order: 40,
47
+ locale: NS,
48
+ }, RezPluginCard))
49
+
50
+ ctx.slots.inject('settings.plugins.tab', () => ctx.slots.register({
51
+ name: 'settings.plugins.tab',
52
+ id: 'rez',
53
+ order: 20,
54
+ label: () => t('settings.title'),
55
+ locale: NS,
56
+ }, RezSettingsPage))
45
57
  }