@rezti/dsh-rez-suite 0.1.26 → 0.1.27
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 +1 -1
- package/lib/client.d.ts +3 -0
- package/lib/client.js +31 -1
- package/lib/index.d.ts +4 -0
- package/lib/index.js +78 -4
- package/package.json +6 -6
- package/src/client/locales.ts +6 -0
- package/src/client/panel/ConfigTab.tsx +15 -1
- package/src/index.ts +9 -0
- package/src/observe-mcp.ts +5 -2
- package/src/presets.ts +1 -0
- package/src/protocol.ts +2 -0
- package/src/store.ts +6 -0
- package/src/tianyancha.ts +79 -0
- package/templates/staff/legal/.agents/skills/staff-legal/SKILL.md +1 -1
- package/templates/staff/legal/AGENTS.md +4 -0
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @rezti/dsh-rez-suite
|
|
2
2
|
|
|
3
|
-
雷泽插件的**唯一安装入口**。Odoo / 企微 / HA / TAPD / GSC / 工作区(老板三室 + 全员助理)都由这一包拉齐,不要再逐个 `plugin add`,**不要**再装 `@rezti/dsh-rez-boss-desk`。
|
|
3
|
+
雷泽插件的**唯一安装入口**。Odoo / 企微 / HA / TAPD / GSC / 天眼查(仅老板) / 工作区(老板三室 + 全员助理)都由这一包拉齐,不要再逐个 `plugin add`,**不要**再装 `@rezti/dsh-rez-boss-desk`。
|
|
4
4
|
|
|
5
5
|
设置里选身份后,左侧自己出现对应房间(全员三助理;老板另加搞钱/产品/运营),不必手动添加工作区。
|
|
6
6
|
|
package/lib/client.d.ts
CHANGED
|
@@ -7300,6 +7300,9 @@ declare const zh: {
|
|
|
7300
7300
|
readonly 'config.gsc': "Google Search Console";
|
|
7301
7301
|
readonly 'config.gsc.siteUrl': "站点 URL";
|
|
7302
7302
|
readonly 'config.gsc.key': "服务账号 JSON 或密钥文件路径";
|
|
7303
|
+
readonly 'config.tianyancha': "天眼查(法务助理 · 仅老板)";
|
|
7304
|
+
readonly 'config.tianyancha.url': "天眼查 MCP URL";
|
|
7305
|
+
readonly 'config.tianyancha.token': "OpenAPI Token";
|
|
7303
7306
|
readonly 'config.fs': "本地文件 (fs-mcp-server)";
|
|
7304
7307
|
readonly 'config.fs.root': "根目录";
|
|
7305
7308
|
readonly 'config.sqlite': "SQLite (sqlite-mcp-server)";
|
package/lib/client.js
CHANGED
|
@@ -61,6 +61,9 @@ const zh = {
|
|
|
61
61
|
"config.gsc": "Google Search Console",
|
|
62
62
|
"config.gsc.siteUrl": "站点 URL",
|
|
63
63
|
"config.gsc.key": "服务账号 JSON 或密钥文件路径",
|
|
64
|
+
"config.tianyancha": "天眼查(法务助理 · 仅老板)",
|
|
65
|
+
"config.tianyancha.url": "天眼查 MCP URL",
|
|
66
|
+
"config.tianyancha.token": "OpenAPI Token",
|
|
64
67
|
"config.fs": "本地文件 (fs-mcp-server)",
|
|
65
68
|
"config.fs.root": "根目录",
|
|
66
69
|
"config.sqlite": "SQLite (sqlite-mcp-server)",
|
|
@@ -174,6 +177,9 @@ const en = {
|
|
|
174
177
|
"config.gsc": "Google Search Console",
|
|
175
178
|
"config.gsc.siteUrl": "Site URL",
|
|
176
179
|
"config.gsc.key": "Service-account JSON or key file path",
|
|
180
|
+
"config.tianyancha": "Tianyancha (Legal assistant, boss only)",
|
|
181
|
+
"config.tianyancha.url": "Tianyancha MCP URL",
|
|
182
|
+
"config.tianyancha.token": "OpenAPI token",
|
|
177
183
|
"config.fs": "Local files (fs-mcp-server)",
|
|
178
184
|
"config.fs.root": "Root directory",
|
|
179
185
|
"config.sqlite": "SQLite (sqlite-mcp-server)",
|
|
@@ -581,7 +587,8 @@ const EMPTY_SECRETS = {
|
|
|
581
587
|
wechat: "",
|
|
582
588
|
homeassistant: "",
|
|
583
589
|
tapd: "",
|
|
584
|
-
gsc: ""
|
|
590
|
+
gsc: "",
|
|
591
|
+
tianyancha: ""
|
|
585
592
|
};
|
|
586
593
|
function Field({ label, children }) {
|
|
587
594
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -745,6 +752,10 @@ function ConfigTab({ api }) {
|
|
|
745
752
|
gsc: {
|
|
746
753
|
...config.connections.gsc,
|
|
747
754
|
secret: secrets.gsc || void 0
|
|
755
|
+
},
|
|
756
|
+
tianyancha: {
|
|
757
|
+
...config.connections.tianyancha,
|
|
758
|
+
secret: secrets.tianyancha || void 0
|
|
748
759
|
}
|
|
749
760
|
}
|
|
750
761
|
};
|
|
@@ -950,6 +961,25 @@ function ConfigTab({ api }) {
|
|
|
950
961
|
},
|
|
951
962
|
secretLabel: tt("config.gsc.key")
|
|
952
963
|
}),
|
|
964
|
+
config.role === "boss" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(McpSection, {
|
|
965
|
+
title: tt("config.tianyancha"),
|
|
966
|
+
row: config.connections.tianyancha,
|
|
967
|
+
secret: secrets.tianyancha,
|
|
968
|
+
onEnabled: (enabled) => {
|
|
969
|
+
patchConnection("tianyancha", { enabled });
|
|
970
|
+
},
|
|
971
|
+
onUrl: (url) => {
|
|
972
|
+
patchConnection("tianyancha", { url });
|
|
973
|
+
},
|
|
974
|
+
onSecret: (value) => {
|
|
975
|
+
setSecrets((prev) => ({
|
|
976
|
+
...prev,
|
|
977
|
+
tianyancha: value
|
|
978
|
+
}));
|
|
979
|
+
},
|
|
980
|
+
urlLabel: tt("config.tianyancha.url"),
|
|
981
|
+
secretLabel: tt("config.tianyancha.token")
|
|
982
|
+
}),
|
|
953
983
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Section, {
|
|
954
984
|
title: tt("config.billing"),
|
|
955
985
|
children: [
|
package/lib/index.d.ts
CHANGED
|
@@ -1730,6 +1730,10 @@ interface RezConnections {
|
|
|
1730
1730
|
enabled: boolean;
|
|
1731
1731
|
siteUrl: string;
|
|
1732
1732
|
};
|
|
1733
|
+
tianyancha: {
|
|
1734
|
+
enabled: boolean;
|
|
1735
|
+
url: string;
|
|
1736
|
+
};
|
|
1733
1737
|
}
|
|
1734
1738
|
/** Full plugin configuration persisted under ~/.dsh/dsh-rez-suite.json. */
|
|
1735
1739
|
interface RezConfig {
|
package/lib/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import { dirname, join, relative } from "node:path";
|
|
4
|
-
import { REZ_CREDENTIAL_REFS, REZ_DEFAULT_CONNECTIONS, REZ_SYSTEMS, lastMcpStartError, mergeConnections, secretConfigured, writeManagedCredential } from "@rezti/dsh-rez-sso";
|
|
4
|
+
import { REZ_CREDENTIAL_REFS, REZ_DEFAULT_CONNECTIONS, REZ_SYSTEMS, lastMcpStartError, loadRezConnections, mergeConnections, secretConfigured, startMcpWhenSecret, writeManagedCredential } from "@rezti/dsh-rez-sso";
|
|
5
5
|
import { existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, renameSync, statSync, writeFileSync } from "node:fs";
|
|
6
6
|
import { DatabaseSync } from "node:sqlite";
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
@@ -2858,7 +2858,8 @@ const COMPOSED_SERVERS = [
|
|
|
2858
2858
|
"wechat",
|
|
2859
2859
|
"homeassistant",
|
|
2860
2860
|
"tapd",
|
|
2861
|
-
"gsc"
|
|
2861
|
+
"gsc",
|
|
2862
|
+
"tianyancha"
|
|
2862
2863
|
];
|
|
2863
2864
|
function mcpToolPrefix(server) {
|
|
2864
2865
|
return "mcp__" + server + "__";
|
|
@@ -2920,7 +2921,7 @@ function observeServer(server, input) {
|
|
|
2920
2921
|
};
|
|
2921
2922
|
}
|
|
2922
2923
|
function observeComposedServers(input) {
|
|
2923
|
-
return COMPOSED_SERVERS.map((server) => observeServer(server, input));
|
|
2924
|
+
return (input.config.role === "boss" ? COMPOSED_SERVERS : COMPOSED_SERVERS.filter((server) => server !== "tianyancha")).map((server) => observeServer(server, input));
|
|
2924
2925
|
}
|
|
2925
2926
|
function testComposedServers(input) {
|
|
2926
2927
|
return observeComposedServers(input).map((row) => {
|
|
@@ -3049,6 +3050,12 @@ function publicConfig(config) {
|
|
|
3049
3050
|
siteUrl: connections.gsc.siteUrl,
|
|
3050
3051
|
secretConfigured: secretConfigured("gsc"),
|
|
3051
3052
|
secretRef: REZ_CREDENTIAL_REFS.gsc
|
|
3053
|
+
},
|
|
3054
|
+
tianyancha: {
|
|
3055
|
+
enabled: connections.tianyancha.enabled,
|
|
3056
|
+
url: connections.tianyancha.url,
|
|
3057
|
+
secretConfigured: secretConfigured("tianyancha"),
|
|
3058
|
+
secretRef: REZ_CREDENTIAL_REFS.tianyancha
|
|
3052
3059
|
}
|
|
3053
3060
|
},
|
|
3054
3061
|
billing: { ...config.billing }
|
|
@@ -8080,6 +8087,65 @@ function liveSelfUpdate() {
|
|
|
8080
8087
|
});
|
|
8081
8088
|
}
|
|
8082
8089
|
//#endregion
|
|
8090
|
+
//#region src/tianyancha.ts
|
|
8091
|
+
const TIANYANCHA_MCP_URL = "https://mcp.tianyancha.com/mcp";
|
|
8092
|
+
function shouldStartTianyancha(role) {
|
|
8093
|
+
return role === "boss";
|
|
8094
|
+
}
|
|
8095
|
+
function bearerAuthorization(secret) {
|
|
8096
|
+
const trimmed = secret.trim();
|
|
8097
|
+
if (/^bearer\s+/i.test(trimmed)) return "Bearer " + trimmed.replace(/^bearer\s+/i, "");
|
|
8098
|
+
return "Bearer " + trimmed;
|
|
8099
|
+
}
|
|
8100
|
+
function tianyanchaMcpConfig(secret) {
|
|
8101
|
+
const { url } = loadRezConnections().tianyancha;
|
|
8102
|
+
return {
|
|
8103
|
+
serverName: "tianyancha",
|
|
8104
|
+
transport: "streamable-http",
|
|
8105
|
+
url: url.length > 0 ? url : TIANYANCHA_MCP_URL,
|
|
8106
|
+
headers: { Authorization: bearerAuthorization(secret) },
|
|
8107
|
+
failOnStartupError: false
|
|
8108
|
+
};
|
|
8109
|
+
}
|
|
8110
|
+
/** Start when role is boss; dispose the mcp-client fiber when the identity leaves. */
|
|
8111
|
+
function mountTianyanchaMcp(ctx, getRole, start = startMcpWhenSecret) {
|
|
8112
|
+
let session;
|
|
8113
|
+
let pending = false;
|
|
8114
|
+
const ensure = () => {
|
|
8115
|
+
(async () => {
|
|
8116
|
+
if (!shouldStartTianyancha(getRole())) {
|
|
8117
|
+
session?.();
|
|
8118
|
+
session = void 0;
|
|
8119
|
+
return;
|
|
8120
|
+
}
|
|
8121
|
+
if (session !== void 0 || pending) return;
|
|
8122
|
+
pending = true;
|
|
8123
|
+
let raced = false;
|
|
8124
|
+
try {
|
|
8125
|
+
const dispose = await start(ctx, {
|
|
8126
|
+
system: "tianyancha",
|
|
8127
|
+
label: "天眼查",
|
|
8128
|
+
config: tianyanchaMcpConfig
|
|
8129
|
+
});
|
|
8130
|
+
if (!shouldStartTianyancha(getRole())) {
|
|
8131
|
+
raced = true;
|
|
8132
|
+
dispose();
|
|
8133
|
+
return;
|
|
8134
|
+
}
|
|
8135
|
+
session = dispose;
|
|
8136
|
+
} catch (error) {
|
|
8137
|
+
const why = error instanceof Error ? error.message : String(error);
|
|
8138
|
+
console.error("[dsh-rez-suite] tianyancha MCP start failed:", why);
|
|
8139
|
+
} finally {
|
|
8140
|
+
pending = false;
|
|
8141
|
+
if (raced && shouldStartTianyancha(getRole()) && session === void 0) ensure();
|
|
8142
|
+
}
|
|
8143
|
+
})();
|
|
8144
|
+
};
|
|
8145
|
+
ensure();
|
|
8146
|
+
return ensure;
|
|
8147
|
+
}
|
|
8148
|
+
//#endregion
|
|
8083
8149
|
//#region src/index.ts
|
|
8084
8150
|
const name = "rez-suite";
|
|
8085
8151
|
const inject = [
|
|
@@ -8133,7 +8199,11 @@ const Config = Schema.object({
|
|
|
8133
8199
|
gsc: Schema.object({
|
|
8134
8200
|
enabled: Schema.boolean().default(true),
|
|
8135
8201
|
siteUrl: Schema.string().default(REZ_DEFAULT_CONNECTIONS.gsc.siteUrl)
|
|
8136
|
-
}).default(REZ_DEFAULT_CONNECTIONS.gsc)
|
|
8202
|
+
}).default(REZ_DEFAULT_CONNECTIONS.gsc),
|
|
8203
|
+
tianyancha: Schema.object({
|
|
8204
|
+
enabled: Schema.boolean().default(true),
|
|
8205
|
+
url: Schema.string().default(REZ_DEFAULT_CONNECTIONS.tianyancha.url)
|
|
8206
|
+
}).default(REZ_DEFAULT_CONNECTIONS.tianyancha)
|
|
8137
8207
|
}).default(REZ_DEFAULT_CONNECTIONS),
|
|
8138
8208
|
billing: Schema.object({
|
|
8139
8209
|
inputCostPer1k: Schema.number().default(.001),
|
|
@@ -8180,6 +8250,7 @@ function delegatedHost(ctx, getConfig) {
|
|
|
8180
8250
|
function apply(ctx, config) {
|
|
8181
8251
|
let current = () => normalize(config);
|
|
8182
8252
|
const syncWorkspaces = mountRezWorkspaces(ctx, () => current().role);
|
|
8253
|
+
const syncTianyancha = mountTianyanchaMcp(ctx, () => current().role);
|
|
8183
8254
|
const tokens = new TokenManager(joinTokenDb());
|
|
8184
8255
|
const host = delegatedHost(ctx, () => current());
|
|
8185
8256
|
ctx.effect(() => () => {
|
|
@@ -8193,6 +8264,7 @@ function apply(ctx, config) {
|
|
|
8193
8264
|
saveConfig(value);
|
|
8194
8265
|
current = () => value;
|
|
8195
8266
|
syncWorkspaces();
|
|
8267
|
+
syncTianyancha();
|
|
8196
8268
|
try {
|
|
8197
8269
|
const settings = ctx.get("settings");
|
|
8198
8270
|
if (settings?.update !== void 0) await settings.update(REZ_SETTINGS_NAMESPACE, value);
|
|
@@ -8250,10 +8322,12 @@ function apply(ctx, config) {
|
|
|
8250
8322
|
current = source;
|
|
8251
8323
|
sync();
|
|
8252
8324
|
syncWorkspaces();
|
|
8325
|
+
syncTianyancha();
|
|
8253
8326
|
},
|
|
8254
8327
|
onChange: () => {
|
|
8255
8328
|
sync();
|
|
8256
8329
|
syncWorkspaces();
|
|
8330
|
+
syncTianyancha();
|
|
8257
8331
|
}
|
|
8258
8332
|
});
|
|
8259
8333
|
sync();
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rezti/dsh-rez-suite",
|
|
3
3
|
"description": "ReZ-TI one-package install for DeepSeek Harness. Update with: dsh plugin --profile web update @rezti/dsh-rez-suite",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.27",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": "^22.19.0 || >=24.0.0"
|
|
@@ -39,15 +39,15 @@
|
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
41
41
|
"zod": "^4.4.3",
|
|
42
|
-
"@rezti/dsh-rez-intent": "0.1.5",
|
|
43
42
|
"@rezti/dsh-rez-nextcloud": "0.1.9",
|
|
44
|
-
"@rezti/dsh-rez-
|
|
45
|
-
"@rezti/dsh-rez-
|
|
43
|
+
"@rezti/dsh-rez-gsc": "0.1.0",
|
|
44
|
+
"@rezti/dsh-rez-intent": "0.1.5",
|
|
45
|
+
"@rezti/dsh-rez-ha-bridge": "0.1.9",
|
|
46
46
|
"@rezti/dsh-rez-tapd": "0.1.0",
|
|
47
|
+
"@rezti/dsh-rez-odoo": "0.1.9",
|
|
47
48
|
"@rezti/dsh-rez-token-manager": "0.1.4",
|
|
48
|
-
"@rezti/dsh-rez-ha-bridge": "0.1.9",
|
|
49
49
|
"@rezti/dsh-rez-wechat": "0.1.14",
|
|
50
|
-
"@rezti/dsh-rez-
|
|
50
|
+
"@rezti/dsh-rez-sso": "0.1.10"
|
|
51
51
|
},
|
|
52
52
|
"peerDependencies": {
|
|
53
53
|
"react": "^18.2.0",
|
package/src/client/locales.ts
CHANGED
|
@@ -52,6 +52,9 @@ export const zh = {
|
|
|
52
52
|
'config.gsc': 'Google Search Console',
|
|
53
53
|
'config.gsc.siteUrl': '站点 URL',
|
|
54
54
|
'config.gsc.key': '服务账号 JSON 或密钥文件路径',
|
|
55
|
+
'config.tianyancha': '天眼查(法务助理 · 仅老板)',
|
|
56
|
+
'config.tianyancha.url': '天眼查 MCP URL',
|
|
57
|
+
'config.tianyancha.token': 'OpenAPI Token',
|
|
55
58
|
'config.fs': '本地文件 (fs-mcp-server)',
|
|
56
59
|
'config.fs.root': '根目录',
|
|
57
60
|
'config.sqlite': 'SQLite (sqlite-mcp-server)',
|
|
@@ -171,6 +174,9 @@ export const en: Record<RezKey, string> = {
|
|
|
171
174
|
'config.gsc': 'Google Search Console',
|
|
172
175
|
'config.gsc.siteUrl': 'Site URL',
|
|
173
176
|
'config.gsc.key': 'Service-account JSON or key file path',
|
|
177
|
+
'config.tianyancha': 'Tianyancha (Legal assistant, boss only)',
|
|
178
|
+
'config.tianyancha.url': 'Tianyancha MCP URL',
|
|
179
|
+
'config.tianyancha.token': 'OpenAPI token',
|
|
174
180
|
'config.fs': 'Local files (fs-mcp-server)',
|
|
175
181
|
'config.fs.root': 'Root directory',
|
|
176
182
|
'config.sqlite': 'SQLite (sqlite-mcp-server)',
|
|
@@ -24,9 +24,10 @@ type SecretDrafts = {
|
|
|
24
24
|
homeassistant: string
|
|
25
25
|
tapd: string
|
|
26
26
|
gsc: string
|
|
27
|
+
tianyancha: string
|
|
27
28
|
}
|
|
28
29
|
|
|
29
|
-
const EMPTY_SECRETS: SecretDrafts = { odoo: '', nextcloud: '', wechat: '', homeassistant: '', tapd: '', gsc: '' }
|
|
30
|
+
const EMPTY_SECRETS: SecretDrafts = { odoo: '', nextcloud: '', wechat: '', homeassistant: '', tapd: '', gsc: '', tianyancha: '' }
|
|
30
31
|
|
|
31
32
|
function Field({ label, children }: { label: string; children: ReactNode }) {
|
|
32
33
|
return (
|
|
@@ -170,6 +171,7 @@ export function ConfigTab({ api }: { api: RezApi }) {
|
|
|
170
171
|
homeassistant: { ...config.connections.homeassistant, secret: secrets.homeassistant || undefined },
|
|
171
172
|
tapd: { ...config.connections.tapd, secret: secrets.tapd || undefined },
|
|
172
173
|
gsc: { ...config.connections.gsc, secret: secrets.gsc || undefined },
|
|
174
|
+
tianyancha: { ...config.connections.tianyancha, secret: secrets.tianyancha || undefined },
|
|
173
175
|
},
|
|
174
176
|
}
|
|
175
177
|
const saved = await api.saveConfig(payload)
|
|
@@ -290,6 +292,18 @@ export function ConfigTab({ api }: { api: RezApi }) {
|
|
|
290
292
|
onSecret={value => { setSecrets(prev => ({ ...prev, gsc: value })) }}
|
|
291
293
|
secretLabel={tt('config.gsc.key')}
|
|
292
294
|
/>
|
|
295
|
+
{config.role === 'boss' && (
|
|
296
|
+
<McpSection
|
|
297
|
+
title={tt('config.tianyancha')}
|
|
298
|
+
row={config.connections.tianyancha}
|
|
299
|
+
secret={secrets.tianyancha}
|
|
300
|
+
onEnabled={enabled => { patchConnection('tianyancha', { enabled }) }}
|
|
301
|
+
onUrl={url => { patchConnection('tianyancha', { url }) }}
|
|
302
|
+
onSecret={value => { setSecrets(prev => ({ ...prev, tianyancha: value })) }}
|
|
303
|
+
urlLabel={tt('config.tianyancha.url')}
|
|
304
|
+
secretLabel={tt('config.tianyancha.token')}
|
|
305
|
+
/>
|
|
306
|
+
)}
|
|
293
307
|
|
|
294
308
|
<Section title={tt('config.billing')}>
|
|
295
309
|
<Field label={tt('config.billing.input')}>
|
package/src/index.ts
CHANGED
|
@@ -29,6 +29,7 @@ import { rezConfigTool, rezStatusTool } from './tools.ts'
|
|
|
29
29
|
import type { RezConfig } from './protocol.ts'
|
|
30
30
|
import { workspaceGuidanceFor, mountRezWorkspaces } from './boss/mount.ts'
|
|
31
31
|
import { liveSelfUpdate } from './self-update.ts'
|
|
32
|
+
import { mountTianyanchaMcp } from './tianyancha.ts'
|
|
32
33
|
|
|
33
34
|
export const name = 'rez-suite'
|
|
34
35
|
|
|
@@ -79,6 +80,10 @@ export const Config: z<RezConfig> = z.object({
|
|
|
79
80
|
enabled: z.boolean().default(true),
|
|
80
81
|
siteUrl: z.string().default(REZ_DEFAULT_CONNECTIONS.gsc.siteUrl),
|
|
81
82
|
}).default(REZ_DEFAULT_CONNECTIONS.gsc),
|
|
83
|
+
tianyancha: z.object({
|
|
84
|
+
enabled: z.boolean().default(true),
|
|
85
|
+
url: z.string().default(REZ_DEFAULT_CONNECTIONS.tianyancha.url),
|
|
86
|
+
}).default(REZ_DEFAULT_CONNECTIONS.tianyancha),
|
|
82
87
|
}).default(REZ_DEFAULT_CONNECTIONS),
|
|
83
88
|
billing: z.object({
|
|
84
89
|
inputCostPer1k: z.number().default(0.001),
|
|
@@ -128,6 +133,7 @@ function delegatedHost(ctx: Context, getConfig: () => RezConfig): McpHost {
|
|
|
128
133
|
export function apply(ctx: Context, config?: RezConfig): void {
|
|
129
134
|
let current: () => RezConfig = () => normalize(config)
|
|
130
135
|
const syncWorkspaces = mountRezWorkspaces(ctx, () => current().role)
|
|
136
|
+
const syncTianyancha = mountTianyanchaMcp(ctx, () => current().role)
|
|
131
137
|
|
|
132
138
|
const tokens = new TokenManager(joinTokenDb())
|
|
133
139
|
const host = delegatedHost(ctx, () => current())
|
|
@@ -145,6 +151,7 @@ export function apply(ctx: Context, config?: RezConfig): void {
|
|
|
145
151
|
saveConfig(value)
|
|
146
152
|
current = () => value
|
|
147
153
|
syncWorkspaces()
|
|
154
|
+
syncTianyancha()
|
|
148
155
|
try {
|
|
149
156
|
const settings = ctx.get('settings') as { update?: (namespace: unknown, value: unknown) => Promise<void> } | undefined
|
|
150
157
|
if (settings?.update !== undefined) await settings.update(REZ_SETTINGS_NAMESPACE, value)
|
|
@@ -198,10 +205,12 @@ export function apply(ctx: Context, config?: RezConfig): void {
|
|
|
198
205
|
current = source
|
|
199
206
|
sync()
|
|
200
207
|
syncWorkspaces()
|
|
208
|
+
syncTianyancha()
|
|
201
209
|
},
|
|
202
210
|
onChange: () => {
|
|
203
211
|
sync()
|
|
204
212
|
syncWorkspaces()
|
|
213
|
+
syncTianyancha()
|
|
205
214
|
},
|
|
206
215
|
})
|
|
207
216
|
|
package/src/observe-mcp.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import { lastMcpStartError, REZ_CREDENTIAL_REFS, secretConfigured, type RezSystem } from '@rezti/dsh-rez-sso'
|
|
10
10
|
import type { RezConfig, RezServerStatus, RezTestResult } from './protocol.ts'
|
|
11
11
|
|
|
12
|
-
export const COMPOSED_SERVERS = ['odoo', 'nextcloud', 'wechat', 'homeassistant', 'tapd', 'gsc'] as const
|
|
12
|
+
export const COMPOSED_SERVERS = ['odoo', 'nextcloud', 'wechat', 'homeassistant', 'tapd', 'gsc', 'tianyancha'] as const
|
|
13
13
|
|
|
14
14
|
export type ComposedServer = (typeof COMPOSED_SERVERS)[number]
|
|
15
15
|
|
|
@@ -102,7 +102,10 @@ export function observeServer(
|
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
export function observeComposedServers(input: ObserveInput): RezServerStatus[] {
|
|
105
|
-
|
|
105
|
+
const servers = input.config.role === 'boss'
|
|
106
|
+
? COMPOSED_SERVERS
|
|
107
|
+
: COMPOSED_SERVERS.filter((server) => server !== 'tianyancha')
|
|
108
|
+
return servers.map(server => observeServer(server, input))
|
|
106
109
|
}
|
|
107
110
|
|
|
108
111
|
export function testComposedServers(input: ObserveInput): RezTestResult[] {
|
package/src/presets.ts
CHANGED
|
@@ -127,6 +127,7 @@ const COMPILED: Record<Exclude<RezRoleId, 'all' | 'boss'>, { server: RezServerId
|
|
|
127
127
|
|
|
128
128
|
/** Whether one MCP tool may be registered for the selected role. */
|
|
129
129
|
export function isToolAllowed(role: RezRoleId, server: string, rawName: string): boolean {
|
|
130
|
+
if (server === 'tianyancha') return role === 'boss'
|
|
130
131
|
if (role === 'all' || role === 'boss') return true
|
|
131
132
|
const rules = COMPILED[role]
|
|
132
133
|
return rules.some(rule => rule.server === server && rule.test.test(rawName))
|
package/src/protocol.ts
CHANGED
|
@@ -48,6 +48,7 @@ export interface RezConnections {
|
|
|
48
48
|
homeassistant: { enabled: boolean; url: string }
|
|
49
49
|
tapd: { enabled: boolean; workspaceId: string; nickName: string }
|
|
50
50
|
gsc: { enabled: boolean; siteUrl: string }
|
|
51
|
+
tianyancha: { enabled: boolean; url: string }
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
export interface RezPublicConnection {
|
|
@@ -90,6 +91,7 @@ export interface RezPublicConfig {
|
|
|
90
91
|
homeassistant: RezPublicConnection
|
|
91
92
|
tapd: RezPublicConnection
|
|
92
93
|
gsc: RezPublicConnection
|
|
94
|
+
tianyancha: RezPublicConnection
|
|
93
95
|
}
|
|
94
96
|
billing: { inputCostPer1k: number; outputCostPer1k: number; monthlyBudget: number }
|
|
95
97
|
}
|
package/src/store.ts
CHANGED
|
@@ -125,6 +125,12 @@ export function publicConfig(config: RezConfig): RezPublicConfig {
|
|
|
125
125
|
secretConfigured: secretConfigured('gsc'),
|
|
126
126
|
secretRef: REZ_CREDENTIAL_REFS.gsc,
|
|
127
127
|
},
|
|
128
|
+
tianyancha: {
|
|
129
|
+
enabled: connections.tianyancha.enabled,
|
|
130
|
+
url: connections.tianyancha.url,
|
|
131
|
+
secretConfigured: secretConfigured('tianyancha'),
|
|
132
|
+
secretRef: REZ_CREDENTIAL_REFS.tianyancha,
|
|
133
|
+
},
|
|
128
134
|
},
|
|
129
135
|
billing: { ...config.billing },
|
|
130
136
|
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TianyanCha official MCP for the 法务助理 workspace.
|
|
3
|
+
* Reuse https://mcp.tianyancha.com/mcp via dsh-mcp-client. Do not spawn a
|
|
4
|
+
* local 162-tool catalog — tools/list only exposes a few agent entrypoints.
|
|
5
|
+
* Start the client only when suite role is boss so other identities never
|
|
6
|
+
* see mcp__tianyancha__* in the prompt.
|
|
7
|
+
*/
|
|
8
|
+
import type { Context } from '@deepseek-ai/cordis'
|
|
9
|
+
import { loadRezConnections, startMcpWhenSecret } from '@rezti/dsh-rez-sso'
|
|
10
|
+
import type { RezRoleId } from './protocol.ts'
|
|
11
|
+
|
|
12
|
+
export const TIANYANCHA_MCP_URL = 'https://mcp.tianyancha.com/mcp'
|
|
13
|
+
|
|
14
|
+
export function shouldStartTianyancha(role: RezRoleId): boolean {
|
|
15
|
+
return role === 'boss'
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function bearerAuthorization(secret: string): string {
|
|
19
|
+
const trimmed = secret.trim()
|
|
20
|
+
if (/^bearer\s+/i.test(trimmed)) return 'Bearer ' + trimmed.replace(/^bearer\s+/i, '')
|
|
21
|
+
return 'Bearer ' + trimmed
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function tianyanchaMcpConfig(secret: string): Record<string, unknown> {
|
|
25
|
+
const { url } = loadRezConnections().tianyancha
|
|
26
|
+
return {
|
|
27
|
+
serverName: 'tianyancha',
|
|
28
|
+
transport: 'streamable-http',
|
|
29
|
+
url: url.length > 0 ? url : TIANYANCHA_MCP_URL,
|
|
30
|
+
headers: { Authorization: bearerAuthorization(secret) },
|
|
31
|
+
failOnStartupError: false,
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Start when role is boss; dispose the mcp-client fiber when the identity leaves. */
|
|
36
|
+
export function mountTianyanchaMcp(
|
|
37
|
+
ctx: Context,
|
|
38
|
+
getRole: () => RezRoleId,
|
|
39
|
+
start: typeof startMcpWhenSecret = startMcpWhenSecret,
|
|
40
|
+
): () => void {
|
|
41
|
+
let session: (() => void) | undefined
|
|
42
|
+
let pending = false
|
|
43
|
+
|
|
44
|
+
const ensure = (): void => {
|
|
45
|
+
void (async () => {
|
|
46
|
+
const want = shouldStartTianyancha(getRole())
|
|
47
|
+
if (!want) {
|
|
48
|
+
session?.()
|
|
49
|
+
session = undefined
|
|
50
|
+
return
|
|
51
|
+
}
|
|
52
|
+
if (session !== undefined || pending) return
|
|
53
|
+
pending = true
|
|
54
|
+
let raced = false
|
|
55
|
+
try {
|
|
56
|
+
const dispose = await start(ctx, {
|
|
57
|
+
system: 'tianyancha',
|
|
58
|
+
label: '天眼查',
|
|
59
|
+
config: tianyanchaMcpConfig,
|
|
60
|
+
})
|
|
61
|
+
if (!shouldStartTianyancha(getRole())) {
|
|
62
|
+
raced = true
|
|
63
|
+
dispose()
|
|
64
|
+
return
|
|
65
|
+
}
|
|
66
|
+
session = dispose
|
|
67
|
+
} catch (error: unknown) {
|
|
68
|
+
const why = error instanceof Error ? error.message : String(error)
|
|
69
|
+
console.error('[dsh-rez-suite] tianyancha MCP start failed:', why)
|
|
70
|
+
} finally {
|
|
71
|
+
pending = false
|
|
72
|
+
if (raced && shouldStartTianyancha(getRole()) && session === undefined) ensure()
|
|
73
|
+
}
|
|
74
|
+
})()
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
ensure()
|
|
78
|
+
return ensure
|
|
79
|
+
}
|