@rezti/dsh-rez-suite 0.1.6 → 0.1.7

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/README.md CHANGED
@@ -8,4 +8,4 @@ dsh plugin --profile web add @rezti/dsh-rez-suite
8
8
 
9
9
  组成见仓库根 README。子包不要单独 `dsh plugin add`。
10
10
 
11
- 配置在 **设置 → 插件 → 插件配置** 展开 **ReZ-TI**(同页还有独立的 ReZ-TI 标签)。Odoo / Nextcloud / WeCom / HA 的地址和密钥都在「配置」页。个人微信在卡片的「微信」页扫码,然后直接在微信里说话。
11
+ 配置在 **设置 → 插件 → 插件配置** 展开 **ReZ-TI**(同页还有独立的 ReZ-TI 标签)。Odoo / Nextcloud / WeCom / HA 的地址和密钥都在「配置」页。个人微信在卡片的「微信」页扫码,然后直接在微信里说话。状态页看的是本机已注册的 `mcp__odoo__*` 等工具,不是直接 ping 远端 MCP。
package/README.zh.md CHANGED
@@ -8,6 +8,6 @@ dsh plugin --profile web add @rezti/dsh-rez-suite
8
8
 
9
9
  密钥用环境变量或 dsh-credentials:`REZ_ODOO_TOKEN`、`REZ_NEXTCLOUD_PASSWORD`、`REZ_WECHAT_WEBHOOK`、`REZ_HA_TOKEN`。未配置时不拉起对应 MCP。
10
10
 
11
- 配置在 **设置 → 插件 → 插件配置** 展开 **ReZ-TI**(同页还有独立的 ReZ-TI 标签)。Odoo / Nextcloud / 企微 / HA 的地址和密钥都在「配置」页。个人微信在卡片的「微信」页扫码,然后直接在微信里说话。
11
+ 配置在 **设置 → 插件 → 插件配置** 展开 **ReZ-TI**(同页还有独立的 ReZ-TI 标签)。Odoo / Nextcloud / 企微 / HA 的地址和密钥都在「配置」页。个人微信在卡片的「微信」页扫码,然后直接在微信里说话。状态页看的是本机已注册的 `mcp__odoo__*` 等工具,不是直接 ping 远端 MCP。
12
12
 
13
13
  子包不要再单独 `dsh plugin add`,否则会重复注册 MCP。
package/lib/client.d.ts CHANGED
@@ -7307,6 +7307,7 @@ declare const zh: {
7307
7307
  readonly 'config.testResult': "{server}: {status}";
7308
7308
  readonly 'config.testOk': "成功({tools} 个工具, {latency}ms)";
7309
7309
  readonly 'config.testFail': "失败:{error}";
7310
+ readonly 'status.hint': "绿标表示本机已注册 mcp__<系统>__* 工具。远端 MCP 通了但这里仍红,说明 dsh-mcp-client 还没挂上。";
7310
7311
  readonly 'status.total': "当前角色 {role},共注册 {count} 个 MCP 工具";
7311
7312
  readonly 'status.col.server': "Server";
7312
7313
  readonly 'status.col.state': "状态";
package/lib/client.js CHANGED
@@ -68,6 +68,7 @@ const zh = {
68
68
  "config.testResult": "{server}: {status}",
69
69
  "config.testOk": "成功({tools} 个工具, {latency}ms)",
70
70
  "config.testFail": "失败:{error}",
71
+ "status.hint": "绿标表示本机已注册 mcp__<系统>__* 工具。远端 MCP 通了但这里仍红,说明 dsh-mcp-client 还没挂上。",
71
72
  "status.total": "当前角色 {role},共注册 {count} 个 MCP 工具",
72
73
  "status.col.server": "Server",
73
74
  "status.col.state": "状态",
@@ -164,6 +165,7 @@ const en = {
164
165
  "config.testResult": "{server}: {status}",
165
166
  "config.testOk": "OK ({tools} tools, {latency}ms)",
166
167
  "config.testFail": "Failed: {error}",
168
+ "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.",
167
169
  "status.total": "Role {role}, {count} MCP tools registered",
168
170
  "status.col.server": "Server",
169
171
  "status.col.state": "State",
@@ -977,6 +979,10 @@ function StatusTab({ api }) {
977
979
  children: tt("common.refresh")
978
980
  })]
979
981
  }),
982
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
983
+ className: panel_module_default.message,
984
+ children: tt("status.hint")
985
+ }),
980
986
  error !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
981
987
  className: panel_module_default.error,
982
988
  children: error
package/lib/index.js CHANGED
@@ -2842,6 +2842,98 @@ defineMethod("transform", [
2842
2842
  "preserve"
2843
2843
  ], ({ inner }, isInner) => inner.toString(isInner));
2844
2844
  //#endregion
2845
+ //#region src/observe-mcp.ts
2846
+ /**
2847
+ * Live MCP status without a second host.
2848
+ *
2849
+ * Official @deepseek-ai/dsh-mcp-client registers tools as mcp__<server>__*.
2850
+ * The settings Status tab used to hard-code disconnected after MCP ownership
2851
+ * moved; this module reads the same registry the agent sees.
2852
+ */
2853
+ const COMPOSED_SERVERS = [
2854
+ "odoo",
2855
+ "nextcloud",
2856
+ "wechat",
2857
+ "homeassistant"
2858
+ ];
2859
+ function mcpToolPrefix(server) {
2860
+ return "mcp__" + server + "__";
2861
+ }
2862
+ /** Count tools whose public name belongs to one composed MCP server. */
2863
+ function countMcpTools(toolNames, server) {
2864
+ const prefix = mcpToolPrefix(server);
2865
+ let count = 0;
2866
+ for (const name of toolNames) if (name.startsWith(prefix)) count += 1;
2867
+ return count;
2868
+ }
2869
+ function listRegisteredToolNames(tools) {
2870
+ if (tools === void 0 || typeof tools.schemas !== "function") return [];
2871
+ try {
2872
+ return tools.schemas().map((schema) => schema.name);
2873
+ } catch {
2874
+ return [];
2875
+ }
2876
+ }
2877
+ function isEnabled(config, server) {
2878
+ return config.connections[server].enabled !== false;
2879
+ }
2880
+ function observeServer(server, input) {
2881
+ const enabled = isEnabled(input.config, server);
2882
+ const secret = (input.secretPresent ?? secretConfigured)(server);
2883
+ const toolCount = countMcpTools(input.toolNames, server);
2884
+ const ref = REZ_CREDENTIAL_REFS[server];
2885
+ if (!enabled) return {
2886
+ server,
2887
+ enabled: false,
2888
+ state: "disconnected",
2889
+ toolCount: 0,
2890
+ registeredTools: 0,
2891
+ lastError: "已在设置中关闭"
2892
+ };
2893
+ if (toolCount > 0) return {
2894
+ server,
2895
+ enabled: true,
2896
+ state: "connected",
2897
+ toolCount,
2898
+ registeredTools: toolCount
2899
+ };
2900
+ if (!secret) return {
2901
+ server,
2902
+ enabled: true,
2903
+ state: "disconnected",
2904
+ toolCount: 0,
2905
+ registeredTools: 0,
2906
+ lastError: "未配置 " + ref
2907
+ };
2908
+ return {
2909
+ server,
2910
+ enabled: true,
2911
+ state: "disconnected",
2912
+ toolCount: 0,
2913
+ registeredTools: 0,
2914
+ lastError: ref + " 已配置,但本机尚未注册 " + mcpToolPrefix(server) + "*(dsh-mcp-client 未挂上)"
2915
+ };
2916
+ }
2917
+ function observeComposedServers(input) {
2918
+ return COMPOSED_SERVERS.map((server) => observeServer(server, input));
2919
+ }
2920
+ function testComposedServers(input) {
2921
+ return observeComposedServers(input).map((row) => {
2922
+ if (row.state === "connected") return {
2923
+ server: row.server,
2924
+ ok: true,
2925
+ toolCount: row.toolCount,
2926
+ serverInfo: row.toolCount + " tools registered (" + mcpToolPrefix(row.server) + "*)"
2927
+ };
2928
+ return {
2929
+ server: row.server,
2930
+ ok: false,
2931
+ toolCount: 0,
2932
+ error: row.lastError ?? "disconnected"
2933
+ };
2934
+ });
2935
+ }
2936
+ //#endregion
2845
2937
  //#region src/protocol.ts
2846
2938
  const REZ_API = {
2847
2939
  config: "/api/dsh-rez-suite/config",
@@ -7617,12 +7709,6 @@ const Config = Schema.object({
7617
7709
  })
7618
7710
  });
7619
7711
  const SECTION_ORDER = 150;
7620
- const COMPOSED_SERVERS = [
7621
- "odoo",
7622
- "nextcloud",
7623
- "wechat",
7624
- "homeassistant"
7625
- ];
7626
7712
  const REZ_GUIDANCE = [
7627
7713
  "本机已安装 ReZ-TI 套件(一次安装):工作身份由 dsh-rez-sso 提供,MCP 由官方 dsh-mcp-client 接入 Odoo / Nextcloud / 企业微信 / Home Assistant。",
7628
7714
  "工具名 mcp__odoo__*、mcp__nextcloud__*、mcp__wechat__*、mcp__homeassistant__*。",
@@ -7634,29 +7720,17 @@ const REZ_GUIDANCE = [
7634
7720
  function normalize(input) {
7635
7721
  return mergeConfig(defaultConfig(), input ?? {});
7636
7722
  }
7637
- function delegatedHost() {
7638
- const status = () => COMPOSED_SERVERS.map((server) => ({
7639
- server,
7640
- enabled: true,
7641
- state: "disconnected",
7642
- toolCount: 0,
7643
- registeredTools: 0,
7644
- lastError: secretConfigured(server) ? "owned by @deepseek-ai/dsh-mcp-client" : "missing " + REZ_CREDENTIAL_REFS[server]
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
- };
7723
+ function delegatedHost(ctx, getConfig) {
7724
+ const snapshot = () => observeComposedServers({
7725
+ config: getConfig(),
7726
+ toolNames: listRegisteredToolNames(ctx.tools)
7656
7727
  });
7657
7728
  return {
7658
- status,
7659
- test,
7729
+ status: () => snapshot(),
7730
+ test: async () => testComposedServers({
7731
+ config: getConfig(),
7732
+ toolNames: listRegisteredToolNames(ctx.tools)
7733
+ }),
7660
7734
  sync: async () => void 0,
7661
7735
  dispose: async () => void 0
7662
7736
  };
@@ -7664,7 +7738,7 @@ function delegatedHost() {
7664
7738
  function apply(ctx, config) {
7665
7739
  let current = () => normalize(config);
7666
7740
  const tokens = new TokenManager(joinTokenDb());
7667
- const host = delegatedHost();
7741
+ const host = delegatedHost(ctx, () => current());
7668
7742
  ctx.effect(() => () => {
7669
7743
  tokens.close();
7670
7744
  }, "dsh-rez-suite: ledger view");
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.6",
4
+ "version": "0.1.7",
5
5
  "type": "module",
6
6
  "engines": {
7
7
  "node": "^22.19.0 || >=24.0.0"
@@ -38,13 +38,13 @@
38
38
  "dependencies": {
39
39
  "@modelcontextprotocol/sdk": "^1.30.0",
40
40
  "zod": "^4.4.3",
41
+ "@rezti/dsh-rez-ha-bridge": "0.1.5",
42
+ "@rezti/dsh-rez-intent": "0.1.3",
41
43
  "@rezti/dsh-rez-nextcloud": "0.1.5",
42
- "@rezti/dsh-rez-odoo": "0.1.5",
43
44
  "@rezti/dsh-rez-sso": "0.1.5",
44
- "@rezti/dsh-rez-ha-bridge": "0.1.5",
45
- "@rezti/dsh-rez-wechat": "0.1.5",
45
+ "@rezti/dsh-rez-odoo": "0.1.5",
46
46
  "@rezti/dsh-rez-token-manager": "0.1.3",
47
- "@rezti/dsh-rez-intent": "0.1.3"
47
+ "@rezti/dsh-rez-wechat": "0.1.5"
48
48
  },
49
49
  "peerDependencies": {
50
50
  "react": "^18.2.0",
@@ -60,6 +60,7 @@ export const zh = {
60
60
  'config.testOk': '成功({tools} 个工具, {latency}ms)',
61
61
  'config.testFail': '失败:{error}',
62
62
  // status
63
+ 'status.hint': '绿标表示本机已注册 mcp__<系统>__* 工具。远端 MCP 通了但这里仍红,说明 dsh-mcp-client 还没挂上。',
63
64
  'status.total': '当前角色 {role},共注册 {count} 个 MCP 工具',
64
65
  'status.col.server': 'Server',
65
66
  'status.col.state': '状态',
@@ -161,6 +162,7 @@ export const en: Record<RezKey, string> = {
161
162
  'config.testResult': '{server}: {status}',
162
163
  'config.testOk': 'OK ({tools} tools, {latency}ms)',
163
164
  'config.testFail': 'Failed: {error}',
165
+ '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.',
164
166
  'status.total': 'Role {role}, {count} MCP tools registered',
165
167
  'status.col.server': 'Server',
166
168
  'status.col.state': 'State',
@@ -36,6 +36,7 @@ export function StatusTab({ api }: { api: RezApi }) {
36
36
  <span className={css.message}>{tt('status.total', { role: status.role, count: status.totalRegisteredTools })}</span>
37
37
  <button type="button" className={css.button} onClick={() => { void load() }}>{tt('common.refresh')}</button>
38
38
  </div>
39
+ <p className={css.message}>{tt('status.hint')}</p>
39
40
  {error !== '' && <div className={css.error}>{error}</div>}
40
41
  <div className={css.tableWrap}>
41
42
  <table className={css.table}>
package/src/index.ts CHANGED
@@ -15,13 +15,18 @@ import z from '@deepseek-ai/schemastery'
15
15
  import type {} from '@deepseek-ai/dsh-host-webserver'
16
16
  import type {} from '@deepseek-ai/dsh-system-prompt'
17
17
  import type {} from '@deepseek-ai/dsh-tools'
18
- import { REZ_CREDENTIAL_REFS, REZ_DEFAULT_CONNECTIONS, secretConfigured } from '@rezti/dsh-rez-sso'
18
+ import { REZ_DEFAULT_CONNECTIONS } from '@rezti/dsh-rez-sso'
19
19
  import type { McpHost } from './mcp-host.ts'
20
+ import {
21
+ listRegisteredToolNames,
22
+ observeComposedServers,
23
+ testComposedServers,
24
+ } from './observe-mcp.ts'
20
25
  import { makeRoutes } from './routes.ts'
21
26
  import { defaultConfig, mergeConfig, saveConfig } from './store.ts'
22
27
  import { TokenManager } from './token-manager.ts'
23
28
  import { rezConfigTool, rezStatusTool } from './tools.ts'
24
- import type { RezConfig, RezServerStatus, RezTestResult } from './protocol.ts'
29
+ import type { RezConfig } from './protocol.ts'
25
30
 
26
31
  export const name = 'rez-suite'
27
32
 
@@ -73,8 +78,6 @@ export const Config: z<RezConfig> = z.object({
73
78
 
74
79
  const SECTION_ORDER = 150
75
80
 
76
- const COMPOSED_SERVERS = ['odoo', 'nextcloud', 'wechat', 'homeassistant'] as const
77
-
78
81
  export const REZ_GUIDANCE = [
79
82
  '本机已安装 ReZ-TI 套件(一次安装):工作身份由 dsh-rez-sso 提供,MCP 由官方 dsh-mcp-client 接入 Odoo / Nextcloud / 企业微信 / Home Assistant。',
80
83
  '工具名 mcp__odoo__*、mcp__nextcloud__*、mcp__wechat__*、mcp__homeassistant__*。',
@@ -88,26 +91,17 @@ function normalize(input: Partial<RezConfig> | undefined): RezConfig {
88
91
  return mergeConfig(defaultConfig(), input ?? {})
89
92
  }
90
93
 
91
- function delegatedHost(): McpHost {
92
- const status = (): RezServerStatus[] => COMPOSED_SERVERS.map(server => ({
93
- server,
94
- enabled: true,
95
- state: 'disconnected' as const,
96
- toolCount: 0,
97
- registeredTools: 0,
98
- lastError: secretConfigured(server)
99
- ? 'owned by @deepseek-ai/dsh-mcp-client'
100
- : 'missing ' + REZ_CREDENTIAL_REFS[server],
101
- }))
102
- const test = async (): Promise<RezTestResult[]> => COMPOSED_SERVERS.map(server => {
103
- const ok = secretConfigured(server)
104
- return ok
105
- ? { server, ok: true, serverInfo: REZ_CREDENTIAL_REFS[server] + ' configured' }
106
- : { server, ok: false, error: 'missing ' + REZ_CREDENTIAL_REFS[server] }
94
+ function delegatedHost(ctx: Context, getConfig: () => RezConfig): McpHost {
95
+ const snapshot = () => observeComposedServers({
96
+ config: getConfig(),
97
+ toolNames: listRegisteredToolNames(ctx.tools),
107
98
  })
108
99
  return {
109
- status,
110
- test,
100
+ status: () => snapshot(),
101
+ test: async () => testComposedServers({
102
+ config: getConfig(),
103
+ toolNames: listRegisteredToolNames(ctx.tools),
104
+ }),
111
105
  sync: async () => undefined,
112
106
  dispose: async () => undefined,
113
107
  } as unknown as McpHost
@@ -117,7 +111,7 @@ export function apply(ctx: Context, config?: RezConfig): void {
117
111
  let current: () => RezConfig = () => normalize(config)
118
112
 
119
113
  const tokens = new TokenManager(joinTokenDb())
120
- const host = delegatedHost()
114
+ const host = delegatedHost(ctx, () => current())
121
115
  ctx.effect(() => () => { tokens.close() }, 'dsh-rez-suite: ledger view')
122
116
 
123
117
  const tools = [
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Live MCP status without a second host.
3
+ *
4
+ * Official @deepseek-ai/dsh-mcp-client registers tools as mcp__<server>__*.
5
+ * The settings Status tab used to hard-code disconnected after MCP ownership
6
+ * moved; this module reads the same registry the agent sees.
7
+ */
8
+
9
+ import { REZ_CREDENTIAL_REFS, secretConfigured, type RezSystem } from '@rezti/dsh-rez-sso'
10
+ import type { RezConfig, RezServerStatus, RezTestResult } from './protocol.ts'
11
+
12
+ export const COMPOSED_SERVERS = ['odoo', 'nextcloud', 'wechat', 'homeassistant'] as const
13
+
14
+ export type ComposedServer = (typeof COMPOSED_SERVERS)[number]
15
+
16
+ export function mcpToolPrefix(server: string): string {
17
+ return 'mcp__' + server + '__'
18
+ }
19
+
20
+ /** Count tools whose public name belongs to one composed MCP server. */
21
+ export function countMcpTools(toolNames: readonly string[], server: string): number {
22
+ const prefix = mcpToolPrefix(server)
23
+ let count = 0
24
+ for (const name of toolNames) {
25
+ if (name.startsWith(prefix)) count += 1
26
+ }
27
+ return count
28
+ }
29
+
30
+ export function listRegisteredToolNames(tools: { schemas?: () => ReadonlyArray<{ name: string }> } | undefined): string[] {
31
+ if (tools === undefined || typeof tools.schemas !== 'function') return []
32
+ try {
33
+ return tools.schemas().map(schema => schema.name)
34
+ } catch {
35
+ return []
36
+ }
37
+ }
38
+
39
+ export interface ObserveInput {
40
+ config: RezConfig
41
+ toolNames: readonly string[]
42
+ secretPresent?: (server: RezSystem) => boolean
43
+ }
44
+
45
+ function isEnabled(config: RezConfig, server: ComposedServer): boolean {
46
+ return config.connections[server].enabled !== false
47
+ }
48
+
49
+ export function observeServer(
50
+ server: ComposedServer,
51
+ input: ObserveInput,
52
+ ): RezServerStatus {
53
+ const enabled = isEnabled(input.config, server)
54
+ const secret = (input.secretPresent ?? secretConfigured)(server)
55
+ const toolCount = countMcpTools(input.toolNames, server)
56
+ const ref = REZ_CREDENTIAL_REFS[server]
57
+
58
+ if (!enabled) {
59
+ return {
60
+ server,
61
+ enabled: false,
62
+ state: 'disconnected',
63
+ toolCount: 0,
64
+ registeredTools: 0,
65
+ lastError: '已在设置中关闭',
66
+ }
67
+ }
68
+
69
+ if (toolCount > 0) {
70
+ return {
71
+ server,
72
+ enabled: true,
73
+ state: 'connected',
74
+ toolCount,
75
+ registeredTools: toolCount,
76
+ }
77
+ }
78
+
79
+ if (!secret) {
80
+ return {
81
+ server,
82
+ enabled: true,
83
+ state: 'disconnected',
84
+ toolCount: 0,
85
+ registeredTools: 0,
86
+ lastError: '未配置 ' + ref,
87
+ }
88
+ }
89
+
90
+ return {
91
+ server,
92
+ enabled: true,
93
+ state: 'disconnected',
94
+ toolCount: 0,
95
+ registeredTools: 0,
96
+ lastError: ref + ' 已配置,但本机尚未注册 ' + mcpToolPrefix(server) + '*(dsh-mcp-client 未挂上)',
97
+ }
98
+ }
99
+
100
+ export function observeComposedServers(input: ObserveInput): RezServerStatus[] {
101
+ return COMPOSED_SERVERS.map(server => observeServer(server, input))
102
+ }
103
+
104
+ export function testComposedServers(input: ObserveInput): RezTestResult[] {
105
+ return observeComposedServers(input).map(row => {
106
+ if (row.state === 'connected') {
107
+ return {
108
+ server: row.server,
109
+ ok: true,
110
+ toolCount: row.toolCount,
111
+ serverInfo: row.toolCount + ' tools registered (' + mcpToolPrefix(row.server) + '*)',
112
+ }
113
+ }
114
+ return {
115
+ server: row.server,
116
+ ok: false,
117
+ toolCount: 0,
118
+ error: row.lastError ?? 'disconnected',
119
+ }
120
+ })
121
+ }