@rezti/dsh-rez-suite 0.1.25 → 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 +17 -0
- package/lib/client.js +173 -6
- package/lib/index.d.ts +4 -0
- package/lib/index.js +256 -7
- package/lib/style.css +81 -0
- package/package.json +8 -7
- package/src/client/api.ts +12 -0
- package/src/client/index.ts +14 -0
- package/src/client/locales.ts +20 -0
- package/src/client/panel/ConfigTab.tsx +15 -1
- package/src/client/panel/panel.module.css +82 -0
- package/src/client/upgrade-button.tsx +115 -0
- package/src/index.ts +11 -0
- package/src/observe-mcp.ts +5 -2
- package/src/presets.ts +1 -0
- package/src/protocol.ts +15 -0
- package/src/routes.ts +30 -2
- package/src/self-update.ts +155 -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/lib/index.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
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";
|
|
8
|
+
import { spawn } from "node:child_process";
|
|
8
9
|
//#region ../../node_modules/.pnpm/@deepseek-ai+cosmokit@1.8.2/node_modules/@deepseek-ai/cosmokit/lib/index.js
|
|
9
10
|
/** Return true when a value is `null` or `undefined`. */
|
|
10
11
|
function isNullable(value) {
|
|
@@ -2857,7 +2858,8 @@ const COMPOSED_SERVERS = [
|
|
|
2857
2858
|
"wechat",
|
|
2858
2859
|
"homeassistant",
|
|
2859
2860
|
"tapd",
|
|
2860
|
-
"gsc"
|
|
2861
|
+
"gsc",
|
|
2862
|
+
"tianyancha"
|
|
2861
2863
|
];
|
|
2862
2864
|
function mcpToolPrefix(server) {
|
|
2863
2865
|
return "mcp__" + server + "__";
|
|
@@ -2919,7 +2921,7 @@ function observeServer(server, input) {
|
|
|
2919
2921
|
};
|
|
2920
2922
|
}
|
|
2921
2923
|
function observeComposedServers(input) {
|
|
2922
|
-
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));
|
|
2923
2925
|
}
|
|
2924
2926
|
function testComposedServers(input) {
|
|
2925
2927
|
return observeComposedServers(input).map((row) => {
|
|
@@ -2957,7 +2959,8 @@ const REZ_API = {
|
|
|
2957
2959
|
audit: "/api/dsh-rez-suite/audit",
|
|
2958
2960
|
auditReset: "/api/dsh-rez-suite/audit/reset",
|
|
2959
2961
|
weixin: "/api/dsh-rez-suite/weixin",
|
|
2960
|
-
weixinVerify: "/api/dsh-rez-suite/weixin/verify"
|
|
2962
|
+
weixinVerify: "/api/dsh-rez-suite/weixin/verify",
|
|
2963
|
+
update: "/api/dsh-rez-suite/update"
|
|
2961
2964
|
};
|
|
2962
2965
|
//#endregion
|
|
2963
2966
|
//#region src/store.ts
|
|
@@ -3047,6 +3050,12 @@ function publicConfig(config) {
|
|
|
3047
3050
|
siteUrl: connections.gsc.siteUrl,
|
|
3048
3051
|
secretConfigured: secretConfigured("gsc"),
|
|
3049
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
|
|
3050
3059
|
}
|
|
3051
3060
|
},
|
|
3052
3061
|
billing: { ...config.billing }
|
|
@@ -3220,7 +3229,7 @@ function applyPublicPatch(current, patch) {
|
|
|
3220
3229
|
}
|
|
3221
3230
|
/** Build every /api/dsh-rez-suite route (exact paths). */
|
|
3222
3231
|
function makeRoutes(deps) {
|
|
3223
|
-
const { getConfig, updateConfig, host, tokens, onCredentialWritten } = deps;
|
|
3232
|
+
const { getConfig, updateConfig, host, tokens, onCredentialWritten, selfUpdate } = deps;
|
|
3224
3233
|
const guard = (req, res, method) => {
|
|
3225
3234
|
if (!isLoopbackRequest(req)) {
|
|
3226
3235
|
writeJson(res, 403, { error: "forbidden: loopback-only" });
|
|
@@ -3292,6 +3301,31 @@ function makeRoutes(deps) {
|
|
|
3292
3301
|
tokens.reset();
|
|
3293
3302
|
writeJson(res, 200, { ok: true });
|
|
3294
3303
|
}
|
|
3304
|
+
},
|
|
3305
|
+
{
|
|
3306
|
+
kind: "exact",
|
|
3307
|
+
path: REZ_API.update,
|
|
3308
|
+
handler: async (req, res) => {
|
|
3309
|
+
const method = req.method === "POST" ? "POST" : "GET";
|
|
3310
|
+
if (!guard(req, res, method)) return;
|
|
3311
|
+
if (selfUpdate === void 0) {
|
|
3312
|
+
writeJson(res, 501, { error: "self-update is not available" });
|
|
3313
|
+
return;
|
|
3314
|
+
}
|
|
3315
|
+
try {
|
|
3316
|
+
if (method === "GET") {
|
|
3317
|
+
writeJson(res, 200, await selfUpdate.status());
|
|
3318
|
+
return;
|
|
3319
|
+
}
|
|
3320
|
+
const result = await selfUpdate.apply();
|
|
3321
|
+
writeJson(res, 200, result);
|
|
3322
|
+
if (result.restarting) setTimeout(() => {
|
|
3323
|
+
selfUpdate.relaunch();
|
|
3324
|
+
}, 400);
|
|
3325
|
+
} catch (error) {
|
|
3326
|
+
writeJson(res, 502, { error: error instanceof Error ? error.message : String(error) });
|
|
3327
|
+
}
|
|
3328
|
+
}
|
|
3295
3329
|
}
|
|
3296
3330
|
];
|
|
3297
3331
|
}
|
|
@@ -7906,6 +7940,212 @@ function mountRezWorkspaces(ctx, getRole) {
|
|
|
7906
7940
|
return run;
|
|
7907
7941
|
}
|
|
7908
7942
|
//#endregion
|
|
7943
|
+
//#region src/self-update.ts
|
|
7944
|
+
/**
|
|
7945
|
+
* In-place upgrade of @rezti/dsh-rez-suite inside the running dsh profile.
|
|
7946
|
+
* pnpm add writes the new bits; Node cannot replace an already-imported host
|
|
7947
|
+
* module, so we respawn the same argv after the HTTP response flushes.
|
|
7948
|
+
*/
|
|
7949
|
+
const SUITE_PACKAGE = "@rezti/dsh-rez-suite";
|
|
7950
|
+
const NPM_LATEST = "https://registry.npmjs.org/@rezti/dsh-rez-suite/latest";
|
|
7951
|
+
const PNPM_TIMEOUT_MS = 18e4;
|
|
7952
|
+
function compareVersions(left, right) {
|
|
7953
|
+
const a = left.split(".").map((part) => Number.parseInt(part, 10) || 0);
|
|
7954
|
+
const b = right.split(".").map((part) => Number.parseInt(part, 10) || 0);
|
|
7955
|
+
const n = Math.max(a.length, b.length);
|
|
7956
|
+
for (let i = 0; i < n; i += 1) {
|
|
7957
|
+
const av = a[i] ?? 0;
|
|
7958
|
+
const bv = b[i] ?? 0;
|
|
7959
|
+
if (av > bv) return 1;
|
|
7960
|
+
if (av < bv) return -1;
|
|
7961
|
+
}
|
|
7962
|
+
return 0;
|
|
7963
|
+
}
|
|
7964
|
+
function readInstalledVersion(packageDir) {
|
|
7965
|
+
const raw = JSON.parse(readFileSync(join(packageDir, "package.json"), "utf8"));
|
|
7966
|
+
if (typeof raw.version !== "string" || raw.version === "") throw new Error("suite package.json missing version");
|
|
7967
|
+
return raw.version;
|
|
7968
|
+
}
|
|
7969
|
+
function findProfileDir(startDir) {
|
|
7970
|
+
let dir = startDir;
|
|
7971
|
+
for (let i = 0; i < 12; i += 1) {
|
|
7972
|
+
const manifest = join(dir, "package.json");
|
|
7973
|
+
if (existsSync(manifest)) try {
|
|
7974
|
+
const raw = JSON.parse(readFileSync(manifest, "utf8"));
|
|
7975
|
+
if (Array.isArray(raw.dsh?.profile?.bundles)) return dir;
|
|
7976
|
+
} catch {}
|
|
7977
|
+
const parent = dirname(dir);
|
|
7978
|
+
if (parent === dir) break;
|
|
7979
|
+
dir = parent;
|
|
7980
|
+
}
|
|
7981
|
+
throw new Error("cannot find dsh profile directory (no package.json with dsh.profile.bundles)");
|
|
7982
|
+
}
|
|
7983
|
+
function shellQuote(value) {
|
|
7984
|
+
return `'${value.replaceAll("'", `'\\''`)}'`;
|
|
7985
|
+
}
|
|
7986
|
+
function relaunchScript(argv, parentPid) {
|
|
7987
|
+
return `while kill -0 ${parentPid} 2>/dev/null; do sleep 0.15; done; sleep 0.25; exec ${argv.map(shellQuote).join(" ")}`;
|
|
7988
|
+
}
|
|
7989
|
+
async function fetchLatestVersion(fetchImpl = fetch) {
|
|
7990
|
+
const response = await fetchImpl(NPM_LATEST, { headers: { accept: "application/json" } });
|
|
7991
|
+
if (!response.ok) throw new Error(`npm registry HTTP ${response.status}`);
|
|
7992
|
+
const body = await response.json();
|
|
7993
|
+
if (typeof body.version !== "string" || body.version === "") throw new Error("npm registry missing version");
|
|
7994
|
+
return body.version;
|
|
7995
|
+
}
|
|
7996
|
+
function runPnpmAdd(cwd, spec, env = process.env) {
|
|
7997
|
+
return new Promise((resolve, reject) => {
|
|
7998
|
+
const child = spawn("pnpm", ["add", spec], {
|
|
7999
|
+
cwd,
|
|
8000
|
+
env,
|
|
8001
|
+
stdio: [
|
|
8002
|
+
"ignore",
|
|
8003
|
+
"pipe",
|
|
8004
|
+
"pipe"
|
|
8005
|
+
]
|
|
8006
|
+
});
|
|
8007
|
+
let stderr = "";
|
|
8008
|
+
child.stderr.on("data", (chunk) => {
|
|
8009
|
+
stderr += chunk.toString();
|
|
8010
|
+
});
|
|
8011
|
+
const timer = setTimeout(() => {
|
|
8012
|
+
child.kill("SIGTERM");
|
|
8013
|
+
reject(/* @__PURE__ */ new Error("pnpm add timed out after 3 minutes"));
|
|
8014
|
+
}, PNPM_TIMEOUT_MS);
|
|
8015
|
+
child.on("error", (error) => {
|
|
8016
|
+
clearTimeout(timer);
|
|
8017
|
+
reject(error);
|
|
8018
|
+
});
|
|
8019
|
+
child.on("close", (code) => {
|
|
8020
|
+
clearTimeout(timer);
|
|
8021
|
+
if (code === 0) resolve();
|
|
8022
|
+
else reject(new Error(stderr.trim() || `pnpm add exited ${String(code)}`));
|
|
8023
|
+
});
|
|
8024
|
+
});
|
|
8025
|
+
}
|
|
8026
|
+
function spawnRelaunch(argv, parentPid, cwd, env = process.env) {
|
|
8027
|
+
spawn("/bin/bash", ["-c", relaunchScript(argv, parentPid)], {
|
|
8028
|
+
cwd,
|
|
8029
|
+
env,
|
|
8030
|
+
detached: true,
|
|
8031
|
+
stdio: "ignore"
|
|
8032
|
+
}).unref();
|
|
8033
|
+
}
|
|
8034
|
+
let inflight;
|
|
8035
|
+
function createSelfUpdate(io) {
|
|
8036
|
+
const status = async () => {
|
|
8037
|
+
const installed = readInstalledVersion(io.packageDir);
|
|
8038
|
+
const latest = await io.fetchLatest();
|
|
8039
|
+
return {
|
|
8040
|
+
package: SUITE_PACKAGE,
|
|
8041
|
+
installed,
|
|
8042
|
+
latest,
|
|
8043
|
+
outdated: compareVersions(latest, installed) > 0
|
|
8044
|
+
};
|
|
8045
|
+
};
|
|
8046
|
+
const apply = async () => {
|
|
8047
|
+
if (inflight !== void 0) throw new Error("upgrade already running");
|
|
8048
|
+
const work = (async () => {
|
|
8049
|
+
const snapshot = await status();
|
|
8050
|
+
if (!snapshot.outdated) return {
|
|
8051
|
+
...snapshot,
|
|
8052
|
+
restarting: false
|
|
8053
|
+
};
|
|
8054
|
+
const profileDir = findProfileDir(io.packageDir);
|
|
8055
|
+
await io.install(profileDir, `${SUITE_PACKAGE}@${snapshot.latest}`);
|
|
8056
|
+
return {
|
|
8057
|
+
...snapshot,
|
|
8058
|
+
installed: snapshot.latest,
|
|
8059
|
+
outdated: false,
|
|
8060
|
+
restarting: true
|
|
8061
|
+
};
|
|
8062
|
+
})();
|
|
8063
|
+
inflight = work;
|
|
8064
|
+
try {
|
|
8065
|
+
return await work;
|
|
8066
|
+
} finally {
|
|
8067
|
+
inflight = void 0;
|
|
8068
|
+
}
|
|
8069
|
+
};
|
|
8070
|
+
return {
|
|
8071
|
+
status,
|
|
8072
|
+
apply,
|
|
8073
|
+
relaunch: io.relaunch
|
|
8074
|
+
};
|
|
8075
|
+
}
|
|
8076
|
+
function liveSelfUpdate() {
|
|
8077
|
+
return createSelfUpdate({
|
|
8078
|
+
packageDir: dirname(fileURLToPath(new URL(".", import.meta.url))),
|
|
8079
|
+
fetchLatest: () => fetchLatestVersion(),
|
|
8080
|
+
install: (profileDir, spec) => runPnpmAdd(profileDir, spec),
|
|
8081
|
+
relaunch: () => {
|
|
8082
|
+
spawnRelaunch(process.argv, process.pid, process.cwd());
|
|
8083
|
+
setTimeout(() => {
|
|
8084
|
+
process.exit(0);
|
|
8085
|
+
}, 50);
|
|
8086
|
+
}
|
|
8087
|
+
});
|
|
8088
|
+
}
|
|
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
|
|
7909
8149
|
//#region src/index.ts
|
|
7910
8150
|
const name = "rez-suite";
|
|
7911
8151
|
const inject = [
|
|
@@ -7959,7 +8199,11 @@ const Config = Schema.object({
|
|
|
7959
8199
|
gsc: Schema.object({
|
|
7960
8200
|
enabled: Schema.boolean().default(true),
|
|
7961
8201
|
siteUrl: Schema.string().default(REZ_DEFAULT_CONNECTIONS.gsc.siteUrl)
|
|
7962
|
-
}).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)
|
|
7963
8207
|
}).default(REZ_DEFAULT_CONNECTIONS),
|
|
7964
8208
|
billing: Schema.object({
|
|
7965
8209
|
inputCostPer1k: Schema.number().default(.001),
|
|
@@ -8006,6 +8250,7 @@ function delegatedHost(ctx, getConfig) {
|
|
|
8006
8250
|
function apply(ctx, config) {
|
|
8007
8251
|
let current = () => normalize(config);
|
|
8008
8252
|
const syncWorkspaces = mountRezWorkspaces(ctx, () => current().role);
|
|
8253
|
+
const syncTianyancha = mountTianyanchaMcp(ctx, () => current().role);
|
|
8009
8254
|
const tokens = new TokenManager(joinTokenDb());
|
|
8010
8255
|
const host = delegatedHost(ctx, () => current());
|
|
8011
8256
|
ctx.effect(() => () => {
|
|
@@ -8019,6 +8264,7 @@ function apply(ctx, config) {
|
|
|
8019
8264
|
saveConfig(value);
|
|
8020
8265
|
current = () => value;
|
|
8021
8266
|
syncWorkspaces();
|
|
8267
|
+
syncTianyancha();
|
|
8022
8268
|
try {
|
|
8023
8269
|
const settings = ctx.get("settings");
|
|
8024
8270
|
if (settings?.update !== void 0) await settings.update(REZ_SETTINGS_NAMESPACE, value);
|
|
@@ -8032,7 +8278,8 @@ function apply(ctx, config) {
|
|
|
8032
8278
|
onCredentialWritten: (ref) => {
|
|
8033
8279
|
const emit = ctx.emit;
|
|
8034
8280
|
emit("credentials/updated", ref);
|
|
8035
|
-
}
|
|
8281
|
+
},
|
|
8282
|
+
selfUpdate: liveSelfUpdate()
|
|
8036
8283
|
});
|
|
8037
8284
|
let disposeRoutes;
|
|
8038
8285
|
let disposeTools;
|
|
@@ -8075,10 +8322,12 @@ function apply(ctx, config) {
|
|
|
8075
8322
|
current = source;
|
|
8076
8323
|
sync();
|
|
8077
8324
|
syncWorkspaces();
|
|
8325
|
+
syncTianyancha();
|
|
8078
8326
|
},
|
|
8079
8327
|
onChange: () => {
|
|
8080
8328
|
sync();
|
|
8081
8329
|
syncWorkspaces();
|
|
8330
|
+
syncTianyancha();
|
|
8082
8331
|
}
|
|
8083
8332
|
});
|
|
8084
8333
|
sync();
|
package/lib/style.css
CHANGED
|
@@ -287,3 +287,84 @@
|
|
|
287
287
|
font-size: 15px;
|
|
288
288
|
font-weight: 600;
|
|
289
289
|
}
|
|
290
|
+
|
|
291
|
+
.LOjPTW_upgradeLayer {
|
|
292
|
+
flex: none;
|
|
293
|
+
align-items: center;
|
|
294
|
+
width: 100%;
|
|
295
|
+
height: 42px;
|
|
296
|
+
margin: 8px 0 0;
|
|
297
|
+
display: flex;
|
|
298
|
+
position: relative;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
.LOjPTW_upgradeLayerRail {
|
|
302
|
+
width: 36px;
|
|
303
|
+
height: 36px;
|
|
304
|
+
margin: 0;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
.LOjPTW_upgradeButton {
|
|
308
|
+
width: calc(100% + 4px);
|
|
309
|
+
height: 42px;
|
|
310
|
+
color: var(--dsw-alias-label-primary);
|
|
311
|
+
cursor: pointer;
|
|
312
|
+
background: none;
|
|
313
|
+
border: none;
|
|
314
|
+
border-radius: 12px;
|
|
315
|
+
align-items: center;
|
|
316
|
+
gap: 8px;
|
|
317
|
+
margin: 0 -2px;
|
|
318
|
+
padding: 0 10px 0 8px;
|
|
319
|
+
font-family: inherit;
|
|
320
|
+
font-size: 14px;
|
|
321
|
+
display: inline-flex;
|
|
322
|
+
overflow: hidden;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
.LOjPTW_upgradeButton:hover, .LOjPTW_upgradeButton[data-outdated] {
|
|
326
|
+
background: var(--dsw-alias-interactive-bg-hover);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
.LOjPTW_upgradeButton:disabled {
|
|
330
|
+
cursor: progress;
|
|
331
|
+
opacity: .72;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
.LOjPTW_upgradeButton:focus-visible {
|
|
335
|
+
outline: 2px solid var(--dsw-alias-brand-primary);
|
|
336
|
+
outline-offset: -2px;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
.LOjPTW_upgradeLayerRail .LOjPTW_upgradeButton {
|
|
340
|
+
border-radius: 50%;
|
|
341
|
+
justify-content: center;
|
|
342
|
+
gap: 0;
|
|
343
|
+
width: 36px;
|
|
344
|
+
height: 36px;
|
|
345
|
+
padding: 0;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
.LOjPTW_upgradeLabel {
|
|
349
|
+
text-overflow: ellipsis;
|
|
350
|
+
white-space: nowrap;
|
|
351
|
+
min-width: 0;
|
|
352
|
+
overflow: hidden;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
.LOjPTW_upgradeMeta {
|
|
356
|
+
color: var(--dsw-alias-label-tertiary);
|
|
357
|
+
font-variant-numeric: tabular-nums;
|
|
358
|
+
flex: none;
|
|
359
|
+
margin-left: auto;
|
|
360
|
+
font-size: 12px;
|
|
361
|
+
line-height: 16px;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
.LOjPTW_upgradeDot {
|
|
365
|
+
background: var(--dsw-alias-brand-primary);
|
|
366
|
+
border-radius: 50%;
|
|
367
|
+
flex: none;
|
|
368
|
+
width: 7px;
|
|
369
|
+
height: 7px;
|
|
370
|
+
}
|
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"
|
|
@@ -30,7 +30,8 @@
|
|
|
30
30
|
"@deepseek-ai/dsh-client-connection",
|
|
31
31
|
"@deepseek-ai/dsh-client-locale",
|
|
32
32
|
"@deepseek-ai/dsh-client-ui-settings",
|
|
33
|
-
"@deepseek-ai/dsh-client-ui-settings-plugins"
|
|
33
|
+
"@deepseek-ai/dsh-client-ui-settings-plugins",
|
|
34
|
+
"@deepseek-ai/dsh-client-ui-sidebar"
|
|
34
35
|
],
|
|
35
36
|
"platform": "web"
|
|
36
37
|
}
|
|
@@ -38,15 +39,15 @@
|
|
|
38
39
|
"dependencies": {
|
|
39
40
|
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
40
41
|
"zod": "^4.4.3",
|
|
41
|
-
"@rezti/dsh-rez-sso": "0.1.9",
|
|
42
|
-
"@rezti/dsh-rez-token-manager": "0.1.4",
|
|
43
|
-
"@rezti/dsh-rez-ha-bridge": "0.1.9",
|
|
44
42
|
"@rezti/dsh-rez-nextcloud": "0.1.9",
|
|
45
|
-
"@rezti/dsh-rez-
|
|
43
|
+
"@rezti/dsh-rez-gsc": "0.1.0",
|
|
46
44
|
"@rezti/dsh-rez-intent": "0.1.5",
|
|
45
|
+
"@rezti/dsh-rez-ha-bridge": "0.1.9",
|
|
46
|
+
"@rezti/dsh-rez-tapd": "0.1.0",
|
|
47
47
|
"@rezti/dsh-rez-odoo": "0.1.9",
|
|
48
|
+
"@rezti/dsh-rez-token-manager": "0.1.4",
|
|
48
49
|
"@rezti/dsh-rez-wechat": "0.1.14",
|
|
49
|
-
"@rezti/dsh-rez-
|
|
50
|
+
"@rezti/dsh-rez-sso": "0.1.10"
|
|
50
51
|
},
|
|
51
52
|
"peerDependencies": {
|
|
52
53
|
"react": "^18.2.0",
|
package/src/client/api.ts
CHANGED
|
@@ -9,6 +9,8 @@ import {
|
|
|
9
9
|
type RezServerStatus,
|
|
10
10
|
type RezStatusResponse,
|
|
11
11
|
type RezTestResult,
|
|
12
|
+
type RezUpdateApplyResult,
|
|
13
|
+
type RezUpdateStatus,
|
|
12
14
|
type RezWeixinStatus,
|
|
13
15
|
} from '../protocol.ts'
|
|
14
16
|
|
|
@@ -97,4 +99,14 @@ export class RezApi {
|
|
|
97
99
|
const response = await fetch(REZ_API.weixin, { method: 'DELETE' })
|
|
98
100
|
return readJson<RezWeixinStatus>(response)
|
|
99
101
|
}
|
|
102
|
+
|
|
103
|
+
async updateStatus(): Promise<RezUpdateStatus> {
|
|
104
|
+
const response = await fetch(REZ_API.update)
|
|
105
|
+
return readJson<RezUpdateStatus>(response)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async applyUpdate(): Promise<RezUpdateApplyResult> {
|
|
109
|
+
const response = await fetch(REZ_API.update, { method: 'POST' })
|
|
110
|
+
return readJson<RezUpdateApplyResult>(response)
|
|
111
|
+
}
|
|
100
112
|
}
|
package/src/client/index.ts
CHANGED
|
@@ -11,6 +11,7 @@ import type {} from '@deepseek-ai/dsh-client-ui-settings/client'
|
|
|
11
11
|
import type {} from '@deepseek-ai/dsh-client-ui-slots'
|
|
12
12
|
import { en, zh, type RezKey } from './locales.ts'
|
|
13
13
|
import { RezPluginCard, RezSettingsPage } from './settings-card.tsx'
|
|
14
|
+
import { UpgradeButton } from './upgrade-button.tsx'
|
|
14
15
|
|
|
15
16
|
const NS = 'dsh-rez-suite'
|
|
16
17
|
|
|
@@ -29,6 +30,11 @@ declare module '@deepseek-ai/dsh-client-ui-slots' {
|
|
|
29
30
|
scope: 'root'
|
|
30
31
|
owner: { children?: never }
|
|
31
32
|
}
|
|
33
|
+
'sidebar.footer.action': {
|
|
34
|
+
kind: 'list'
|
|
35
|
+
scope: 'root'
|
|
36
|
+
owner: { wide: boolean }
|
|
37
|
+
}
|
|
32
38
|
}
|
|
33
39
|
}
|
|
34
40
|
|
|
@@ -53,4 +59,12 @@ export function apply(ctx: ClientContext): void {
|
|
|
53
59
|
label: () => t('settings.title'),
|
|
54
60
|
locale: NS,
|
|
55
61
|
}, RezSettingsPage))
|
|
62
|
+
|
|
63
|
+
ctx.slots.inject('sidebar.footer.action', () => ctx.slots.register({
|
|
64
|
+
name: 'sidebar.footer.action',
|
|
65
|
+
id: 'rez-upgrade',
|
|
66
|
+
order: 10,
|
|
67
|
+
label: () => t('upgrade.label'),
|
|
68
|
+
locale: NS,
|
|
69
|
+
}, UpgradeButton))
|
|
56
70
|
}
|
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)',
|
|
@@ -111,6 +114,13 @@ export const zh = {
|
|
|
111
114
|
'weixin.listening': '通道在听:微信 → 网页「微信」会话(同一条,不另开窗口)→ 回微信。',
|
|
112
115
|
'weixin.loggedIn': '已登录,通道未在听。点「重新连接」即可拉起,不必再扫码。',
|
|
113
116
|
'weixin.loggedOut': '尚未登录。',
|
|
117
|
+
'upgrade.label': '升级',
|
|
118
|
+
'upgrade.current': '已最新',
|
|
119
|
+
'upgrade.aria': '升级 ReZ-TI 插件',
|
|
120
|
+
'upgrade.running': '升级中…',
|
|
121
|
+
'upgrade.reloading': '正在重载',
|
|
122
|
+
'upgrade.already': '已是 {version}',
|
|
123
|
+
'upgrade.reloadTimeout': '升级完成,但网页还没重新连上。请刷新这一页。',
|
|
114
124
|
} as const
|
|
115
125
|
|
|
116
126
|
export type RezKey = keyof typeof zh
|
|
@@ -164,6 +174,9 @@ export const en: Record<RezKey, string> = {
|
|
|
164
174
|
'config.gsc': 'Google Search Console',
|
|
165
175
|
'config.gsc.siteUrl': 'Site URL',
|
|
166
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',
|
|
167
180
|
'config.fs': 'Local files (fs-mcp-server)',
|
|
168
181
|
'config.fs.root': 'Root directory',
|
|
169
182
|
'config.sqlite': 'SQLite (sqlite-mcp-server)',
|
|
@@ -220,6 +233,13 @@ export const en: Record<RezKey, string> = {
|
|
|
220
233
|
'weixin.listening': 'Channel is live: WeChat → the WeChat folder session on the left → WeChat.',
|
|
221
234
|
'weixin.loggedIn': 'Logged in, listener idle. Click Reconnect — no need to scan again.',
|
|
222
235
|
'weixin.loggedOut': 'Not logged in.',
|
|
236
|
+
'upgrade.label': 'Upgrade',
|
|
237
|
+
'upgrade.current': 'Up to date',
|
|
238
|
+
'upgrade.aria': 'Upgrade the ReZ-TI plugin',
|
|
239
|
+
'upgrade.running': 'Updating…',
|
|
240
|
+
'upgrade.reloading': 'Reloading',
|
|
241
|
+
'upgrade.already': 'Already {version}',
|
|
242
|
+
'upgrade.reloadTimeout': 'Update finished, but the page did not reconnect. Refresh this tab.',
|
|
223
243
|
}
|
|
224
244
|
|
|
225
245
|
/** Minimal {name} interpolator shared by the panel helpers. */
|
|
@@ -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')}>
|