mirror of
https://github.com/hansjone/dsh-im-ops.git
synced 2026-10-09 03:03:24 +08:00
feat: add npm update checks and manual installation
Add profile-scoped npm updates for Desktop and Web, protect source installations, and keep restarts manual. Cover stale status recovery, request races, and dialog focus with regression tests. Verified with npm run check (1626 tests) and isolated Desktop/Web npm installation flows. Refs #61
This commit is contained in:
parent
5888641297
commit
f3cefd8a84
23 changed files with 4058 additions and 271 deletions
585
lib/client.js
585
lib/client.js
|
|
@ -42,7 +42,7 @@ __export(index_exports, {
|
|||
name: () => name
|
||||
});
|
||||
module.exports = __toCommonJS(index_exports);
|
||||
var React20 = __toESM(require("react"), 1);
|
||||
var React21 = __toESM(require("react"), 1);
|
||||
|
||||
// package.json
|
||||
var package_default = {
|
||||
|
|
@ -149,7 +149,8 @@ var package_default = {
|
|||
"https-proxy-agent": "5.0.1",
|
||||
react: "18.3.1",
|
||||
"react-dom": "18.3.1",
|
||||
"react-test-renderer": "18.3.1"
|
||||
"react-test-renderer": "18.3.1",
|
||||
semver: "7.8.5"
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -310,6 +311,62 @@ var EN = Object.freeze({
|
|||
"IM \u6E20\u9053": "IM channels",
|
||||
"\u8BA9 DeepSeek Harness \u89E6\u624B\u53EF\u53CA": "Connecting DeepSeek Harness",
|
||||
"\u5F53\u524D\u7248\u672C": "Current version",
|
||||
"DSH-IM \u66F4\u65B0": "DSH-IM update",
|
||||
"\u68C0\u67E5\u66F4\u65B0": "Check for updates",
|
||||
"\u91CD\u65B0\u68C0\u67E5": "Check again",
|
||||
"\u5237\u65B0\u72B6\u6001": "Refresh status",
|
||||
"\u66F4\u65B0\u81F3": "Update to",
|
||||
"\u5B89\u88C5\u66F4\u65B0": "Install update",
|
||||
"\u6B63\u5728\u66F4\u65B0\u2026": "Updating\u2026",
|
||||
"\u5F85\u624B\u52A8\u91CD\u542F": "Restart needed",
|
||||
"\u8FD0\u884C\u7248\u672C": "Running version",
|
||||
"\u5DF2\u5B89\u88C5\u7248\u672C": "Installed version",
|
||||
"npm \u6700\u65B0\u7248\u672C": "Latest on npm",
|
||||
"\u76EE\u6807 profile": "Target profile",
|
||||
"\u76EE\u6807\u7248\u672C": "Target version",
|
||||
"\u66F4\u65B0\u6765\u6E90": "Update source",
|
||||
"\u9875\u9762\u7248\u672C": "Page version",
|
||||
"\u65E0\u6CD5\u786E\u8BA4": "Unknown",
|
||||
"\u53D1\u73B0\u65B0\u7248\u672C": "Update available",
|
||||
"\u5DF2\u662F\u6700\u65B0\u7248\u672C": "Up to date",
|
||||
"\u5F53\u524D\u7248\u672C\u65E0\u9700\u66F4\u65B0": "No newer version available",
|
||||
"\u5DF2\u83B7\u53D6 npm \u6700\u65B0\u7248\u672C": "Latest npm version retrieved",
|
||||
"\u68C0\u67E5 npm \u6700\u65B0\u7248\u672C\uFF0C\u4E0D\u4F1A\u81EA\u52A8\u5B89\u88C5\u3002": "Check the latest npm version. Nothing is installed automatically.",
|
||||
"\u6B63\u5728\u4ECE npm \u68C0\u67E5\u6700\u65B0\u7248\u672C\u2026": "Checking npm for the latest version\u2026",
|
||||
"\u6B63\u5728\u5B89\u88C5\uFF0C\u8BF7\u7A0D\u5019\u2026": "Installing, please wait\u2026",
|
||||
"\u6B63\u5728\u6821\u9A8C\u5B89\u88C5\u7ED3\u679C\u2026": "Verifying the installation\u2026",
|
||||
"\u5DF2\u5B89\u88C5\uFF0C\u5F85\u624B\u52A8\u91CD\u542F": "Installed \u2014 manual restart needed",
|
||||
"\u66F4\u65B0\u5DF2\u751F\u6548": "Updated version is active",
|
||||
"\u66F4\u65B0\u5931\u8D25": "Update failed",
|
||||
"\u66F4\u65B0\u8BF7\u6C42\u5931\u8D25": "Update request failed",
|
||||
"\u4EC5\u66F4\u65B0 DSH-IM\u3002\u5B89\u88C5\u5B8C\u6210\u540E\u9700\u624B\u52A8\u91CD\u542F\u540E\u53F0\uFF1B\u672C\u529F\u80FD\u4E0D\u4F1A\u81EA\u52A8\u91CD\u542F\u6216\u4E3B\u52A8\u5237\u65B0\u9875\u9762\u3002": "Only DSH-IM will be updated. Restart the backend manually after installation. This feature does not automatically restart it or initiate a page refresh.",
|
||||
"\u5F53\u524D\u662F\u6E90\u7801\u6216\u94FE\u63A5\u5B89\u88C5\uFF0C\u53EA\u80FD\u68C0\u67E5\u7248\u672C\uFF1B\u8BF7\u624B\u52A8\u66F4\u65B0\u6E90\u7801\uFF0C\u6216\u8FC1\u79FB\u5230 npm \u5B89\u88C5\u3002": "This is a source or linked installation. You can check versions, but must update the source manually or migrate to an npm installation.",
|
||||
"\u65E0\u6CD5\u786E\u8BA4\u5F53\u524D profile\uFF0C\u8BF7\u5728\u5BF9\u5E94\u7684 Harness \u73AF\u5883\u4E2D\u624B\u52A8\u66F4\u65B0\u3002": "The current profile could not be identified. Update manually in the corresponding Harness environment.",
|
||||
"\u5F53\u524D\u8FD0\u884C\u73AF\u5883\u4E0D\u652F\u6301\u6309\u94AE\u5B89\u88C5\uFF0C\u8BF7\u624B\u52A8\u66F4\u65B0\u63D2\u4EF6\u3002": "This runtime cannot install through this button. Update the plugin manually.",
|
||||
"\u5F53\u524D npm \u6E90\u914D\u7F6E\u4E0E\u5B98\u65B9\u6E90\u4E0D\u4E00\u81F4\uFF0C\u8BF7\u5148\u68C0\u67E5 registry \u914D\u7F6E\u3002": "The npm registry configuration differs from the official registry. Check your registry settings first.",
|
||||
"\u5F53\u524D Host \u7684 Node.js \u7248\u672C\u4E0D\u6EE1\u8DB3\u65B0\u7248\u8981\u6C42\uFF0C\u8BF7\u5148\u66F4\u65B0\u8FD0\u884C\u73AF\u5883\u3002": "The Host Node.js version does not meet the new version\u2019s requirements. Update the runtime first.",
|
||||
"\u65B0\u7248\u672C\u5DF2\u5B89\u88C5\uFF0C\u8BF7\u5728\u65B9\u4FBF\u65F6\u624B\u52A8\u91CD\u542F\u5F53\u524D Harness \u6216 Desktop\u3002": "The new version is installed. Manually restart this Harness or Desktop when convenient.",
|
||||
"\u4E0A\u6B21\u5B89\u88C5\u7ED3\u679C\u65E0\u6CD5\u786E\u8BA4\uFF0C\u8BF7\u5148\u68C0\u67E5\u6B64 profile \u7684\u63D2\u4EF6\u5B89\u88C5\u72B6\u6001\u3002": "The previous installation result is uncertain. Check this profile\u2019s plugin installation first.",
|
||||
"\u65E0\u6CD5\u8BBF\u95EE npm \u6216\u8BF7\u6C42\u8D85\u65F6\uFF0C\u8BF7\u7A0D\u540E\u91CD\u65B0\u68C0\u67E5\u3002": "npm is unreachable or the request timed out. Check again later.",
|
||||
"npm \u8FD4\u56DE\u7684\u7248\u672C\u4FE1\u606F\u65E0\u6548\uFF0C\u6682\u65F6\u65E0\u6CD5\u66F4\u65B0\u3002": "npm returned invalid release information. Updating is unavailable.",
|
||||
"\u7248\u672C\u786E\u8BA4\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u91CD\u65B0\u68C0\u67E5\u540E\u518D\u5B89\u88C5\u3002": "The version confirmation expired. Check again before installing.",
|
||||
"\u63D2\u4EF6\u5B89\u88C5\u72B6\u6001\u5DF2\u53D1\u751F\u53D8\u5316\uFF0C\u8BF7\u91CD\u65B0\u68C0\u67E5\u3002": "The plugin installation changed. Check again.",
|
||||
"\u6B64 profile \u6B63\u5728\u66F4\u65B0\uFF0C\u8BF7\u7A0D\u540E\u67E5\u770B\u72B6\u6001\u3002": "This profile is already updating. Check its status shortly.",
|
||||
"\u5B89\u88C5\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u5F53\u524D\u5B89\u88C5\u72B6\u6001\u540E\u91CD\u8BD5\u3002": "Installation failed. Check the current installation before retrying.",
|
||||
"\u5B89\u88C5\u7ED3\u679C\u6821\u9A8C\u5931\u8D25\uFF0C\u8BF7\u624B\u52A8\u68C0\u67E5\u63D2\u4EF6\u7248\u672C\u3002": "Installation verification failed. Check the plugin version manually.",
|
||||
"\u65E0\u6CD5\u5B89\u5168\u4FDD\u5B58\u66F4\u65B0\u72B6\u6001\uFF0C\u8BF7\u5148\u68C0\u67E5\u5F53\u524D\u5B89\u88C5\u7ED3\u679C\u3002": "Update state could not be saved safely. Check the current installation result before retrying.",
|
||||
"\u4E0A\u6B21\u66F4\u65B0\u5DF2\u4E2D\u65AD\uFF0C\u8BF7\u68C0\u67E5\u5B89\u88C5\u72B6\u6001\u540E\u91CD\u8BD5\u3002": "The previous update was interrupted. Check the installation before retrying.",
|
||||
"\u66F4\u65B0\u670D\u52A1\u5DF2\u5173\u95ED\uFF0C\u8BF7\u624B\u52A8\u91CD\u65B0\u6253\u5F00\u8BBE\u7F6E\u9875\u3002": "The update service has closed. Reopen settings manually.",
|
||||
"\u66F4\u65B0\u8BF7\u6C42\u65E0\u6548\uFF0C\u8BF7\u91CD\u65B0\u68C0\u67E5\u7248\u672C\u3002": "The update request is invalid. Check the version again.",
|
||||
"\u5F53\u524D\u63D2\u4EF6\u5B89\u88C5\u4E0D\u5B8C\u6574\u6216\u4E0E profile \u4E0D\u7B26\uFF0C\u8BF7\u624B\u52A8\u68C0\u67E5\u5B89\u88C5\u914D\u7F6E\u3002": "The plugin installation is incomplete or does not match this profile. Check the installation configuration manually.",
|
||||
"\u65E0\u6CD5\u786E\u8BA4\u5F53\u524D npm \u6E90\u914D\u7F6E\uFF0C\u6682\u65F6\u4E0D\u80FD\u5B89\u88C5\u66F4\u65B0\u3002": "The npm registry configuration could not be verified. Installation is unavailable.",
|
||||
"\u5B89\u88C5\u8D85\u65F6\uFF0C\u8BF7\u5148\u786E\u8BA4\u5F53\u524D\u5B89\u88C5\u72B6\u6001\uFF0C\u518D\u51B3\u5B9A\u662F\u5426\u91CD\u8BD5\u3002": "Installation timed out. Check the current installation before retrying.",
|
||||
"\u66F4\u65B0\u8BF7\u6C42\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002": "The update request failed. Try again.",
|
||||
"\u66F4\u65B0\u670D\u52A1\u8FD4\u56DE\u4E86\u65E0\u6CD5\u8BC6\u522B\u7684\u54CD\u5E94\u3002": "The update service returned an unrecognized response.",
|
||||
"\u5F53\u524D Host \u4E0D\u652F\u6301\u66F4\u65B0\u63A5\u53E3\uFF0C\u8BF7\u5148\u624B\u52A8\u66F4\u65B0\u63D2\u4EF6\u5E76\u91CD\u542F\u3002": "This Host does not support the update API. Update the plugin manually and restart first.",
|
||||
"\u5173\u95ED\u7A97\u53E3\u4E0D\u4F1A\u53D6\u6D88\u5B89\u88C5\u3002\u8BF7\u52FF\u540C\u65F6\u5728\u5176\u4ED6\u7A97\u53E3\u7BA1\u7406\u6B64 profile \u7684\u63D2\u4EF6\u3002": "Closing this window does not cancel installation. Do not manage this profile\u2019s plugins in another window at the same time.",
|
||||
"\u9875\u9762\u7248\u672C\u4E0E\u8FD0\u884C\u7248\u672C\u4E0D\u540C\uFF0C\u8BF7\u624B\u52A8\u5237\u65B0\u9875\u9762\uFF1B\u82E5\u4ECD\u4E0D\u4E00\u81F4\uFF0C\u8BF7\u624B\u52A8\u91CD\u542F Harness \u6216 Desktop\u3002": "The page and running versions differ. Refresh the page manually; if they still differ, manually restart Harness or Desktop.",
|
||||
"\u8BF7\u5728\u673A\u5668\u4EBA\u7A7A\u95F2\u65F6\u5B89\u88C5\uFF1B\u5B89\u88C5\u4F1A\u4FEE\u6539\u5F53\u524D profile \u7684\u4F9D\u8D56\uFF0C\u5B8C\u6210\u540E\u9700\u624B\u52A8\u91CD\u542F\u3002": "Install while bots are idle. This changes the current profile\u2019s dependencies and requires a manual restart afterward.",
|
||||
"AI Office": "AI Office",
|
||||
"\uFF08\u5B9E\u9A8C\u529F\u80FD\uFF09": "(Experimental)",
|
||||
"AI Office \u8BBE\u7F6E": "AI Office settings",
|
||||
|
|
@ -2414,7 +2471,7 @@ function AccountCard({
|
|||
const state = busy === "reconnect" ? "connecting" : account.state;
|
||||
const tone = account.connected ? "success" : state === "error" ? "error" : "warning";
|
||||
const stateLabel2 = account.connected ? "\u8FD0\u884C\u6B63\u5E38" : state === "connecting" ? "\u6B63\u5728\u8FDE\u63A5" : "\u8FDE\u63A5\u672A\u5C31\u7EEA";
|
||||
const summary = account.error?.message ?? (account.connected ? null : account.health.summary);
|
||||
const summary2 = account.error?.message ?? (account.connected ? null : account.health.summary);
|
||||
return h2(
|
||||
"article",
|
||||
{ className: "ddt-card dim-botCard", tabIndex: -1, "data-bot-id": account.botId },
|
||||
|
|
@ -2474,7 +2531,7 @@ function AccountCard({
|
|||
"\u79FB\u9664\u63A5\u5165"
|
||||
)
|
||||
),
|
||||
summary ? h2("div", { className: "ddt-summary dim-cardSummary" }, summary) : null,
|
||||
summary2 ? h2("div", { className: "ddt-summary dim-cardSummary" }, summary2) : null,
|
||||
account.lastMessageError ? h2(LastMessageErrorSummary, {
|
||||
className: "ddt-summary",
|
||||
error: account.lastMessageError
|
||||
|
|
@ -3137,11 +3194,11 @@ function createTokenChannelApi(channel4, connectionSummary, {
|
|||
agentPresetCatalog: normalizeAgentPresetCatalog(source.agentPresetCatalog)
|
||||
};
|
||||
};
|
||||
const presentError10 = (error) => ({
|
||||
const presentError11 = (error) => ({
|
||||
code: text2(error?.code, `${channel4.toUpperCase()}_ERROR`, 80),
|
||||
message: text2(error?.message, `${channel4}\u64CD\u4F5C\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5`)
|
||||
});
|
||||
return Object.freeze({ unwrapRpcResult: unwrapRpcResult10, normalizeSnapshot: normalizeSnapshot9, presentError: presentError10 });
|
||||
return Object.freeze({ unwrapRpcResult: unwrapRpcResult10, normalizeSnapshot: normalizeSnapshot9, presentError: presentError11 });
|
||||
}
|
||||
|
||||
// plugin-src/client/channels/discord/api.js
|
||||
|
|
@ -3210,7 +3267,7 @@ function createTokenChannelSettings(definition) {
|
|||
const state = busy === "reconnect" ? "connecting" : account.state;
|
||||
const tone = account.connected ? "success" : state === "error" ? "error" : "warning";
|
||||
const stateLabel2 = account.connected ? "\u8FD0\u884C\u6B63\u5E38" : state === "connecting" ? "\u6B63\u5728\u8FDE\u63A5" : "\u8FDE\u63A5\u672A\u5C31\u7EEA";
|
||||
const summary = account.error?.message ?? (account.connected ? null : account.health.summary);
|
||||
const summary2 = account.error?.message ?? (account.connected ? null : account.health.summary);
|
||||
const identity = account.bot.username ? `@${account.bot.username}` : account.bot.idMasked;
|
||||
return h2(
|
||||
"article",
|
||||
|
|
@ -3281,7 +3338,7 @@ function createTokenChannelSettings(definition) {
|
|||
disabled: Boolean(busy)
|
||||
}, "\u79FB\u9664\u63A5\u5165")
|
||||
),
|
||||
summary ? h2("div", { className: "ddt-summary dim-cardSummary" }, summary) : null,
|
||||
summary2 ? h2("div", { className: "ddt-summary dim-cardSummary" }, summary2) : null,
|
||||
account.lastMessageError ? h2(LastMessageErrorSummary, {
|
||||
className: "ddt-summary",
|
||||
error: account.lastMessageError
|
||||
|
|
@ -4497,7 +4554,7 @@ function BotCard({
|
|||
const repairTooltipId = React12.useId();
|
||||
const stateForDisplay = busy === "reconnect" ? "connecting" : state;
|
||||
const tone = stateForDisplay === "connected" ? "success" : stateForDisplay === "connecting" ? "warning" : "error";
|
||||
const summary = actionError?.message ?? connection.error?.message ?? (connected ? null : health.summary);
|
||||
const summary2 = actionError?.message ?? connection.error?.message ?? (connected ? null : health.summary);
|
||||
const titleId = `bxf-bot-${connection.botId.replace(/[^a-zA-Z0-9_-]/g, "-")}`;
|
||||
return h2(
|
||||
"article",
|
||||
|
|
@ -4611,10 +4668,10 @@ function BotCard({
|
|||
"aria-label": `\u4ECE DeepSeek Harness \u79FB\u9664${bot.name}`
|
||||
}, "\u79FB\u9664\u63A5\u5165")
|
||||
),
|
||||
summary ? h2(
|
||||
summary2 ? h2(
|
||||
"div",
|
||||
{ className: "bxf-healthSummary dim-cardSummary", "data-error": actionError || connection.error ? "true" : void 0 },
|
||||
summary
|
||||
summary2
|
||||
) : null,
|
||||
connection.lastMessageError ? h2(LastMessageErrorSummary, {
|
||||
className: "bxf-healthSummary",
|
||||
|
|
@ -6358,7 +6415,7 @@ function AccountCard2({
|
|||
}) {
|
||||
const tone = account.connected ? "success" : account.state === "error" ? "error" : "warning";
|
||||
const stateLabel2 = account.connected ? "\u8FD0\u884C\u6B63\u5E38" : account.state === "connecting" ? "\u6B63\u5728\u8FDE\u63A5" : "\u8FDE\u63A5\u672A\u5C31\u7EEA";
|
||||
const summary = account.error?.message ?? (account.connected ? null : account.health.summary);
|
||||
const summary2 = account.error?.message ?? (account.connected ? null : account.health.summary);
|
||||
return h2(
|
||||
"article",
|
||||
{ className: "ddt-card dim-botCard", "data-bot-id": account.botId },
|
||||
|
|
@ -6410,7 +6467,7 @@ function AccountCard2({
|
|||
h2(Button7, { className: "dim-cardAction", onClick: onReconnect, disabled: Boolean(busy) }, busy === "reconnect" ? "\u68C0\u67E5\u4E2D\u2026" : account.connected ? "\u68C0\u67E5\u8FDE\u63A5" : "\u91CD\u8BD5\u8FDE\u63A5"),
|
||||
h2(Button7, { className: "dim-cardAction", kind: "danger", onClick: onRequestRemove, disabled: Boolean(busy) }, "\u79FB\u9664\u63A5\u5165")
|
||||
),
|
||||
summary ? h2("div", { className: "ddt-summary dim-cardSummary" }, summary) : null,
|
||||
summary2 ? h2("div", { className: "ddt-summary dim-cardSummary" }, summary2) : null,
|
||||
account.lastMessageError ? h2(LastMessageErrorSummary, {
|
||||
className: "ddt-summary",
|
||||
error: account.lastMessageError
|
||||
|
|
@ -7906,7 +7963,7 @@ function AccountCard3({
|
|||
}) {
|
||||
const tone = account.connected ? "success" : account.state === "error" ? "error" : "warning";
|
||||
const stateLabel2 = account.connected ? "\u8FD0\u884C\u6B63\u5E38" : account.state === "connecting" ? "\u6B63\u5728\u8FDE\u63A5" : "\u8FDE\u63A5\u672A\u5C31\u7EEA";
|
||||
const summary = account.error?.message ?? (account.connected ? null : account.health.summary);
|
||||
const summary2 = account.error?.message ?? (account.connected ? null : account.health.summary);
|
||||
return h2(
|
||||
"article",
|
||||
{ className: "ddt-card dim-botCard", "data-bot-id": account.botId },
|
||||
|
|
@ -7958,7 +8015,7 @@ function AccountCard3({
|
|||
h2(Button10, { className: "dim-cardAction", onClick: onReconnect, disabled: Boolean(busy) }, busy === "reconnect" ? "\u68C0\u67E5\u4E2D\u2026" : account.connected ? "\u68C0\u67E5\u8FDE\u63A5" : "\u91CD\u8BD5\u8FDE\u63A5"),
|
||||
h2(Button10, { className: "dim-cardAction", kind: "danger", onClick: onRequestRemove, disabled: Boolean(busy) }, "\u79FB\u9664\u63A5\u5165")
|
||||
),
|
||||
summary ? h2("div", { className: "ddt-summary dim-cardSummary" }, summary) : null,
|
||||
summary2 ? h2("div", { className: "ddt-summary dim-cardSummary" }, summary2) : null,
|
||||
account.lastMessageError ? h2(LastMessageErrorSummary, {
|
||||
className: "ddt-summary",
|
||||
error: account.lastMessageError
|
||||
|
|
@ -8730,7 +8787,7 @@ function AccountCard4({
|
|||
}) {
|
||||
const state = busy === "reconnect" ? "connecting" : account.state;
|
||||
const tone = account.connected ? "success" : state === "error" ? "error" : "warning";
|
||||
const summary = account.error?.message ?? (account.connected ? null : account.health.summary);
|
||||
const summary2 = account.error?.message ?? (account.connected ? null : account.health.summary);
|
||||
return h2(
|
||||
"article",
|
||||
{ className: "dxw-card dim-botCard", tabIndex: -1, "data-bot-id": account.botId },
|
||||
|
|
@ -8781,7 +8838,7 @@ function AccountCard4({
|
|||
),
|
||||
h2(Button12, { className: "dim-cardAction", kind: "danger", onClick: onRequestRemove, disabled: Boolean(busy) }, "\u79FB\u9664\u63A5\u5165")
|
||||
),
|
||||
summary ? h2("div", { className: "dxw-summary dim-cardSummary" }, summary) : null,
|
||||
summary2 ? h2("div", { className: "dxw-summary dim-cardSummary" }, summary2) : null,
|
||||
account.lastMessageError ? h2(LastMessageErrorSummary, {
|
||||
className: "dxw-summary",
|
||||
error: account.lastMessageError
|
||||
|
|
@ -9933,7 +9990,7 @@ function WhatsappAccountCard({
|
|||
const state = busy === "reconnect" ? "connecting" : account.state;
|
||||
const tone = account.connected ? "success" : state === "error" ? "error" : "warning";
|
||||
const stateLabel2 = account.connected ? "\u8FD0\u884C\u6B63\u5E38" : state === "connecting" ? "\u6B63\u5728\u8FDE\u63A5" : "\u8FDE\u63A5\u672A\u5C31\u7EEA";
|
||||
const summary = account.error?.message ?? (account.connected ? null : account.health.summary);
|
||||
const summary2 = account.error?.message ?? (account.connected ? null : account.health.summary);
|
||||
return h2(
|
||||
"article",
|
||||
{ className: "ddt-card dim-botCard", "data-bot-id": account.botId },
|
||||
|
|
@ -10002,7 +10059,7 @@ function WhatsappAccountCard({
|
|||
disabled: Boolean(busy)
|
||||
}, "\u79FB\u9664\u63A5\u5165")
|
||||
),
|
||||
summary ? h2("div", { className: "ddt-summary dim-cardSummary" }, summary) : null,
|
||||
summary2 ? h2("div", { className: "ddt-summary dim-cardSummary" }, summary2) : null,
|
||||
account.lastMessageError ? h2(LastMessageErrorSummary, {
|
||||
className: "ddt-summary",
|
||||
error: account.lastMessageError
|
||||
|
|
@ -10387,6 +10444,32 @@ var CSS11 = String.raw`
|
|||
.dim-brandName { color: var(--dsw-alias-label-primary, #1f2329); font-size: 20px; line-height: 24px; font-weight: 800; letter-spacing: .04em; }
|
||||
.dim-brandVersion { color: var(--dsw-alias-label-tertiary, #8f959e); font: 500 10px/16px ui-monospace, SFMono-Regular, Menlo, monospace; letter-spacing: 0; }
|
||||
.dim-title p { margin: 0; color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; line-height: 18px; font-weight: 500; white-space: nowrap; }
|
||||
.dim-titleActions { display: flex; align-items: center; justify-content: flex-end; gap: 8px; flex-wrap: wrap; }
|
||||
.dim-updateButton { min-height: 30px; display: inline-flex; align-items: center; justify-content: center; gap: 3px; padding: 5px 10px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 8px; color: var(--dsw-alias-label-secondary, #646a73); background: var(--dsw-alias-bg-layer-1, #fff); font: inherit; font-size: 12px; line-height: 18px; font-weight: 560; cursor: pointer; }
|
||||
.dim-updateButton:hover:not(:disabled) { border-color: #aeb3bb; color: var(--dsw-alias-label-primary, #1f2329); background: var(--dsw-alias-interactive-bg-hover, #f7f8fa); }
|
||||
.dim-updateButton:focus-visible { outline: 2px solid var(--dsw-alias-state-business-primary, #3370ff); outline-offset: 2px; }
|
||||
.dim-updateButton:disabled { opacity: .55; cursor: default; }
|
||||
.dim-updateTrigger { white-space: nowrap; }
|
||||
.dim-updateBackdrop { position: fixed; inset: 0; z-index: 1000; display: grid; place-items: center; padding: 24px; background: rgb(15 17 21 / 42%); }
|
||||
.dim-updateBackdrop, .dim-updateBackdrop * { box-sizing: border-box; }
|
||||
.dim-updateDialog { width: min(480px, 100%); max-height: calc(100vh - 48px); overflow-y: auto; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 16px; color: var(--dsw-alias-label-primary, #1f2329); background: var(--dsw-alias-bg-layer-3, #fff); box-shadow: 0 20px 70px rgb(0 0 0 / 20%); text-align: left; }
|
||||
.dim-updateDialog:focus { outline: none; }
|
||||
.dim-updateDialog h3 { margin: 22px 24px 8px; font-size: 18px; line-height: 25px; font-weight: 680; }
|
||||
.dim-updateDescription { margin: 0 24px; color: var(--dsw-alias-label-secondary, #646a73); font-size: 13px; line-height: 20px; }
|
||||
.dim-updateBody { padding: 18px 24px 20px; }
|
||||
.dim-updateVersions { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: 8px 18px; margin: 0 0 18px; font-size: 12px; line-height: 18px; }
|
||||
.dim-updateVersions dt { color: var(--dsw-alias-label-secondary, #646a73); }
|
||||
.dim-updateVersions dd { min-width: 0; margin: 0; overflow-wrap: anywhere; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
|
||||
.dim-updateStatus { padding: 12px 14px; border: 1px solid var(--dsw-alias-border-l1, #eef0f3); border-radius: 10px; background: var(--dsw-alias-bg-layer-1, #f7f8fa); font-size: 13px; line-height: 20px; }
|
||||
.dim-updateStatus strong { font-weight: 600; }
|
||||
.dim-updateStatus p { margin: 6px 0 0; color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; line-height: 19px; overflow-wrap: anywhere; }
|
||||
.dim-updateStatusError { border-color: color-mix(in srgb, var(--dsw-alias-state-danger-primary, #d92d20) 25%, var(--dsw-alias-border-l2, #dfe1e5)); }
|
||||
.dim-updateHint, .dim-updateError { margin: 12px 0 0; font-size: 12px; line-height: 19px; overflow-wrap: anywhere; }
|
||||
.dim-updateHint { color: var(--dsw-alias-label-secondary, #646a73); }
|
||||
.dim-updateError { color: var(--dsw-alias-state-danger-primary, #d92d20); }
|
||||
.dim-updateFooter { display: flex; align-items: center; justify-content: flex-end; flex-wrap: wrap; gap: 8px; padding: 14px 24px; border-top: 1px solid var(--dsw-alias-border-l1, #eef0f3); }
|
||||
.dim-updateFooter .dim-updateButton:first-child { margin-right: auto; }
|
||||
.dim-updatePrimary, .dim-updatePrimary:hover:not(:disabled) { border-color: var(--dsw-alias-state-business-primary, #3370ff); color: #fff; background: var(--dsw-alias-state-business-primary, #3370ff); }
|
||||
.dim-githubAction { position: relative; display: inline-flex; flex: none; }
|
||||
.dim-githubLink { min-height: 30px; display: inline-flex; align-items: center; gap: 5px; flex: none; padding: 0 10px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 8px; color: var(--dsw-alias-label-secondary, #646a73); background: var(--dsw-alias-bg-layer-1, #fff); font-size: 12px; line-height: normal; font-weight: 560; text-decoration: none; transition: border-color .15s ease, color .15s ease, background .15s ease; }
|
||||
.dim-githubLink:hover { border-color: #aeb3bb; color: var(--dsw-alias-label-primary, #1f2329); background: var(--dsw-alias-interactive-bg-hover, #f7f8fa); }
|
||||
|
|
@ -10654,6 +10737,13 @@ var CSS11 = String.raw`
|
|||
@media (max-width: 560px) {
|
||||
.dim-title { flex-direction: column; gap: 10px; }
|
||||
.dim-title p { white-space: normal; }
|
||||
.dim-titleActions { justify-content: flex-start; }
|
||||
.dim-updateBackdrop { padding: 12px; }
|
||||
.dim-updateDialog { max-height: calc(100vh - 24px); }
|
||||
.dim-updateDialog h3 { margin: 18px 18px 8px; }
|
||||
.dim-updateDescription { margin: 0 18px; }
|
||||
.dim-updateBody { padding: 16px 18px; }
|
||||
.dim-updateFooter { padding: 12px 18px; }
|
||||
.dim-githubTooltip { right: auto; left: 0; }
|
||||
.dim-rail { grid-template-columns: minmax(0, 1fr); }
|
||||
.dim-loopbackRecovery { align-items: stretch; flex-direction: column; gap: 12px; }
|
||||
|
|
@ -10686,6 +10776,393 @@ function installImStyles() {
|
|||
return () => style.remove();
|
||||
}
|
||||
|
||||
// plugin-src/client/update-panel.js
|
||||
var React20 = __toESM(require("react"), 1);
|
||||
var import_react_dom2 = require("react-dom");
|
||||
var UPDATE_RPC_CHANNEL = "/dsh-im";
|
||||
var ACTIVE_STATES4 = /* @__PURE__ */ new Set(["installing", "verifying"]);
|
||||
var BLOCKED_REASONS = Object.freeze({
|
||||
"source-install": "\u5F53\u524D\u662F\u6E90\u7801\u6216\u94FE\u63A5\u5B89\u88C5\uFF0C\u53EA\u80FD\u68C0\u67E5\u7248\u672C\uFF1B\u8BF7\u624B\u52A8\u66F4\u65B0\u6E90\u7801\uFF0C\u6216\u8FC1\u79FB\u5230 npm \u5B89\u88C5\u3002",
|
||||
"unknown-profile": "\u65E0\u6CD5\u786E\u8BA4\u5F53\u524D profile\uFF0C\u8BF7\u5728\u5BF9\u5E94\u7684 Harness \u73AF\u5883\u4E2D\u624B\u52A8\u66F4\u65B0\u3002",
|
||||
"unsupported-runtime": "\u5F53\u524D\u8FD0\u884C\u73AF\u5883\u4E0D\u652F\u6301\u6309\u94AE\u5B89\u88C5\uFF0C\u8BF7\u624B\u52A8\u66F4\u65B0\u63D2\u4EF6\u3002",
|
||||
"registry-conflict": "\u5F53\u524D npm \u6E90\u914D\u7F6E\u4E0E\u5B98\u65B9\u6E90\u4E0D\u4E00\u81F4\uFF0C\u8BF7\u5148\u68C0\u67E5 registry \u914D\u7F6E\u3002",
|
||||
"incompatible-node": "\u5F53\u524D Host \u7684 Node.js \u7248\u672C\u4E0D\u6EE1\u8DB3\u65B0\u7248\u8981\u6C42\uFF0C\u8BF7\u5148\u66F4\u65B0\u8FD0\u884C\u73AF\u5883\u3002",
|
||||
"pending-restart": "\u65B0\u7248\u672C\u5DF2\u5B89\u88C5\uFF0C\u8BF7\u5728\u65B9\u4FBF\u65F6\u624B\u52A8\u91CD\u542F\u5F53\u524D Harness \u6216 Desktop\u3002",
|
||||
"recovery-required": "\u4E0A\u6B21\u5B89\u88C5\u7ED3\u679C\u65E0\u6CD5\u786E\u8BA4\uFF0C\u8BF7\u5148\u68C0\u67E5\u6B64 profile \u7684\u63D2\u4EF6\u5B89\u88C5\u72B6\u6001\u3002"
|
||||
});
|
||||
var ERROR_MESSAGES = Object.freeze({
|
||||
"check-failed": "\u65E0\u6CD5\u8BBF\u95EE npm \u6216\u8BF7\u6C42\u8D85\u65F6\uFF0C\u8BF7\u7A0D\u540E\u91CD\u65B0\u68C0\u67E5\u3002",
|
||||
"update-failed": "\u66F4\u65B0\u8BF7\u6C42\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002",
|
||||
"invalid-release": "npm \u8FD4\u56DE\u7684\u7248\u672C\u4FE1\u606F\u65E0\u6548\uFF0C\u6682\u65F6\u65E0\u6CD5\u66F4\u65B0\u3002",
|
||||
"check-expired": "\u7248\u672C\u786E\u8BA4\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u91CD\u65B0\u68C0\u67E5\u540E\u518D\u5B89\u88C5\u3002",
|
||||
"installation-changed": "\u63D2\u4EF6\u5B89\u88C5\u72B6\u6001\u5DF2\u53D1\u751F\u53D8\u5316\uFF0C\u8BF7\u91CD\u65B0\u68C0\u67E5\u3002",
|
||||
"update-busy": "\u6B64 profile \u6B63\u5728\u66F4\u65B0\uFF0C\u8BF7\u7A0D\u540E\u67E5\u770B\u72B6\u6001\u3002",
|
||||
"install-failed": "\u5B89\u88C5\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u5F53\u524D\u5B89\u88C5\u72B6\u6001\u540E\u91CD\u8BD5\u3002",
|
||||
"verify-failed": "\u5B89\u88C5\u7ED3\u679C\u6821\u9A8C\u5931\u8D25\uFF0C\u8BF7\u624B\u52A8\u68C0\u67E5\u63D2\u4EF6\u7248\u672C\u3002",
|
||||
"state-unavailable": "\u65E0\u6CD5\u5B89\u5168\u4FDD\u5B58\u66F4\u65B0\u72B6\u6001\uFF0C\u8BF7\u5148\u68C0\u67E5\u5F53\u524D\u5B89\u88C5\u7ED3\u679C\u3002",
|
||||
interrupted: "\u4E0A\u6B21\u66F4\u65B0\u5DF2\u4E2D\u65AD\uFF0C\u8BF7\u68C0\u67E5\u5B89\u88C5\u72B6\u6001\u540E\u91CD\u8BD5\u3002",
|
||||
disposed: "\u66F4\u65B0\u670D\u52A1\u5DF2\u5173\u95ED\uFF0C\u8BF7\u624B\u52A8\u91CD\u65B0\u6253\u5F00\u8BBE\u7F6E\u9875\u3002",
|
||||
"bad-request": "\u66F4\u65B0\u8BF7\u6C42\u65E0\u6548\uFF0C\u8BF7\u91CD\u65B0\u68C0\u67E5\u7248\u672C\u3002",
|
||||
"invalid-installation": "\u5F53\u524D\u63D2\u4EF6\u5B89\u88C5\u4E0D\u5B8C\u6574\u6216\u4E0E profile \u4E0D\u7B26\uFF0C\u8BF7\u624B\u52A8\u68C0\u67E5\u5B89\u88C5\u914D\u7F6E\u3002",
|
||||
"executor-unavailable": BLOCKED_REASONS["unsupported-runtime"],
|
||||
"registry-check-failed": "\u65E0\u6CD5\u786E\u8BA4\u5F53\u524D npm \u6E90\u914D\u7F6E\uFF0C\u6682\u65F6\u4E0D\u80FD\u5B89\u88C5\u66F4\u65B0\u3002",
|
||||
"install-interrupted": "\u4E0A\u6B21\u66F4\u65B0\u5DF2\u4E2D\u65AD\uFF0C\u8BF7\u68C0\u67E5\u5B89\u88C5\u72B6\u6001\u540E\u91CD\u8BD5\u3002",
|
||||
"install-timeout": "\u5B89\u88C5\u8D85\u65F6\uFF0C\u8BF7\u5148\u786E\u8BA4\u5F53\u524D\u5B89\u88C5\u72B6\u6001\uFF0C\u518D\u51B3\u5B9A\u662F\u5426\u91CD\u8BD5\u3002",
|
||||
"invalid-version": "npm \u8FD4\u56DE\u7684\u7248\u672C\u4FE1\u606F\u65E0\u6548\uFF0C\u6682\u65F6\u65E0\u6CD5\u66F4\u65B0\u3002"
|
||||
});
|
||||
function unwrapSnapshot(result) {
|
||||
if (result?.ok === false) {
|
||||
const error = new Error(result.error?.message || "\u66F4\u65B0\u8BF7\u6C42\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002");
|
||||
error.code = result.error?.code;
|
||||
throw error;
|
||||
}
|
||||
const value = result?.value;
|
||||
if (result?.ok !== true || typeof value?.runningVersion !== "string" || typeof value?.canInstall !== "boolean") {
|
||||
throw new Error("\u66F4\u65B0\u670D\u52A1\u8FD4\u56DE\u4E86\u65E0\u6CD5\u8BC6\u522B\u7684\u54CD\u5E94\u3002");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
function presentError10(error) {
|
||||
const code = error?.rpcError?.code ?? error?.code ?? "";
|
||||
const message = error?.rpcError?.message ?? error?.message ?? "";
|
||||
if (code === "update-unavailable" || /(?:not[-_ ]found|unimplemented|unknown[-_ ](?:endpoint|channel)|no[-_ ]handler)/i.test(code) || /(?:not found|unknown (?:endpoint|channel)|not registered|no .*handler|HTTP 404)/i.test(message)) {
|
||||
return "\u5F53\u524D Host \u4E0D\u652F\u6301\u66F4\u65B0\u63A5\u53E3\uFF0C\u8BF7\u5148\u624B\u52A8\u66F4\u65B0\u63D2\u4EF6\u5E76\u91CD\u542F\u3002";
|
||||
}
|
||||
return BLOCKED_REASONS[code] ?? ERROR_MESSAGES[code] ?? (message.slice(0, 400) || "\u66F4\u65B0\u8BF7\u6C42\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002");
|
||||
}
|
||||
function summary(snapshot, action, error) {
|
||||
if (action === "checking") return "\u6B63\u5728\u4ECE npm \u68C0\u67E5\u6700\u65B0\u7248\u672C\u2026";
|
||||
if (action === "starting" || snapshot?.job?.state === "installing") return "\u6B63\u5728\u5B89\u88C5\uFF0C\u8BF7\u7A0D\u5019\u2026";
|
||||
if (snapshot?.job?.state === "verifying") return "\u6B63\u5728\u6821\u9A8C\u5B89\u88C5\u7ED3\u679C\u2026";
|
||||
if (snapshot?.job?.state === "restart-required" || snapshot?.blockedReason === "pending-restart") {
|
||||
return "\u5DF2\u5B89\u88C5\uFF0C\u5F85\u624B\u52A8\u91CD\u542F";
|
||||
}
|
||||
if (snapshot?.job?.state === "completed") return "\u66F4\u65B0\u5DF2\u751F\u6548";
|
||||
if (snapshot?.job?.state === "failed") return "\u66F4\u65B0\u5931\u8D25";
|
||||
if (snapshot?.job?.state === "interrupted") return "\u4E0A\u6B21\u66F4\u65B0\u5DF2\u4E2D\u65AD\uFF0C\u8BF7\u68C0\u67E5\u5B89\u88C5\u72B6\u6001\u540E\u91CD\u8BD5\u3002";
|
||||
if (error) return "\u66F4\u65B0\u8BF7\u6C42\u5931\u8D25";
|
||||
if (snapshot?.canInstall) return "\u53D1\u73B0\u65B0\u7248\u672C";
|
||||
if (snapshot?.checkedAt && snapshot.latestVersion === snapshot.runningVersion) return "\u5DF2\u662F\u6700\u65B0\u7248\u672C";
|
||||
if (snapshot?.blockedReason === "no-update") return "\u5F53\u524D\u7248\u672C\u65E0\u9700\u66F4\u65B0";
|
||||
if (snapshot?.checkedAt) return "\u5DF2\u83B7\u53D6 npm \u6700\u65B0\u7248\u672C";
|
||||
return "\u68C0\u67E5 npm \u6700\u65B0\u7248\u672C\uFF0C\u4E0D\u4F1A\u81EA\u52A8\u5B89\u88C5\u3002";
|
||||
}
|
||||
function retainDialogFocus(event) {
|
||||
event?.currentTarget?.closest?.(".dim-updateDialog")?.focus?.({ preventScroll: true });
|
||||
}
|
||||
function UpdateDialog({ children, onClose }) {
|
||||
const dialogRef = React20.useRef(null);
|
||||
const titleId = React20.useId();
|
||||
const descriptionId = React20.useId();
|
||||
React20.useEffect(() => {
|
||||
const previous = globalThis.document?.activeElement;
|
||||
dialogRef.current?.focus?.();
|
||||
return () => {
|
||||
if (previous?.isConnected) previous.focus?.();
|
||||
};
|
||||
}, []);
|
||||
const content = h2(
|
||||
"div",
|
||||
{
|
||||
className: "dim-updateBackdrop",
|
||||
onMouseDown: (event) => {
|
||||
if (event.target === event.currentTarget) onClose();
|
||||
}
|
||||
},
|
||||
h2(
|
||||
"section",
|
||||
{
|
||||
ref: dialogRef,
|
||||
className: "dim-updateDialog",
|
||||
role: "dialog",
|
||||
"aria-modal": "true",
|
||||
"aria-labelledby": titleId,
|
||||
"aria-describedby": descriptionId,
|
||||
tabIndex: -1,
|
||||
onKeyDown: (event) => {
|
||||
if (event.key === "Escape") {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
onClose();
|
||||
}
|
||||
if (event.key !== "Tab") return;
|
||||
const buttons = dialogRef.current?.querySelectorAll?.("button:not(:disabled)");
|
||||
if (!buttons?.length) return;
|
||||
const first = buttons[0];
|
||||
const last = buttons[buttons.length - 1];
|
||||
const active = globalThis.document?.activeElement;
|
||||
if (event.shiftKey && (active === first || active === dialogRef.current)) {
|
||||
event.preventDefault();
|
||||
last.focus();
|
||||
} else if (!event.shiftKey && (active === last || active === dialogRef.current)) {
|
||||
event.preventDefault();
|
||||
first.focus();
|
||||
}
|
||||
}
|
||||
},
|
||||
h2("h3", { id: titleId }, "DSH-IM \u66F4\u65B0"),
|
||||
h2(
|
||||
"p",
|
||||
{ id: descriptionId, className: "dim-updateDescription" },
|
||||
"\u4EC5\u66F4\u65B0 DSH-IM\u3002\u5B89\u88C5\u5B8C\u6210\u540E\u9700\u624B\u52A8\u91CD\u542F\u540E\u53F0\uFF1B\u672C\u529F\u80FD\u4E0D\u4F1A\u81EA\u52A8\u91CD\u542F\u6216\u4E3B\u52A8\u5237\u65B0\u9875\u9762\u3002"
|
||||
),
|
||||
children
|
||||
)
|
||||
);
|
||||
return typeof document !== "undefined" && document.body ? (0, import_react_dom2.createPortal)(content, document.body) : content;
|
||||
}
|
||||
function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
|
||||
const [snapshot, setSnapshot] = React20.useState(null);
|
||||
const [action, setAction] = React20.useState("status");
|
||||
const [error, setError] = React20.useState(null);
|
||||
const [open, setOpen] = React20.useState(false);
|
||||
const [uncertainInstall, setUncertainInstall] = React20.useState(false);
|
||||
const mounted = React20.useRef(false);
|
||||
const busy = React20.useRef(false);
|
||||
const readController = React20.useRef(null);
|
||||
const pollReadController = React20.useRef(null);
|
||||
const installRequest = React20.useRef(null);
|
||||
const onStatusRef = React20.useRef(onStatus);
|
||||
onStatusRef.current = onStatus;
|
||||
const accept = React20.useCallback((next) => {
|
||||
setSnapshot(next);
|
||||
onStatusRef.current?.(next);
|
||||
}, []);
|
||||
const acceptAuthoritative = (next) => {
|
||||
pollReadController.current?.abort();
|
||||
setUncertainInstall(false);
|
||||
accept(next);
|
||||
};
|
||||
const invoke = React20.useCallback(async (endpoint, payload = {}, signal) => {
|
||||
if (typeof rpcCall !== "function") {
|
||||
const unavailable = new Error("\u5F53\u524D Host \u4E0D\u652F\u6301\u66F4\u65B0\u63A5\u53E3\uFF0C\u8BF7\u5148\u624B\u52A8\u66F4\u65B0\u63D2\u4EF6\u5E76\u91CD\u542F\u3002");
|
||||
unavailable.code = "update-unavailable";
|
||||
throw unavailable;
|
||||
}
|
||||
return unwrapSnapshot(await rpcCall(endpoint, payload, signal));
|
||||
}, [rpcCall]);
|
||||
React20.useEffect(() => {
|
||||
mounted.current = true;
|
||||
busy.current = true;
|
||||
const controller = new AbortController();
|
||||
void invoke("update.status", {}, controller.signal).then((next) => {
|
||||
if (!controller.signal.aborted) accept(next);
|
||||
}).catch((cause) => {
|
||||
if (!controller.signal.aborted) setError(presentError10(cause));
|
||||
}).finally(() => {
|
||||
if (!controller.signal.aborted) {
|
||||
busy.current = false;
|
||||
setAction(null);
|
||||
}
|
||||
});
|
||||
return () => {
|
||||
mounted.current = false;
|
||||
controller.abort();
|
||||
readController.current?.abort();
|
||||
};
|
||||
}, [accept, invoke]);
|
||||
const activeJob = ACTIVE_STATES4.has(snapshot?.job?.state);
|
||||
const restartRequired = snapshot?.job?.state === "restart-required" || snapshot?.blockedReason === "pending-restart";
|
||||
const shouldPoll = activeJob || uncertainInstall;
|
||||
React20.useEffect(() => {
|
||||
if (!shouldPoll) return void 0;
|
||||
let controller;
|
||||
const scheduler = createPollScheduler({
|
||||
setTimeoutFn: (callback, delay) => globalThis.setTimeout(callback, delay),
|
||||
clearTimeoutFn: (timer) => globalThis.clearTimeout(timer)
|
||||
});
|
||||
const poll = async () => {
|
||||
const pendingController = new AbortController();
|
||||
controller = pendingController;
|
||||
pollReadController.current = pendingController;
|
||||
let keepPolling = true;
|
||||
try {
|
||||
const next = await invoke("update.status", {}, pendingController.signal);
|
||||
if (!pendingController.signal.aborted) {
|
||||
accept(next);
|
||||
setError(null);
|
||||
setUncertainInstall(false);
|
||||
keepPolling = ACTIVE_STATES4.has(next.job?.state);
|
||||
}
|
||||
} catch (cause) {
|
||||
if (!pendingController.signal.aborted) setError(presentError10(cause));
|
||||
} finally {
|
||||
if (pollReadController.current === pendingController) pollReadController.current = null;
|
||||
}
|
||||
if (keepPolling) scheduler.schedule(poll, 1e3);
|
||||
};
|
||||
scheduler.schedule(poll, 1e3);
|
||||
return () => {
|
||||
controller?.abort();
|
||||
scheduler.dispose();
|
||||
};
|
||||
}, [accept, invoke, shouldPoll]);
|
||||
const readSnapshot = async (endpoint) => {
|
||||
const checkLatest = endpoint === "update.check";
|
||||
if (!mounted.current || busy.current || checkLatest && (activeJob || restartRequired)) return;
|
||||
busy.current = true;
|
||||
setAction(checkLatest ? "checking" : "status");
|
||||
setError(null);
|
||||
if (checkLatest) {
|
||||
installRequest.current = null;
|
||||
setSnapshot((current) => current ? { ...current, canInstall: false, checkId: null, latestVersion: null, checkedAt: null } : current);
|
||||
}
|
||||
const controller = new AbortController();
|
||||
readController.current = controller;
|
||||
try {
|
||||
const next = await invoke(endpoint, {}, controller.signal);
|
||||
if (!controller.signal.aborted && mounted.current) {
|
||||
if (checkLatest) accept(next);
|
||||
else acceptAuthoritative(next);
|
||||
}
|
||||
} catch (cause) {
|
||||
if (!controller.signal.aborted && mounted.current) setError(presentError10(cause));
|
||||
} finally {
|
||||
if (!controller.signal.aborted && mounted.current) {
|
||||
busy.current = false;
|
||||
setAction(null);
|
||||
}
|
||||
}
|
||||
};
|
||||
const check = () => readSnapshot("update.check");
|
||||
const refreshStatus = () => readSnapshot("update.status");
|
||||
const install = async () => {
|
||||
if (busy.current || !snapshot?.canInstall || !snapshot.checkId) return;
|
||||
busy.current = true;
|
||||
setAction("starting");
|
||||
setError(null);
|
||||
if (installRequest.current?.checkId !== snapshot.checkId) {
|
||||
installRequest.current = {
|
||||
checkId: snapshot.checkId,
|
||||
requestId: globalThis.crypto?.randomUUID?.() ?? `update-${Date.now()}-${Math.random().toString(36).slice(2)}`
|
||||
};
|
||||
}
|
||||
try {
|
||||
const next = await invoke("update.install", installRequest.current);
|
||||
if (mounted.current) acceptAuthoritative(next);
|
||||
} catch (cause) {
|
||||
if (mounted.current) {
|
||||
setError(presentError10(cause));
|
||||
setUncertainInstall(true);
|
||||
}
|
||||
} finally {
|
||||
if (mounted.current) {
|
||||
busy.current = false;
|
||||
setAction(null);
|
||||
}
|
||||
}
|
||||
};
|
||||
const busyAction = action !== null;
|
||||
const blocked = BLOCKED_REASONS[snapshot?.blockedReason] ?? ERROR_MESSAGES[snapshot?.blockedReason];
|
||||
const canConfirm = snapshot?.canInstall && snapshot.checkId && !activeJob && !restartRequired;
|
||||
const versionsDiffer = snapshot && clientVersion && snapshot.runningVersion !== clientVersion;
|
||||
const failedJob = ["failed", "interrupted"].includes(snapshot?.job?.state);
|
||||
const jobMessage = snapshot?.job?.message;
|
||||
const targetVersion = snapshot?.job?.targetVersion;
|
||||
const buttonLabel = action === "checking" ? "\u68C0\u67E5\u4E2D\u2026" : action === "starting" || activeJob ? "\u6B63\u5728\u66F4\u65B0\u2026" : restartRequired ? "\u5F85\u624B\u52A8\u91CD\u542F" : snapshot?.canInstall ? "\u66F4\u65B0\u81F3" : "\u68C0\u67E5\u66F4\u65B0";
|
||||
return h2(
|
||||
React20.Fragment,
|
||||
null,
|
||||
h2("button", {
|
||||
type: "button",
|
||||
className: "dim-updateButton dim-updateTrigger",
|
||||
disabled: busyAction,
|
||||
"aria-haspopup": "dialog",
|
||||
onClick: () => {
|
||||
setOpen(true);
|
||||
if (restartRequired) void refreshStatus();
|
||||
else if (!snapshot?.canInstall && !snapshot?.job) void check();
|
||||
}
|
||||
}, buttonLabel, buttonLabel === "\u66F4\u65B0\u81F3" ? ` v${snapshot.latestVersion}` : null),
|
||||
open ? h2(
|
||||
UpdateDialog,
|
||||
{ onClose: () => setOpen(false) },
|
||||
h2(
|
||||
"div",
|
||||
{ className: "dim-updateBody" },
|
||||
h2(
|
||||
"dl",
|
||||
{ className: "dim-updateVersions" },
|
||||
h2("dt", null, "\u8FD0\u884C\u7248\u672C"),
|
||||
h2("dd", null, `v${snapshot?.runningVersion ?? clientVersion}`),
|
||||
snapshot?.installedVersion && snapshot.installedVersion !== snapshot.runningVersion ? h2(
|
||||
React20.Fragment,
|
||||
null,
|
||||
h2("dt", null, "\u5DF2\u5B89\u88C5\u7248\u672C"),
|
||||
h2("dd", null, `v${snapshot.installedVersion}`)
|
||||
) : null,
|
||||
h2("dt", null, "npm \u6700\u65B0\u7248\u672C"),
|
||||
h2("dd", null, snapshot?.latestVersion ? `v${snapshot.latestVersion}` : "\u5C1A\u672A\u68C0\u67E5"),
|
||||
targetVersion ? h2(
|
||||
React20.Fragment,
|
||||
null,
|
||||
h2("dt", null, "\u76EE\u6807\u7248\u672C"),
|
||||
h2("dd", null, `v${targetVersion}`)
|
||||
) : null,
|
||||
h2("dt", null, "\u76EE\u6807 profile"),
|
||||
h2("dd", null, snapshot?.profileName ?? "\u65E0\u6CD5\u786E\u8BA4"),
|
||||
h2("dt", null, "\u66F4\u65B0\u6765\u6E90"),
|
||||
h2("dd", null, "registry.npmjs.org"),
|
||||
versionsDiffer ? h2(
|
||||
React20.Fragment,
|
||||
null,
|
||||
h2("dt", null, "\u9875\u9762\u7248\u672C"),
|
||||
h2("dd", null, `v${clientVersion}`)
|
||||
) : null
|
||||
),
|
||||
h2(
|
||||
"div",
|
||||
{
|
||||
className: `dim-updateStatus${error || failedJob ? " dim-updateStatusError" : ""}`,
|
||||
role: "status",
|
||||
"aria-live": "polite",
|
||||
"aria-atomic": "true"
|
||||
},
|
||||
h2("strong", null, summary(snapshot, action, error)),
|
||||
activeJob || action === "starting" ? h2("p", null, "\u5173\u95ED\u7A97\u53E3\u4E0D\u4F1A\u53D6\u6D88\u5B89\u88C5\u3002\u8BF7\u52FF\u540C\u65F6\u5728\u5176\u4ED6\u7A97\u53E3\u7BA1\u7406\u6B64 profile \u7684\u63D2\u4EF6\u3002") : null,
|
||||
restartRequired ? h2("p", null, BLOCKED_REASONS["pending-restart"]) : null,
|
||||
failedJob && jobMessage ? h2(
|
||||
"p",
|
||||
null,
|
||||
BLOCKED_REASONS[jobMessage] ?? ERROR_MESSAGES[jobMessage] ?? jobMessage
|
||||
) : null
|
||||
),
|
||||
error ? h2("p", { className: "dim-updateError", role: "alert" }, error) : null,
|
||||
blocked && !restartRequired ? h2("p", { className: "dim-updateHint" }, blocked) : null,
|
||||
versionsDiffer && !restartRequired ? h2("p", { className: "dim-updateHint" }, "\u9875\u9762\u7248\u672C\u4E0E\u8FD0\u884C\u7248\u672C\u4E0D\u540C\uFF0C\u8BF7\u624B\u52A8\u5237\u65B0\u9875\u9762\uFF1B\u82E5\u4ECD\u4E0D\u4E00\u81F4\uFF0C\u8BF7\u624B\u52A8\u91CD\u542F Harness \u6216 Desktop\u3002") : null,
|
||||
canConfirm ? h2(
|
||||
"p",
|
||||
{ className: "dim-updateHint" },
|
||||
"\u8BF7\u5728\u673A\u5668\u4EBA\u7A7A\u95F2\u65F6\u5B89\u88C5\uFF1B\u5B89\u88C5\u4F1A\u4FEE\u6539\u5F53\u524D profile \u7684\u4F9D\u8D56\uFF0C\u5B8C\u6210\u540E\u9700\u624B\u52A8\u91CD\u542F\u3002"
|
||||
) : null
|
||||
),
|
||||
h2(
|
||||
"footer",
|
||||
{ className: "dim-updateFooter" },
|
||||
h2("button", { type: "button", className: "dim-updateButton", onClick: () => setOpen(false) }, "\u5173\u95ED"),
|
||||
restartRequired || !activeJob ? h2("button", {
|
||||
type: "button",
|
||||
className: "dim-updateButton",
|
||||
disabled: busyAction,
|
||||
onClick: (event) => {
|
||||
retainDialogFocus(event);
|
||||
void (restartRequired ? refreshStatus() : check());
|
||||
}
|
||||
}, restartRequired ? "\u5237\u65B0\u72B6\u6001" : "\u91CD\u65B0\u68C0\u67E5") : null,
|
||||
canConfirm ? h2("button", {
|
||||
type: "button",
|
||||
className: "dim-updateButton dim-updatePrimary",
|
||||
disabled: busyAction,
|
||||
onClick: (event) => {
|
||||
retainDialogFocus(event);
|
||||
void install();
|
||||
}
|
||||
}, "\u5B89\u88C5\u66F4\u65B0") : null
|
||||
)
|
||||
) : null
|
||||
);
|
||||
}
|
||||
|
||||
// plugin-src/client/index.js
|
||||
var name = "im-settings";
|
||||
var inject = ["slots", "connection", "locale", "workspaces"];
|
||||
|
|
@ -10808,18 +11285,23 @@ function IMSettingsTab({
|
|||
weixinRpcCall,
|
||||
whatsappRpcCall,
|
||||
officeRpcCall,
|
||||
updateRpcCall,
|
||||
workspaceDirectoryPicker,
|
||||
browserLocation = globalThis.location,
|
||||
navigateToRecoveryUrl = replacePageLocation
|
||||
}) {
|
||||
const [selected, setSelected] = React20.useState("weixin");
|
||||
const [loopbackRecovery, setLoopbackRecovery] = React20.useState(null);
|
||||
const githubTooltipId = React20.useId();
|
||||
const [selected, setSelected] = React21.useState("weixin");
|
||||
const [loopbackRecovery, setLoopbackRecovery] = React21.useState(null);
|
||||
const [runningVersion, setRunningVersion] = React21.useState(IM_PLUGIN_VERSION);
|
||||
const githubTooltipId = React21.useId();
|
||||
const active = CHANNELS.find((channel4) => channel4.id === selected) ?? CHANNELS[0];
|
||||
const reportLoopbackRecovery = React20.useCallback((recovery) => {
|
||||
const reportLoopbackRecovery = React21.useCallback((recovery) => {
|
||||
setLoopbackRecovery((current) => current?.url === recovery.url ? current : recovery);
|
||||
}, []);
|
||||
const rpcCalls = React20.useMemo(() => createLoopbackAwareRpcCalls({
|
||||
const reportUpdateStatus = React21.useCallback((snapshot) => {
|
||||
setRunningVersion(snapshot.runningVersion);
|
||||
}, []);
|
||||
const rpcCalls = React21.useMemo(() => createLoopbackAwareRpcCalls({
|
||||
dingtalkRpcCall,
|
||||
discordRpcCall,
|
||||
feishuRpcCall,
|
||||
|
|
@ -10829,7 +11311,8 @@ function IMSettingsTab({
|
|||
wecomRpcCall,
|
||||
weixinRpcCall,
|
||||
whatsappRpcCall,
|
||||
officeRpcCall
|
||||
officeRpcCall,
|
||||
updateRpcCall
|
||||
}, {
|
||||
location: browserLocation,
|
||||
onRecovery: reportLoopbackRecovery
|
||||
|
|
@ -10843,6 +11326,7 @@ function IMSettingsTab({
|
|||
reportLoopbackRecovery,
|
||||
slackRpcCall,
|
||||
telegramRpcCall,
|
||||
updateRpcCall,
|
||||
wecomRpcCall,
|
||||
weixinRpcCall,
|
||||
whatsappRpcCall
|
||||
|
|
@ -10863,31 +11347,40 @@ function IMSettingsTab({
|
|||
"div",
|
||||
{ className: "dim-brandHeading" },
|
||||
h2("strong", { className: "dim-brandName" }, "DSH-IM"),
|
||||
h2("span", { className: "dim-brandVersion" }, `v${IM_PLUGIN_VERSION}`)
|
||||
h2("span", { className: "dim-brandVersion" }, `v${runningVersion}`)
|
||||
),
|
||||
h2("p", null, "\u8BA9 DeepSeek Harness \u89E6\u624B\u53EF\u53CA")
|
||||
),
|
||||
h2(
|
||||
"span",
|
||||
{ className: "dim-githubAction" },
|
||||
"div",
|
||||
{ className: "dim-titleActions" },
|
||||
h2(UpdatePanel, {
|
||||
rpcCall: rpcCalls.updateRpcCall,
|
||||
clientVersion: IM_PLUGIN_VERSION,
|
||||
onStatus: reportUpdateStatus
|
||||
}),
|
||||
h2(
|
||||
"a",
|
||||
{
|
||||
className: "dim-githubLink",
|
||||
href: "https://github.com/xmanrui/dsh-im",
|
||||
target: "_blank",
|
||||
rel: "noopener noreferrer",
|
||||
"aria-label": "dsh-im GitHub",
|
||||
"aria-describedby": githubTooltipId
|
||||
},
|
||||
h2("span", null, "GitHub"),
|
||||
h2("span", { className: "dim-githubArrow", "aria-hidden": "true" }, "\u2197")
|
||||
),
|
||||
h2("span", {
|
||||
id: githubTooltipId,
|
||||
className: "dim-githubTooltip",
|
||||
role: "tooltip"
|
||||
}, "\u5E2E\u52A9\u4E0E\u53CD\u9988 \xB7 \u524D\u5F80 GitHub")
|
||||
"span",
|
||||
{ className: "dim-githubAction" },
|
||||
h2(
|
||||
"a",
|
||||
{
|
||||
className: "dim-githubLink",
|
||||
href: "https://github.com/xmanrui/dsh-im",
|
||||
target: "_blank",
|
||||
rel: "noopener noreferrer",
|
||||
"aria-label": "dsh-im GitHub",
|
||||
"aria-describedby": githubTooltipId
|
||||
},
|
||||
h2("span", null, "GitHub"),
|
||||
h2("span", { className: "dim-githubArrow", "aria-hidden": "true" }, "\u2197")
|
||||
),
|
||||
h2("span", {
|
||||
id: githubTooltipId,
|
||||
className: "dim-githubTooltip",
|
||||
role: "tooltip"
|
||||
}, "\u5E2E\u52A9\u4E0E\u53CD\u9988 \xB7 \u524D\u5F80 GitHub")
|
||||
)
|
||||
)
|
||||
),
|
||||
h2(
|
||||
|
|
@ -10970,6 +11463,7 @@ function apply(ctx) {
|
|||
const whatsappRpcCall = (endpoint, payload, signal) => ctx.connection.rpc.call(WHATSAPP_RPC_CHANNEL, endpoint, payload, signal);
|
||||
const slackRpcCall = (endpoint, payload, signal) => ctx.connection.rpc.call(SLACK_RPC_CHANNEL, endpoint, payload, signal);
|
||||
const officeRpcCall = (endpoint, payload, signal) => ctx.connection.rpc.call(OFFICE_RPC_CHANNEL, endpoint, payload, signal);
|
||||
const updateRpcCall = (endpoint, payload, signal) => ctx.connection.rpc.call(UPDATE_RPC_CHANNEL, endpoint, payload, signal);
|
||||
const workspaceDirectoryPicker = Object.freeze({
|
||||
listDirectory: (path, signal) => ctx.workspaces.listDirectory(path, signal),
|
||||
pickDirectory: () => ctx.workspaces.pickDirectory()
|
||||
|
|
@ -10991,6 +11485,7 @@ function apply(ctx) {
|
|||
weixinRpcCall,
|
||||
whatsappRpcCall,
|
||||
officeRpcCall,
|
||||
updateRpcCall,
|
||||
workspaceDirectoryPicker
|
||||
})
|
||||
}, IMSettingsTab));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue