@xmanrui/dsh-im 4.10.0 → 4.11.0

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.
Files changed (56) hide show
  1. package/PROACTIVE_DELIVERY.en.md +15 -1
  2. package/PROACTIVE_DELIVERY.md +15 -1
  3. package/README.en.md +5 -1
  4. package/README.md +6 -2
  5. package/lib/client.js +1619 -769
  6. package/lib/index.js +248 -244
  7. package/package.json +4 -3
  8. package/plugin-src/client/channel-card-meta.js +9 -1
  9. package/plugin-src/client/channels/dingtalk/api.js +4 -0
  10. package/plugin-src/client/channels/dingtalk/index.js +25 -2
  11. package/plugin-src/client/channels/feishu/api.js +4 -0
  12. package/plugin-src/client/channels/feishu/group-settings.js +581 -0
  13. package/plugin-src/client/channels/feishu/index.js +26 -204
  14. package/plugin-src/client/channels/qq/api.js +4 -0
  15. package/plugin-src/client/channels/qq/index.js +25 -2
  16. package/plugin-src/client/channels/shared/token-api.js +4 -0
  17. package/plugin-src/client/channels/shared/token-channel.js +25 -3
  18. package/plugin-src/client/channels/wecom/api.js +4 -0
  19. package/plugin-src/client/channels/wecom/index.js +25 -2
  20. package/plugin-src/client/channels/weixin/api.js +4 -0
  21. package/plugin-src/client/channels/weixin/index.js +25 -2
  22. package/plugin-src/client/channels/whatsapp/api.js +4 -0
  23. package/plugin-src/client/channels/whatsapp/index.js +24 -2
  24. package/plugin-src/client/delivery-settings.js +73 -5
  25. package/plugin-src/client/i18n.js +20 -0
  26. package/plugin-src/client/model-setting.js +104 -0
  27. package/plugin-src/client/styles.js +50 -0
  28. package/plugin-src/host/channels/dingtalk/production.mjs +3 -0
  29. package/plugin-src/host/channels/dingtalk/rpc.mjs +11 -0
  30. package/plugin-src/host/channels/feishu/production.mjs +3 -0
  31. package/plugin-src/host/channels/feishu/rpc.mjs +16 -0
  32. package/plugin-src/host/channels/qq/production.mjs +3 -0
  33. package/plugin-src/host/channels/qq/rpc.mjs +11 -0
  34. package/plugin-src/host/channels/shared/model-setting-rpc.mjs +19 -0
  35. package/plugin-src/host/channels/shared/production.mjs +3 -0
  36. package/plugin-src/host/channels/shared/rpc.mjs +9 -0
  37. package/plugin-src/host/channels/shared/workspace-rpc.mjs +2 -0
  38. package/plugin-src/host/channels/slack/production.mjs +3 -0
  39. package/plugin-src/host/channels/slack/rpc.mjs +9 -0
  40. package/plugin-src/host/channels/wecom/production.mjs +3 -0
  41. package/plugin-src/host/channels/wecom/rpc.mjs +11 -0
  42. package/plugin-src/host/channels/weixin/production.mjs +3 -0
  43. package/plugin-src/host/channels/weixin/rpc.mjs +11 -0
  44. package/plugin-src/host/channels/whatsapp/production.mjs +3 -0
  45. package/plugin-src/host/channels/whatsapp/rpc.mjs +11 -0
  46. package/plugin-src/host/delivery-adapter.mjs +141 -3
  47. package/plugin-src/host/delivery-rpc.mjs +12 -0
  48. package/plugin-src/host/delivery-service.mjs +110 -3
  49. package/plugin-src/host/delivery-suggestions.mjs +46 -0
  50. package/plugin-src/host/harness-session-coordinator.mjs +6 -5
  51. package/plugin-src/host/index.mjs +18 -1
  52. package/plugin-src/host/session-sync-coordinator.mjs +217 -0
  53. package/scripts/verify-package.mjs +12 -6
  54. package/src/channels/shared/bot-workspace-store.mjs +285 -38
  55. package/src/channels/shared/harness-client.mjs +124 -43
  56. package/src/channels/shared/model-setting.mjs +110 -0
package/lib/client.js CHANGED
@@ -566,12 +566,12 @@ __export(index_exports, {
566
566
  name: () => name
567
567
  });
568
568
  module.exports = __toCommonJS(index_exports);
569
- var React24 = __toESM(require("react"), 1);
569
+ var React26 = __toESM(require("react"), 1);
570
570
 
571
571
  // package.json
572
572
  var package_default = {
573
573
  name: "@xmanrui/dsh-im",
574
- version: "4.10.0",
574
+ version: "4.11.0",
575
575
  description: "\u628A\u4E5D\u79CD IM \u673A\u5668\u4EBA\u548C\u516C\u7F51 AI Office \u63A5\u5165\u672C\u673A DeepSeek Harness\u3002 Connect nine IM channels and a public AI Office to a local DeepSeek Harness.",
576
576
  keywords: [
577
577
  "deepseek-harness",
@@ -660,11 +660,12 @@ var package_default = {
660
660
  platform: "web"
661
661
  },
662
662
  compatibility: {
663
- dsh: "0.1.2-alpha.4 || 0.1.2-alpha.5 || 0.1.2-rc.1",
663
+ dsh: "0.1.2-alpha.4 || 0.1.2-alpha.5 || 0.1.2-rc.1 || 0.1.3-alpha.1",
664
664
  dshReleases: {
665
665
  "0.1.2-alpha.4": "compatible",
666
666
  "0.1.2-alpha.5": "compatible",
667
- "0.1.2-rc.1": "compatible"
667
+ "0.1.2-rc.1": "compatible",
668
+ "0.1.3-alpha.1": "compatible"
668
669
  },
669
670
  profiles: [
670
671
  "web"
@@ -1204,6 +1205,13 @@ var EN = Object.freeze({
1204
1205
  "\u8BF7\u9009\u62E9 Agent Preset\u3002": "Choose an Agent Preset.",
1205
1206
  "Agent Preset \u65E0\u6548\u3002": "The Agent Preset is invalid.",
1206
1207
  "Agent Preset \u4E0D\u5B58\u5728\u6216\u4E0D\u53EF\u7528\u3002": "The Agent Preset does not exist or is unavailable.",
1208
+ "\u6A21\u578B": "Model",
1209
+ "\u67E5\u770B\u6A21\u578B\u8BBE\u7F6E\u8BF4\u660E": "View model setting help",
1210
+ "\u8DDF\u968F\u9ED8\u8BA4\u6A21\u578B": "Follow the default model",
1211
+ "\u6A21\u578B\u4FEE\u6539\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002": "Could not update the model. Try again.",
1212
+ "\u6A21\u578B\u65E0\u6548\u3002": "The model is invalid.",
1213
+ "\u6A21\u578B\u4E0D\u5B58\u5728\u6216\u4E0D\u53EF\u7528\u3002": "The model does not exist or is unavailable.",
1214
+ "\u5F53\u524D\u6A21\u578B\u5DF2\u4E0D\u53EF\u7528\uFF0C\u8BF7\u9009\u62E9\u5176\u4ED6\u6A21\u578B\u6216\u8DDF\u968F\u9ED8\u8BA4\u6A21\u578B\u3002": "The current model is unavailable. Choose another model or follow the default.",
1207
1215
  "\u5C1A\u672A\u68C0\u67E5": "Not checked yet",
1208
1216
  "\u521A\u521A": "Just now",
1209
1217
  "\u68C0\u67E5\u8FDE\u63A5": "Check connection",
@@ -1619,6 +1627,10 @@ var EN = Object.freeze({
1619
1627
  "\u586B\u5199\u63A5\u6536\u6D88\u606F\u7684\u7528\u6237 ID": "Enter the recipient user ID",
1620
1628
  "\u79C1\u804A": "Direct message",
1621
1629
  "\u7FA4\u804A": "Group",
1630
+ "\u7FA4\u804A\u8BBE\u7F6E": "Group settings",
1631
+ "\u5F53\u524D\u673A\u5668\u4EBA": "Current bot",
1632
+ "\u98DE\u4E66\u7FA4\u804A\u8BBE\u7F6E\u6682\u4E0D\u53EF\u7528\u3002": "Feishu group settings are currently unavailable.",
1633
+ "\u98DE\u4E66\u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u5F53\u524D\u673A\u5668\u4EBA\u7684\u8BBE\u7F6E": "Feishu did not return settings for this bot",
1622
1634
  "\u7FA4 Chat ID": "Group Chat ID",
1623
1635
  "\u7528\u6237 ID": "User ID",
1624
1636
  "\u586B\u5199\u9489\u9489\u7528\u6237 ID": "Enter the DingTalk user ID",
@@ -1681,6 +1693,15 @@ var EN = Object.freeze({
1681
1693
  "\uFF1F\u4F7F\u7528\u8FD9\u4E2A targetId \u7684\u5916\u90E8\u8C03\u7528\u5C06\u8FD4\u56DE unknown-target\u3002": "? External calls using this targetId will return unknown-target.",
1682
1694
  "\u6B63\u5728\u5220\u9664\u2026": "Deleting\u2026",
1683
1695
  "\u786E\u8BA4\u5220\u9664": "Delete target",
1696
+ "\u4F1A\u8BDD\u53CC\u5411\u540C\u6B65": "Two-way Session sync",
1697
+ "\u81EA\u52A8\u8DDF\u968F\u8BE5\u79C1\u804A\u7684\u5F53\u524D\u4F1A\u8BDD": "Automatically follow this direct message\u2019s current Session",
1698
+ "\u7B49\u5F85\u8BE5\u79C1\u804A\u5EFA\u7ACB\u65B0\u4F1A\u8BDD": "Waiting for this direct message to establish a new Session",
1699
+ "\u5173\u95ED\u65F6\u4E0D\u53D1\u9001 DSH \u4F1A\u8BDD\u6D88\u606F": "DSH Session messages are not sent while disabled",
1700
+ "\u4EC5\u652F\u6301\u5DF2\u7ECF\u804A\u8FC7\u3001\u5DF2\u5EFA\u7ACB\u5F53\u524D\u4F1A\u8BDD\u4E14\u4F7F\u7528\u5F53\u524D Host Harness \u7684\u79C1\u804A\u76EE\u6807": "Only a known direct message with a current Session on this Host Harness is supported",
1701
+ "\u4F1A\u8BDD\u540C\u6B65\u4EC5\u652F\u6301\u5DF2\u7ECF\u804A\u8FC7\u3001\u5DF2\u5EFA\u7ACB\u5F53\u524D\u4F1A\u8BDD\u4E14\u4F7F\u7528\u5F53\u524D Host Harness \u7684\u79C1\u804A\u76EE\u6807\u3002": "Session sync requires a known direct message with a current Session on this Host Harness.",
1702
+ "\u4F1A\u8BDD\u540C\u6B65\u8BBE\u7F6E\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002": "Could not update Session sync. Try again later.",
1703
+ "\u4FEE\u6539\u63A5\u6536\u4F4D\u7F6E\u4F1A\u5173\u95ED\u4F1A\u8BDD\u53CC\u5411\u540C\u6B65\u3002": "Changing the destination disables two-way Session sync.",
1704
+ " \u4F1A\u8BDD\u540C\u6B65\u8BBE\u7F6E\u4E5F\u4F1A\u4E00\u5E76\u5220\u9664\u3002": " Its Session sync setting will also be deleted.",
1684
1705
  "\u6295\u9012\u76EE\u6807\u8BBE\u7F6E\u6682\u4E0D\u53EF\u7528\u3002": "Delivery target settings are unavailable.",
1685
1706
  "\u65E0\u6CD5\u8BFB\u53D6\u6295\u9012\u76EE\u6807\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002": "Could not load delivery targets. Try again later.",
1686
1707
  "\u2190 \u8FD4\u56DE\u673A\u5668\u4EBA\u5217\u8868": "\u2190 Back to bots",
@@ -1988,6 +2009,186 @@ function AgentPresetEditor({ agentPreset = "", disabled = false, onSave }) {
1988
2009
  );
1989
2010
  }
1990
2011
 
2012
+ // plugin-src/client/model-setting.js
2013
+ var React4 = __toESM(require("react"), 1);
2014
+
2015
+ // src/channels/shared/model-setting.mjs
2016
+ var MAX_PROVIDER_ID_LENGTH = 256;
2017
+ var MAX_MODEL_ID_LENGTH = 1024;
2018
+ var MAX_LABEL_LENGTH = 256;
2019
+ var UNSAFE_DISPLAY_TEXT = /[\p{Cc}\p{Cf}\p{Zl}\p{Zp}]+/gu;
2020
+ var UNSAFE_ID_TEXT = /[\p{Cc}\p{Cf}\p{Zl}\p{Zp}]/u;
2021
+ function cleanId(value, maxLength) {
2022
+ if (typeof value !== "string") return "";
2023
+ const id5 = value.trim();
2024
+ if (!id5 || id5.length > maxLength || UNSAFE_ID_TEXT.test(id5)) return "";
2025
+ return id5;
2026
+ }
2027
+ function cleanLabel(value, fallback) {
2028
+ if (typeof value !== "string") return fallback;
2029
+ const label = value.replace(UNSAFE_DISPLAY_TEXT, " ").replace(/\s+/gu, " ").trim();
2030
+ return label ? label.slice(0, MAX_LABEL_LENGTH) : fallback;
2031
+ }
2032
+ var EMPTY_MODEL_CATALOG = Object.freeze({
2033
+ groups: Object.freeze([]),
2034
+ failures: Object.freeze([])
2035
+ });
2036
+ function normalizeModelSelection(value) {
2037
+ if (value == null) return null;
2038
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
2039
+ const provider = cleanId(value.provider, MAX_PROVIDER_ID_LENGTH);
2040
+ const model = cleanId(value.model, MAX_MODEL_ID_LENGTH);
2041
+ return provider && model ? { provider, model } : null;
2042
+ }
2043
+ function modelSelectionId(value) {
2044
+ const selection = normalizeModelSelection(value);
2045
+ return selection ? `${selection.provider}/${selection.model}` : "";
2046
+ }
2047
+ function normalizeModelCatalog(value) {
2048
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
2049
+ return { groups: [], failures: [] };
2050
+ }
2051
+ const groups = [];
2052
+ const seen = /* @__PURE__ */ new Set();
2053
+ for (const candidate of Array.isArray(value.groups) ? value.groups : []) {
2054
+ if (!candidate || typeof candidate !== "object" || Array.isArray(candidate)) continue;
2055
+ const provider = cleanId(candidate.id, MAX_PROVIDER_ID_LENGTH);
2056
+ if (!provider) continue;
2057
+ const models = [];
2058
+ for (const entry of Array.isArray(candidate.models) ? candidate.models : []) {
2059
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) continue;
2060
+ const model = cleanId(entry.id, MAX_MODEL_ID_LENGTH);
2061
+ if (!model) continue;
2062
+ const key = `${provider}\0${model}`;
2063
+ if (seen.has(key)) continue;
2064
+ seen.add(key);
2065
+ models.push({
2066
+ id: model,
2067
+ name: cleanLabel(entry.name, model)
2068
+ });
2069
+ }
2070
+ if (models.length > 0) {
2071
+ groups.push({
2072
+ id: provider,
2073
+ name: cleanLabel(candidate.name, provider),
2074
+ models
2075
+ });
2076
+ }
2077
+ }
2078
+ const failures = [];
2079
+ for (const candidate of Array.isArray(value.failures) ? value.failures : []) {
2080
+ if (!candidate || typeof candidate !== "object" || Array.isArray(candidate)) continue;
2081
+ const id5 = cleanId(candidate.id, MAX_PROVIDER_ID_LENGTH);
2082
+ if (!id5) continue;
2083
+ failures.push({ id: id5, name: cleanLabel(candidate.name, id5) });
2084
+ }
2085
+ return { groups, failures };
2086
+ }
2087
+
2088
+ // plugin-src/client/model-setting.js
2089
+ var SET_MODEL_ENDPOINT = "bot.model.set";
2090
+ var ModelCatalogContext = React4.createContext(EMPTY_MODEL_CATALOG);
2091
+ function optionValue(selection) {
2092
+ return JSON.stringify([selection.provider, selection.model]);
2093
+ }
2094
+ function ModelEditor({ model = null, disabled = false, onSave }) {
2095
+ const catalog = normalizeModelCatalog(
2096
+ React4.useContext(ModelCatalogContext) ?? EMPTY_MODEL_CATALOG
2097
+ );
2098
+ const helpId = React4.useId();
2099
+ const current = normalizeModelSelection(model);
2100
+ const [saving, setSaving] = React4.useState(false);
2101
+ const [error, setError] = React4.useState(null);
2102
+ const currentValue = current ? optionValue(current) : "";
2103
+ const currentAvailable = !current || catalog.groups.some(
2104
+ (group) => group.id === current.provider && group.models.some((entry) => entry.id === current.model)
2105
+ );
2106
+ const selections = /* @__PURE__ */ new Map();
2107
+ for (const group of catalog.groups) {
2108
+ for (const entry of group.models) {
2109
+ const selection = { provider: group.id, model: entry.id };
2110
+ selections.set(optionValue(selection), selection);
2111
+ }
2112
+ }
2113
+ if (current && !currentAvailable) selections.set(currentValue, current);
2114
+ const change = async (event) => {
2115
+ const nextValue = event.target.value;
2116
+ if (nextValue === currentValue || saving || disabled) return;
2117
+ const next = nextValue ? selections.get(nextValue) : null;
2118
+ if (nextValue && !next) return;
2119
+ setSaving(true);
2120
+ setError(null);
2121
+ try {
2122
+ await onSave?.(next);
2123
+ } catch (cause) {
2124
+ setError(cause?.message ?? "\u6A21\u578B\u4FEE\u6539\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002");
2125
+ } finally {
2126
+ setSaving(false);
2127
+ }
2128
+ };
2129
+ return h2(
2130
+ "div",
2131
+ { className: "dim-preset dim-modelSetting" },
2132
+ h2(
2133
+ "div",
2134
+ { className: "dim-presetHeader" },
2135
+ h2(
2136
+ "span",
2137
+ { className: "dim-presetTitle" },
2138
+ h2("span", null, "\u6A21\u578B"),
2139
+ h2(
2140
+ "span",
2141
+ { className: "dim-presetHelp" },
2142
+ h2("button", {
2143
+ type: "button",
2144
+ className: "dim-presetHelpButton",
2145
+ "aria-label": "\u67E5\u770B\u6A21\u578B\u8BBE\u7F6E\u8BF4\u660E",
2146
+ "aria-describedby": helpId
2147
+ }, h2("span", { "aria-hidden": "true" }, "?")),
2148
+ h2("span", {
2149
+ id: helpId,
2150
+ className: "dim-presetTooltip",
2151
+ role: "tooltip"
2152
+ }, "\u53EA\u5F71\u54CD\u65B0\u5EFA\u4F1A\u8BDD\uFF1B\u82E5\u5F53\u524D\u804A\u5929\u5DF2\u6709\u4F1A\u8BDD\uFF0C\u5148\u53D1\u9001 /new\uFF0C\u518D\u53D1\u9001\u666E\u901A\u6D88\u606F\u751F\u6548\u3002")
2153
+ )
2154
+ ),
2155
+ saving ? h2("span", { className: "dim-presetStatus" }, "\u4FDD\u5B58\u4E2D\u2026") : null
2156
+ ),
2157
+ h2(
2158
+ "select",
2159
+ {
2160
+ className: "dim-presetSelect dim-modelSelect",
2161
+ value: currentValue,
2162
+ disabled: disabled || saving,
2163
+ "aria-label": "\u6A21\u578B",
2164
+ onChange: (event) => {
2165
+ void change(event);
2166
+ }
2167
+ },
2168
+ h2("option", { value: "" }, "\u8DDF\u968F\u9ED8\u8BA4\u6A21\u578B"),
2169
+ ...catalog.groups.map((group) => h2(
2170
+ "optgroup",
2171
+ { key: group.id, label: group.name },
2172
+ ...group.models.map((entry) => {
2173
+ const selection = { provider: group.id, model: entry.id };
2174
+ const id5 = modelSelectionId(selection);
2175
+ return h2(
2176
+ "option",
2177
+ { key: id5, value: optionValue(selection) },
2178
+ entry.name && entry.name !== entry.id ? `${entry.name}\uFF08${id5}\uFF09` : id5
2179
+ );
2180
+ })
2181
+ )),
2182
+ current && !currentAvailable ? h2("option", { value: currentValue }, [modelSelectionId(current), "\uFF08\u5DF2\u4E0D\u53EF\u7528\uFF09"]) : null
2183
+ ),
2184
+ error || !currentAvailable ? h2(
2185
+ "p",
2186
+ { className: "dim-presetError", role: error ? "alert" : "status" },
2187
+ error ?? "\u5F53\u524D\u6A21\u578B\u5DF2\u4E0D\u53EF\u7528\uFF0C\u8BF7\u9009\u62E9\u5176\u4ED6\u6A21\u578B\u6216\u8DDF\u968F\u9ED8\u8BA4\u6A21\u578B\u3002"
2188
+ ) : null
2189
+ );
2190
+ }
2191
+
1991
2192
  // plugin-src/client/last-message-error.js
1992
2193
  function text(value, maxLength) {
1993
2194
  if (typeof value !== "string") return null;
@@ -2241,6 +2442,7 @@ var DINGTALK_ENDPOINTS = Object.freeze({
2241
2442
  reconnectBot: "bot.reconnect",
2242
2443
  deleteBot: "bot.delete",
2243
2444
  setWorkspace: "bot.workspace.set",
2445
+ setModel: SET_MODEL_ENDPOINT,
2244
2446
  setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
2245
2447
  setContextEnhancement: "bot.context-enhancement.set",
2246
2448
  setAccessPolicy: "bot.access-policy.set"
@@ -2386,6 +2588,7 @@ function normalizeBot(value) {
2386
2588
  connected,
2387
2589
  configured: value.configured !== false,
2388
2590
  workspace: optionalString(value.workspace, 4096) ?? "",
2591
+ model: normalizeModelSelection(value.model),
2389
2592
  agentPreset: normalizeAgentPresetId(value.agentPreset),
2390
2593
  contextEnhancement: normalizeContextEnhancementConfig(value.contextEnhancement),
2391
2594
  ...Object.hasOwn(value, "accessPolicy") ? { accessPolicy: normalizeAccessPolicy(value.accessPolicy) } : {},
@@ -2429,7 +2632,8 @@ function normalizeSnapshot(value) {
2429
2632
  },
2430
2633
  provisioning: source.provisioning ? normalizeProvisioning(source.provisioning) : null,
2431
2634
  testMessage: normalizeTestMessage(source.testMessage),
2432
- agentPresetCatalog: normalizeAgentPresetCatalog(source.agentPresetCatalog)
2635
+ agentPresetCatalog: normalizeAgentPresetCatalog(source.agentPresetCatalog),
2636
+ modelCatalog: normalizeModelCatalog(source.modelCatalog)
2433
2637
  };
2434
2638
  }
2435
2639
  function connectionTestFeedback(result) {
@@ -2452,10 +2656,10 @@ function formatRemaining(milliseconds) {
2452
2656
  }
2453
2657
 
2454
2658
  // plugin-src/client/channels/dingtalk/index.js
2455
- var React10 = __toESM(require("react"), 1);
2659
+ var React11 = __toESM(require("react"), 1);
2456
2660
 
2457
2661
  // plugin-src/client/credential-binding.js
2458
- var React4 = __toESM(require("react"), 1);
2662
+ var React5 = __toESM(require("react"), 1);
2459
2663
  function ActionIcon({ children }) {
2460
2664
  return h2("svg", {
2461
2665
  className: "dim-actionIcon",
@@ -2515,9 +2719,9 @@ function CredentialBindingPanel({
2515
2719
  onSubmit,
2516
2720
  onCancel
2517
2721
  }) {
2518
- const [identity, setIdentity] = React4.useState("");
2519
- const [secret, setSecret] = React4.useState("");
2520
- const headingId = React4.useId();
2722
+ const [identity, setIdentity] = React5.useState("");
2723
+ const [secret, setSecret] = React5.useState("");
2724
+ const headingId = React5.useId();
2521
2725
  const hasIdentity = Boolean(identityLabel);
2522
2726
  const submit = (event) => {
2523
2727
  event.preventDefault();
@@ -2596,10 +2800,10 @@ function CredentialBindingPanel({
2596
2800
  }
2597
2801
 
2598
2802
  // plugin-src/client/workspace-editor.js
2599
- var React6 = __toESM(require("react"), 1);
2803
+ var React7 = __toESM(require("react"), 1);
2600
2804
 
2601
2805
  // plugin-src/client/workspace-directory-picker.js
2602
- var React5 = __toESM(require("react"), 1);
2806
+ var React6 = __toESM(require("react"), 1);
2603
2807
  var import_react_dom = require("react-dom");
2604
2808
  function pickerErrorCode(error) {
2605
2809
  return error?.rpcError?.code ?? error?.code;
@@ -2620,7 +2824,7 @@ function pickerErrorMessage(error) {
2620
2824
  return error?.rpcError?.message ?? error?.message ?? "\u65E0\u6CD5\u8BFB\u53D6\u76EE\u5F55\uFF0C\u8BF7\u91CD\u8BD5\u3002";
2621
2825
  }
2622
2826
  function FolderIcon() {
2623
- return React5.createElement(
2827
+ return React6.createElement(
2624
2828
  "svg",
2625
2829
  {
2626
2830
  viewBox: "0 0 24 24",
@@ -2631,11 +2835,11 @@ function FolderIcon() {
2631
2835
  strokeLinejoin: "round",
2632
2836
  "aria-hidden": "true"
2633
2837
  },
2634
- React5.createElement("path", { d: "M3.5 7.25A2.25 2.25 0 0 1 5.75 5h4.1l1.8 2h6.6a2.25 2.25 0 0 1 2.25 2.25v7A2.75 2.75 0 0 1 17.75 19h-12A2.25 2.25 0 0 1 3.5 16.75v-9.5Z" })
2838
+ React6.createElement("path", { d: "M3.5 7.25A2.25 2.25 0 0 1 5.75 5h4.1l1.8 2h6.6a2.25 2.25 0 0 1 2.25 2.25v7A2.75 2.75 0 0 1 17.75 19h-12A2.25 2.25 0 0 1 3.5 16.75v-9.5Z" })
2635
2839
  );
2636
2840
  }
2637
2841
  function ChevronIcon() {
2638
- return React5.createElement("svg", {
2842
+ return React6.createElement("svg", {
2639
2843
  viewBox: "0 0 20 20",
2640
2844
  fill: "none",
2641
2845
  stroke: "currentColor",
@@ -2643,7 +2847,7 @@ function ChevronIcon() {
2643
2847
  strokeLinecap: "round",
2644
2848
  strokeLinejoin: "round",
2645
2849
  "aria-hidden": "true"
2646
- }, React5.createElement("path", { d: "m7.5 4.5 5 5.5-5 5.5" }));
2850
+ }, React6.createElement("path", { d: "m7.5 4.5 5 5.5-5 5.5" }));
2647
2851
  }
2648
2852
  function displayCrumbs(listing) {
2649
2853
  const homeIndex = listing.crumbs.findIndex((crumb) => crumb.path === listing.home);
@@ -2659,28 +2863,28 @@ function WorkspaceDirectoryPicker({
2659
2863
  onPicked,
2660
2864
  onCancel
2661
2865
  }) {
2662
- const [listing, setListing] = React5.useState(null);
2663
- const [loading, setLoading] = React5.useState(false);
2664
- const [error, setError] = React5.useState(null);
2665
- const [pathDraft, setPathDraft] = React5.useState(startPath ?? "");
2666
- const [showHidden, setShowHidden] = React5.useState(false);
2667
- const [retryKey, setRetryKey] = React5.useState(0);
2668
- const requestRef = React5.useRef(0);
2669
- const controllerRef = React5.useRef(null);
2670
- const dialogRef = React5.useRef(null);
2671
- const bodyRef = React5.useRef(null);
2672
- const titleId = React5.useId();
2673
- const noticeId = React5.useId();
2674
- const pathInputId = React5.useId();
2675
- const errorId = React5.useId();
2676
- const initialPathRef = React5.useRef(startPath);
2677
- const onPickedRef = React5.useRef(onPicked);
2678
- const onCancelRef = React5.useRef(onCancel);
2679
- const busyRef = React5.useRef(busy);
2866
+ const [listing, setListing] = React6.useState(null);
2867
+ const [loading, setLoading] = React6.useState(false);
2868
+ const [error, setError] = React6.useState(null);
2869
+ const [pathDraft, setPathDraft] = React6.useState(startPath ?? "");
2870
+ const [showHidden, setShowHidden] = React6.useState(false);
2871
+ const [retryKey, setRetryKey] = React6.useState(0);
2872
+ const requestRef = React6.useRef(0);
2873
+ const controllerRef = React6.useRef(null);
2874
+ const dialogRef = React6.useRef(null);
2875
+ const bodyRef = React6.useRef(null);
2876
+ const titleId = React6.useId();
2877
+ const noticeId = React6.useId();
2878
+ const pathInputId = React6.useId();
2879
+ const errorId = React6.useId();
2880
+ const initialPathRef = React6.useRef(startPath);
2881
+ const onPickedRef = React6.useRef(onPicked);
2882
+ const onCancelRef = React6.useRef(onCancel);
2883
+ const busyRef = React6.useRef(busy);
2680
2884
  onPickedRef.current = onPicked;
2681
2885
  onCancelRef.current = onCancel;
2682
2886
  busyRef.current = busy;
2683
- const loadDirectory = React5.useCallback(async (path, { reportError = true } = {}) => {
2887
+ const loadDirectory = React6.useCallback(async (path, { reportError = true } = {}) => {
2684
2888
  const request = requestRef.current + 1;
2685
2889
  requestRef.current = request;
2686
2890
  controllerRef.current?.abort();
@@ -2704,7 +2908,7 @@ function WorkspaceDirectoryPicker({
2704
2908
  if (request === requestRef.current) setLoading(false);
2705
2909
  }
2706
2910
  }, [picker]);
2707
- React5.useEffect(() => {
2911
+ React6.useEffect(() => {
2708
2912
  if (!open) return void 0;
2709
2913
  let active = true;
2710
2914
  setListing(null);
@@ -2784,10 +2988,10 @@ function WorkspaceDirectoryPicker({
2784
2988
  "nav",
2785
2989
  { className: "dim-directoryCrumbs", "aria-label": "\u5F53\u524D\u76EE\u5F55" },
2786
2990
  crumbs.map((crumb, index) => h2(
2787
- React5.Fragment,
2991
+ React6.Fragment,
2788
2992
  { key: crumb.path },
2789
2993
  index > 0 ? h2("span", { className: "dim-directoryCrumbSeparator", "aria-hidden": "true" }, "\u203A") : null,
2790
- React5.createElement("button", {
2994
+ React6.createElement("button", {
2791
2995
  type: "button",
2792
2996
  title: crumb.path,
2793
2997
  disabled: loading || busy,
@@ -2850,7 +3054,7 @@ function WorkspaceDirectoryPicker({
2850
3054
  ) : listing ? entries.length > 0 ? h2("ul", { className: "dim-directoryList" }, entries.map((entry) => h2(
2851
3055
  "li",
2852
3056
  { key: entry.path },
2853
- React5.createElement(
3057
+ React6.createElement(
2854
3058
  "button",
2855
3059
  {
2856
3060
  type: "button",
@@ -2859,7 +3063,7 @@ function WorkspaceDirectoryPicker({
2859
3063
  onClick: () => void loadDirectory(entry.path)
2860
3064
  },
2861
3065
  h2("span", { className: "dim-directoryFolder" }, h2(FolderIcon)),
2862
- React5.createElement("span", { className: "dim-directoryName" }, entry.name),
3066
+ React6.createElement("span", { className: "dim-directoryName" }, entry.name),
2863
3067
  h2("span", { className: "dim-directoryChevron" }, h2(ChevronIcon))
2864
3068
  )
2865
3069
  ))) : h2(
@@ -2912,21 +3116,21 @@ function WorkspaceDirectoryPicker({
2912
3116
  }
2913
3117
 
2914
3118
  // plugin-src/client/workspace-editor.js
2915
- var WorkspaceDirectoryPickerContext = React6.createContext(null);
3119
+ var WorkspaceDirectoryPickerContext = React7.createContext(null);
2916
3120
  function WorkspaceEditor({ workspace, directoryPicker, disabled = false, onSave }) {
2917
- const sharedDirectoryPicker = React6.useContext(WorkspaceDirectoryPickerContext);
3121
+ const sharedDirectoryPicker = React7.useContext(WorkspaceDirectoryPickerContext);
2918
3122
  const activeDirectoryPicker = directoryPicker ?? sharedDirectoryPicker;
2919
- const [open, setOpen] = React6.useState(false);
2920
- const [saving, setSaving] = React6.useState(false);
2921
- const [error, setError] = React6.useState(null);
2922
- const editButtonRef = React6.useRef(null);
2923
- const savingRef = React6.useRef(false);
2924
- const close = React6.useCallback(() => {
3123
+ const [open, setOpen] = React7.useState(false);
3124
+ const [saving, setSaving] = React7.useState(false);
3125
+ const [error, setError] = React7.useState(null);
3126
+ const editButtonRef = React7.useRef(null);
3127
+ const savingRef = React7.useRef(false);
3128
+ const close = React7.useCallback(() => {
2925
3129
  setOpen(false);
2926
3130
  setError(null);
2927
3131
  queueMicrotask(() => editButtonRef.current?.focus?.());
2928
3132
  }, []);
2929
- const pick = React6.useCallback(async (value) => {
3133
+ const pick = React7.useCallback(async (value) => {
2930
3134
  if (!value || savingRef.current || disabled) return;
2931
3135
  if (value === workspace) {
2932
3136
  close();
@@ -2963,7 +3167,7 @@ function WorkspaceEditor({ workspace, directoryPicker, disabled = false, onSave
2963
3167
  disabled: disabled || !activeDirectoryPicker
2964
3168
  }, "\u9009\u62E9\u76EE\u5F55")
2965
3169
  ),
2966
- workspace ? React6.createElement("code", {
3170
+ workspace ? React7.createElement("code", {
2967
3171
  className: "dim-workspacePath",
2968
3172
  title: workspace
2969
3173
  }, workspace) : h2("code", { className: "dim-workspacePath" }, "\u672A\u8BBE\u7F6E"),
@@ -2980,7 +3184,7 @@ function WorkspaceEditor({ workspace, directoryPicker, disabled = false, onSave
2980
3184
  }
2981
3185
 
2982
3186
  // plugin-src/client/context-enhancement.js
2983
- var React7 = __toESM(require("react"), 1);
3187
+ var React8 = __toESM(require("react"), 1);
2984
3188
  var import_react_dom2 = require("react-dom");
2985
3189
  var FIELD_LABELS = Object.freeze({
2986
3190
  channel: "\u6E20\u9053",
@@ -3225,7 +3429,7 @@ function ContextEnhancementScopeEditor({
3225
3429
  );
3226
3430
  }
3227
3431
  function ContextEnhancementDialog({ config, groupSupported, disabled, onSave, onClose, returnFocusRef, id: id5 }) {
3228
- const [draft, setDraft] = React7.useState(() => {
3432
+ const [draft, setDraft] = React8.useState(() => {
3229
3433
  const normalized = normalizeContextEnhancementConfig(config);
3230
3434
  return {
3231
3435
  ...normalized,
@@ -3234,23 +3438,23 @@ function ContextEnhancementDialog({ config, groupSupported, disabled, onSave, on
3234
3438
  } : {}
3235
3439
  };
3236
3440
  });
3237
- const [saving, setSaving] = React7.useState(false);
3238
- const [error, setError] = React7.useState(null);
3239
- const [activeScope, setActiveScope] = React7.useState("direct");
3240
- const savingRef = React7.useRef(false);
3241
- const dialogRef = React7.useRef(null);
3242
- const mountedRef = React7.useRef(true);
3243
- const groupTabRef = React7.useRef(null);
3244
- const directTabRef = React7.useRef(null);
3245
- const titleId = React7.useId();
3246
- const descriptionId = React7.useId();
3247
- const scopeIdPrefix = React7.useId();
3441
+ const [saving, setSaving] = React8.useState(false);
3442
+ const [error, setError] = React8.useState(null);
3443
+ const [activeScope, setActiveScope] = React8.useState("direct");
3444
+ const savingRef = React8.useRef(false);
3445
+ const dialogRef = React8.useRef(null);
3446
+ const mountedRef = React8.useRef(true);
3447
+ const groupTabRef = React8.useRef(null);
3448
+ const directTabRef = React8.useRef(null);
3449
+ const titleId = React8.useId();
3450
+ const descriptionId = React8.useId();
3451
+ const scopeIdPrefix = React8.useId();
3248
3452
  const groupGuidanceExample = localizeText(CONTEXT_GROUP_GUIDANCE_EXAMPLE);
3249
3453
  const directGuidanceExample = localizeText(CONTEXT_DIRECT_GUIDANCE_EXAMPLE);
3250
3454
  const busy = disabled || saving;
3251
3455
  const scopeKinds = ["direct", "group"];
3252
3456
  const tabRefs = { group: groupTabRef, direct: directTabRef };
3253
- React7.useEffect(() => {
3457
+ React8.useEffect(() => {
3254
3458
  mountedRef.current = true;
3255
3459
  dialogRef.current?.focus?.();
3256
3460
  const keepFocus = (event) => {
@@ -3441,17 +3645,17 @@ function ContextEnhancementDialog({ config, groupSupported, disabled, onSave, on
3441
3645
  return globalThis.document?.body ? (0, import_react_dom2.createPortal)(content, document.body) : content;
3442
3646
  }
3443
3647
  function ContextEnhancementEditor({ config, groupSupported = true, disabled = false, onSave }) {
3444
- const [open, setOpen] = React7.useState(false);
3445
- const entryRef = React7.useRef(null);
3446
- const dialogId = React7.useId();
3447
- const statusId = React7.useId();
3648
+ const [open, setOpen] = React8.useState(false);
3649
+ const entryRef = React8.useRef(null);
3650
+ const dialogId = React8.useId();
3651
+ const statusId = React8.useId();
3448
3652
  const saved = normalizeContextEnhancementConfig(config);
3449
3653
  const label = contextEnhancementLabel(groupSupported ? saved : {
3450
3654
  ...saved,
3451
3655
  group: { ...saved.group, enabled: false }
3452
3656
  });
3453
3657
  return h2(
3454
- React7.Fragment,
3658
+ React8.Fragment,
3455
3659
  null,
3456
3660
  h2(
3457
3661
  "button",
@@ -3485,10 +3689,10 @@ function ContextEnhancementEditor({ config, groupSupported = true, disabled = fa
3485
3689
  }
3486
3690
 
3487
3691
  // plugin-src/client/workspace-snapshot-fence.js
3488
- var React8 = __toESM(require("react"), 1);
3692
+ var React9 = __toESM(require("react"), 1);
3489
3693
  function useWorkspaceSnapshotFence() {
3490
- const state = React8.useRef({ version: 0, pendingMutations: 0 });
3491
- return React8.useMemo(() => Object.freeze({
3694
+ const state = React9.useRef({ version: 0, pendingMutations: 0 });
3695
+ return React9.useMemo(() => Object.freeze({
3492
3696
  beginStatus() {
3493
3697
  return state.current.pendingMutations === 0 ? state.current.version : null;
3494
3698
  },
@@ -3511,8 +3715,8 @@ function useWorkspaceSnapshotFence() {
3511
3715
  }
3512
3716
 
3513
3717
  // plugin-src/client/channel-card-meta.js
3514
- var React9 = __toESM(require("react"), 1);
3515
- var BotSettingsContext = React9.createContext(Object.freeze({
3718
+ var React10 = __toESM(require("react"), 1);
3719
+ var BotSettingsContext = React10.createContext(Object.freeze({
3516
3720
  openBotSettings() {
3517
3721
  }
3518
3722
  }));
@@ -3534,9 +3738,16 @@ function SettingsGlyph() {
3534
3738
  h2("path", { d: "M19.4 15a1.7 1.7 0 0 0 .34 1.88l.06.06-2.83 2.83-.06-.06a1.7 1.7 0 0 0-1.88-.34 1.7 1.7 0 0 0-1.03 1.55V21h-4v-.08A1.7 1.7 0 0 0 8.97 19.4a1.7 1.7 0 0 0-1.88.34l-.06.06-2.83-2.83.06-.06A1.7 1.7 0 0 0 4.6 15a1.7 1.7 0 0 0-1.52-1.03H3v-4h.08A1.7 1.7 0 0 0 4.6 8.97a1.7 1.7 0 0 0-.34-1.88l-.06-.06L7.03 4.2l.06.06a1.7 1.7 0 0 0 1.88.34A1.7 1.7 0 0 0 10 3.08V3h4v.08a1.7 1.7 0 0 0 1.03 1.52 1.7 1.7 0 0 0 1.88-.34l.06-.06 2.83 2.83-.06.06a1.7 1.7 0 0 0-.34 1.88A1.7 1.7 0 0 0 20.92 10H21v4h-.08A1.7 1.7 0 0 0 19.4 15Z" })
3535
3739
  );
3536
3740
  }
3537
- function BotSettingsButton({ channel: channel4, botId, botName, connected, accessPolicy }) {
3538
- const { openBotSettings } = React9.useContext(BotSettingsContext);
3539
- const tooltipId = React9.useId();
3741
+ function BotSettingsButton({
3742
+ channel: channel4,
3743
+ botId,
3744
+ botName,
3745
+ connected,
3746
+ accessPolicy,
3747
+ channelSettings
3748
+ }) {
3749
+ const { openBotSettings } = React10.useContext(BotSettingsContext);
3750
+ const tooltipId = React10.useId();
3540
3751
  return h2(
3541
3752
  "span",
3542
3753
  { className: "dim-botSettingsAction" },
@@ -3547,6 +3758,7 @@ function BotSettingsButton({ channel: channel4, botId, botName, connected, acces
3547
3758
  "aria-label": "\u66F4\u591A\u673A\u5668\u4EBA\u8BBE\u7F6E",
3548
3759
  "aria-describedby": tooltipId,
3549
3760
  onClick: () => openBotSettings?.({
3761
+ ...channelSettings && typeof channelSettings === "object" ? channelSettings : {},
3550
3762
  channel: channel4,
3551
3763
  botId,
3552
3764
  botName,
@@ -3575,7 +3787,7 @@ function messageErrorTime(value) {
3575
3787
  }
3576
3788
  }
3577
3789
  function ChannelListHeading({ className = "", id: id5, title, connectionLabel }) {
3578
- const helpId = React9.useId();
3790
+ const helpId = React10.useId();
3579
3791
  return h2(
3580
3792
  "div",
3581
3793
  { className: `${className} dim-listHeading`.trim() },
@@ -3656,7 +3868,7 @@ function LastMessageErrorSummary({ className = "", error }) {
3656
3868
  h2("span", null, "\u53C2\u8003\u53F7"),
3657
3869
  ` ${error.referenceId}`,
3658
3870
  occurredAt ? h2(
3659
- React9.Fragment,
3871
+ React10.Fragment,
3660
3872
  null,
3661
3873
  " \xB7 ",
3662
3874
  h2("time", { dateTime: new Date(error.at).toISOString() }, occurredAt)
@@ -3819,7 +4031,7 @@ function DingtalkIcon({ size = 28 }) {
3819
4031
  d: "M37.05 22.783c-6.758-5.216-14.378-12.128-22.73-19.538-.655-.585-1.242-.354-1.536.42-1.88 4.973-.058 9.386 2.889 11.932s7.368 4.912 10.058 6.155c.105.049.013.203-.093.163-4.953-2.182-8.397-3.765-13.07-7.368-.497-.388-1.01-.242-1.07.521-.384 4.748 2.657 8.483 6.058 9.745 2.1.781 4.398 1.212 6.53 1.474.109.015.084.178-.027.178-2.747.01-6.058-.654-8.935-1.751-.606-.233-.818.25-.722.633.491 2.008 2.974 5.076 6.926 5.73a12 12 0 0 0 2.228.115c.164 0 .208.089.154.217q-2.685 4.6-2.803 4.797c-.091.152-.036.275.156.275h3.543c.164 0 .264.106.18.246l-4.958 8.196c-.191.328.035.565.395.301s15.212-11.133 15.636-11.448c.195-.142.148-.327-.124-.327h-3.18c-.206 0-.252-.14-.111-.28.14-.141 3.602-3.594 4.837-4.888 1.283-1.35 1.938-3.825-.231-5.498"
3820
4032
  }));
3821
4033
  }
3822
- var Button = React10.forwardRef(function Button2({ children, kind = "secondary", className = "", ...props }, ref) {
4034
+ var Button = React11.forwardRef(function Button2({ children, kind = "secondary", className = "", ...props }, ref) {
3823
4035
  return h2("button", {
3824
4036
  ...props,
3825
4037
  ref,
@@ -3915,13 +4127,13 @@ function EmptyView({ busy, onStart }) {
3915
4127
  );
3916
4128
  }
3917
4129
  function QrPanel({ provision, now, busy, onRefresh, onCancel }) {
3918
- const [imageFailed, setImageFailed] = React10.useState(false);
4130
+ const [imageFailed, setImageFailed] = React11.useState(false);
3919
4131
  const source = safeQrSource(provision.qrCodeDataUrl);
3920
4132
  const remaining = Math.max(0, provision.expiresAt - now);
3921
4133
  const expired = remaining === 0 || provision.status === "expired";
3922
4134
  const duration = Math.max(1, provision.durationMs ?? 10 * 6e4);
3923
4135
  const progress = Math.round(Math.min(1, remaining / duration) * 100);
3924
- React10.useEffect(() => setImageFailed(false), [source]);
4136
+ React11.useEffect(() => setImageFailed(false), [source]);
3925
4137
  return h2(
3926
4138
  "div",
3927
4139
  { className: "ddt-card dim-surfaceCard" },
@@ -4013,7 +4225,7 @@ function ConnectionErrorDiagnostic({ error }) {
4013
4225
  { className: "ddt-errorCode" },
4014
4226
  h2("span", null, "\u9519\u8BEF\u7801"),
4015
4227
  `: ${error.code}`,
4016
- error.referenceId ? h2(React10.Fragment, null, " \xB7 ", h2("span", null, "\u53C2\u8003\u53F7"), `: ${error.referenceId}`) : null
4228
+ error.referenceId ? h2(React11.Fragment, null, " \xB7 ", h2("span", null, "\u53C2\u8003\u53F7"), `: ${error.referenceId}`) : null
4017
4229
  )
4018
4230
  );
4019
4231
  }
@@ -4036,7 +4248,7 @@ function ProvisionError({ provision, busy, onRetry, onClose }) {
4036
4248
  "div",
4037
4249
  { className: "ddt-actions dim-viewActions" },
4038
4250
  connectionFailed ? h2(Button, { kind: "primary", onClick: onClose, disabled: busy }, "\u67E5\u770B\u5DF2\u4FDD\u5B58\u7684\u673A\u5668\u4EBA") : h2(
4039
- React10.Fragment,
4251
+ React11.Fragment,
4040
4252
  null,
4041
4253
  h2(Button, { kind: "primary", onClick: onRetry, disabled: busy }, "\u91CD\u65B0\u751F\u6210\u4E8C\u7EF4\u7801"),
4042
4254
  h2(Button, { onClick: onClose, disabled: busy }, "\u5173\u95ED")
@@ -4058,8 +4270,8 @@ function checkedTime(value) {
4058
4270
  }
4059
4271
  }
4060
4272
  function RemoveConfirmation({ account, busy, onConfirm, onCancel }) {
4061
- const cancelRef = React10.useRef(null);
4062
- React10.useEffect(() => cancelRef.current?.focus(), []);
4273
+ const cancelRef = React11.useRef(null);
4274
+ React11.useEffect(() => cancelRef.current?.focus(), []);
4063
4275
  return h2(
4064
4276
  "div",
4065
4277
  {
@@ -4091,6 +4303,7 @@ function AccountCard({
4091
4303
  removing,
4092
4304
  onReconnect,
4093
4305
  onWorkspaceSave,
4306
+ onModelSave,
4094
4307
  onAgentPresetSave,
4095
4308
  onContextEnhancementSave,
4096
4309
  onRequestRemove,
@@ -4146,6 +4359,11 @@ function AccountCard({
4146
4359
  disabled: Boolean(busy),
4147
4360
  onSave: onWorkspaceSave
4148
4361
  }),
4362
+ h2(ModelEditor, {
4363
+ model: account.model,
4364
+ disabled: Boolean(busy),
4365
+ onSave: onModelSave
4366
+ }),
4149
4367
  h2(AgentPresetEditor, {
4150
4368
  agentPreset: account.agentPreset,
4151
4369
  disabled: Boolean(busy),
@@ -4216,6 +4434,7 @@ function AccountList(props) {
4216
4434
  removing: props.removeTarget === account.botId,
4217
4435
  onReconnect: () => props.onReconnect(account),
4218
4436
  onWorkspaceSave: (workspace) => props.onWorkspaceSave(account, workspace),
4437
+ onModelSave: (model) => props.onModelSave(account, model),
4219
4438
  onAgentPresetSave: (agentPreset) => props.onAgentPresetSave(account, agentPreset),
4220
4439
  onContextEnhancementSave: (config) => props.onContextEnhancementSave(account, config),
4221
4440
  onRequestRemove: () => props.onRequestRemove(account),
@@ -4227,30 +4446,31 @@ function AccountList(props) {
4227
4446
  }
4228
4447
  var EMPTY_TOTALS = Object.freeze({ configured: 0, connected: 0 });
4229
4448
  function DingtalkSettingsTab({ rpcCall }) {
4230
- const [model, setModel] = React10.useState({
4449
+ const [model, setModel] = React11.useState({
4231
4450
  phase: "loading",
4232
4451
  bots: [],
4233
4452
  totals: EMPTY_TOTALS,
4234
4453
  revision: 0,
4235
4454
  error: null,
4236
- agentPresetCatalog: EMPTY_AGENT_PRESET_CATALOG
4455
+ agentPresetCatalog: EMPTY_AGENT_PRESET_CATALOG,
4456
+ modelCatalog: EMPTY_MODEL_CATALOG
4237
4457
  });
4238
- const [provision, setProvision] = React10.useState(null);
4239
- const [busy, setBusy] = React10.useState(false);
4240
- const [busyByBot, setBusyByBot] = React10.useState({});
4241
- const [feedbackByBot, setFeedbackByBot] = React10.useState({});
4242
- const [removeTarget, setRemoveTarget] = React10.useState(null);
4243
- const [credentialOpen, setCredentialOpen] = React10.useState(false);
4244
- const [credentialError, setCredentialError] = React10.useState(null);
4245
- const [notice, setNotice] = React10.useState("");
4246
- const [now, setNow] = React10.useState(() => Date.now());
4247
- const addButtonRef = React10.useRef(null);
4248
- const mountedRef = React10.useRef(true);
4249
- const statusRequestRef = React10.useRef(0);
4458
+ const [provision, setProvision] = React11.useState(null);
4459
+ const [busy, setBusy] = React11.useState(false);
4460
+ const [busyByBot, setBusyByBot] = React11.useState({});
4461
+ const [feedbackByBot, setFeedbackByBot] = React11.useState({});
4462
+ const [removeTarget, setRemoveTarget] = React11.useState(null);
4463
+ const [credentialOpen, setCredentialOpen] = React11.useState(false);
4464
+ const [credentialError, setCredentialError] = React11.useState(null);
4465
+ const [notice, setNotice] = React11.useState("");
4466
+ const [now, setNow] = React11.useState(() => Date.now());
4467
+ const addButtonRef = React11.useRef(null);
4468
+ const mountedRef = React11.useRef(true);
4469
+ const statusRequestRef = React11.useRef(0);
4250
4470
  const workspaceFence = useWorkspaceSnapshotFence();
4251
- const noticeFrameRef = React10.useRef(null);
4252
- const focusFrameRef = React10.useRef(null);
4253
- React10.useEffect(() => {
4471
+ const noticeFrameRef = React11.useRef(null);
4472
+ const focusFrameRef = React11.useRef(null);
4473
+ React11.useEffect(() => {
4254
4474
  mountedRef.current = true;
4255
4475
  return () => {
4256
4476
  mountedRef.current = false;
@@ -4265,8 +4485,8 @@ function DingtalkSettingsTab({ rpcCall }) {
4265
4485
  }
4266
4486
  };
4267
4487
  }, []);
4268
- React10.useEffect(() => installDingtalkStyles(), []);
4269
- const announce = React10.useCallback((message) => {
4488
+ React11.useEffect(() => installDingtalkStyles(), []);
4489
+ const announce = React11.useCallback((message) => {
4270
4490
  if (!mountedRef.current) return;
4271
4491
  if (noticeFrameRef.current !== null) {
4272
4492
  window.cancelAnimationFrame(noticeFrameRef.current);
@@ -4280,7 +4500,7 @@ function DingtalkSettingsTab({ rpcCall }) {
4280
4500
  });
4281
4501
  }
4282
4502
  }, []);
4283
- const discardStaleFeedback = React10.useCallback((snapshot) => {
4503
+ const discardStaleFeedback = React11.useCallback((snapshot) => {
4284
4504
  const botsById = new Map(snapshot.bots.map((bot) => [bot.botId, bot]));
4285
4505
  setFeedbackByBot((current) => {
4286
4506
  let changed = false;
@@ -4295,7 +4515,7 @@ function DingtalkSettingsTab({ rpcCall }) {
4295
4515
  return changed ? next : current;
4296
4516
  });
4297
4517
  }, []);
4298
- const focusAddButton = React10.useCallback(() => {
4518
+ const focusAddButton = React11.useCallback(() => {
4299
4519
  if (!mountedRef.current) return;
4300
4520
  if (focusFrameRef.current !== null) window.cancelAnimationFrame(focusFrameRef.current);
4301
4521
  focusFrameRef.current = window.requestAnimationFrame(() => {
@@ -4303,11 +4523,11 @@ function DingtalkSettingsTab({ rpcCall }) {
4303
4523
  if (mountedRef.current) addButtonRef.current?.focus();
4304
4524
  });
4305
4525
  }, []);
4306
- const invoke = React10.useCallback(async (endpoint, payload = {}, signal) => {
4526
+ const invoke = React11.useCallback(async (endpoint, payload = {}, signal) => {
4307
4527
  if (typeof rpcCall !== "function") throw new TypeError("\u9489\u9489\u8BBE\u7F6E\u9875\u7F3A\u5C11 RPC \u8FDE\u63A5");
4308
4528
  return unwrapRpcResult(await rpcCall(endpoint, payload, signal));
4309
4529
  }, [rpcCall]);
4310
- const loadStatus = React10.useCallback(async ({
4530
+ const loadStatus = React11.useCallback(async ({
4311
4531
  signal,
4312
4532
  silent = false,
4313
4533
  restoreProvisioning = false
@@ -4330,7 +4550,8 @@ function DingtalkSettingsTab({ rpcCall }) {
4330
4550
  totals: snapshot.totals,
4331
4551
  revision: snapshot.revision,
4332
4552
  error: null,
4333
- agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
4553
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
4554
+ modelCatalog: snapshot.modelCatalog ?? EMPTY_MODEL_CATALOG
4334
4555
  });
4335
4556
  discardStaleFeedback(snapshot);
4336
4557
  if (restoreProvisioning && snapshot.provisioning) {
@@ -4351,12 +4572,12 @@ function DingtalkSettingsTab({ rpcCall }) {
4351
4572
  return void 0;
4352
4573
  }
4353
4574
  }, [discardStaleFeedback, invoke, workspaceFence]);
4354
- React10.useEffect(() => {
4575
+ React11.useEffect(() => {
4355
4576
  const controller = new AbortController();
4356
4577
  void loadStatus({ signal: controller.signal, restoreProvisioning: true });
4357
4578
  return () => controller.abort();
4358
4579
  }, [loadStatus]);
4359
- React10.useEffect(() => {
4580
+ React11.useEffect(() => {
4360
4581
  if (model.phase !== "ready") return void 0;
4361
4582
  const controller = new AbortController();
4362
4583
  let running = false;
@@ -4375,14 +4596,14 @@ function DingtalkSettingsTab({ rpcCall }) {
4375
4596
  window.clearInterval(timer);
4376
4597
  };
4377
4598
  }, [loadStatus, model.phase]);
4378
- React10.useEffect(() => {
4599
+ React11.useEffect(() => {
4379
4600
  if (!provision || !ACTIVE_PROVISION_STATES.has(provision.status)) return void 0;
4380
4601
  const timer = window.setInterval(() => {
4381
4602
  if (mountedRef.current) setNow(Date.now());
4382
4603
  }, 1e3);
4383
4604
  return () => window.clearInterval(timer);
4384
4605
  }, [provision?.attemptId, provision?.status]);
4385
- const startProvisioning = React10.useCallback(async ({ replace = false } = {}) => {
4606
+ const startProvisioning = React11.useCallback(async ({ replace = false } = {}) => {
4386
4607
  if (!mountedRef.current) return;
4387
4608
  setCredentialOpen(false);
4388
4609
  setCredentialError(null);
@@ -4420,7 +4641,7 @@ function DingtalkSettingsTab({ rpcCall }) {
4420
4641
  if (mountedRef.current) setBusy(false);
4421
4642
  }
4422
4643
  }, [announce, invoke, provision?.attemptId]);
4423
- const bindCredentials = React10.useCallback(async ({ identity, secret }) => {
4644
+ const bindCredentials = React11.useCallback(async ({ identity, secret }) => {
4424
4645
  if (!mountedRef.current) return;
4425
4646
  const snapshotVersion = workspaceFence.beginMutation();
4426
4647
  setBusy(true);
@@ -4438,7 +4659,8 @@ function DingtalkSettingsTab({ rpcCall }) {
4438
4659
  totals: snapshot.totals,
4439
4660
  revision: snapshot.revision,
4440
4661
  error: null,
4441
- agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
4662
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
4663
+ modelCatalog: snapshot.modelCatalog ?? EMPTY_MODEL_CATALOG
4442
4664
  });
4443
4665
  discardStaleFeedback(snapshot);
4444
4666
  }
@@ -4452,7 +4674,7 @@ function DingtalkSettingsTab({ rpcCall }) {
4452
4674
  if (mountedRef.current) setBusy(false);
4453
4675
  }
4454
4676
  }, [announce, discardStaleFeedback, invoke, loadStatus, workspaceFence]);
4455
- const cancelProvisioning = React10.useCallback(async () => {
4677
+ const cancelProvisioning = React11.useCallback(async () => {
4456
4678
  if (!mountedRef.current) return;
4457
4679
  setBusy(true);
4458
4680
  try {
@@ -4470,7 +4692,7 @@ function DingtalkSettingsTab({ rpcCall }) {
4470
4692
  if (mountedRef.current) setBusy(false);
4471
4693
  }
4472
4694
  }, [announce, focusAddButton, invoke, provision?.attemptId, provision?.status]);
4473
- React10.useEffect(() => {
4695
+ React11.useEffect(() => {
4474
4696
  const attemptId = provision?.attemptId;
4475
4697
  if (!attemptId || !ACTIVE_PROVISION_STATES.has(provision.status)) return void 0;
4476
4698
  const controller = new AbortController();
@@ -4533,7 +4755,7 @@ function DingtalkSettingsTab({ rpcCall }) {
4533
4755
  timer = null;
4534
4756
  };
4535
4757
  }, [announce, invoke, loadStatus, provision?.attemptId, provision?.pollIntervalMs, provision?.status]);
4536
- const setBotBusy = React10.useCallback((botId, operation) => {
4758
+ const setBotBusy = React11.useCallback((botId, operation) => {
4537
4759
  if (!mountedRef.current) return;
4538
4760
  setBusyByBot((current) => {
4539
4761
  const next = { ...current };
@@ -4542,7 +4764,7 @@ function DingtalkSettingsTab({ rpcCall }) {
4542
4764
  return next;
4543
4765
  });
4544
4766
  }, []);
4545
- const runBotAction = React10.useCallback(async ({ account, operation, endpoint, payload, success }) => {
4767
+ const runBotAction = React11.useCallback(async ({ account, operation, endpoint, payload, success }) => {
4546
4768
  if (!mountedRef.current) return void 0;
4547
4769
  const snapshotVersion = workspaceFence.beginMutation();
4548
4770
  setBotBusy(account.botId, operation);
@@ -4563,7 +4785,8 @@ function DingtalkSettingsTab({ rpcCall }) {
4563
4785
  totals: snapshot.totals,
4564
4786
  revision: snapshot.revision,
4565
4787
  error: null,
4566
- agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
4788
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
4789
+ modelCatalog: snapshot.modelCatalog ?? EMPTY_MODEL_CATALOG
4567
4790
  });
4568
4791
  discardStaleFeedback(snapshot);
4569
4792
  }
@@ -4597,7 +4820,7 @@ function DingtalkSettingsTab({ rpcCall }) {
4597
4820
  if (mountedRef.current) setBotBusy(account.botId, null);
4598
4821
  }
4599
4822
  }, [announce, discardStaleFeedback, invoke, loadStatus, setBotBusy, workspaceFence]);
4600
- const reconnect = React10.useCallback((account) => runBotAction({
4823
+ const reconnect = React11.useCallback((account) => runBotAction({
4601
4824
  account,
4602
4825
  operation: "reconnect",
4603
4826
  endpoint: DINGTALK_ENDPOINTS.reconnectBot,
@@ -4608,7 +4831,7 @@ function DingtalkSettingsTab({ rpcCall }) {
4608
4831
  return connectionTestFeedback(snapshot.testMessage) ?? "\u9489\u9489\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\u3002";
4609
4832
  }
4610
4833
  }), [runBotAction]);
4611
- const saveWorkspace = React10.useCallback(async (account, workspace) => {
4834
+ const saveWorkspace = React11.useCallback(async (account, workspace) => {
4612
4835
  const workspaceVersion = workspaceFence.beginMutation();
4613
4836
  setBotBusy(account.botId, "workspace");
4614
4837
  try {
@@ -4623,7 +4846,8 @@ function DingtalkSettingsTab({ rpcCall }) {
4623
4846
  totals: snapshot.totals,
4624
4847
  revision: snapshot.revision,
4625
4848
  error: null,
4626
- agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
4849
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
4850
+ modelCatalog: snapshot.modelCatalog ?? EMPTY_MODEL_CATALOG
4627
4851
  });
4628
4852
  discardStaleFeedback(snapshot);
4629
4853
  }
@@ -4633,7 +4857,7 @@ function DingtalkSettingsTab({ rpcCall }) {
4633
4857
  if (mountedRef.current) setBotBusy(account.botId, null);
4634
4858
  }
4635
4859
  }, [discardStaleFeedback, invoke, loadStatus, setBotBusy, workspaceFence]);
4636
- const saveBotSetting = React10.useCallback(async (account, operation, endpoint, payload) => {
4860
+ const saveBotSetting = React11.useCallback(async (account, operation, endpoint, payload) => {
4637
4861
  const snapshotVersion = workspaceFence.beginMutation();
4638
4862
  setBotBusy(account.botId, operation);
4639
4863
  try {
@@ -4648,7 +4872,8 @@ function DingtalkSettingsTab({ rpcCall }) {
4648
4872
  totals: snapshot.totals,
4649
4873
  revision: snapshot.revision,
4650
4874
  error: null,
4651
- agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
4875
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
4876
+ modelCatalog: snapshot.modelCatalog ?? EMPTY_MODEL_CATALOG
4652
4877
  });
4653
4878
  discardStaleFeedback(snapshot);
4654
4879
  }
@@ -4658,7 +4883,7 @@ function DingtalkSettingsTab({ rpcCall }) {
4658
4883
  if (mountedRef.current) setBotBusy(account.botId, null);
4659
4884
  }
4660
4885
  }, [discardStaleFeedback, invoke, loadStatus, setBotBusy, workspaceFence]);
4661
- const remove = React10.useCallback(async (account) => {
4886
+ const remove = React11.useCallback(async (account) => {
4662
4887
  const snapshot = await runBotAction({
4663
4888
  account,
4664
4889
  operation: "delete",
@@ -4712,7 +4937,9 @@ function DingtalkSettingsTab({ rpcCall }) {
4712
4937
  setCredentialError(null);
4713
4938
  }
4714
4939
  }) : null;
4715
- return h2(AgentPresetCatalogContext.Provider, {
4940
+ return h2(ModelCatalogContext.Provider, {
4941
+ value: model.modelCatalog ?? EMPTY_MODEL_CATALOG
4942
+ }, h2(AgentPresetCatalogContext.Provider, {
4716
4943
  value: model.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
4717
4944
  }, h2(
4718
4945
  "section",
@@ -4742,7 +4969,7 @@ function DingtalkSettingsTab({ rpcCall }) {
4742
4969
  h2(Button, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6")
4743
4970
  )
4744
4971
  ) : h2(
4745
- React10.Fragment,
4972
+ React11.Fragment,
4746
4973
  null,
4747
4974
  credentialView,
4748
4975
  provisionView,
@@ -4754,6 +4981,12 @@ function DingtalkSettingsTab({ rpcCall }) {
4754
4981
  removeTarget,
4755
4982
  onReconnect: (account) => void reconnect(account),
4756
4983
  onWorkspaceSave: saveWorkspace,
4984
+ onModelSave: (account, selectedModel) => saveBotSetting(
4985
+ account,
4986
+ "model",
4987
+ DINGTALK_ENDPOINTS.setModel,
4988
+ { model: selectedModel }
4989
+ ),
4757
4990
  onAgentPresetSave: (account, agentPreset) => saveBotSetting(
4758
4991
  account,
4759
4992
  "preset",
@@ -4771,7 +5004,7 @@ function DingtalkSettingsTab({ rpcCall }) {
4771
5004
  onCancelRemove: () => setRemoveTarget(null)
4772
5005
  }) : null
4773
5006
  )
4774
- ));
5007
+ )));
4775
5008
  }
4776
5009
 
4777
5010
  // plugin-src/client/channels/shared/token-api.js
@@ -4797,6 +5030,7 @@ var TOKEN_BOT_ENDPOINTS = Object.freeze({
4797
5030
  reconnectBot: "bot.reconnect",
4798
5031
  deleteBot: "bot.delete",
4799
5032
  setWorkspace: "bot.workspace.set",
5033
+ setModel: SET_MODEL_ENDPOINT,
4800
5034
  setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
4801
5035
  setContextEnhancement: "bot.context-enhancement.set",
4802
5036
  setAccessPolicy: "bot.access-policy.set"
@@ -4825,6 +5059,7 @@ function createTokenChannelApi(channel4, connectionSummary, {
4825
5059
  connected,
4826
5060
  state: connected ? "connected" : state,
4827
5061
  workspace: text2(value.workspace, "", 4096),
5062
+ model: normalizeModelSelection(value.model),
4828
5063
  agentPreset: normalizeAgentPresetId(value.agentPreset),
4829
5064
  contextEnhancement: normalizeContextEnhancementConfig(value.contextEnhancement),
4830
5065
  ...Object.hasOwn(value, "accessPolicy") ? { accessPolicy: normalizeAccessPolicy(value.accessPolicy) } : {},
@@ -4858,7 +5093,8 @@ function createTokenChannelApi(channel4, connectionSummary, {
4858
5093
  revision: Number.isSafeInteger(source.revision) ? source.revision : 0,
4859
5094
  bots,
4860
5095
  totals: { configured: bots.length, connected: bots.filter((bot) => bot.connected).length },
4861
- agentPresetCatalog: normalizeAgentPresetCatalog(source.agentPresetCatalog)
5096
+ agentPresetCatalog: normalizeAgentPresetCatalog(source.agentPresetCatalog),
5097
+ modelCatalog: normalizeModelCatalog(source.modelCatalog)
4862
5098
  };
4863
5099
  };
4864
5100
  const presentError13 = (error) => ({
@@ -4877,8 +5113,8 @@ var normalizeSnapshot2 = api.normalizeSnapshot;
4877
5113
  var presentError2 = api.presentError;
4878
5114
 
4879
5115
  // plugin-src/client/channels/shared/token-channel.js
4880
- var React11 = __toESM(require("react"), 1);
4881
- var Button3 = React11.forwardRef(function Button4({ children, kind = "secondary", className = "", ...props }, ref) {
5116
+ var React12 = __toESM(require("react"), 1);
5117
+ var Button3 = React12.forwardRef(function Button4({ children, kind = "secondary", className = "", ...props }, ref) {
4882
5118
  return h2("button", {
4883
5119
  ...props,
4884
5120
  ref,
@@ -4930,7 +5166,7 @@ function createTokenChannelSettings(definition) {
4930
5166
  AccountSettings = null,
4931
5167
  accountSettingsEndpoint = null
4932
5168
  } = definition;
4933
- function AccountCard5({ account, busy, testNotice, removing, onReconnect, onWorkspaceSave, onAgentPresetSave, onContextEnhancementSave, onAccountSettingsSave, onRequestRemove, onConfirmRemove, onCancelRemove }) {
5169
+ function AccountCard5({ account, busy, testNotice, removing, onReconnect, onWorkspaceSave, onModelSave, onAgentPresetSave, onContextEnhancementSave, onAccountSettingsSave, onRequestRemove, onConfirmRemove, onCancelRemove }) {
4934
5170
  const state = busy === "reconnect" ? "connecting" : account.state;
4935
5171
  const tone = account.connected ? "success" : state === "error" ? "error" : "warning";
4936
5172
  const stateLabel2 = account.connected ? "\u8FD0\u884C\u6B63\u5E38" : state === "connecting" ? "\u6B63\u5728\u8FDE\u63A5" : "\u8FDE\u63A5\u672A\u5C31\u7EEA";
@@ -4985,6 +5221,11 @@ function createTokenChannelSettings(definition) {
4985
5221
  disabled: Boolean(busy),
4986
5222
  onSave: onWorkspaceSave
4987
5223
  }),
5224
+ h2(ModelEditor, {
5225
+ model: account.model,
5226
+ disabled: Boolean(busy),
5227
+ onSave: onModelSave
5228
+ }),
4988
5229
  h2(AgentPresetEditor, {
4989
5230
  agentPreset: account.agentPreset,
4990
5231
  disabled: Boolean(busy),
@@ -5052,22 +5293,23 @@ function createTokenChannelSettings(definition) {
5052
5293
  );
5053
5294
  }
5054
5295
  function SettingsTab({ rpcCall }) {
5055
- const [model, setModel] = React11.useState({
5296
+ const [model, setModel] = React12.useState({
5056
5297
  phase: "loading",
5057
5298
  bots: [],
5058
5299
  totals: { configured: 0, connected: 0 },
5059
5300
  error: null,
5060
- agentPresetCatalog: EMPTY_AGENT_PRESET_CATALOG
5301
+ agentPresetCatalog: EMPTY_AGENT_PRESET_CATALOG,
5302
+ modelCatalog: EMPTY_MODEL_CATALOG
5061
5303
  });
5062
- const [credentialOpen, setCredentialOpen] = React11.useState(false);
5063
- const [credentialError, setCredentialError] = React11.useState(null);
5064
- const [busy, setBusy] = React11.useState(false);
5065
- const [busyByBot, setBusyByBot] = React11.useState({});
5066
- const [testNoticeByBot, setTestNoticeByBot] = React11.useState({});
5067
- const [removeTarget, setRemoveTarget] = React11.useState(null);
5068
- const mounted = React11.useRef(true);
5304
+ const [credentialOpen, setCredentialOpen] = React12.useState(false);
5305
+ const [credentialError, setCredentialError] = React12.useState(null);
5306
+ const [busy, setBusy] = React12.useState(false);
5307
+ const [busyByBot, setBusyByBot] = React12.useState({});
5308
+ const [testNoticeByBot, setTestNoticeByBot] = React12.useState({});
5309
+ const [removeTarget, setRemoveTarget] = React12.useState(null);
5310
+ const mounted = React12.useRef(true);
5069
5311
  const workspaceFence = useWorkspaceSnapshotFence();
5070
- React11.useEffect(() => {
5312
+ React12.useEffect(() => {
5071
5313
  const disposeDingtalk = installDingtalkStyles();
5072
5314
  const disposeChannel = installStyles();
5073
5315
  mounted.current = true;
@@ -5077,11 +5319,11 @@ function createTokenChannelSettings(definition) {
5077
5319
  disposeDingtalk();
5078
5320
  };
5079
5321
  }, []);
5080
- const invoke = React11.useCallback(async (endpoint, payload = {}, signal) => {
5322
+ const invoke = React12.useCallback(async (endpoint, payload = {}, signal) => {
5081
5323
  if (typeof rpcCall !== "function") throw new TypeError(`${channel4} \u8BBE\u7F6E\u9875\u7F3A\u5C11 RPC \u8FDE\u63A5`);
5082
5324
  return api4.unwrapRpcResult(await rpcCall(endpoint, payload, signal));
5083
5325
  }, [rpcCall]);
5084
- const loadStatus = React11.useCallback(async ({ signal, silent = false } = {}) => {
5326
+ const loadStatus = React12.useCallback(async ({ signal, silent = false } = {}) => {
5085
5327
  const workspaceVersion = workspaceFence.beginStatus();
5086
5328
  if (workspaceVersion === null) return;
5087
5329
  if (!silent && mounted.current) setModel((current) => ({ ...current, phase: "loading", error: null }));
@@ -5093,7 +5335,8 @@ function createTokenChannelSettings(definition) {
5093
5335
  bots: snapshot.bots,
5094
5336
  totals: snapshot.totals,
5095
5337
  error: null,
5096
- agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
5338
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
5339
+ modelCatalog: snapshot.modelCatalog ?? EMPTY_MODEL_CATALOG
5097
5340
  });
5098
5341
  } catch (error) {
5099
5342
  if (error?.name !== "AbortError" && mounted.current && !signal?.aborted && workspaceFence.canCommitStatus(workspaceVersion)) {
@@ -5105,12 +5348,12 @@ function createTokenChannelSettings(definition) {
5105
5348
  }
5106
5349
  }
5107
5350
  }, [invoke, workspaceFence]);
5108
- React11.useEffect(() => {
5351
+ React12.useEffect(() => {
5109
5352
  const controller = new AbortController();
5110
5353
  void loadStatus({ signal: controller.signal });
5111
5354
  return () => controller.abort();
5112
5355
  }, [loadStatus]);
5113
- React11.useEffect(() => {
5356
+ React12.useEffect(() => {
5114
5357
  if (model.phase !== "ready") return void 0;
5115
5358
  const controller = new AbortController();
5116
5359
  const timer = window.setInterval(
@@ -5122,7 +5365,7 @@ function createTokenChannelSettings(definition) {
5122
5365
  window.clearInterval(timer);
5123
5366
  };
5124
5367
  }, [loadStatus, model.phase]);
5125
- const bindCredentials = React11.useCallback(async (values) => {
5368
+ const bindCredentials = React12.useCallback(async (values) => {
5126
5369
  const snapshotVersion = workspaceFence.beginMutation();
5127
5370
  setBusy(true);
5128
5371
  setCredentialError(null);
@@ -5138,7 +5381,8 @@ function createTokenChannelSettings(definition) {
5138
5381
  bots: snapshot.bots,
5139
5382
  totals: snapshot.totals,
5140
5383
  error: null,
5141
- agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
5384
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
5385
+ modelCatalog: snapshot.modelCatalog ?? EMPTY_MODEL_CATALOG
5142
5386
  });
5143
5387
  }
5144
5388
  setCredentialOpen(false);
@@ -5150,7 +5394,7 @@ function createTokenChannelSettings(definition) {
5150
5394
  if (mounted.current) setBusy(false);
5151
5395
  }
5152
5396
  }, [invoke, loadStatus, workspaceFence]);
5153
- const botAction = React11.useCallback(async (account, operation, endpoint, payload) => {
5397
+ const botAction = React12.useCallback(async (account, operation, endpoint, payload) => {
5154
5398
  const snapshotVersion = workspaceFence.beginMutation();
5155
5399
  setBusyByBot((current) => ({ ...current, [account.botId]: operation }));
5156
5400
  try {
@@ -5162,7 +5406,8 @@ function createTokenChannelSettings(definition) {
5162
5406
  bots: snapshot.bots,
5163
5407
  totals: snapshot.totals,
5164
5408
  error: null,
5165
- agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
5409
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
5410
+ modelCatalog: snapshot.modelCatalog ?? EMPTY_MODEL_CATALOG
5166
5411
  });
5167
5412
  }
5168
5413
  if (mounted.current && operation === "reconnect") {
@@ -5214,6 +5459,12 @@ function createTokenChannelSettings(definition) {
5214
5459
  endpoints.setWorkspace,
5215
5460
  { botId: account.botId, workspace }
5216
5461
  ),
5462
+ onModelSave: (selectedModel) => botAction(
5463
+ account,
5464
+ "model",
5465
+ endpoints.setModel,
5466
+ { botId: account.botId, model: selectedModel }
5467
+ ),
5217
5468
  onAgentPresetSave: (agentPreset) => botAction(
5218
5469
  account,
5219
5470
  "preset",
@@ -5243,7 +5494,9 @@ function createTokenChannelSettings(definition) {
5243
5494
  }
5244
5495
  }))))
5245
5496
  ) : null;
5246
- return h2(AgentPresetCatalogContext.Provider, {
5497
+ return h2(ModelCatalogContext.Provider, {
5498
+ value: model.modelCatalog ?? EMPTY_MODEL_CATALOG
5499
+ }, h2(AgentPresetCatalogContext.Provider, {
5247
5500
  value: model.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
5248
5501
  }, h2(
5249
5502
  "section",
@@ -5293,7 +5546,7 @@ function createTokenChannelSettings(definition) {
5293
5546
  h2(Button3, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6")
5294
5547
  )
5295
5548
  ) : h2(
5296
- React11.Fragment,
5549
+ React12.Fragment,
5297
5550
  null,
5298
5551
  credentialOpen ? CredentialPanel ? h2(CredentialPanel, {
5299
5552
  busy,
@@ -5349,7 +5602,7 @@ function createTokenChannelSettings(definition) {
5349
5602
  ) : null,
5350
5603
  botList
5351
5604
  )
5352
- ));
5605
+ )));
5353
5606
  }
5354
5607
  return { SettingsTab, AccountCard: AccountCard5 };
5355
5608
  }
@@ -5394,7 +5647,7 @@ var DiscordSettingsTab = channel.SettingsTab;
5394
5647
  var DiscordAccountCard = channel.AccountCard;
5395
5648
 
5396
5649
  // plugin-src/client/channels/feishu/index.js
5397
- var React13 = __toESM(require("react"), 1);
5650
+ var React14 = __toESM(require("react"), 1);
5398
5651
 
5399
5652
  // plugin-src/client/channels/feishu/api.js
5400
5653
  var FEISHU_RPC_CHANNEL = "/feishu";
@@ -5410,6 +5663,7 @@ var FEISHU_ENDPOINTS = Object.freeze({
5410
5663
  disconnectBot: "bot.disconnect",
5411
5664
  deleteBot: "bot.delete",
5412
5665
  setWorkspace: "bot.workspace.set",
5666
+ setModel: SET_MODEL_ENDPOINT,
5413
5667
  setAgentPreset: "bot.preset.set",
5414
5668
  setContextEnhancement: "bot.context-enhancement.set",
5415
5669
  setAccessPolicy: "bot.access-policy.set",
@@ -5561,6 +5815,7 @@ function normalizeBotConnection(value, fallbackBotId) {
5561
5815
  connected,
5562
5816
  configured: value.configured !== false,
5563
5817
  workspace: optionalString2(value.workspace)?.slice(0, 4096) ?? "",
5818
+ model: normalizeModelSelection(value.model),
5564
5819
  agentPreset: normalizeAgentPresetId(value.agentPreset),
5565
5820
  contextEnhancement: normalizeContextEnhancementConfig(value.contextEnhancement),
5566
5821
  ...Object.hasOwn(value, "accessPolicy") ? { accessPolicy: normalizeAccessPolicy(value.accessPolicy) } : {},
@@ -5610,7 +5865,8 @@ function normalizeBotsSnapshot(value) {
5610
5865
  totals: { configured, connected },
5611
5866
  provisioning: value.provisioning ? normalizeProvisioning2(value.provisioning) : void 0,
5612
5867
  error: normalizeError2(value.error),
5613
- agentPresetCatalog: normalizeAgentPresetCatalog(value.agentPresetCatalog)
5868
+ agentPresetCatalog: normalizeAgentPresetCatalog(value.agentPresetCatalog),
5869
+ modelCatalog: normalizeModelCatalog(value.modelCatalog)
5614
5870
  };
5615
5871
  }
5616
5872
  function normalizeConnectionSnapshot(value) {
@@ -5660,7 +5916,7 @@ function formatRemaining2(milliseconds) {
5660
5916
  }
5661
5917
 
5662
5918
  // plugin-src/client/lifecycle.js
5663
- var React12 = __toESM(require("react"), 1);
5919
+ var React13 = __toESM(require("react"), 1);
5664
5920
  function createPollScheduler({ setTimeoutFn, clearTimeoutFn }) {
5665
5921
  let disposed = false;
5666
5922
  let timer;
@@ -5722,8 +5978,8 @@ function createAnimationFrameScheduler({ requestFrame, cancelFrame }) {
5722
5978
  };
5723
5979
  }
5724
5980
  function useAnimationFrameScheduler() {
5725
- const schedulerRef = React12.useRef(null);
5726
- React12.useEffect(() => {
5981
+ const schedulerRef = React13.useRef(null);
5982
+ React13.useEffect(() => {
5727
5983
  const scheduler = createAnimationFrameScheduler({
5728
5984
  requestFrame: (callback) => window.requestAnimationFrame(callback),
5729
5985
  cancelFrame: (frame) => window.cancelAnimationFrame(frame)
@@ -5734,7 +5990,7 @@ function useAnimationFrameScheduler() {
5734
5990
  if (schedulerRef.current === scheduler) schedulerRef.current = null;
5735
5991
  };
5736
5992
  }, []);
5737
- return React12.useCallback(
5993
+ return React13.useCallback(
5738
5994
  (callback, key) => schedulerRef.current?.schedule(callback, key) ?? false,
5739
5995
  []
5740
5996
  );
@@ -5809,7 +6065,7 @@ function QrIcon({ size = 58 }) {
5809
6065
  fill: "currentColor"
5810
6066
  }));
5811
6067
  }
5812
- var Button5 = React13.forwardRef(function Button6({ children, kind = "secondary", size, icon, className = "", ...props }, ref) {
6068
+ var Button5 = React14.forwardRef(function Button6({ children, kind = "secondary", size, icon, className = "", ...props }, ref) {
5813
6069
  return h2("button", {
5814
6070
  ...props,
5815
6071
  ref,
@@ -5926,7 +6182,7 @@ function safeQrSource2(value) {
5926
6182
  return /^data:image\/(?:png|webp|svg\+xml)(?:;charset=[^;,]+)?;base64,/i.test(value) ? value : void 0;
5927
6183
  }
5928
6184
  function QrPane({ provision, now, onRefresh, onCancel, busy }) {
5929
- const [imageFailed, setImageFailed] = React13.useState(false);
6185
+ const [imageFailed, setImageFailed] = React14.useState(false);
5930
6186
  const qrSource = safeQrSource2(provision.qrCodeDataUrl);
5931
6187
  const href = safeVerificationHref(provision.verificationUrl);
5932
6188
  const remaining = Math.max(0, provision.expiresAt - now);
@@ -5935,7 +6191,7 @@ function QrPane({ provision, now, onRefresh, onCancel, busy }) {
5935
6191
  const repairing = isCallbackRepair(provision);
5936
6192
  const grantingGroupMessages = isGroupMessagePermission(provision);
5937
6193
  const botName = provision.botName ?? "\u6B64\u673A\u5668\u4EBA";
5938
- React13.useEffect(() => setImageFailed(false), [qrSource]);
6194
+ React14.useEffect(() => setImageFailed(false), [qrSource]);
5939
6195
  return h2(
5940
6196
  "div",
5941
6197
  { className: "bxf-card bxf-provisionCard dim-surfaceCard" },
@@ -6098,11 +6354,11 @@ function connectionTestNotice2(value) {
6098
6354
  return value?.testMessage ? "\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\uFF0C\u4F46\u6D4B\u8BD5\u6D88\u606F\u53D1\u9001\u5931\u8D25\u3002" : null;
6099
6355
  }
6100
6356
  function RemoveConfirmation2({ bot, busy, onConfirm, onCancel }) {
6101
- const cancelRef = React13.useRef(null);
6357
+ const cancelRef = React14.useRef(null);
6102
6358
  const idPart = bot.botId.replace(/[^a-zA-Z0-9_-]/g, "-");
6103
6359
  const titleId = `bxf-remove-title-${idPart}`;
6104
6360
  const descriptionId = `bxf-remove-description-${idPart}`;
6105
- React13.useEffect(() => cancelRef.current?.focus(), []);
6361
+ React14.useEffect(() => cancelRef.current?.focus(), []);
6106
6362
  return h2(
6107
6363
  "div",
6108
6364
  {
@@ -6135,146 +6391,6 @@ function RemoveConfirmation2({ bot, busy, onConfirm, onCancel }) {
6135
6391
  )
6136
6392
  );
6137
6393
  }
6138
- function GroupResponseModeEditor({
6139
- value,
6140
- permissionGranted = false,
6141
- disabled = false,
6142
- authorizationDisabled = false,
6143
- onSave,
6144
- onAuthorize
6145
- }) {
6146
- const current = normalizeGroupResponseMode(value);
6147
- const [saving, setSaving] = React13.useState(false);
6148
- const [authorizing, setAuthorizing] = React13.useState(false);
6149
- const [error, setError] = React13.useState(null);
6150
- const change = async (event) => {
6151
- const next = normalizeGroupResponseMode(event.target.value);
6152
- if (next === current || saving || disabled) return;
6153
- setSaving(true);
6154
- setError(null);
6155
- try {
6156
- await onSave?.(next);
6157
- } catch (cause) {
6158
- setError(cause?.message ?? "\u7FA4\u804A\u54CD\u5E94\u65B9\u5F0F\u4FEE\u6539\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002");
6159
- } finally {
6160
- setSaving(false);
6161
- }
6162
- };
6163
- const authorize = async () => {
6164
- if (current !== "all" || saving || authorizing || disabled || authorizationDisabled) return;
6165
- setAuthorizing(true);
6166
- setError(null);
6167
- try {
6168
- await onAuthorize?.();
6169
- } catch (cause) {
6170
- setError(cause?.message ?? "\u7FA4\u6D88\u606F\u6743\u9650\u6388\u6743\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002");
6171
- } finally {
6172
- setAuthorizing(false);
6173
- }
6174
- };
6175
- return h2(
6176
- "div",
6177
- { className: "bxf-responseMode dim-responseMode" },
6178
- h2(
6179
- "div",
6180
- { className: "bxf-responseModeHeader dim-responseModeHeader" },
6181
- h2("span", null, "\u7FA4\u804A\u54CD\u5E94\u65B9\u5F0F"),
6182
- saving || authorizing ? h2(
6183
- "span",
6184
- { className: "bxf-responseModeStatus dim-responseModeStatus" },
6185
- saving ? "\u4FDD\u5B58\u4E2D\u2026" : "\u6B63\u5728\u51C6\u5907\u6388\u6743\u2026"
6186
- ) : null
6187
- ),
6188
- h2(
6189
- "select",
6190
- {
6191
- className: "bxf-responseModeSelect dim-responseModeSelect",
6192
- value: current,
6193
- disabled: disabled || saving,
6194
- "aria-label": "\u7FA4\u804A\u54CD\u5E94\u65B9\u5F0F",
6195
- onChange: (event) => {
6196
- void change(event);
6197
- }
6198
- },
6199
- h2("option", { value: "mention" }, "\u4EC5\u5728 @\u673A\u5668\u4EBA\u65F6\u54CD\u5E94\uFF08\u63A8\u8350\uFF09"),
6200
- h2("option", { value: "all" }, "\u54CD\u5E94\u6240\u6709\u7FA4\u6D88\u606F")
6201
- ),
6202
- h2(
6203
- "small",
6204
- { className: "bxf-responseModeHelp dim-responseModeHelp" },
6205
- current === "mention" ? permissionGranted ? "\u79C1\u804A\u59CB\u7EC8\u54CD\u5E94\uFF1B\u7FA4\u804A\u4EC5\u5904\u7406\u660E\u786E @\u5F53\u524D\u673A\u5668\u4EBA\u7684\u6D88\u606F\u3002\u7FA4\u6D88\u606F\u6743\u9650\u5DF2\u5F00\u901A\uFF0C\u518D\u6B21\u5207\u6362\u65E0\u9700\u6388\u6743\u3002" : "\u79C1\u804A\u59CB\u7EC8\u54CD\u5E94\uFF1B\u7FA4\u804A\u4EC5\u5904\u7406\u660E\u786E @\u5F53\u524D\u673A\u5668\u4EBA\u7684\u6D88\u606F\u3002\u9009\u62E9\u5168\u90E8\u6D88\u606F\u540E\u4F1A\u6253\u5F00\u98DE\u4E66\u5B98\u65B9\u6388\u6743\u6D41\u7A0B\u3002" : permissionGranted ? "\u5DF2\u5F00\u901A\u201C\u83B7\u53D6\u7FA4\u7EC4\u4E2D\u6240\u6709\u6D88\u606F\u201D\u6743\u9650\uFF08im:message.group_msg\uFF09\uFF1B\u673A\u5668\u4EBA\u4F1A\u5904\u7406\u7FA4\u804A\u4E2D\u7684\u6240\u6709\u53EF\u89C1\u6D88\u606F\u3002" : "\u5C1A\u672A\u786E\u8BA4\u201C\u83B7\u53D6\u7FA4\u7EC4\u4E2D\u6240\u6709\u6D88\u606F\u201D\u6743\u9650\uFF0C\u8BF7\u5B8C\u6210\u98DE\u4E66\u6388\u6743\u3002"
6206
- ),
6207
- current === "all" ? h2(
6208
- "div",
6209
- { className: "bxf-responseModePermissionAction dim-responseModePermissionAction" },
6210
- h2(Button5, {
6211
- className: "bxf-responseModePermissionButton",
6212
- size: "small",
6213
- disabled: disabled || authorizationDisabled || saving || authorizing,
6214
- "aria-busy": authorizing ? "true" : void 0,
6215
- "aria-label": permissionGranted ? "\u91CD\u65B0\u6388\u6743\u7FA4\u6D88\u606F\u6743\u9650" : "\u6388\u6743\u7FA4\u6D88\u606F\u6743\u9650",
6216
- onClick: () => {
6217
- void authorize();
6218
- }
6219
- }, authorizing ? "\u6B63\u5728\u51C6\u5907\u2026" : permissionGranted ? "\u91CD\u65B0\u6388\u6743" : "\u53BB\u6388\u6743")
6220
- ) : null,
6221
- error ? h2("p", {
6222
- className: "bxf-responseModeError dim-responseModeError",
6223
- role: "alert"
6224
- }, error) : null
6225
- );
6226
- }
6227
- function GroupTopicReplyEditor({ value = false, disabled = false, onSave }) {
6228
- const current = value === true ? "on" : "off";
6229
- const [saving, setSaving] = React13.useState(false);
6230
- const [error, setError] = React13.useState(null);
6231
- const change = async (event) => {
6232
- const next = event.target.value === "on";
6233
- if ((next ? "on" : "off") === current || saving || disabled) return;
6234
- setSaving(true);
6235
- setError(null);
6236
- try {
6237
- await onSave?.(next);
6238
- } catch (cause) {
6239
- setError(cause?.message ?? "\u7FA4\u804A\u4EE5\u8BDD\u9898\u65B9\u5F0F\u56DE\u590D\u8BBE\u7F6E\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002");
6240
- } finally {
6241
- setSaving(false);
6242
- }
6243
- };
6244
- return h2(
6245
- "div",
6246
- { className: "bxf-responseMode dim-responseMode" },
6247
- h2(
6248
- "div",
6249
- { className: "bxf-responseModeHeader dim-responseModeHeader" },
6250
- h2("span", null, "\u7FA4\u804A\u4EE5\u8BDD\u9898\u65B9\u5F0F\u56DE\u590D"),
6251
- saving ? h2("span", { className: "bxf-responseModeStatus dim-responseModeStatus" }, "\u4FDD\u5B58\u4E2D\u2026") : null
6252
- ),
6253
- h2(
6254
- "select",
6255
- {
6256
- className: "bxf-responseModeSelect dim-responseModeSelect",
6257
- value: current,
6258
- disabled: disabled || saving,
6259
- "aria-label": "\u7FA4\u804A\u4EE5\u8BDD\u9898\u65B9\u5F0F\u56DE\u590D",
6260
- onChange: (event) => {
6261
- void change(event);
6262
- }
6263
- },
6264
- h2("option", { value: "off" }, "\u5173\u95ED\uFF08\u7FA4\u5185\u76F4\u63A5\u56DE\u590D\uFF09"),
6265
- h2("option", { value: "on" }, "\u5F00\u542F\uFF08\u81EA\u52A8\u5F00\u542F\u72EC\u7ACB\u98DE\u4E66\u8BDD\u9898\uFF09")
6266
- ),
6267
- h2(
6268
- "small",
6269
- { className: "bxf-responseModeHelp dim-responseModeHelp" },
6270
- "\u5F00\u542F\u540E\uFF0C\u7FA4\u804A\u4E2D\u5411\u673A\u5668\u4EBA\u63D0\u95EE\u4F1A\u81EA\u52A8\u5F00\u542F\u72EC\u7ACB\u98DE\u4E66\u8BDD\u9898\uFF0C\u56DE\u590D\u843D\u5728\u8BDD\u9898\u5185\uFF1B\u6BCF\u4E2A\u8BDD\u9898\u662F dsh \u4F1A\u8BDD\u5217\u8868\u91CC\u4E00\u6761\u72EC\u7ACB\u4F1A\u8BDD\uFF0C\u4E0A\u4E0B\u6587\u4E92\u4E0D\u4E32\u3002\u79C1\u804A\u4E0D\u53D7\u5F71\u54CD\u3002"
6271
- ),
6272
- error ? h2("p", {
6273
- className: "bxf-responseModeError dim-responseModeError",
6274
- role: "alert"
6275
- }, error) : null
6276
- );
6277
- }
6278
6394
  function BotCard({
6279
6395
  connection,
6280
6396
  busy,
@@ -6287,11 +6403,9 @@ function BotCard({
6287
6403
  onReconnect,
6288
6404
  onRepairCallback,
6289
6405
  onWorkspaceSave,
6406
+ onModelSave,
6290
6407
  onAgentPresetSave,
6291
6408
  onContextEnhancementSave,
6292
- onGroupResponseModeSave,
6293
- onGroupTopicReplySave,
6294
- onGroupMessagePermissionAuthorize,
6295
6409
  onRequestRemove,
6296
6410
  onConfirmRemove,
6297
6411
  onCancelRemove,
@@ -6299,7 +6413,7 @@ function BotCard({
6299
6413
  removeButtonRef
6300
6414
  }) {
6301
6415
  const { bot, health, state, connected } = connection;
6302
- const repairTooltipId = React13.useId();
6416
+ const repairTooltipId = React14.useId();
6303
6417
  const stateForDisplay = busy === "reconnect" ? "connecting" : state;
6304
6418
  const tone = stateForDisplay === "connected" ? "success" : stateForDisplay === "connecting" ? "warning" : "error";
6305
6419
  const summary2 = actionError?.message ?? connection.error?.message ?? (connected ? null : health.summary);
@@ -6351,7 +6465,12 @@ function BotCard({
6351
6465
  botId: connection.botId,
6352
6466
  botName: bot.name,
6353
6467
  connected,
6354
- accessPolicy: connection.accessPolicy
6468
+ accessPolicy: connection.accessPolicy,
6469
+ channelSettings: {
6470
+ groupResponseMode: connection.groupResponseMode,
6471
+ groupTopicReply: connection.groupTopicReply,
6472
+ groupMessagePermissionGranted: connection.groupMessagePermissionGranted
6473
+ }
6355
6474
  })
6356
6475
  )
6357
6476
  ),
@@ -6360,6 +6479,11 @@ function BotCard({
6360
6479
  disabled: Boolean(busy),
6361
6480
  onSave: onWorkspaceSave
6362
6481
  }),
6482
+ h2(ModelEditor, {
6483
+ model: connection.model,
6484
+ disabled: Boolean(busy),
6485
+ onSave: onModelSave
6486
+ }),
6363
6487
  h2(AgentPresetEditor, {
6364
6488
  agentPreset: connection.agentPreset,
6365
6489
  disabled: Boolean(busy),
@@ -6370,19 +6494,6 @@ function BotCard({
6370
6494
  disabled: Boolean(busy),
6371
6495
  onSave: onContextEnhancementSave
6372
6496
  }),
6373
- h2(GroupResponseModeEditor, {
6374
- value: connection.groupResponseMode,
6375
- permissionGranted: connection.groupMessagePermissionGranted,
6376
- disabled: Boolean(busy),
6377
- authorizationDisabled: repairDisabled,
6378
- onSave: onGroupResponseModeSave,
6379
- onAuthorize: onGroupMessagePermissionAuthorize
6380
- }),
6381
- h2(GroupTopicReplyEditor, {
6382
- value: connection.groupTopicReply,
6383
- disabled: Boolean(busy),
6384
- onSave: onGroupTopicReplySave
6385
- }),
6386
6497
  provisionContent ? h2("section", {
6387
6498
  className: "bxf-botProvision dim-botProvision",
6388
6499
  "aria-label": `${bot.name}\u7684\u98DE\u4E66\u6388\u6743\u6D41\u7A0B`,
@@ -6489,11 +6600,9 @@ function BotList(props) {
6489
6600
  onReconnect: () => props.onReconnect(bot),
6490
6601
  onRepairCallback: () => props.onRepairCallback(bot),
6491
6602
  onWorkspaceSave: (workspace) => props.onWorkspaceSave(bot, workspace),
6603
+ onModelSave: (model) => props.onModelSave(bot, model),
6492
6604
  onAgentPresetSave: (agentPreset) => props.onAgentPresetSave(bot, agentPreset),
6493
6605
  onContextEnhancementSave: (config) => props.onContextEnhancementSave(bot, config),
6494
- onGroupResponseModeSave: (groupResponseMode) => props.onGroupResponseModeSave(bot, groupResponseMode),
6495
- onGroupTopicReplySave: (groupTopicReply) => props.onGroupTopicReplySave(bot, groupTopicReply),
6496
- onGroupMessagePermissionAuthorize: () => props.onGroupMessagePermissionAuthorize(bot),
6497
6606
  onRequestRemove: () => props.onRequestRemove(bot),
6498
6607
  onConfirmRemove: () => props.onConfirmRemove(bot),
6499
6608
  onCancelRemove: props.onCancelRemove,
@@ -6552,11 +6661,12 @@ function mergeFeishuSnapshotState(current, snapshot, { restoreProvisioning = fal
6552
6661
  provisioning,
6553
6662
  pageError: null,
6554
6663
  statusError: null,
6555
- agentPresetCatalog: snapshot.agentPresetCatalog ?? current.agentPresetCatalog
6664
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? current.agentPresetCatalog,
6665
+ modelCatalog: snapshot.modelCatalog ?? current.modelCatalog
6556
6666
  };
6557
6667
  }
6558
6668
  function FeishuSettingsTab({ rpcCall }) {
6559
- const [model, setModel] = React13.useState({
6669
+ const [model, setModel] = React14.useState({
6560
6670
  phase: "loading",
6561
6671
  revision: 0,
6562
6672
  bots: [],
@@ -6564,43 +6674,44 @@ function FeishuSettingsTab({ rpcCall }) {
6564
6674
  provisioning: null,
6565
6675
  pageError: null,
6566
6676
  statusError: null,
6567
- agentPresetCatalog: EMPTY_AGENT_PRESET_CATALOG
6677
+ agentPresetCatalog: EMPTY_AGENT_PRESET_CATALOG,
6678
+ modelCatalog: EMPTY_MODEL_CATALOG
6568
6679
  });
6569
- const [pageBusy, setPageBusy] = React13.useState(false);
6570
- const [provisionBusy, setProvisionBusy] = React13.useState(false);
6571
- const [credentialOpen, setCredentialOpen] = React13.useState(false);
6572
- const [credentialBusy, setCredentialBusy] = React13.useState(false);
6573
- const [credentialError, setCredentialError] = React13.useState(null);
6574
- const [busyByBot, setBusyByBot] = React13.useState({});
6575
- const [errorsByBot, setErrorsByBot] = React13.useState({});
6576
- const [testNoticesByBot, setTestNoticesByBot] = React13.useState({});
6577
- const [removeTargetId, setRemoveTargetId] = React13.useState(null);
6578
- const [announcement, setAnnouncement] = React13.useState("");
6579
- const [now, setNow] = React13.useState(() => Date.now());
6580
- const [focusBotId, setFocusBotId] = React13.useState(null);
6581
- const cardRefs = React13.useRef(/* @__PURE__ */ new Map());
6582
- const removeButtonRefs = React13.useRef(/* @__PURE__ */ new Map());
6583
- const targetedProvisionRef = React13.useRef(null);
6584
- const addButtonRef = React13.useRef(null);
6585
- const mountedRef = React13.useRef(true);
6680
+ const [pageBusy, setPageBusy] = React14.useState(false);
6681
+ const [provisionBusy, setProvisionBusy] = React14.useState(false);
6682
+ const [credentialOpen, setCredentialOpen] = React14.useState(false);
6683
+ const [credentialBusy, setCredentialBusy] = React14.useState(false);
6684
+ const [credentialError, setCredentialError] = React14.useState(null);
6685
+ const [busyByBot, setBusyByBot] = React14.useState({});
6686
+ const [errorsByBot, setErrorsByBot] = React14.useState({});
6687
+ const [testNoticesByBot, setTestNoticesByBot] = React14.useState({});
6688
+ const [removeTargetId, setRemoveTargetId] = React14.useState(null);
6689
+ const [announcement, setAnnouncement] = React14.useState("");
6690
+ const [now, setNow] = React14.useState(() => Date.now());
6691
+ const [focusBotId, setFocusBotId] = React14.useState(null);
6692
+ const cardRefs = React14.useRef(/* @__PURE__ */ new Map());
6693
+ const removeButtonRefs = React14.useRef(/* @__PURE__ */ new Map());
6694
+ const targetedProvisionRef = React14.useRef(null);
6695
+ const addButtonRef = React14.useRef(null);
6696
+ const mountedRef = React14.useRef(true);
6586
6697
  const workspaceFence = useWorkspaceSnapshotFence();
6587
6698
  const scheduleAnimationFrame = useAnimationFrameScheduler();
6588
- React13.useEffect(() => {
6699
+ React14.useEffect(() => {
6589
6700
  mountedRef.current = true;
6590
6701
  return () => {
6591
6702
  mountedRef.current = false;
6592
6703
  };
6593
6704
  }, []);
6594
- const announce = React13.useCallback((message) => {
6705
+ const announce = React14.useCallback((message) => {
6595
6706
  setAnnouncement("");
6596
6707
  scheduleAnimationFrame(() => {
6597
6708
  if (message) setAnnouncement(message);
6598
6709
  }, "announcement");
6599
6710
  }, [scheduleAnimationFrame]);
6600
- const invoke = React13.useCallback(async (endpoint, payload = {}, signal) => {
6711
+ const invoke = React14.useCallback(async (endpoint, payload = {}, signal) => {
6601
6712
  return unwrapRpcResult3(await rpcCall(endpoint, payload, signal));
6602
6713
  }, [rpcCall]);
6603
- const mergeSnapshot = React13.useCallback((snapshot, { restoreProvisioning = false } = {}) => {
6714
+ const mergeSnapshot = React14.useCallback((snapshot, { restoreProvisioning = false } = {}) => {
6604
6715
  const now2 = Date.now();
6605
6716
  setModel((current) => mergeFeishuSnapshotState(
6606
6717
  current,
@@ -6608,7 +6719,7 @@ function FeishuSettingsTab({ rpcCall }) {
6608
6719
  { restoreProvisioning, now: now2 }
6609
6720
  ));
6610
6721
  }, []);
6611
- const loadStatus = React13.useCallback(async ({ signal, silent = false, restoreProvisioning = false } = {}) => {
6722
+ const loadStatus = React14.useCallback(async ({ signal, silent = false, restoreProvisioning = false } = {}) => {
6612
6723
  const workspaceVersion = workspaceFence.beginStatus();
6613
6724
  if (workspaceVersion === null || !mountedRef.current) return void 0;
6614
6725
  if (!silent) setPageBusy(true);
@@ -6626,12 +6737,12 @@ function FeishuSettingsTab({ rpcCall }) {
6626
6737
  if (!silent && !signal?.aborted && mountedRef.current) setPageBusy(false);
6627
6738
  }
6628
6739
  }, [invoke, mergeSnapshot, workspaceFence]);
6629
- React13.useEffect(() => {
6740
+ React14.useEffect(() => {
6630
6741
  const controller = new AbortController();
6631
6742
  void loadStatus({ signal: controller.signal, restoreProvisioning: true });
6632
6743
  return () => controller.abort();
6633
6744
  }, [loadStatus]);
6634
- React13.useEffect(() => {
6745
+ React14.useEffect(() => {
6635
6746
  if (model.phase !== "ready") return void 0;
6636
6747
  const controller = new AbortController();
6637
6748
  let inFlight = false;
@@ -6650,7 +6761,7 @@ function FeishuSettingsTab({ rpcCall }) {
6650
6761
  window.clearInterval(timer);
6651
6762
  };
6652
6763
  }, [loadStatus, model.phase]);
6653
- React13.useEffect(() => {
6764
+ React14.useEffect(() => {
6654
6765
  if (!focusBotId) return;
6655
6766
  const node = cardRefs.current.get(focusBotId);
6656
6767
  if (!node) return;
@@ -6659,7 +6770,7 @@ function FeishuSettingsTab({ rpcCall }) {
6659
6770
  setFocusBotId(null);
6660
6771
  }, [focusBotId, model.bots]);
6661
6772
  const targetedProvisionFocusKey = isTargetedAppUpdate2(model.provisioning) ? `${model.provisioning.botId}:${model.provisioning.attemptId ?? "preparing"}:${model.provisioning.phase}` : null;
6662
- React13.useEffect(() => {
6773
+ React14.useEffect(() => {
6663
6774
  if (!targetedProvisionFocusKey) return;
6664
6775
  scheduleAnimationFrame(() => {
6665
6776
  const node = targetedProvisionRef.current;
@@ -6668,7 +6779,7 @@ function FeishuSettingsTab({ rpcCall }) {
6668
6779
  node.focus?.({ preventScroll: true });
6669
6780
  }, "targeted-provision-focus");
6670
6781
  }, [scheduleAnimationFrame, targetedProvisionFocusKey]);
6671
- const startProvisioning = React13.useCallback(async ({
6782
+ const startProvisioning = React14.useCallback(async ({
6672
6783
  replace = false,
6673
6784
  operation = FEISHU_REGISTRATION_OPERATIONS.PROVISION,
6674
6785
  bot
@@ -6744,7 +6855,7 @@ function FeishuSettingsTab({ rpcCall }) {
6744
6855
  model.provisioning?.botId,
6745
6856
  model.provisioning?.botName
6746
6857
  ]);
6747
- const bindCredentials = React13.useCallback(async ({ identity, secret }) => {
6858
+ const bindCredentials = React14.useCallback(async ({ identity, secret }) => {
6748
6859
  const snapshotVersion = workspaceFence.beginMutation();
6749
6860
  setCredentialBusy(true);
6750
6861
  setCredentialError(null);
@@ -6766,7 +6877,7 @@ function FeishuSettingsTab({ rpcCall }) {
6766
6877
  setCredentialBusy(false);
6767
6878
  }
6768
6879
  }, [announce, invoke, loadStatus, mergeSnapshot, workspaceFence]);
6769
- const cancelProvisioning = React13.useCallback(async () => {
6880
+ const cancelProvisioning = React14.useCallback(async () => {
6770
6881
  const activeProvision = model.provisioning;
6771
6882
  const attemptId = activeProvision?.attemptId;
6772
6883
  const repairing = isCallbackRepair(activeProvision);
@@ -6831,7 +6942,7 @@ function FeishuSettingsTab({ rpcCall }) {
6831
6942
  const countdownPhase = model.provisioning?.phase;
6832
6943
  const countdownExpiresAt = model.provisioning?.expiresAt;
6833
6944
  const countdownExpired = model.provisioning?.expired;
6834
- React13.useEffect(() => {
6945
+ React14.useEffect(() => {
6835
6946
  if (!countdownAttemptId || countdownPhase !== "qr" || countdownExpired) return void 0;
6836
6947
  const tick = () => {
6837
6948
  const timestamp7 = Date.now();
@@ -6844,7 +6955,7 @@ function FeishuSettingsTab({ rpcCall }) {
6844
6955
  const timer = window.setInterval(tick, 1e3);
6845
6956
  return () => window.clearInterval(timer);
6846
6957
  }, [countdownAttemptId, countdownPhase, countdownExpiresAt, countdownExpired]);
6847
- React13.useEffect(() => {
6958
+ React14.useEffect(() => {
6848
6959
  const provision2 = model.provisioning;
6849
6960
  if (!provision2 || !["qr", "connecting"].includes(provision2.phase) || !provision2.attemptId || provision2.expired) return void 0;
6850
6961
  const controller = new AbortController();
@@ -6912,7 +7023,7 @@ function FeishuSettingsTab({ rpcCall }) {
6912
7023
  window.clearTimeout(timer);
6913
7024
  };
6914
7025
  }, [announce, invoke, loadStatus, model.provisioning]);
6915
- const setBotBusy = React13.useCallback((botId, value) => {
7026
+ const setBotBusy = React14.useCallback((botId, value) => {
6916
7027
  setBusyByBot((current) => {
6917
7028
  const next = { ...current };
6918
7029
  if (value) next[botId] = value;
@@ -6920,7 +7031,7 @@ function FeishuSettingsTab({ rpcCall }) {
6920
7031
  return next;
6921
7032
  });
6922
7033
  }, []);
6923
- const setBotError = React13.useCallback((botId, error) => {
7034
+ const setBotError = React14.useCallback((botId, error) => {
6924
7035
  setErrorsByBot((current) => {
6925
7036
  const next = { ...current };
6926
7037
  if (error) next[botId] = presentError3(error);
@@ -6928,7 +7039,7 @@ function FeishuSettingsTab({ rpcCall }) {
6928
7039
  return next;
6929
7040
  });
6930
7041
  }, []);
6931
- const repairCallback = React13.useCallback((connection) => {
7042
+ const repairCallback = React14.useCallback((connection) => {
6932
7043
  if (model.provisioning) return;
6933
7044
  setRemoveTargetId(null);
6934
7045
  setBotError(connection.botId, null);
@@ -6942,7 +7053,7 @@ function FeishuSettingsTab({ rpcCall }) {
6942
7053
  bot: connection
6943
7054
  });
6944
7055
  }, [model.provisioning, setBotError, startProvisioning]);
6945
- const reconnectOneBot = React13.useCallback(async (connection) => {
7056
+ const reconnectOneBot = React14.useCallback(async (connection) => {
6946
7057
  const { botId, bot } = connection;
6947
7058
  const snapshotVersion = workspaceFence.beginMutation();
6948
7059
  setBotBusy(botId, "reconnect");
@@ -6982,7 +7093,7 @@ function FeishuSettingsTab({ rpcCall }) {
6982
7093
  setBotBusy(botId, null);
6983
7094
  }
6984
7095
  }, [announce, invoke, loadStatus, mergeSnapshot, setBotBusy, setBotError, workspaceFence]);
6985
- const saveWorkspace = React13.useCallback(async (connection, workspace) => {
7096
+ const saveWorkspace = React14.useCallback(async (connection, workspace) => {
6986
7097
  const { botId } = connection;
6987
7098
  const workspaceVersion = workspaceFence.beginMutation();
6988
7099
  setBotBusy(botId, "workspace");
@@ -7001,7 +7112,7 @@ function FeishuSettingsTab({ rpcCall }) {
7001
7112
  if (mountedRef.current) setBotBusy(botId, null);
7002
7113
  }
7003
7114
  }, [invoke, loadStatus, mergeSnapshot, setBotBusy, setBotError, workspaceFence]);
7004
- const saveBotSetting = React13.useCallback(async (connection, operation, endpoint, payload) => {
7115
+ const saveBotSetting = React14.useCallback(async (connection, operation, endpoint, payload) => {
7005
7116
  const { botId } = connection;
7006
7117
  const snapshotVersion = workspaceFence.beginMutation();
7007
7118
  setBotBusy(botId, operation);
@@ -7020,63 +7131,15 @@ function FeishuSettingsTab({ rpcCall }) {
7020
7131
  if (mountedRef.current) setBotBusy(botId, null);
7021
7132
  }
7022
7133
  }, [invoke, loadStatus, mergeSnapshot, setBotBusy, setBotError, workspaceFence]);
7023
- const authorizeGroupMessages = React13.useCallback(async (connection) => {
7024
- const { botId } = connection;
7025
- if (model.provisioning) {
7026
- throw new Error("\u8BF7\u5148\u5B8C\u6210\u5F53\u524D\u98DE\u4E66\u6388\u6743\u64CD\u4F5C\uFF0C\u518D\u5F00\u901A\u7FA4\u6D88\u606F\u6743\u9650\u3002");
7027
- }
7028
- setRemoveTargetId(null);
7029
- setBotError(botId, null);
7030
- setTestNoticesByBot((current) => {
7031
- const next = { ...current };
7032
- delete next[botId];
7033
- return next;
7034
- });
7035
- await startProvisioning({
7036
- operation: GROUP_MESSAGE_PERMISSION_OPERATION,
7037
- bot: connection
7038
- });
7039
- }, [model.provisioning, setBotError, startProvisioning]);
7040
- const saveGroupResponseMode = React13.useCallback(async (connection, groupResponseMode) => {
7041
- const { botId } = connection;
7042
- if (groupResponseMode === "all" && connection.groupMessagePermissionGranted !== true) {
7043
- await authorizeGroupMessages(connection);
7044
- return;
7045
- }
7046
- const snapshotVersion = workspaceFence.beginMutation();
7047
- setBotBusy(botId, "group-response-mode");
7048
- setBotError(botId, null);
7049
- try {
7050
- const snapshot = normalizeBotsSnapshot(await invoke(
7051
- FEISHU_ENDPOINTS.setGroupResponseMode,
7052
- { botId, groupResponseMode }
7053
- ));
7054
- if (mountedRef.current && workspaceFence.canCommitMutation(snapshotVersion)) {
7055
- mergeSnapshot(snapshot);
7056
- }
7057
- } finally {
7058
- const shouldRefresh = workspaceFence.endMutation();
7059
- if (shouldRefresh && mountedRef.current) void loadStatus({ silent: true });
7060
- if (mountedRef.current) setBotBusy(botId, null);
7061
- }
7062
- }, [
7063
- invoke,
7064
- authorizeGroupMessages,
7065
- loadStatus,
7066
- mergeSnapshot,
7067
- setBotBusy,
7068
- setBotError,
7069
- workspaceFence
7070
- ]);
7071
- const requestRemove = React13.useCallback((connection) => {
7134
+ const requestRemove = React14.useCallback((connection) => {
7072
7135
  setRemoveTargetId(connection.botId);
7073
7136
  }, []);
7074
- const cancelRemove = React13.useCallback(() => {
7137
+ const cancelRemove = React14.useCallback(() => {
7075
7138
  const botId = removeTargetId;
7076
7139
  setRemoveTargetId(null);
7077
7140
  scheduleAnimationFrame(() => removeButtonRefs.current.get(botId)?.focus(), "focus");
7078
7141
  }, [removeTargetId, scheduleAnimationFrame]);
7079
- const confirmRemove = React13.useCallback(async (connection) => {
7142
+ const confirmRemove = React14.useCallback(async (connection) => {
7080
7143
  const { botId, bot } = connection;
7081
7144
  const snapshotVersion = workspaceFence.beginMutation();
7082
7145
  setBotBusy(botId, "delete");
@@ -7162,15 +7225,17 @@ function FeishuSettingsTab({ rpcCall }) {
7162
7225
  setCredentialError(null);
7163
7226
  }
7164
7227
  }) : null;
7165
- const setCardRef = React13.useCallback((botId, node) => {
7228
+ const setCardRef = React14.useCallback((botId, node) => {
7166
7229
  if (node) cardRefs.current.set(botId, node);
7167
7230
  else cardRefs.current.delete(botId);
7168
7231
  }, []);
7169
- const setRemoveButtonRef = React13.useCallback((botId, node) => {
7232
+ const setRemoveButtonRef = React14.useCallback((botId, node) => {
7170
7233
  if (node) removeButtonRefs.current.set(botId, node);
7171
7234
  else removeButtonRefs.current.delete(botId);
7172
7235
  }, []);
7173
- return h2(AgentPresetCatalogContext.Provider, {
7236
+ return h2(ModelCatalogContext.Provider, {
7237
+ value: model.modelCatalog ?? EMPTY_MODEL_CATALOG
7238
+ }, h2(AgentPresetCatalogContext.Provider, {
7174
7239
  value: model.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
7175
7240
  }, h2(
7176
7241
  "section",
@@ -7205,7 +7270,7 @@ function FeishuSettingsTab({ rpcCall }) {
7205
7270
  onRetry: () => void loadStatus(),
7206
7271
  busy: pageBusy
7207
7272
  }) : h2(
7208
- React13.Fragment,
7273
+ React14.Fragment,
7209
7274
  null,
7210
7275
  credentialContent,
7211
7276
  targetedProvisioning ? null : provisionContent,
@@ -7222,6 +7287,12 @@ function FeishuSettingsTab({ rpcCall }) {
7222
7287
  onReconnect: (bot) => void reconnectOneBot(bot),
7223
7288
  onRepairCallback: repairCallback,
7224
7289
  onWorkspaceSave: saveWorkspace,
7290
+ onModelSave: (connection, selectedModel) => saveBotSetting(
7291
+ connection,
7292
+ "model",
7293
+ FEISHU_ENDPOINTS.setModel,
7294
+ { model: selectedModel }
7295
+ ),
7225
7296
  onAgentPresetSave: (connection, agentPreset) => saveBotSetting(
7226
7297
  connection,
7227
7298
  "preset",
@@ -7234,14 +7305,6 @@ function FeishuSettingsTab({ rpcCall }) {
7234
7305
  FEISHU_ENDPOINTS.setContextEnhancement,
7235
7306
  { config }
7236
7307
  ),
7237
- onGroupResponseModeSave: saveGroupResponseMode,
7238
- onGroupTopicReplySave: (connection, groupTopicReply) => saveBotSetting(
7239
- connection,
7240
- "group-topic-reply",
7241
- FEISHU_ENDPOINTS.setGroupTopicReply,
7242
- { groupTopicReply }
7243
- ),
7244
- onGroupMessagePermissionAuthorize: authorizeGroupMessages,
7245
7308
  onRequestRemove: requestRemove,
7246
7309
  onConfirmRemove: (bot) => void confirmRemove(bot),
7247
7310
  onCancelRemove: cancelRemove,
@@ -7249,7 +7312,7 @@ function FeishuSettingsTab({ rpcCall }) {
7249
7312
  setRemoveButtonRef
7250
7313
  }) : null
7251
7314
  )
7252
- ));
7315
+ )));
7253
7316
  }
7254
7317
 
7255
7318
  // plugin-src/client/channels/feishu/styles.js
@@ -7835,6 +7898,7 @@ var QQ_ENDPOINTS = Object.freeze({
7835
7898
  reconnectBot: "bot.reconnect",
7836
7899
  deleteBot: "bot.delete",
7837
7900
  setWorkspace: "bot.workspace.set",
7901
+ setModel: SET_MODEL_ENDPOINT,
7838
7902
  setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
7839
7903
  setContextEnhancement: "bot.context-enhancement.set",
7840
7904
  setAccessPolicy: "bot.access-policy.set"
@@ -7901,6 +7965,7 @@ function normalizeBot3(value) {
7901
7965
  connected,
7902
7966
  state: connected ? "connected" : state,
7903
7967
  workspace: text3(value.workspace, "", 4096),
7968
+ model: normalizeModelSelection(value.model),
7904
7969
  agentPreset: normalizeAgentPresetId(value.agentPreset),
7905
7970
  contextEnhancement: normalizeContextEnhancementConfig(value.contextEnhancement),
7906
7971
  ...Object.hasOwn(value, "accessPolicy") ? { accessPolicy: normalizeAccessPolicy(value.accessPolicy) } : {},
@@ -7939,6 +8004,7 @@ function normalizeSnapshot3(value) {
7939
8004
  totals: { configured: bots.length, connected: bots.filter((bot) => bot.connected).length },
7940
8005
  provisioning: source.provisioning ? normalizeProvisioning3(source.provisioning) : null,
7941
8006
  agentPresetCatalog: normalizeAgentPresetCatalog(source.agentPresetCatalog),
8007
+ modelCatalog: normalizeModelCatalog(source.modelCatalog),
7942
8008
  ...testMessage ? { testMessage } : {}
7943
8009
  };
7944
8010
  }
@@ -7961,7 +8027,7 @@ function formatRemaining3(milliseconds) {
7961
8027
  }
7962
8028
 
7963
8029
  // plugin-src/client/channels/qq/index.js
7964
- var React14 = __toESM(require("react"), 1);
8030
+ var React15 = __toESM(require("react"), 1);
7965
8031
 
7966
8032
  // plugin-src/client/channels/qq/styles.js
7967
8033
  var QQ_STYLE_ID = "xmanrui-dsh-im-qq-settings";
@@ -7986,7 +8052,7 @@ function installQqStyles() {
7986
8052
 
7987
8053
  // plugin-src/client/channels/qq/index.js
7988
8054
  var ACTIVE_STATES = /* @__PURE__ */ new Set(["pending", "refreshing", "connecting"]);
7989
- var Button7 = React14.forwardRef(function Button8({ children, kind = "secondary", className = "", ...props }, ref) {
8055
+ var Button7 = React15.forwardRef(function Button8({ children, kind = "secondary", className = "", ...props }, ref) {
7990
8056
  return h2("button", {
7991
8057
  ...props,
7992
8058
  ref,
@@ -8200,6 +8266,7 @@ function AccountCard2({
8200
8266
  removing,
8201
8267
  onReconnect,
8202
8268
  onWorkspaceSave,
8269
+ onModelSave,
8203
8270
  onAgentPresetSave,
8204
8271
  onContextEnhancementSave,
8205
8272
  onRequestRemove,
@@ -8254,6 +8321,11 @@ function AccountCard2({
8254
8321
  disabled: Boolean(busy),
8255
8322
  onSave: onWorkspaceSave
8256
8323
  }),
8324
+ h2(ModelEditor, {
8325
+ model: account.model,
8326
+ disabled: Boolean(busy),
8327
+ onSave: onModelSave
8328
+ }),
8257
8329
  h2(AgentPresetEditor, {
8258
8330
  agentPreset: account.agentPreset,
8259
8331
  disabled: Boolean(busy),
@@ -8298,25 +8370,26 @@ function AccountCard2({
8298
8370
  );
8299
8371
  }
8300
8372
  function QqSettingsTab({ rpcCall }) {
8301
- const [model, setModel] = React14.useState({
8373
+ const [model, setModel] = React15.useState({
8302
8374
  phase: "loading",
8303
8375
  bots: [],
8304
8376
  totals: { configured: 0, connected: 0 },
8305
8377
  error: null,
8306
- agentPresetCatalog: EMPTY_AGENT_PRESET_CATALOG
8378
+ agentPresetCatalog: EMPTY_AGENT_PRESET_CATALOG,
8379
+ modelCatalog: EMPTY_MODEL_CATALOG
8307
8380
  });
8308
- const [provision, setProvision] = React14.useState(null);
8309
- const [busy, setBusy] = React14.useState(false);
8310
- const [busyByBot, setBusyByBot] = React14.useState({});
8311
- const [feedbackByBot, setFeedbackByBot] = React14.useState({});
8312
- const [removeTarget, setRemoveTarget] = React14.useState(null);
8313
- const [credentialOpen, setCredentialOpen] = React14.useState(false);
8314
- const [credentialError, setCredentialError] = React14.useState(null);
8315
- const [now, setNow] = React14.useState(Date.now());
8316
- const mounted = React14.useRef(true);
8381
+ const [provision, setProvision] = React15.useState(null);
8382
+ const [busy, setBusy] = React15.useState(false);
8383
+ const [busyByBot, setBusyByBot] = React15.useState({});
8384
+ const [feedbackByBot, setFeedbackByBot] = React15.useState({});
8385
+ const [removeTarget, setRemoveTarget] = React15.useState(null);
8386
+ const [credentialOpen, setCredentialOpen] = React15.useState(false);
8387
+ const [credentialError, setCredentialError] = React15.useState(null);
8388
+ const [now, setNow] = React15.useState(Date.now());
8389
+ const mounted = React15.useRef(true);
8317
8390
  const workspaceFence = useWorkspaceSnapshotFence();
8318
- const addButtonRef = React14.useRef(null);
8319
- React14.useEffect(() => {
8391
+ const addButtonRef = React15.useRef(null);
8392
+ React15.useEffect(() => {
8320
8393
  const disposeDingtalk = installDingtalkStyles();
8321
8394
  const disposeQq = installQqStyles();
8322
8395
  mounted.current = true;
@@ -8326,11 +8399,11 @@ function QqSettingsTab({ rpcCall }) {
8326
8399
  disposeDingtalk();
8327
8400
  };
8328
8401
  }, []);
8329
- const invoke = React14.useCallback(async (endpoint, payload = {}, signal) => {
8402
+ const invoke = React15.useCallback(async (endpoint, payload = {}, signal) => {
8330
8403
  if (typeof rpcCall !== "function") throw new TypeError("QQ \u8BBE\u7F6E\u9875\u7F3A\u5C11 RPC \u8FDE\u63A5");
8331
8404
  return unwrapRpcResult4(await rpcCall(endpoint, payload, signal));
8332
8405
  }, [rpcCall]);
8333
- const loadStatus = React14.useCallback(async ({ signal, silent = false, restore = false } = {}) => {
8406
+ const loadStatus = React15.useCallback(async ({ signal, silent = false, restore = false } = {}) => {
8334
8407
  const workspaceVersion = workspaceFence.beginStatus();
8335
8408
  if (workspaceVersion === null) return void 0;
8336
8409
  if (!silent && mounted.current) setModel((current) => ({ ...current, phase: "loading", error: null }));
@@ -8342,7 +8415,8 @@ function QqSettingsTab({ rpcCall }) {
8342
8415
  bots: snapshot.bots,
8343
8416
  totals: snapshot.totals,
8344
8417
  error: null,
8345
- agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
8418
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
8419
+ modelCatalog: snapshot.modelCatalog ?? EMPTY_MODEL_CATALOG
8346
8420
  });
8347
8421
  if (restore && snapshot.provisioning) setProvision({
8348
8422
  ...snapshot.provisioning,
@@ -8356,12 +8430,12 @@ function QqSettingsTab({ rpcCall }) {
8356
8430
  return void 0;
8357
8431
  }
8358
8432
  }, [invoke, workspaceFence]);
8359
- React14.useEffect(() => {
8433
+ React15.useEffect(() => {
8360
8434
  const controller = new AbortController();
8361
8435
  void loadStatus({ signal: controller.signal, restore: true });
8362
8436
  return () => controller.abort();
8363
8437
  }, [loadStatus]);
8364
- React14.useEffect(() => {
8438
+ React15.useEffect(() => {
8365
8439
  if (model.phase !== "ready") return void 0;
8366
8440
  const controller = new AbortController();
8367
8441
  const timer = window.setInterval(() => void loadStatus({ signal: controller.signal, silent: true }), 15e3);
@@ -8370,12 +8444,12 @@ function QqSettingsTab({ rpcCall }) {
8370
8444
  window.clearInterval(timer);
8371
8445
  };
8372
8446
  }, [loadStatus, model.phase]);
8373
- React14.useEffect(() => {
8447
+ React15.useEffect(() => {
8374
8448
  if (!provision || !ACTIVE_STATES.has(provision.status)) return void 0;
8375
8449
  const timer = window.setInterval(() => mounted.current && setNow(Date.now()), 1e3);
8376
8450
  return () => window.clearInterval(timer);
8377
8451
  }, [provision?.attemptId, provision?.status]);
8378
- const startProvisioning = React14.useCallback(async (replace = false) => {
8452
+ const startProvisioning = React15.useCallback(async (replace = false) => {
8379
8453
  setCredentialOpen(false);
8380
8454
  setCredentialError(null);
8381
8455
  setBusy(true);
@@ -8393,7 +8467,7 @@ function QqSettingsTab({ rpcCall }) {
8393
8467
  if (mounted.current) setBusy(false);
8394
8468
  }
8395
8469
  }, [invoke, provision?.attemptId]);
8396
- const bindCredentials = React14.useCallback(async ({ identity, secret }) => {
8470
+ const bindCredentials = React15.useCallback(async ({ identity, secret }) => {
8397
8471
  const snapshotVersion = workspaceFence.beginMutation();
8398
8472
  setBusy(true);
8399
8473
  setCredentialError(null);
@@ -8409,7 +8483,8 @@ function QqSettingsTab({ rpcCall }) {
8409
8483
  bots: snapshot.bots,
8410
8484
  totals: snapshot.totals,
8411
8485
  error: null,
8412
- agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
8486
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
8487
+ modelCatalog: snapshot.modelCatalog ?? EMPTY_MODEL_CATALOG
8413
8488
  });
8414
8489
  }
8415
8490
  setCredentialOpen(false);
@@ -8421,7 +8496,7 @@ function QqSettingsTab({ rpcCall }) {
8421
8496
  if (mounted.current) setBusy(false);
8422
8497
  }
8423
8498
  }, [invoke, loadStatus, workspaceFence]);
8424
- const closeProvision = React14.useCallback(async () => {
8499
+ const closeProvision = React15.useCallback(async () => {
8425
8500
  setBusy(true);
8426
8501
  try {
8427
8502
  if (provision?.attemptId && ACTIVE_STATES.has(provision.status)) {
@@ -8432,7 +8507,7 @@ function QqSettingsTab({ rpcCall }) {
8432
8507
  if (mounted.current) setBusy(false);
8433
8508
  }
8434
8509
  }, [invoke, provision?.attemptId, provision?.status]);
8435
- React14.useEffect(() => {
8510
+ React15.useEffect(() => {
8436
8511
  const attemptId = provision?.attemptId;
8437
8512
  if (!attemptId || !ACTIVE_STATES.has(provision.status)) return void 0;
8438
8513
  const controller = new AbortController();
@@ -8462,7 +8537,7 @@ function QqSettingsTab({ rpcCall }) {
8462
8537
  window.clearTimeout(timer);
8463
8538
  };
8464
8539
  }, [invoke, loadStatus, provision?.attemptId, provision?.pollIntervalMs, provision?.status]);
8465
- const botAction = React14.useCallback(async (account, operation, endpoint, payload) => {
8540
+ const botAction = React15.useCallback(async (account, operation, endpoint, payload) => {
8466
8541
  const snapshotVersion = workspaceFence.beginMutation();
8467
8542
  setBusyByBot((current) => ({ ...current, [account.botId]: operation }));
8468
8543
  try {
@@ -8473,7 +8548,8 @@ function QqSettingsTab({ rpcCall }) {
8473
8548
  bots: snapshot.bots,
8474
8549
  totals: snapshot.totals,
8475
8550
  error: null,
8476
- agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
8551
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
8552
+ modelCatalog: snapshot.modelCatalog ?? EMPTY_MODEL_CATALOG
8477
8553
  });
8478
8554
  }
8479
8555
  return snapshot;
@@ -8487,7 +8563,7 @@ function QqSettingsTab({ rpcCall }) {
8487
8563
  });
8488
8564
  }
8489
8565
  }, [invoke, loadStatus, workspaceFence]);
8490
- const reconnect = React14.useCallback(async (account) => {
8566
+ const reconnect = React15.useCallback(async (account) => {
8491
8567
  setFeedbackByBot((current) => {
8492
8568
  const next = { ...current };
8493
8569
  delete next[account.botId];
@@ -8550,6 +8626,12 @@ function QqSettingsTab({ rpcCall }) {
8550
8626
  QQ_ENDPOINTS.setWorkspace,
8551
8627
  { botId: account.botId, workspace }
8552
8628
  ),
8629
+ onModelSave: (selectedModel) => botAction(
8630
+ account,
8631
+ "model",
8632
+ QQ_ENDPOINTS.setModel,
8633
+ { botId: account.botId, model: selectedModel }
8634
+ ),
8553
8635
  onAgentPresetSave: (agentPreset) => botAction(
8554
8636
  account,
8555
8637
  "preset",
@@ -8584,7 +8666,9 @@ function QqSettingsTab({ rpcCall }) {
8584
8666
  setCredentialError(null);
8585
8667
  }
8586
8668
  }) : null;
8587
- return h2(AgentPresetCatalogContext.Provider, {
8669
+ return h2(ModelCatalogContext.Provider, {
8670
+ value: model.modelCatalog ?? EMPTY_MODEL_CATALOG
8671
+ }, h2(AgentPresetCatalogContext.Provider, {
8588
8672
  value: model.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
8589
8673
  }, h2(
8590
8674
  "section",
@@ -8602,14 +8686,14 @@ function QqSettingsTab({ rpcCall }) {
8602
8686
  addButtonRef
8603
8687
  }),
8604
8688
  model.phase === "loading" ? h2(LoadingView3) : model.phase === "error" ? h2("div", { className: "ddt-card dim-surfaceCard" }, h2("div", { className: "ddt-inlineError dim-inlineError" }, h2("h3", null, "\u65E0\u6CD5\u8BFB\u53D6 QQ \u673A\u5668\u4EBA\u72B6\u6001"), h2("p", null, model.error?.message), h2(Button7, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6"))) : h2(
8605
- React14.Fragment,
8689
+ React15.Fragment,
8606
8690
  null,
8607
8691
  credentialView,
8608
8692
  provisionView,
8609
8693
  model.bots.length === 0 && !provision && !credentialOpen ? h2(EmptyView3, { busy, onStart: () => void startProvisioning() }) : null,
8610
8694
  botList
8611
8695
  )
8612
- ));
8696
+ )));
8613
8697
  }
8614
8698
 
8615
8699
  // src/channels/office/protocol.mjs
@@ -8691,7 +8775,7 @@ function normalizeOfficeStatus(value) {
8691
8775
  }
8692
8776
 
8693
8777
  // plugin-src/client/channels/office/index.js
8694
- var React15 = __toESM(require("react"), 1);
8778
+ var React16 = __toESM(require("react"), 1);
8695
8779
  function Button9({ children, kind = "secondary", ...props }) {
8696
8780
  return h2("button", { ...props, type: "button", className: "ddt-button", "data-kind": kind }, children);
8697
8781
  }
@@ -8720,12 +8804,12 @@ function stateLabel(model) {
8720
8804
  return "\u5DF2\u914D\u7F6E";
8721
8805
  }
8722
8806
  function OfficeSettingsTab({ rpcCall, initialStatus }) {
8723
- const [model, setModel] = React15.useState(normalizeOfficeStatus(initialStatus));
8724
- const [phase, setPhase] = React15.useState(initialStatus === void 0 ? "loading" : "ready");
8725
- const [busy, setBusy] = React15.useState("");
8726
- const [error, setError] = React15.useState("");
8727
- const [notice, setNotice] = React15.useState("");
8728
- const [form, setForm] = React15.useState({
8807
+ const [model, setModel] = React16.useState(normalizeOfficeStatus(initialStatus));
8808
+ const [phase, setPhase] = React16.useState(initialStatus === void 0 ? "loading" : "ready");
8809
+ const [busy, setBusy] = React16.useState("");
8810
+ const [error, setError] = React16.useState("");
8811
+ const [notice, setNotice] = React16.useState("");
8812
+ const [form, setForm] = React16.useState({
8729
8813
  baseUrl: "",
8730
8814
  deviceId: "local-harness",
8731
8815
  deviceToken: "",
@@ -8734,11 +8818,11 @@ function OfficeSettingsTab({ rpcCall, initialStatus }) {
8734
8818
  workspaces: "",
8735
8819
  instructionPresets: ""
8736
8820
  });
8737
- const invoke = React15.useCallback(async (endpoint, payload = {}) => {
8821
+ const invoke = React16.useCallback(async (endpoint, payload = {}) => {
8738
8822
  if (typeof rpcCall !== "function") throw new Error("AI Office \u8BBE\u7F6E\u9875\u7F3A\u5C11 RPC \u8FDE\u63A5");
8739
8823
  return unwrapOfficeRpc(await rpcCall(endpoint, payload));
8740
8824
  }, [rpcCall]);
8741
- const adopt = React15.useCallback((value) => {
8825
+ const adopt = React16.useCallback((value) => {
8742
8826
  const next = normalizeOfficeStatus(value?.snapshot ?? value);
8743
8827
  setModel(next);
8744
8828
  if (next.config) setForm((current) => ({
@@ -8753,7 +8837,7 @@ function OfficeSettingsTab({ rpcCall, initialStatus }) {
8753
8837
  }));
8754
8838
  return next;
8755
8839
  }, []);
8756
- const load = React15.useCallback(async () => {
8840
+ const load = React16.useCallback(async () => {
8757
8841
  try {
8758
8842
  adopt(await invoke(OFFICE_RPC_ENDPOINTS.status));
8759
8843
  setPhase("ready");
@@ -8763,7 +8847,7 @@ function OfficeSettingsTab({ rpcCall, initialStatus }) {
8763
8847
  setError(caught.message);
8764
8848
  }
8765
8849
  }, [adopt, invoke]);
8766
- React15.useEffect(() => {
8850
+ React16.useEffect(() => {
8767
8851
  void load();
8768
8852
  }, [load]);
8769
8853
  const run = async (name2, operation) => {
@@ -8780,7 +8864,7 @@ function OfficeSettingsTab({ rpcCall, initialStatus }) {
8780
8864
  setBusy("");
8781
8865
  }
8782
8866
  };
8783
- const hooks = React15.useMemo(() => {
8867
+ const hooks = React16.useMemo(() => {
8784
8868
  try {
8785
8869
  return officeHookUrls(form.baseUrl);
8786
8870
  } catch {
@@ -8965,7 +9049,7 @@ var normalizeSnapshot4 = api2.normalizeSnapshot;
8965
9049
  var presentError5 = api2.presentError;
8966
9050
 
8967
9051
  // plugin-src/client/channels/slack/index.js
8968
- var React16 = __toESM(require("react"), 1);
9052
+ var React17 = __toESM(require("react"), 1);
8969
9053
 
8970
9054
  // src/channels/slack/manifest.mjs
8971
9055
  var SLACK_APP_MANIFEST_YAML = `_metadata:
@@ -9043,10 +9127,10 @@ function installSlackStyles() {
9043
9127
 
9044
9128
  // plugin-src/client/channels/slack/index.js
9045
9129
  function SlackCredentialPanel({ busy, error, onSubmit, onCancel }) {
9046
- const [botToken, setBotToken] = React16.useState("");
9047
- const [appToken, setAppToken] = React16.useState("");
9048
- const [copied, setCopied] = React16.useState(false);
9049
- const headingId = React16.useId();
9130
+ const [botToken, setBotToken] = React17.useState("");
9131
+ const [appToken, setAppToken] = React17.useState("");
9132
+ const [copied, setCopied] = React17.useState(false);
9133
+ const headingId = React17.useId();
9050
9134
  const copyManifest = async () => {
9051
9135
  try {
9052
9136
  await navigator.clipboard.writeText(SLACK_APP_MANIFEST_YAML);
@@ -9241,6 +9325,7 @@ var WECOM_ENDPOINTS = Object.freeze({
9241
9325
  reconnectBot: "bot.reconnect",
9242
9326
  deleteBot: "bot.delete",
9243
9327
  setWorkspace: "bot.workspace.set",
9328
+ setModel: SET_MODEL_ENDPOINT,
9244
9329
  setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
9245
9330
  setContextEnhancement: "bot.context-enhancement.set",
9246
9331
  setAccessPolicy: "bot.access-policy.set"
@@ -9313,6 +9398,7 @@ function normalizeBot4(value) {
9313
9398
  connected,
9314
9399
  state: connected ? "connected" : state,
9315
9400
  workspace: text4(value.workspace, "", 4096),
9401
+ model: normalizeModelSelection(value.model),
9316
9402
  agentPreset: normalizeAgentPresetId(value.agentPreset),
9317
9403
  contextEnhancement: normalizeContextEnhancementConfig(value.contextEnhancement),
9318
9404
  ...Object.hasOwn(value, "accessPolicy") ? { accessPolicy: normalizeAccessPolicy(value.accessPolicy) } : {},
@@ -9341,7 +9427,8 @@ function normalizeSnapshot6(value) {
9341
9427
  totals: { configured: bots.length, connected: bots.filter((bot) => bot.connected).length },
9342
9428
  provisioning: source.provisioning ? normalizeProvisioning4(source.provisioning) : null,
9343
9429
  testMessage: normalizeTestMessage3(source.testMessage),
9344
- agentPresetCatalog: normalizeAgentPresetCatalog(source.agentPresetCatalog)
9430
+ agentPresetCatalog: normalizeAgentPresetCatalog(source.agentPresetCatalog),
9431
+ modelCatalog: normalizeModelCatalog(source.modelCatalog)
9345
9432
  };
9346
9433
  }
9347
9434
  function presentError7(error) {
@@ -9356,7 +9443,7 @@ function formatRemaining4(milliseconds) {
9356
9443
  }
9357
9444
 
9358
9445
  // plugin-src/client/channels/wecom/index.js
9359
- var React17 = __toESM(require("react"), 1);
9446
+ var React18 = __toESM(require("react"), 1);
9360
9447
 
9361
9448
  // plugin-src/client/channels/wecom/styles.js
9362
9449
  var WECOM_STYLE_ID = "xmanrui-dsh-im-wecom-settings";
@@ -9381,7 +9468,7 @@ function installWecomStyles() {
9381
9468
 
9382
9469
  // plugin-src/client/channels/wecom/index.js
9383
9470
  var ACTIVE_STATES2 = /* @__PURE__ */ new Set(["pending", "refreshing", "connecting"]);
9384
- var Button10 = React17.forwardRef(function Button11({ children, kind = "secondary", className = "", ...props }, ref) {
9471
+ var Button10 = React18.forwardRef(function Button11({ children, kind = "secondary", className = "", ...props }, ref) {
9385
9472
  return h2("button", {
9386
9473
  ...props,
9387
9474
  ref,
@@ -9595,6 +9682,7 @@ function AccountCard3({
9595
9682
  removing,
9596
9683
  onReconnect,
9597
9684
  onWorkspaceSave,
9685
+ onModelSave,
9598
9686
  onAgentPresetSave,
9599
9687
  onContextEnhancementSave,
9600
9688
  onRequestRemove,
@@ -9649,6 +9737,11 @@ function AccountCard3({
9649
9737
  disabled: Boolean(busy),
9650
9738
  onSave: onWorkspaceSave
9651
9739
  }),
9740
+ h2(ModelEditor, {
9741
+ model: account.model,
9742
+ disabled: Boolean(busy),
9743
+ onSave: onModelSave
9744
+ }),
9652
9745
  h2(AgentPresetEditor, {
9653
9746
  agentPreset: account.agentPreset,
9654
9747
  disabled: Boolean(busy),
@@ -9693,27 +9786,28 @@ function AccountCard3({
9693
9786
  );
9694
9787
  }
9695
9788
  function WecomSettingsTab({ rpcCall }) {
9696
- const [model, setModel] = React17.useState({
9789
+ const [model, setModel] = React18.useState({
9697
9790
  phase: "loading",
9698
9791
  bots: [],
9699
9792
  totals: { configured: 0, connected: 0 },
9700
9793
  error: null,
9701
- agentPresetCatalog: EMPTY_AGENT_PRESET_CATALOG
9794
+ agentPresetCatalog: EMPTY_AGENT_PRESET_CATALOG,
9795
+ modelCatalog: EMPTY_MODEL_CATALOG
9702
9796
  });
9703
- const [provision, setProvision] = React17.useState(null);
9704
- const [busy, setBusy] = React17.useState(false);
9705
- const [busyByBot, setBusyByBot] = React17.useState({});
9706
- const [feedbackByBot, setFeedbackByBot] = React17.useState({});
9707
- const [removeTarget, setRemoveTarget] = React17.useState(null);
9708
- const [credentialOpen, setCredentialOpen] = React17.useState(false);
9709
- const [credentialError, setCredentialError] = React17.useState(null);
9710
- const [notice, setNotice] = React17.useState("");
9711
- const [now, setNow] = React17.useState(Date.now());
9712
- const mounted = React17.useRef(true);
9797
+ const [provision, setProvision] = React18.useState(null);
9798
+ const [busy, setBusy] = React18.useState(false);
9799
+ const [busyByBot, setBusyByBot] = React18.useState({});
9800
+ const [feedbackByBot, setFeedbackByBot] = React18.useState({});
9801
+ const [removeTarget, setRemoveTarget] = React18.useState(null);
9802
+ const [credentialOpen, setCredentialOpen] = React18.useState(false);
9803
+ const [credentialError, setCredentialError] = React18.useState(null);
9804
+ const [notice, setNotice] = React18.useState("");
9805
+ const [now, setNow] = React18.useState(Date.now());
9806
+ const mounted = React18.useRef(true);
9713
9807
  const workspaceFence = useWorkspaceSnapshotFence();
9714
- const addButtonRef = React17.useRef(null);
9715
- const noticeFrameRef = React17.useRef(null);
9716
- const announce = React17.useCallback((message) => {
9808
+ const addButtonRef = React18.useRef(null);
9809
+ const noticeFrameRef = React18.useRef(null);
9810
+ const announce = React18.useCallback((message) => {
9717
9811
  if (!mounted.current) return;
9718
9812
  if (noticeFrameRef.current !== null) {
9719
9813
  window.cancelAnimationFrame(noticeFrameRef.current);
@@ -9727,7 +9821,7 @@ function WecomSettingsTab({ rpcCall }) {
9727
9821
  });
9728
9822
  }
9729
9823
  }, []);
9730
- React17.useEffect(() => {
9824
+ React18.useEffect(() => {
9731
9825
  const disposeDingtalk = installDingtalkStyles();
9732
9826
  const disposeWecom = installWecomStyles();
9733
9827
  mounted.current = true;
@@ -9741,11 +9835,11 @@ function WecomSettingsTab({ rpcCall }) {
9741
9835
  disposeDingtalk();
9742
9836
  };
9743
9837
  }, []);
9744
- const invoke = React17.useCallback(async (endpoint, payload = {}, signal) => {
9838
+ const invoke = React18.useCallback(async (endpoint, payload = {}, signal) => {
9745
9839
  if (typeof rpcCall !== "function") throw new TypeError("\u4F01\u4E1A\u5FAE\u4FE1\u8BBE\u7F6E\u9875\u7F3A\u5C11 RPC \u8FDE\u63A5");
9746
9840
  return unwrapRpcResult7(await rpcCall(endpoint, payload, signal));
9747
9841
  }, [rpcCall]);
9748
- const loadStatus = React17.useCallback(async ({ signal, silent = false, restore = false } = {}) => {
9842
+ const loadStatus = React18.useCallback(async ({ signal, silent = false, restore = false } = {}) => {
9749
9843
  const workspaceVersion = workspaceFence.beginStatus();
9750
9844
  if (workspaceVersion === null) return void 0;
9751
9845
  if (!silent && mounted.current) setModel((current) => ({ ...current, phase: "loading", error: null }));
@@ -9757,7 +9851,8 @@ function WecomSettingsTab({ rpcCall }) {
9757
9851
  bots: snapshot.bots,
9758
9852
  totals: snapshot.totals,
9759
9853
  error: null,
9760
- agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
9854
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
9855
+ modelCatalog: snapshot.modelCatalog ?? EMPTY_MODEL_CATALOG
9761
9856
  });
9762
9857
  if (restore && snapshot.provisioning) setProvision({
9763
9858
  ...snapshot.provisioning,
@@ -9771,12 +9866,12 @@ function WecomSettingsTab({ rpcCall }) {
9771
9866
  return void 0;
9772
9867
  }
9773
9868
  }, [invoke, workspaceFence]);
9774
- React17.useEffect(() => {
9869
+ React18.useEffect(() => {
9775
9870
  const controller = new AbortController();
9776
9871
  void loadStatus({ signal: controller.signal, restore: true });
9777
9872
  return () => controller.abort();
9778
9873
  }, [loadStatus]);
9779
- React17.useEffect(() => {
9874
+ React18.useEffect(() => {
9780
9875
  if (model.phase !== "ready") return void 0;
9781
9876
  const controller = new AbortController();
9782
9877
  const timer = window.setInterval(() => void loadStatus({ signal: controller.signal, silent: true }), 15e3);
@@ -9785,12 +9880,12 @@ function WecomSettingsTab({ rpcCall }) {
9785
9880
  window.clearInterval(timer);
9786
9881
  };
9787
9882
  }, [loadStatus, model.phase]);
9788
- React17.useEffect(() => {
9883
+ React18.useEffect(() => {
9789
9884
  if (!provision || !ACTIVE_STATES2.has(provision.status)) return void 0;
9790
9885
  const timer = window.setInterval(() => mounted.current && setNow(Date.now()), 1e3);
9791
9886
  return () => window.clearInterval(timer);
9792
9887
  }, [provision?.attemptId, provision?.status]);
9793
- const startProvisioning = React17.useCallback(async (replace = false) => {
9888
+ const startProvisioning = React18.useCallback(async (replace = false) => {
9794
9889
  setCredentialOpen(false);
9795
9890
  setCredentialError(null);
9796
9891
  setBusy(true);
@@ -9808,7 +9903,7 @@ function WecomSettingsTab({ rpcCall }) {
9808
9903
  if (mounted.current) setBusy(false);
9809
9904
  }
9810
9905
  }, [invoke, provision?.attemptId]);
9811
- const bindCredentials = React17.useCallback(async ({ identity, secret }) => {
9906
+ const bindCredentials = React18.useCallback(async ({ identity, secret }) => {
9812
9907
  const snapshotVersion = workspaceFence.beginMutation();
9813
9908
  setBusy(true);
9814
9909
  setCredentialError(null);
@@ -9824,7 +9919,8 @@ function WecomSettingsTab({ rpcCall }) {
9824
9919
  bots: snapshot.bots,
9825
9920
  totals: snapshot.totals,
9826
9921
  error: null,
9827
- agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
9922
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
9923
+ modelCatalog: snapshot.modelCatalog ?? EMPTY_MODEL_CATALOG
9828
9924
  });
9829
9925
  }
9830
9926
  setCredentialOpen(false);
@@ -9836,7 +9932,7 @@ function WecomSettingsTab({ rpcCall }) {
9836
9932
  if (mounted.current) setBusy(false);
9837
9933
  }
9838
9934
  }, [invoke, loadStatus, workspaceFence]);
9839
- const closeProvision = React17.useCallback(async () => {
9935
+ const closeProvision = React18.useCallback(async () => {
9840
9936
  setBusy(true);
9841
9937
  try {
9842
9938
  if (provision?.attemptId && ACTIVE_STATES2.has(provision.status)) {
@@ -9847,7 +9943,7 @@ function WecomSettingsTab({ rpcCall }) {
9847
9943
  if (mounted.current) setBusy(false);
9848
9944
  }
9849
9945
  }, [invoke, provision?.attemptId, provision?.status]);
9850
- React17.useEffect(() => {
9946
+ React18.useEffect(() => {
9851
9947
  const attemptId = provision?.attemptId;
9852
9948
  if (!attemptId || !ACTIVE_STATES2.has(provision.status)) return void 0;
9853
9949
  const controller = new AbortController();
@@ -9877,7 +9973,7 @@ function WecomSettingsTab({ rpcCall }) {
9877
9973
  window.clearTimeout(timer);
9878
9974
  };
9879
9975
  }, [invoke, loadStatus, provision?.attemptId, provision?.pollIntervalMs, provision?.status]);
9880
- const botAction = React17.useCallback(async (account, operation, endpoint, payload) => {
9976
+ const botAction = React18.useCallback(async (account, operation, endpoint, payload) => {
9881
9977
  const snapshotVersion = workspaceFence.beginMutation();
9882
9978
  setBusyByBot((current) => ({ ...current, [account.botId]: operation }));
9883
9979
  try {
@@ -9888,7 +9984,8 @@ function WecomSettingsTab({ rpcCall }) {
9888
9984
  bots: snapshot.bots,
9889
9985
  totals: snapshot.totals,
9890
9986
  error: null,
9891
- agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
9987
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
9988
+ modelCatalog: snapshot.modelCatalog ?? EMPTY_MODEL_CATALOG
9892
9989
  });
9893
9990
  }
9894
9991
  return snapshot;
@@ -9902,7 +9999,7 @@ function WecomSettingsTab({ rpcCall }) {
9902
9999
  });
9903
10000
  }
9904
10001
  }, [invoke, loadStatus, workspaceFence]);
9905
- const reconnect = React17.useCallback(async (account) => {
10002
+ const reconnect = React18.useCallback(async (account) => {
9906
10003
  setFeedbackByBot((current) => {
9907
10004
  const next = { ...current };
9908
10005
  delete next[account.botId];
@@ -9976,6 +10073,12 @@ function WecomSettingsTab({ rpcCall }) {
9976
10073
  WECOM_ENDPOINTS.setWorkspace,
9977
10074
  { botId: account.botId, workspace }
9978
10075
  ),
10076
+ onModelSave: (selectedModel) => botAction(
10077
+ account,
10078
+ "model",
10079
+ WECOM_ENDPOINTS.setModel,
10080
+ { botId: account.botId, model: selectedModel }
10081
+ ),
9979
10082
  onAgentPresetSave: (agentPreset) => botAction(
9980
10083
  account,
9981
10084
  "preset",
@@ -10010,7 +10113,9 @@ function WecomSettingsTab({ rpcCall }) {
10010
10113
  setCredentialError(null);
10011
10114
  }
10012
10115
  }) : null;
10013
- return h2(AgentPresetCatalogContext.Provider, {
10116
+ return h2(ModelCatalogContext.Provider, {
10117
+ value: model.modelCatalog ?? EMPTY_MODEL_CATALOG
10118
+ }, h2(AgentPresetCatalogContext.Provider, {
10014
10119
  value: model.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
10015
10120
  }, h2(
10016
10121
  "section",
@@ -10029,18 +10134,18 @@ function WecomSettingsTab({ rpcCall }) {
10029
10134
  }),
10030
10135
  h2("div", { className: "ddt-visuallyHidden", role: "status", "aria-live": "polite" }, notice),
10031
10136
  model.phase === "loading" ? h2(LoadingView4) : model.phase === "error" ? h2("div", { className: "ddt-card dim-surfaceCard" }, h2("div", { className: "ddt-inlineError dim-inlineError" }, h2("h3", null, "\u65E0\u6CD5\u8BFB\u53D6\u4F01\u4E1A\u5FAE\u4FE1\u673A\u5668\u4EBA\u72B6\u6001"), h2("p", null, model.error?.message), h2(Button10, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6"))) : h2(
10032
- React17.Fragment,
10137
+ React18.Fragment,
10033
10138
  null,
10034
10139
  credentialView,
10035
10140
  provisionView,
10036
10141
  model.bots.length === 0 && !provision && !credentialOpen ? h2(EmptyView4, { busy, onStart: () => void startProvisioning() }) : null,
10037
10142
  botList
10038
10143
  )
10039
- ));
10144
+ )));
10040
10145
  }
10041
10146
 
10042
10147
  // plugin-src/client/channels/weixin/index.js
10043
- var React18 = __toESM(require("react"), 1);
10148
+ var React19 = __toESM(require("react"), 1);
10044
10149
 
10045
10150
  // plugin-src/client/channels/weixin/api.js
10046
10151
  var WEIXIN_RPC_CHANNEL = "/weixin";
@@ -10053,6 +10158,7 @@ var WEIXIN_ENDPOINTS = Object.freeze({
10053
10158
  reconnectBot: "bot.reconnect",
10054
10159
  deleteBot: "bot.delete",
10055
10160
  setWorkspace: "bot.workspace.set",
10161
+ setModel: SET_MODEL_ENDPOINT,
10056
10162
  setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
10057
10163
  setContextEnhancement: "bot.context-enhancement.set",
10058
10164
  setAccessPolicy: "bot.access-policy.set"
@@ -10145,6 +10251,7 @@ function normalizeBot5(value) {
10145
10251
  connected,
10146
10252
  configured: value.configured === true,
10147
10253
  workspace: string(value.workspace).slice(0, 4096),
10254
+ model: normalizeModelSelection(value.model),
10148
10255
  agentPreset: normalizeAgentPresetId(value.agentPreset),
10149
10256
  contextEnhancement: normalizeContextEnhancementConfig(value.contextEnhancement),
10150
10257
  ...Object.hasOwn(value, "accessPolicy") ? { accessPolicy: normalizeAccessPolicy(value.accessPolicy) } : {},
@@ -10184,7 +10291,8 @@ function normalizeSnapshot7(value) {
10184
10291
  },
10185
10292
  provisioning: value.provisioning ? normalizeProvisioning5(value.provisioning) : null,
10186
10293
  testMessage: normalizeTestMessage4(value.testMessage),
10187
- agentPresetCatalog: normalizeAgentPresetCatalog(value.agentPresetCatalog)
10294
+ agentPresetCatalog: normalizeAgentPresetCatalog(value.agentPresetCatalog),
10295
+ modelCatalog: normalizeModelCatalog(value.modelCatalog)
10188
10296
  };
10189
10297
  }
10190
10298
  function presentError8(error) {
@@ -10199,7 +10307,7 @@ function formatRemaining5(milliseconds) {
10199
10307
  }
10200
10308
 
10201
10309
  // plugin-src/client/channels/weixin/index.js
10202
- var Button12 = React18.forwardRef(function Button13({ children, kind = "secondary", className = "", ...props }, ref) {
10310
+ var Button12 = React19.forwardRef(function Button13({ children, kind = "secondary", className = "", ...props }, ref) {
10203
10311
  return h2("button", {
10204
10312
  ...props,
10205
10313
  ref,
@@ -10272,14 +10380,14 @@ function EmptyView5({ onStart, busy }) {
10272
10380
  );
10273
10381
  }
10274
10382
  function QrPanel4({ provision, now, busy, onRefresh, onCancel }) {
10275
- const [imageFailed, setImageFailed] = React18.useState(false);
10383
+ const [imageFailed, setImageFailed] = React19.useState(false);
10276
10384
  const source = safeQrSource5(provision.qrCodeDataUrl);
10277
10385
  const href = safeVerificationUrl(provision.verificationUrl);
10278
10386
  const remaining = Math.max(0, provision.expiresAt - now);
10279
10387
  const expired = remaining === 0 || provision.status === "expired";
10280
10388
  const duration = Math.max(1, provision.durationMs ?? 5 * 6e4);
10281
10389
  const progress = Math.round(Math.min(1, remaining / duration) * 100);
10282
- React18.useEffect(() => setImageFailed(false), [source]);
10390
+ React19.useEffect(() => setImageFailed(false), [source]);
10283
10391
  return h2(
10284
10392
  "div",
10285
10393
  { className: "dxw-card dim-surfaceCard" },
@@ -10346,9 +10454,9 @@ function QrPanel4({ provision, now, busy, onRefresh, onCancel }) {
10346
10454
  );
10347
10455
  }
10348
10456
  function VerificationPanel({ provision, busy, onSubmit, onCancel }) {
10349
- const [code, setCode] = React18.useState("");
10457
+ const [code, setCode] = React19.useState("");
10350
10458
  const valid = /^\d{4,8}$/.test(code);
10351
- React18.useEffect(() => setCode(""), [provision.attemptId]);
10459
+ React19.useEffect(() => setCode(""), [provision.attemptId]);
10352
10460
  return h2(
10353
10461
  "div",
10354
10462
  { className: "dxw-card dim-surfaceCard" },
@@ -10446,6 +10554,7 @@ function AccountCard4({
10446
10554
  removing,
10447
10555
  onReconnect,
10448
10556
  onWorkspaceSave,
10557
+ onModelSave,
10449
10558
  onAgentPresetSave,
10450
10559
  onContextEnhancementSave,
10451
10560
  onRequestRemove,
@@ -10495,6 +10604,11 @@ function AccountCard4({
10495
10604
  disabled: Boolean(busy),
10496
10605
  onSave: onWorkspaceSave
10497
10606
  }),
10607
+ h2(ModelEditor, {
10608
+ model: account.model,
10609
+ disabled: Boolean(busy),
10610
+ onSave: onModelSave
10611
+ }),
10498
10612
  h2(AgentPresetEditor, {
10499
10613
  agentPreset: account.agentPreset,
10500
10614
  disabled: Boolean(busy),
@@ -10572,6 +10686,7 @@ function AccountList2(props) {
10572
10686
  removing: props.removeTarget === account.botId,
10573
10687
  onReconnect: () => props.onReconnect(account),
10574
10688
  onWorkspaceSave: (workspace) => props.onWorkspaceSave(account, workspace),
10689
+ onModelSave: (model) => props.onModelSave(account, model),
10575
10690
  onAgentPresetSave: (agentPreset) => props.onAgentPresetSave(account, agentPreset),
10576
10691
  onContextEnhancementSave: (config) => props.onContextEnhancementSave(account, config),
10577
10692
  onRequestRemove: () => props.onRequestRemove(account),
@@ -10592,41 +10707,42 @@ function mergeWeixinProvisioningSnapshot(current, incoming, { restoreProvisionin
10592
10707
  };
10593
10708
  }
10594
10709
  function WeixinSettingsTab({ rpcCall }) {
10595
- const [model, setModel] = React18.useState({
10710
+ const [model, setModel] = React19.useState({
10596
10711
  phase: "loading",
10597
10712
  bots: [],
10598
10713
  totals: EMPTY_TOTALS3,
10599
10714
  revision: 0,
10600
10715
  error: null,
10601
- agentPresetCatalog: EMPTY_AGENT_PRESET_CATALOG
10716
+ agentPresetCatalog: EMPTY_AGENT_PRESET_CATALOG,
10717
+ modelCatalog: EMPTY_MODEL_CATALOG
10602
10718
  });
10603
- const [provision, setProvision] = React18.useState(null);
10604
- const [busy, setBusy] = React18.useState(false);
10605
- const [busyByBot, setBusyByBot] = React18.useState({});
10606
- const [feedbackByBot, setFeedbackByBot] = React18.useState({});
10607
- const [removeTarget, setRemoveTarget] = React18.useState(null);
10608
- const [notice, setNotice] = React18.useState("");
10609
- const [now, setNow] = React18.useState(() => Date.now());
10610
- const addButtonRef = React18.useRef(null);
10611
- const mountedRef = React18.useRef(true);
10719
+ const [provision, setProvision] = React19.useState(null);
10720
+ const [busy, setBusy] = React19.useState(false);
10721
+ const [busyByBot, setBusyByBot] = React19.useState({});
10722
+ const [feedbackByBot, setFeedbackByBot] = React19.useState({});
10723
+ const [removeTarget, setRemoveTarget] = React19.useState(null);
10724
+ const [notice, setNotice] = React19.useState("");
10725
+ const [now, setNow] = React19.useState(() => Date.now());
10726
+ const addButtonRef = React19.useRef(null);
10727
+ const mountedRef = React19.useRef(true);
10612
10728
  const workspaceFence = useWorkspaceSnapshotFence();
10613
10729
  const scheduleAnimationFrame = useAnimationFrameScheduler();
10614
- React18.useEffect(() => {
10730
+ React19.useEffect(() => {
10615
10731
  mountedRef.current = true;
10616
10732
  return () => {
10617
10733
  mountedRef.current = false;
10618
10734
  };
10619
10735
  }, []);
10620
- const announce = React18.useCallback((value) => {
10736
+ const announce = React19.useCallback((value) => {
10621
10737
  setNotice("");
10622
10738
  scheduleAnimationFrame(() => {
10623
10739
  if (value) setNotice(value);
10624
10740
  }, "announcement");
10625
10741
  }, [scheduleAnimationFrame]);
10626
- const invoke = React18.useCallback(async (endpoint, payload = {}, signal) => {
10742
+ const invoke = React19.useCallback(async (endpoint, payload = {}, signal) => {
10627
10743
  return unwrapRpcResult8(await rpcCall(endpoint, payload, signal));
10628
10744
  }, [rpcCall]);
10629
- const loadStatus = React18.useCallback(async ({
10745
+ const loadStatus = React19.useCallback(async ({
10630
10746
  signal,
10631
10747
  silent = false,
10632
10748
  restoreProvisioning = false
@@ -10643,7 +10759,8 @@ function WeixinSettingsTab({ rpcCall }) {
10643
10759
  totals: snapshot.totals,
10644
10760
  revision: snapshot.revision,
10645
10761
  error: null,
10646
- agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
10762
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
10763
+ modelCatalog: snapshot.modelCatalog ?? EMPTY_MODEL_CATALOG
10647
10764
  });
10648
10765
  if (snapshot.provisioning) {
10649
10766
  setProvision((current) => mergeWeixinProvisioningSnapshot(
@@ -10663,12 +10780,12 @@ function WeixinSettingsTab({ rpcCall }) {
10663
10780
  return void 0;
10664
10781
  }
10665
10782
  }, [invoke, workspaceFence]);
10666
- React18.useEffect(() => {
10783
+ React19.useEffect(() => {
10667
10784
  const controller = new AbortController();
10668
10785
  void loadStatus({ signal: controller.signal, restoreProvisioning: true });
10669
10786
  return () => controller.abort();
10670
10787
  }, [loadStatus]);
10671
- React18.useEffect(() => {
10788
+ React19.useEffect(() => {
10672
10789
  if (model.phase !== "ready") return void 0;
10673
10790
  const controller = new AbortController();
10674
10791
  let running = false;
@@ -10687,12 +10804,12 @@ function WeixinSettingsTab({ rpcCall }) {
10687
10804
  window.clearInterval(timer);
10688
10805
  };
10689
10806
  }, [loadStatus, model.phase]);
10690
- React18.useEffect(() => {
10807
+ React19.useEffect(() => {
10691
10808
  if (!provision || !["pending", "scanned"].includes(provision.status)) return void 0;
10692
10809
  const timer = window.setInterval(() => setNow(Date.now()), 1e3);
10693
10810
  return () => window.clearInterval(timer);
10694
10811
  }, [provision?.attemptId, provision?.status]);
10695
- const startProvisioning = React18.useCallback(async ({ replace = false } = {}) => {
10812
+ const startProvisioning = React19.useCallback(async ({ replace = false } = {}) => {
10696
10813
  setBusy(true);
10697
10814
  try {
10698
10815
  if (replace && provision?.attemptId) {
@@ -10713,7 +10830,7 @@ function WeixinSettingsTab({ rpcCall }) {
10713
10830
  setBusy(false);
10714
10831
  }
10715
10832
  }, [announce, invoke, provision?.attemptId]);
10716
- const cancelProvisioning = React18.useCallback(async () => {
10833
+ const cancelProvisioning = React19.useCallback(async () => {
10717
10834
  setBusy(true);
10718
10835
  try {
10719
10836
  if (provision?.attemptId && !["failed", "expired", "cancelled"].includes(provision.status)) {
@@ -10728,7 +10845,7 @@ function WeixinSettingsTab({ rpcCall }) {
10728
10845
  setBusy(false);
10729
10846
  }
10730
10847
  }, [announce, invoke, provision?.attemptId, provision?.status, scheduleAnimationFrame]);
10731
- const submitVerification = React18.useCallback(async (verifyCode) => {
10848
+ const submitVerification = React19.useCallback(async (verifyCode) => {
10732
10849
  if (!provision?.attemptId) return;
10733
10850
  setBusy(true);
10734
10851
  try {
@@ -10744,7 +10861,7 @@ function WeixinSettingsTab({ rpcCall }) {
10744
10861
  setBusy(false);
10745
10862
  }
10746
10863
  }, [announce, invoke, provision?.attemptId]);
10747
- React18.useEffect(() => {
10864
+ React19.useEffect(() => {
10748
10865
  const attemptId = provision?.attemptId;
10749
10866
  if (!attemptId || !["pending", "scanned", "connecting"].includes(provision.status)) return void 0;
10750
10867
  const controller = new AbortController();
@@ -10792,7 +10909,7 @@ function WeixinSettingsTab({ rpcCall }) {
10792
10909
  controller.abort();
10793
10910
  };
10794
10911
  }, [announce, invoke, loadStatus, provision?.attemptId, provision?.status, provision?.pollIntervalMs]);
10795
- const setBotBusy = React18.useCallback((botId, value) => {
10912
+ const setBotBusy = React19.useCallback((botId, value) => {
10796
10913
  setBusyByBot((current) => {
10797
10914
  const next = { ...current };
10798
10915
  if (value) next[botId] = value;
@@ -10800,7 +10917,7 @@ function WeixinSettingsTab({ rpcCall }) {
10800
10917
  return next;
10801
10918
  });
10802
10919
  }, []);
10803
- const reconnect = React18.useCallback(async (account) => {
10920
+ const reconnect = React19.useCallback(async (account) => {
10804
10921
  const snapshotVersion = workspaceFence.beginMutation();
10805
10922
  setBotBusy(account.botId, "reconnect");
10806
10923
  setFeedbackByBot((current) => {
@@ -10819,7 +10936,8 @@ function WeixinSettingsTab({ rpcCall }) {
10819
10936
  bots: snapshot.bots,
10820
10937
  totals: snapshot.totals,
10821
10938
  revision: snapshot.revision,
10822
- agentPresetCatalog: snapshot.agentPresetCatalog ?? current.agentPresetCatalog
10939
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? current.agentPresetCatalog,
10940
+ modelCatalog: snapshot.modelCatalog ?? current.modelCatalog
10823
10941
  }));
10824
10942
  }
10825
10943
  const refreshed = snapshot.bots.find((bot) => bot.botId === account.botId);
@@ -10851,7 +10969,7 @@ function WeixinSettingsTab({ rpcCall }) {
10851
10969
  setBotBusy(account.botId, null);
10852
10970
  }
10853
10971
  }, [announce, invoke, loadStatus, setBotBusy, workspaceFence]);
10854
- const saveWorkspace = React18.useCallback(async (account, workspace) => {
10972
+ const saveWorkspace = React19.useCallback(async (account, workspace) => {
10855
10973
  const workspaceVersion = workspaceFence.beginMutation();
10856
10974
  setBotBusy(account.botId, "workspace");
10857
10975
  try {
@@ -10866,7 +10984,8 @@ function WeixinSettingsTab({ rpcCall }) {
10866
10984
  totals: snapshot.totals,
10867
10985
  revision: snapshot.revision,
10868
10986
  error: null,
10869
- agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
10987
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
10988
+ modelCatalog: snapshot.modelCatalog ?? EMPTY_MODEL_CATALOG
10870
10989
  });
10871
10990
  }
10872
10991
  } finally {
@@ -10875,7 +10994,7 @@ function WeixinSettingsTab({ rpcCall }) {
10875
10994
  if (mountedRef.current) setBotBusy(account.botId, null);
10876
10995
  }
10877
10996
  }, [invoke, loadStatus, setBotBusy, workspaceFence]);
10878
- const saveBotSetting = React18.useCallback(async (account, operation, endpoint, payload) => {
10997
+ const saveBotSetting = React19.useCallback(async (account, operation, endpoint, payload) => {
10879
10998
  const snapshotVersion = workspaceFence.beginMutation();
10880
10999
  setBotBusy(account.botId, operation);
10881
11000
  try {
@@ -10890,7 +11009,8 @@ function WeixinSettingsTab({ rpcCall }) {
10890
11009
  totals: snapshot.totals,
10891
11010
  revision: snapshot.revision,
10892
11011
  error: null,
10893
- agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
11012
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
11013
+ modelCatalog: snapshot.modelCatalog ?? EMPTY_MODEL_CATALOG
10894
11014
  });
10895
11015
  }
10896
11016
  } finally {
@@ -10899,7 +11019,7 @@ function WeixinSettingsTab({ rpcCall }) {
10899
11019
  if (mountedRef.current) setBotBusy(account.botId, null);
10900
11020
  }
10901
11021
  }, [invoke, loadStatus, setBotBusy, workspaceFence]);
10902
- const remove = React18.useCallback(async (account) => {
11022
+ const remove = React19.useCallback(async (account) => {
10903
11023
  const snapshotVersion = workspaceFence.beginMutation();
10904
11024
  setBotBusy(account.botId, "delete");
10905
11025
  try {
@@ -10913,7 +11033,8 @@ function WeixinSettingsTab({ rpcCall }) {
10913
11033
  bots: snapshot.bots,
10914
11034
  totals: snapshot.totals,
10915
11035
  revision: snapshot.revision,
10916
- agentPresetCatalog: snapshot.agentPresetCatalog ?? current.agentPresetCatalog
11036
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? current.agentPresetCatalog,
11037
+ modelCatalog: snapshot.modelCatalog ?? current.modelCatalog
10917
11038
  }));
10918
11039
  }
10919
11040
  setRemoveTarget(null);
@@ -10958,7 +11079,9 @@ function WeixinSettingsTab({ rpcCall }) {
10958
11079
  onClose: () => void cancelProvisioning()
10959
11080
  });
10960
11081
  }
10961
- return h2(AgentPresetCatalogContext.Provider, {
11082
+ return h2(ModelCatalogContext.Provider, {
11083
+ value: model.modelCatalog ?? EMPTY_MODEL_CATALOG
11084
+ }, h2(AgentPresetCatalogContext.Provider, {
10962
11085
  value: model.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
10963
11086
  }, h2(
10964
11087
  "section",
@@ -10983,7 +11106,7 @@ function WeixinSettingsTab({ rpcCall }) {
10983
11106
  h2(Button12, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6")
10984
11107
  )
10985
11108
  ) : h2(
10986
- React18.Fragment,
11109
+ React19.Fragment,
10987
11110
  null,
10988
11111
  provisionView,
10989
11112
  model.bots.length === 0 && !provision ? h2(EmptyView5, { onStart: () => void startProvisioning(), busy }) : null,
@@ -10994,6 +11117,12 @@ function WeixinSettingsTab({ rpcCall }) {
10994
11117
  removeTarget,
10995
11118
  onReconnect: (account) => void reconnect(account),
10996
11119
  onWorkspaceSave: saveWorkspace,
11120
+ onModelSave: (account, selectedModel) => saveBotSetting(
11121
+ account,
11122
+ "model",
11123
+ WEIXIN_ENDPOINTS.setModel,
11124
+ { model: selectedModel }
11125
+ ),
10997
11126
  onAgentPresetSave: (account, agentPreset) => saveBotSetting(
10998
11127
  account,
10999
11128
  "preset",
@@ -11011,7 +11140,7 @@ function WeixinSettingsTab({ rpcCall }) {
11011
11140
  onCancelRemove: () => setRemoveTarget(null)
11012
11141
  }) : null
11013
11142
  )
11014
- ));
11143
+ )));
11015
11144
  }
11016
11145
 
11017
11146
  // plugin-src/client/channels/weixin/styles.js
@@ -11142,6 +11271,7 @@ var WHATSAPP_ENDPOINTS = Object.freeze({
11142
11271
  deleteBot: "bot.delete",
11143
11272
  setAccessPolicy: "bot.access-policy.set",
11144
11273
  setWorkspace: "bot.workspace.set",
11274
+ setModel: SET_MODEL_ENDPOINT,
11145
11275
  setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
11146
11276
  setContextEnhancement: "bot.context-enhancement.set"
11147
11277
  });
@@ -11208,6 +11338,7 @@ function normalizeBot6(value) {
11208
11338
  connected,
11209
11339
  state: connected ? "connected" : state,
11210
11340
  workspace: text5(value.workspace, "", 4096),
11341
+ model: normalizeModelSelection(value.model),
11211
11342
  agentPreset: normalizeAgentPresetId(value.agentPreset),
11212
11343
  contextEnhancement: normalizeContextEnhancementConfig(value.contextEnhancement),
11213
11344
  ...Object.hasOwn(value, "accessPolicy") ? { accessPolicy: normalizeAccessPolicy(value.accessPolicy) } : {},
@@ -11237,7 +11368,8 @@ function normalizeSnapshot8(value) {
11237
11368
  bots,
11238
11369
  totals: { configured: bots.length, connected: bots.filter((bot) => bot.connected).length },
11239
11370
  provisioning: source.provisioning ? normalizeProvisioning6(source.provisioning) : null,
11240
- agentPresetCatalog: normalizeAgentPresetCatalog(source.agentPresetCatalog)
11371
+ agentPresetCatalog: normalizeAgentPresetCatalog(source.agentPresetCatalog),
11372
+ modelCatalog: normalizeModelCatalog(source.modelCatalog)
11241
11373
  };
11242
11374
  }
11243
11375
  function presentError9(error) {
@@ -11252,7 +11384,7 @@ function formatRemaining6(milliseconds) {
11252
11384
  }
11253
11385
 
11254
11386
  // plugin-src/client/channels/whatsapp/index.js
11255
- var React19 = __toESM(require("react"), 1);
11387
+ var React20 = __toESM(require("react"), 1);
11256
11388
 
11257
11389
  // plugin-src/client/channels/whatsapp/styles.js
11258
11390
  var WHATSAPP_STYLE_ID = "xmanrui-dsh-im-whatsapp-settings";
@@ -11277,7 +11409,7 @@ function installWhatsappStyles() {
11277
11409
 
11278
11410
  // plugin-src/client/channels/whatsapp/index.js
11279
11411
  var ACTIVE_STATES3 = /* @__PURE__ */ new Set(["pending", "connecting"]);
11280
- var Button14 = React19.forwardRef(function Button15({ children, kind = "secondary", className = "", ...props }, ref) {
11412
+ var Button14 = React20.forwardRef(function Button15({ children, kind = "secondary", className = "", ...props }, ref) {
11281
11413
  return h2("button", {
11282
11414
  ...props,
11283
11415
  ref,
@@ -11499,6 +11631,7 @@ function WhatsappAccountCard({
11499
11631
  removing,
11500
11632
  onReconnect,
11501
11633
  onWorkspaceSave,
11634
+ onModelSave,
11502
11635
  onAgentPresetSave,
11503
11636
  onContextEnhancementSave,
11504
11637
  onRequestRemove,
@@ -11557,6 +11690,11 @@ function WhatsappAccountCard({
11557
11690
  disabled: Boolean(busy),
11558
11691
  onSave: onWorkspaceSave
11559
11692
  }),
11693
+ h2(ModelEditor, {
11694
+ model: account.model,
11695
+ disabled: Boolean(busy),
11696
+ onSave: onModelSave
11697
+ }),
11560
11698
  h2(AgentPresetEditor, {
11561
11699
  agentPreset: account.agentPreset,
11562
11700
  disabled: Boolean(busy),
@@ -11609,23 +11747,24 @@ function WhatsappAccountCard({
11609
11747
  );
11610
11748
  }
11611
11749
  function WhatsappSettingsTab({ rpcCall }) {
11612
- const [model, setModel] = React19.useState({
11750
+ const [model, setModel] = React20.useState({
11613
11751
  phase: "loading",
11614
11752
  bots: [],
11615
11753
  totals: { configured: 0, connected: 0 },
11616
11754
  error: null,
11617
- agentPresetCatalog: EMPTY_AGENT_PRESET_CATALOG
11755
+ agentPresetCatalog: EMPTY_AGENT_PRESET_CATALOG,
11756
+ modelCatalog: EMPTY_MODEL_CATALOG
11618
11757
  });
11619
- const [provision, setProvision] = React19.useState(null);
11620
- const [busy, setBusy] = React19.useState(false);
11621
- const [busyByBot, setBusyByBot] = React19.useState({});
11622
- const [testNoticeByBot, setTestNoticeByBot] = React19.useState({});
11623
- const [removeTarget, setRemoveTarget] = React19.useState(null);
11624
- const [now, setNow] = React19.useState(Date.now());
11625
- const mounted = React19.useRef(true);
11758
+ const [provision, setProvision] = React20.useState(null);
11759
+ const [busy, setBusy] = React20.useState(false);
11760
+ const [busyByBot, setBusyByBot] = React20.useState({});
11761
+ const [testNoticeByBot, setTestNoticeByBot] = React20.useState({});
11762
+ const [removeTarget, setRemoveTarget] = React20.useState(null);
11763
+ const [now, setNow] = React20.useState(Date.now());
11764
+ const mounted = React20.useRef(true);
11626
11765
  const workspaceFence = useWorkspaceSnapshotFence();
11627
- const addButtonRef = React19.useRef(null);
11628
- React19.useEffect(() => {
11766
+ const addButtonRef = React20.useRef(null);
11767
+ React20.useEffect(() => {
11629
11768
  const disposeDingtalk = installDingtalkStyles();
11630
11769
  const disposeWhatsapp = installWhatsappStyles();
11631
11770
  mounted.current = true;
@@ -11635,11 +11774,11 @@ function WhatsappSettingsTab({ rpcCall }) {
11635
11774
  disposeDingtalk();
11636
11775
  };
11637
11776
  }, []);
11638
- const invoke = React19.useCallback(async (endpoint, payload = {}, signal) => {
11777
+ const invoke = React20.useCallback(async (endpoint, payload = {}, signal) => {
11639
11778
  if (typeof rpcCall !== "function") throw new TypeError("WhatsApp \u8BBE\u7F6E\u9875\u7F3A\u5C11 RPC \u8FDE\u63A5");
11640
11779
  return unwrapRpcResult9(await rpcCall(endpoint, payload, signal));
11641
11780
  }, [rpcCall]);
11642
- const loadStatus = React19.useCallback(async ({ signal, silent = false, restore = false } = {}) => {
11781
+ const loadStatus = React20.useCallback(async ({ signal, silent = false, restore = false } = {}) => {
11643
11782
  const workspaceVersion = workspaceFence.beginStatus();
11644
11783
  if (workspaceVersion === null) return void 0;
11645
11784
  if (!silent && mounted.current) setModel((current) => ({ ...current, phase: "loading", error: null }));
@@ -11651,7 +11790,8 @@ function WhatsappSettingsTab({ rpcCall }) {
11651
11790
  bots: snapshot.bots,
11652
11791
  totals: snapshot.totals,
11653
11792
  error: null,
11654
- agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
11793
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
11794
+ modelCatalog: snapshot.modelCatalog ?? EMPTY_MODEL_CATALOG
11655
11795
  });
11656
11796
  if (restore && snapshot.provisioning) setProvision({
11657
11797
  ...snapshot.provisioning,
@@ -11669,12 +11809,12 @@ function WhatsappSettingsTab({ rpcCall }) {
11669
11809
  return void 0;
11670
11810
  }
11671
11811
  }, [invoke, workspaceFence]);
11672
- React19.useEffect(() => {
11812
+ React20.useEffect(() => {
11673
11813
  const controller = new AbortController();
11674
11814
  void loadStatus({ signal: controller.signal, restore: true });
11675
11815
  return () => controller.abort();
11676
11816
  }, [loadStatus]);
11677
- React19.useEffect(() => {
11817
+ React20.useEffect(() => {
11678
11818
  if (model.phase !== "ready") return void 0;
11679
11819
  const controller = new AbortController();
11680
11820
  const timer = window.setInterval(
@@ -11686,12 +11826,12 @@ function WhatsappSettingsTab({ rpcCall }) {
11686
11826
  window.clearInterval(timer);
11687
11827
  };
11688
11828
  }, [loadStatus, model.phase]);
11689
- React19.useEffect(() => {
11829
+ React20.useEffect(() => {
11690
11830
  if (!provision || !ACTIVE_STATES3.has(provision.status)) return void 0;
11691
11831
  const timer = window.setInterval(() => mounted.current && setNow(Date.now()), 1e3);
11692
11832
  return () => window.clearInterval(timer);
11693
11833
  }, [provision?.attemptId, provision?.status]);
11694
- const startProvisioning = React19.useCallback(async (replace = false) => {
11834
+ const startProvisioning = React20.useCallback(async (replace = false) => {
11695
11835
  setBusy(true);
11696
11836
  try {
11697
11837
  if (replace && provision?.attemptId) {
@@ -11709,7 +11849,7 @@ function WhatsappSettingsTab({ rpcCall }) {
11709
11849
  if (mounted.current) setBusy(false);
11710
11850
  }
11711
11851
  }, [invoke, provision?.attemptId]);
11712
- const closeProvision = React19.useCallback(async () => {
11852
+ const closeProvision = React20.useCallback(async () => {
11713
11853
  setBusy(true);
11714
11854
  try {
11715
11855
  if (provision?.attemptId && ACTIVE_STATES3.has(provision.status)) {
@@ -11720,7 +11860,7 @@ function WhatsappSettingsTab({ rpcCall }) {
11720
11860
  if (mounted.current) setBusy(false);
11721
11861
  }
11722
11862
  }, [invoke, provision?.attemptId, provision?.status]);
11723
- React19.useEffect(() => {
11863
+ React20.useEffect(() => {
11724
11864
  const attemptId = provision?.attemptId;
11725
11865
  if (!attemptId || !ACTIVE_STATES3.has(provision.status)) return void 0;
11726
11866
  const controller = new AbortController();
@@ -11761,7 +11901,7 @@ function WhatsappSettingsTab({ rpcCall }) {
11761
11901
  if (timer) window.clearTimeout(timer);
11762
11902
  };
11763
11903
  }, [invoke, loadStatus, provision?.attemptId, provision?.status]);
11764
- const botAction = React19.useCallback(async (account, operation, endpoint, payload) => {
11904
+ const botAction = React20.useCallback(async (account, operation, endpoint, payload) => {
11765
11905
  const snapshotVersion = workspaceFence.beginMutation();
11766
11906
  setBusyByBot((current) => ({ ...current, [account.botId]: operation }));
11767
11907
  if (operation === "reconnect") {
@@ -11780,7 +11920,8 @@ function WhatsappSettingsTab({ rpcCall }) {
11780
11920
  bots: snapshot.bots,
11781
11921
  totals: snapshot.totals,
11782
11922
  error: null,
11783
- agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
11923
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
11924
+ modelCatalog: snapshot.modelCatalog ?? EMPTY_MODEL_CATALOG
11784
11925
  });
11785
11926
  if (operation === "reconnect") {
11786
11927
  setTestNoticeByBot((current) => ({
@@ -11832,6 +11973,12 @@ function WhatsappSettingsTab({ rpcCall }) {
11832
11973
  WHATSAPP_ENDPOINTS.setWorkspace,
11833
11974
  { botId: account.botId, workspace }
11834
11975
  ),
11976
+ onModelSave: (selectedModel) => botAction(
11977
+ account,
11978
+ "model",
11979
+ WHATSAPP_ENDPOINTS.setModel,
11980
+ { botId: account.botId, model: selectedModel }
11981
+ ),
11835
11982
  onAgentPresetSave: (agentPreset) => botAction(
11836
11983
  account,
11837
11984
  "preset",
@@ -11855,7 +12002,9 @@ function WhatsappSettingsTab({ rpcCall }) {
11855
12002
  }
11856
12003
  }))))
11857
12004
  ) : null;
11858
- return h2(AgentPresetCatalogContext.Provider, {
12005
+ return h2(ModelCatalogContext.Provider, {
12006
+ value: model.modelCatalog ?? EMPTY_MODEL_CATALOG
12007
+ }, h2(AgentPresetCatalogContext.Provider, {
11859
12008
  value: model.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
11860
12009
  }, h2(
11861
12010
  "section",
@@ -11880,7 +12029,7 @@ function WhatsappSettingsTab({ rpcCall }) {
11880
12029
  h2(Button14, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6")
11881
12030
  )
11882
12031
  ) : h2(
11883
- React19.Fragment,
12032
+ React20.Fragment,
11884
12033
  null,
11885
12034
  provision?.status === "pending" ? h2(QrPanel5, {
11886
12035
  provision,
@@ -11896,14 +12045,14 @@ function WhatsappSettingsTab({ rpcCall }) {
11896
12045
  }) : model.bots.length === 0 ? h2(EmptyView6, { busy, onStart: () => void startProvisioning(false) }) : null,
11897
12046
  botList
11898
12047
  )
11899
- ));
12048
+ )));
11900
12049
  }
11901
12050
 
11902
12051
  // plugin-src/client/delivery-settings.js
11903
- var React21 = __toESM(require("react"), 1);
12052
+ var React23 = __toESM(require("react"), 1);
11904
12053
 
11905
12054
  // plugin-src/client/access-policy-settings.js
11906
- var React20 = __toESM(require("react"), 1);
12055
+ var React21 = __toESM(require("react"), 1);
11907
12056
  var ACCESS_POLICY_ENDPOINT = "bot.access-policy.set";
11908
12057
  var ACCESS_CHANNEL_DEFINITIONS = Object.freeze({
11909
12058
  weixin: Object.freeze({
@@ -12005,8 +12154,8 @@ function ScenePolicyEditor({
12005
12154
  unsupported = false,
12006
12155
  onChange
12007
12156
  }) {
12008
- const ownerHelpId = React20.useId();
12009
- const emptyAllowlistHelpId = React20.useId();
12157
+ const ownerHelpId = React21.useId();
12158
+ const emptyAllowlistHelpId = React21.useId();
12010
12159
  const allowlist = policy.mode === "allowlist";
12011
12160
  const collectionKey = allowlist ? "users" : "commandPermissionOverrides";
12012
12161
  const branchKey = allowlist ? "allowlist" : "open";
@@ -12058,7 +12207,7 @@ function ScenePolicyEditor({
12058
12207
  h2("strong", null, "\u5F53\u524D\u6E20\u9053\u4E0D\u652F\u6301\u7FA4\u804A"),
12059
12208
  h2("p", null, "\u6B64\u533A\u57DF\u65E0\u9700\u914D\u7F6E\uFF0C\u4FDD\u5B58\u79C1\u804A\u8BBE\u7F6E\u65F6\u4F1A\u4FDD\u7559\u73B0\u6709\u7FA4\u804A\u7B56\u7565\u3002")
12060
12209
  ) : h2(
12061
- React20.Fragment,
12210
+ React21.Fragment,
12062
12211
  null,
12063
12212
  h2(
12064
12213
  "div",
@@ -12199,16 +12348,16 @@ function AccessPolicySettingsPage({ channel: channel4, account, rpcCall, onSaved
12199
12348
  const definition = ACCESS_CHANNEL_DEFINITIONS[channel4];
12200
12349
  const initialPolicy = normalizeAccessPolicy(account?.accessPolicy);
12201
12350
  const initialKey = JSON.stringify(initialPolicy);
12202
- const [draft, setDraft] = React20.useState(() => clonePolicy(
12351
+ const [draft, setDraft] = React21.useState(() => clonePolicy(
12203
12352
  initialPolicy ?? DEFAULT_ACCESS_POLICY
12204
12353
  ));
12205
- const [saving, setSaving] = React20.useState(false);
12206
- const [feedback, setFeedback] = React20.useState(null);
12207
- React20.useEffect(() => {
12354
+ const [saving, setSaving] = React21.useState(false);
12355
+ const [feedback, setFeedback] = React21.useState(null);
12356
+ React21.useEffect(() => {
12208
12357
  const next = normalizeAccessPolicy(account?.accessPolicy);
12209
12358
  setDraft(clonePolicy(next ?? DEFAULT_ACCESS_POLICY));
12210
12359
  }, [account?.botId, initialKey]);
12211
- React20.useEffect(() => {
12360
+ React21.useEffect(() => {
12212
12361
  setFeedback(null);
12213
12362
  }, [account?.botId]);
12214
12363
  if (!definition) {
@@ -12298,80 +12447,682 @@ function AccessPolicySettingsPage({ channel: channel4, account, rpcCall, onSaved
12298
12447
  );
12299
12448
  }
12300
12449
 
12301
- // plugin-src/client/delivery-settings.js
12302
- var DELIVERY_RPC_CHANNEL = "/dsh-im-delivery";
12303
- var DELIVERY_DOCS_URL = Object.freeze({
12304
- zh: "https://github.com/xmanrui/dsh-im/blob/main/PROACTIVE_DELIVERY.md",
12305
- en: "https://github.com/xmanrui/dsh-im/blob/main/PROACTIVE_DELIVERY.en.md"
12306
- });
12307
- var DELIVERY_ENDPOINTS = Object.freeze({
12308
- list: "target.list",
12309
- listSuggestions: "target.suggestion.list",
12310
- create: "target.create",
12311
- update: "target.update",
12312
- delete: "target.delete",
12313
- test: "target.test"
12314
- });
12315
- var BOT_SETTINGS_TABS = Object.freeze([
12316
- Object.freeze({ id: "delivery", label: "\u6295\u9012\u8BBE\u7F6E" }),
12317
- Object.freeze({ id: "access", label: "\u8BBF\u95EE\u8BBE\u7F6E" })
12318
- ]);
12319
- var CHANNEL_DEFINITIONS = Object.freeze({
12320
- weixin: {
12321
- label: "\u5FAE\u4FE1",
12322
- kinds: [{ value: "user", label: "\u7528\u6237" }],
12323
- fields: { user: [{ key: "toUserId", label: "\u5FAE\u4FE1\u7528\u6237 ID", placeholder: "\u586B\u5199\u63A5\u6536\u6D88\u606F\u7684\u7528\u6237 ID" }] }
12324
- },
12325
- feishu: {
12326
- label: "\u98DE\u4E66",
12327
- kinds: [{ value: "user", label: "\u79C1\u804A" }, { value: "group", label: "\u7FA4\u804A" }],
12328
- fields: {
12329
- user: [{ key: "openId", label: "Open ID", placeholder: "ou_xxx" }],
12330
- group: [{ key: "chatId", label: "\u7FA4 Chat ID", placeholder: "oc_xxx" }]
12331
- }
12332
- },
12333
- dingtalk: {
12334
- label: "\u9489\u9489",
12335
- kinds: [{ value: "user", label: "\u7528\u6237" }, { value: "group", label: "\u7FA4\u804A" }],
12336
- fields: {
12337
- user: [{ key: "userId", label: "\u7528\u6237 ID", placeholder: "\u586B\u5199\u9489\u9489\u7528\u6237 ID" }],
12338
- group: [{ key: "openConversationId", label: "\u7FA4 Open Conversation ID", placeholder: "cidxxx" }]
12339
- }
12340
- },
12341
- wecom: {
12342
- label: "\u4F01\u4E1A\u5FAE\u4FE1",
12343
- kinds: [{ value: "user", label: "\u79C1\u804A" }, { value: "group", label: "\u7FA4\u804A" }],
12344
- fields: {
12345
- user: [{ key: "chatId", label: "\u7528\u6237 ID", placeholder: "\u586B\u5199\u4F01\u4E1A\u5FAE\u4FE1\u7528\u6237 ID" }],
12346
- group: [{ key: "chatId", label: "\u7FA4 Chat ID", placeholder: "\u586B\u5199\u7FA4 chatid" }]
12347
- }
12348
- },
12349
- qq: {
12350
- label: "QQ",
12351
- kinds: [{ value: "user", label: "\u5355\u804A" }, { value: "group", label: "\u7FA4\u804A" }],
12352
- fields: {
12353
- user: [{ key: "userOpenId", label: "\u7528\u6237 Open ID", placeholder: "\u586B\u5199 user_openid" }],
12354
- group: [{ key: "groupOpenId", label: "\u7FA4 Open ID", placeholder: "\u586B\u5199 group_openid" }]
12355
- }
12356
- },
12357
- slack: {
12358
- label: "Slack",
12359
- kinds: [{ value: "conversation", label: "\u4F1A\u8BDD" }, { value: "thread", label: "\u7EBF\u7A0B" }],
12360
- fields: {
12361
- conversation: [{ key: "channelId", label: "Channel ID", placeholder: "C0123456789" }],
12362
- thread: [
12363
- { key: "channelId", label: "Channel ID", placeholder: "C0123456789" },
12364
- { key: "threadTs", label: "Thread \u65F6\u95F4\u6233", placeholder: "1712345678.123456" }
12365
- ]
12366
- }
12367
- },
12368
- telegram: {
12369
- label: "Telegram",
12370
- kinds: [{ value: "chat", label: "\u804A\u5929" }, { value: "topic", label: "\u8BDD\u9898" }],
12371
- fields: {
12372
- chat: [{ key: "chatId", label: "Chat ID", placeholder: "-1001234567890", inputMode: "numeric" }],
12373
- topic: [
12374
- { key: "chatId", label: "Chat ID", placeholder: "-1001234567890", inputMode: "numeric" },
12450
+ // plugin-src/client/channels/feishu/group-settings.js
12451
+ var React22 = __toESM(require("react"), 1);
12452
+ var GROUP_MESSAGE_PERMISSION_OPERATION2 = FEISHU_REGISTRATION_OPERATIONS.GROUP_MESSAGE_PERMISSION;
12453
+ function SettingsButton({ children, kind = "secondary", className = "", ...props }) {
12454
+ return h2("button", {
12455
+ ...props,
12456
+ type: "button",
12457
+ className: `dim-deliveryButton ${className}`.trim(),
12458
+ "data-kind": kind
12459
+ }, children);
12460
+ }
12461
+ function groupSettingsFrom(value) {
12462
+ return {
12463
+ groupResponseMode: normalizeGroupResponseMode(value?.groupResponseMode),
12464
+ groupTopicReply: value?.groupTopicReply === true,
12465
+ groupMessagePermissionGranted: value?.groupMessagePermissionGranted === true
12466
+ };
12467
+ }
12468
+ function targetBotFromSnapshot(value, botId) {
12469
+ const snapshot = normalizeBotsSnapshot(value);
12470
+ const bot = snapshot.bots.find((entry) => entry.botId === botId);
12471
+ if (!bot) throw new Error("\u98DE\u4E66\u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u5F53\u524D\u673A\u5668\u4EBA\u7684\u8BBE\u7F6E");
12472
+ return { snapshot, bot };
12473
+ }
12474
+ function safeVerificationHref2(value) {
12475
+ if (!value) return void 0;
12476
+ try {
12477
+ const url = new URL(value);
12478
+ return url.protocol === "https:" && [
12479
+ "accounts.feishu.cn",
12480
+ "accounts.larksuite.com",
12481
+ "open.feishu.cn",
12482
+ "open.larksuite.com"
12483
+ ].includes(url.hostname) && !url.port && !url.username && !url.password ? url.toString() : void 0;
12484
+ } catch {
12485
+ return void 0;
12486
+ }
12487
+ }
12488
+ function safeQrSource7(value) {
12489
+ if (!value) return void 0;
12490
+ return /^data:image\/(?:png|webp|svg\+xml)(?:;charset=[^;,]+)?;base64,/i.test(value) ? value : void 0;
12491
+ }
12492
+ function QrPlaceholder() {
12493
+ return h2("svg", {
12494
+ viewBox: "0 0 24 24",
12495
+ width: 50,
12496
+ height: 50,
12497
+ fill: "currentColor",
12498
+ "aria-hidden": "true"
12499
+ }, h2("path", {
12500
+ d: "M3 3h7v7H3V3Zm2 2v3h3V5H5Zm9-2h7v7h-7V3Zm2 2v3h3V5h-3ZM3 14h7v7H3v-7Zm2 2v3h3v-3H5Zm9-2h3v3h-3v-3Zm4 0h3v7h-3v-3h-2v3h-2v-5h4v-2Z"
12501
+ }));
12502
+ }
12503
+ function GroupResponseModeEditor({
12504
+ value,
12505
+ permissionGranted = false,
12506
+ disabled = false,
12507
+ authorizationDisabled = false,
12508
+ onSave,
12509
+ onAuthorize
12510
+ }) {
12511
+ const current = normalizeGroupResponseMode(value);
12512
+ const [saving, setSaving] = React22.useState(false);
12513
+ const [authorizing, setAuthorizing] = React22.useState(false);
12514
+ const [error, setError] = React22.useState(null);
12515
+ const change = async (event) => {
12516
+ const next = normalizeGroupResponseMode(event.target.value);
12517
+ if (next === current || saving || disabled) return;
12518
+ setSaving(true);
12519
+ setError(null);
12520
+ try {
12521
+ await onSave?.(next);
12522
+ } catch (cause) {
12523
+ setError(cause?.message ?? "\u7FA4\u804A\u54CD\u5E94\u65B9\u5F0F\u4FEE\u6539\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002");
12524
+ } finally {
12525
+ setSaving(false);
12526
+ }
12527
+ };
12528
+ const authorize = async () => {
12529
+ if (current !== "all" || saving || authorizing || disabled || authorizationDisabled) return;
12530
+ setAuthorizing(true);
12531
+ setError(null);
12532
+ try {
12533
+ await onAuthorize?.();
12534
+ } catch (cause) {
12535
+ setError(cause?.message ?? "\u7FA4\u6D88\u606F\u6743\u9650\u6388\u6743\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002");
12536
+ } finally {
12537
+ setAuthorizing(false);
12538
+ }
12539
+ };
12540
+ return h2(
12541
+ "section",
12542
+ {
12543
+ className: "dim-feishuGroupControl",
12544
+ "aria-labelledby": "dim-feishu-group-response-title"
12545
+ },
12546
+ h2(
12547
+ "div",
12548
+ { className: "dim-feishuGroupControlHeader" },
12549
+ h2("h3", { id: "dim-feishu-group-response-title" }, "\u7FA4\u804A\u54CD\u5E94\u65B9\u5F0F"),
12550
+ saving || authorizing ? h2(
12551
+ "span",
12552
+ { className: "dim-feishuGroupControlStatus", role: "status" },
12553
+ saving ? "\u4FDD\u5B58\u4E2D\u2026" : "\u6B63\u5728\u51C6\u5907\u6388\u6743\u2026"
12554
+ ) : null
12555
+ ),
12556
+ h2(
12557
+ "select",
12558
+ {
12559
+ className: "dim-feishuGroupSelect",
12560
+ value: current,
12561
+ disabled: disabled || saving,
12562
+ "aria-label": "\u7FA4\u804A\u54CD\u5E94\u65B9\u5F0F",
12563
+ onChange: (event) => {
12564
+ void change(event);
12565
+ }
12566
+ },
12567
+ h2("option", { value: "mention" }, "\u4EC5\u5728 @\u673A\u5668\u4EBA\u65F6\u54CD\u5E94\uFF08\u63A8\u8350\uFF09"),
12568
+ h2("option", { value: "all" }, "\u54CD\u5E94\u6240\u6709\u7FA4\u6D88\u606F")
12569
+ ),
12570
+ h2(
12571
+ "p",
12572
+ { className: "dim-feishuGroupHelp" },
12573
+ current === "mention" ? permissionGranted ? "\u79C1\u804A\u59CB\u7EC8\u54CD\u5E94\uFF1B\u7FA4\u804A\u4EC5\u5904\u7406\u660E\u786E @\u5F53\u524D\u673A\u5668\u4EBA\u7684\u6D88\u606F\u3002\u7FA4\u6D88\u606F\u6743\u9650\u5DF2\u5F00\u901A\uFF0C\u518D\u6B21\u5207\u6362\u65E0\u9700\u6388\u6743\u3002" : "\u79C1\u804A\u59CB\u7EC8\u54CD\u5E94\uFF1B\u7FA4\u804A\u4EC5\u5904\u7406\u660E\u786E @\u5F53\u524D\u673A\u5668\u4EBA\u7684\u6D88\u606F\u3002\u9009\u62E9\u5168\u90E8\u6D88\u606F\u540E\u4F1A\u6253\u5F00\u98DE\u4E66\u5B98\u65B9\u6388\u6743\u6D41\u7A0B\u3002" : permissionGranted ? "\u5DF2\u5F00\u901A\u201C\u83B7\u53D6\u7FA4\u7EC4\u4E2D\u6240\u6709\u6D88\u606F\u201D\u6743\u9650\uFF08im:message.group_msg\uFF09\uFF1B\u673A\u5668\u4EBA\u4F1A\u5904\u7406\u7FA4\u804A\u4E2D\u7684\u6240\u6709\u53EF\u89C1\u6D88\u606F\u3002" : "\u5C1A\u672A\u786E\u8BA4\u201C\u83B7\u53D6\u7FA4\u7EC4\u4E2D\u6240\u6709\u6D88\u606F\u201D\u6743\u9650\uFF0C\u8BF7\u5B8C\u6210\u98DE\u4E66\u6388\u6743\u3002"
12574
+ ),
12575
+ current === "all" ? h2(
12576
+ "div",
12577
+ { className: "dim-feishuGroupPermissionAction" },
12578
+ h2(SettingsButton, {
12579
+ disabled: disabled || authorizationDisabled || saving || authorizing,
12580
+ "aria-busy": authorizing ? "true" : void 0,
12581
+ "aria-label": permissionGranted ? "\u91CD\u65B0\u6388\u6743\u7FA4\u6D88\u606F\u6743\u9650" : "\u6388\u6743\u7FA4\u6D88\u606F\u6743\u9650",
12582
+ onClick: () => {
12583
+ void authorize();
12584
+ }
12585
+ }, authorizing ? "\u6B63\u5728\u51C6\u5907\u2026" : permissionGranted ? "\u91CD\u65B0\u6388\u6743" : "\u53BB\u6388\u6743")
12586
+ ) : null,
12587
+ error ? h2("p", {
12588
+ className: "dim-feishuGroupError",
12589
+ role: "alert"
12590
+ }, error) : null
12591
+ );
12592
+ }
12593
+ function GroupTopicReplyEditor({ value = false, disabled = false, onSave }) {
12594
+ const current = value === true ? "on" : "off";
12595
+ const [saving, setSaving] = React22.useState(false);
12596
+ const [error, setError] = React22.useState(null);
12597
+ const change = async (event) => {
12598
+ const next = event.target.value === "on";
12599
+ if ((next ? "on" : "off") === current || saving || disabled) return;
12600
+ setSaving(true);
12601
+ setError(null);
12602
+ try {
12603
+ await onSave?.(next);
12604
+ } catch (cause) {
12605
+ setError(cause?.message ?? "\u7FA4\u804A\u4EE5\u8BDD\u9898\u65B9\u5F0F\u56DE\u590D\u8BBE\u7F6E\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002");
12606
+ } finally {
12607
+ setSaving(false);
12608
+ }
12609
+ };
12610
+ return h2(
12611
+ "section",
12612
+ {
12613
+ className: "dim-feishuGroupControl",
12614
+ "aria-labelledby": "dim-feishu-group-topic-title"
12615
+ },
12616
+ h2(
12617
+ "div",
12618
+ { className: "dim-feishuGroupControlHeader" },
12619
+ h2("h3", { id: "dim-feishu-group-topic-title" }, "\u7FA4\u804A\u4EE5\u8BDD\u9898\u65B9\u5F0F\u56DE\u590D"),
12620
+ saving ? h2("span", { className: "dim-feishuGroupControlStatus", role: "status" }, "\u4FDD\u5B58\u4E2D\u2026") : null
12621
+ ),
12622
+ h2(
12623
+ "select",
12624
+ {
12625
+ className: "dim-feishuGroupSelect",
12626
+ value: current,
12627
+ disabled: disabled || saving,
12628
+ "aria-label": "\u7FA4\u804A\u4EE5\u8BDD\u9898\u65B9\u5F0F\u56DE\u590D",
12629
+ onChange: (event) => {
12630
+ void change(event);
12631
+ }
12632
+ },
12633
+ h2("option", { value: "off" }, "\u5173\u95ED\uFF08\u7FA4\u5185\u76F4\u63A5\u56DE\u590D\uFF09"),
12634
+ h2("option", { value: "on" }, "\u5F00\u542F\uFF08\u81EA\u52A8\u5F00\u542F\u72EC\u7ACB\u98DE\u4E66\u8BDD\u9898\uFF09")
12635
+ ),
12636
+ h2(
12637
+ "p",
12638
+ { className: "dim-feishuGroupHelp" },
12639
+ "\u5F00\u542F\u540E\uFF0C\u7FA4\u804A\u4E2D\u5411\u673A\u5668\u4EBA\u63D0\u95EE\u4F1A\u81EA\u52A8\u5F00\u542F\u72EC\u7ACB\u98DE\u4E66\u8BDD\u9898\uFF0C\u56DE\u590D\u843D\u5728\u8BDD\u9898\u5185\uFF1B\u6BCF\u4E2A\u8BDD\u9898\u662F dsh \u4F1A\u8BDD\u5217\u8868\u91CC\u4E00\u6761\u72EC\u7ACB\u4F1A\u8BDD\uFF0C\u4E0A\u4E0B\u6587\u4E92\u4E0D\u4E32\u3002\u79C1\u804A\u4E0D\u53D7\u5F71\u54CD\u3002"
12640
+ ),
12641
+ error ? h2("p", {
12642
+ className: "dim-feishuGroupError",
12643
+ role: "alert"
12644
+ }, error) : null
12645
+ );
12646
+ }
12647
+ function PermissionFlow({ provision, now, busy, botName, onRetry, onCancel, onClose }) {
12648
+ if (!provision) return null;
12649
+ const phase = provision.phase;
12650
+ const error = provision.error;
12651
+ if (phase === "creating" || phase === "connecting") {
12652
+ return h2(
12653
+ "section",
12654
+ {
12655
+ className: "dim-feishuGroupAuthorization dim-feishuGroupAuthorizationState",
12656
+ "aria-live": "polite",
12657
+ "aria-busy": "true"
12658
+ },
12659
+ h2("span", { className: "dim-feishuGroupSpinner", "aria-hidden": "true" }),
12660
+ h2(
12661
+ "div",
12662
+ null,
12663
+ h2("h3", null, phase === "creating" ? "\u6B63\u5728\u51C6\u5907\u6743\u9650\u6388\u6743\u4E8C\u7EF4\u7801" : "\u5DF2\u786E\u8BA4\uFF0C\u6B63\u5728\u542F\u7528\u5168\u90E8\u6D88\u606F\u6A21\u5F0F"),
12664
+ h2("p", null, phase === "creating" ? "\u6B63\u5728\u4E3A\u73B0\u6709\u98DE\u4E66\u5E94\u7528\u7533\u8BF7\u7FA4\u6D88\u606F\u6743\u9650\u4E8C\u7EF4\u7801\uFF0C\u8BF7\u7A0D\u5019\u3002" : "\u6743\u9650\u914D\u7F6E\u5DF2\u63D0\u4EA4\uFF0C\u6B63\u5728\u4FDD\u5B58\u8BBE\u7F6E\u5E76\u91CD\u8FDE\u6B64\u673A\u5668\u4EBA\uFF1B\u6B64\u9636\u6BB5\u65E0\u6CD5\u53D6\u6D88\uFF0C\u5176\u4ED6\u673A\u5668\u4EBA\u4E0D\u4F1A\u4E2D\u65AD\u3002")
12665
+ )
12666
+ );
12667
+ }
12668
+ if (phase === "error") {
12669
+ return h2(
12670
+ "section",
12671
+ {
12672
+ className: "dim-feishuGroupAuthorization dim-feishuGroupAuthorizationError",
12673
+ role: "alert"
12674
+ },
12675
+ h2(
12676
+ "div",
12677
+ null,
12678
+ h2("h3", null, "\u7FA4\u6D88\u606F\u6743\u9650\u6CA1\u6709\u5F00\u901A\u5B8C\u6210"),
12679
+ h2("p", null, error?.message ?? "\u7FA4\u6D88\u606F\u6743\u9650\u6388\u6743\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002"),
12680
+ error?.code ? h2("code", null, error.code) : null
12681
+ ),
12682
+ h2(
12683
+ "div",
12684
+ { className: "dim-feishuGroupAuthorizationActions" },
12685
+ h2(
12686
+ SettingsButton,
12687
+ { kind: "primary", onClick: onRetry, disabled: busy },
12688
+ busy ? "\u6B63\u5728\u51C6\u5907\u2026" : "\u91CD\u65B0\u751F\u6210\u4E8C\u7EF4\u7801"
12689
+ ),
12690
+ h2(SettingsButton, { onClick: onClose, disabled: busy }, "\u5173\u95ED")
12691
+ )
12692
+ );
12693
+ }
12694
+ const qrSource = safeQrSource7(provision.qrCodeDataUrl);
12695
+ const href = safeVerificationHref2(provision.verificationUrl);
12696
+ const remaining = Math.max(0, (provision.expiresAt ?? now) - now);
12697
+ const expired = provision.expired === true || remaining === 0;
12698
+ const progress = Math.min(1, remaining / Math.max(1, provision.durationMs ?? remaining));
12699
+ return h2(
12700
+ "section",
12701
+ {
12702
+ className: "dim-feishuGroupAuthorization",
12703
+ "aria-label": `${botName}\u7684\u98DE\u4E66\u6388\u6743\u6D41\u7A0B`
12704
+ },
12705
+ h2(
12706
+ "div",
12707
+ { className: "dim-feishuGroupQrColumn" },
12708
+ h2(
12709
+ "div",
12710
+ { className: "dim-feishuGroupQrFrame" },
12711
+ qrSource ? h2("img", {
12712
+ src: qrSource,
12713
+ alt: `\u7528\u4E8E\u4E3A${botName}\u5F00\u901A\u7FA4\u6D88\u606F\u6743\u9650\u7684\u4E00\u6B21\u6027\u6388\u6743\u4E8C\u7EF4\u7801`
12714
+ }) : h2(
12715
+ "div",
12716
+ { className: "dim-feishuGroupQrFallback" },
12717
+ h2(QrPlaceholder),
12718
+ h2("span", null, "\u4E8C\u7EF4\u7801\u672A\u5C31\u7EEA\uFF0C\u8BF7\u6253\u5F00\u6388\u6743\u94FE\u63A5")
12719
+ ),
12720
+ expired ? h2(
12721
+ "div",
12722
+ { className: "dim-feishuGroupQrExpired", role: "status" },
12723
+ h2("span", null, "\u4E8C\u7EF4\u7801\u5DF2\u5931\u6548"),
12724
+ h2("small", null, "\u8BF7\u5237\u65B0\u540E\u91CD\u65B0\u626B\u7801")
12725
+ ) : null
12726
+ ),
12727
+ h2(
12728
+ "div",
12729
+ {
12730
+ className: "dim-feishuGroupCountdown",
12731
+ "aria-label": expired ? "\u4E8C\u7EF4\u7801\u5DF2\u5931\u6548" : `\u4E8C\u7EF4\u7801\u5269\u4F59 ${formatRemaining2(remaining)}`
12732
+ },
12733
+ h2("span", null, expired ? "\u7B49\u5F85\u5237\u65B0" : "\u4E8C\u7EF4\u7801\u6709\u6548\u65F6\u95F4"),
12734
+ h2("strong", null, formatRemaining2(remaining)),
12735
+ h2(
12736
+ "span",
12737
+ { className: "dim-feishuGroupProgress", "aria-hidden": "true" },
12738
+ h2("span", { style: { width: `${Math.round(progress * 100)}%` } })
12739
+ )
12740
+ )
12741
+ ),
12742
+ h2(
12743
+ "div",
12744
+ { className: "dim-feishuGroupAuthorizationCopy" },
12745
+ h2(
12746
+ "span",
12747
+ { className: "dim-feishuGroupAuthorizationEyebrow" },
12748
+ `\u6B63\u5728\u4E3A\u300C${botName}\u300D\u5F00\u901A\u7FA4\u6D88\u606F\u6743\u9650`
12749
+ ),
12750
+ h2("h3", null, expired ? "\u5237\u65B0\u4E8C\u7EF4\u7801\u540E\u7EE7\u7EED" : "\u4F7F\u7528\u98DE\u4E66\u786E\u8BA4\u7FA4\u6D88\u606F\u6743\u9650"),
12751
+ h2(
12752
+ "p",
12753
+ null,
12754
+ "\u626B\u7801\u4F1A\u66F4\u65B0\u73B0\u6709\u98DE\u4E66\u5E94\u7528\uFF0C\u53EA\u589E\u91CF\u5F00\u901A\u201C\u83B7\u53D6\u7FA4\u7EC4\u4E2D\u6240\u6709\u6D88\u606F\u201D\u6743\u9650\uFF1B\u4E0D\u4F1A\u521B\u5EFA\u65B0\u5E94\u7528\u3002\u786E\u8BA4\u540E\u4F1A\u81EA\u52A8\u542F\u7528\u201C\u54CD\u5E94\u6240\u6709\u7FA4\u6D88\u606F\u201D\uFF0C\u5176\u4ED6\u673A\u5668\u4EBA\u4E0D\u53D7\u5F71\u54CD\u3002"
12755
+ ),
12756
+ h2(
12757
+ "ol",
12758
+ null,
12759
+ h2("li", null, "\u6253\u5F00\u98DE\u4E66\u79FB\u52A8\u7AEF\uFF0C\u4F7F\u7528\u626B\u4E00\u626B\u8BFB\u53D6\u4E8C\u7EF4\u7801"),
12760
+ h2("li", null, "\u6838\u5BF9\u73B0\u6709\u5E94\u7528\uFF0C\u5E76\u786E\u8BA4\u201C\u83B7\u53D6\u7FA4\u7EC4\u4E2D\u6240\u6709\u6D88\u606F\u201D\u6743\u9650"),
12761
+ h2("li", null, "\u4FDD\u6301\u672C\u9875\u6253\u5F00\uFF0C\u7B49\u5F85\u6743\u9650\u751F\u6548\u5E76\u81EA\u52A8\u5207\u6362\u54CD\u5E94\u65B9\u5F0F")
12762
+ ),
12763
+ h2(
12764
+ "div",
12765
+ { className: "dim-feishuGroupAuthorizationActions" },
12766
+ expired ? h2(
12767
+ SettingsButton,
12768
+ { kind: "primary", onClick: onRetry, disabled: busy },
12769
+ busy ? "\u5237\u65B0\u4E2D\u2026" : "\u5237\u65B0\u4E8C\u7EF4\u7801"
12770
+ ) : href ? h2("a", {
12771
+ className: "dim-deliveryButton dim-feishuGroupAuthorizationLink",
12772
+ href,
12773
+ target: "_blank",
12774
+ rel: "noopener noreferrer"
12775
+ }, "\u5728\u98DE\u4E66\u4E2D\u6253\u5F00") : null,
12776
+ !expired ? h2(SettingsButton, { onClick: onRetry, disabled: busy }, "\u6362\u4E00\u4E2A\u4E8C\u7EF4\u7801") : null,
12777
+ h2(SettingsButton, { onClick: onCancel, disabled: busy }, "\u53D6\u6D88\u6388\u6743")
12778
+ )
12779
+ )
12780
+ );
12781
+ }
12782
+ function FeishuGroupSettingsPage({ account, rpcCall }) {
12783
+ const [settings, setSettings] = React22.useState(() => groupSettingsFrom(account));
12784
+ const [refreshing, setRefreshing] = React22.useState(false);
12785
+ const [refreshError, setRefreshError] = React22.useState(null);
12786
+ const [provision, setProvision] = React22.useState(null);
12787
+ const [provisionBusy, setProvisionBusy] = React22.useState(false);
12788
+ const [now, setNow] = React22.useState(() => Date.now());
12789
+ const mounted = React22.useRef(true);
12790
+ React22.useEffect(() => {
12791
+ setSettings(groupSettingsFrom(account));
12792
+ }, [
12793
+ account.botId,
12794
+ account.groupResponseMode,
12795
+ account.groupTopicReply,
12796
+ account.groupMessagePermissionGranted
12797
+ ]);
12798
+ React22.useEffect(() => {
12799
+ mounted.current = true;
12800
+ return () => {
12801
+ mounted.current = false;
12802
+ };
12803
+ }, []);
12804
+ const invoke = React22.useCallback(async (endpoint, payload = {}, signal) => {
12805
+ if (typeof rpcCall !== "function") throw new Error("\u98DE\u4E66\u7FA4\u804A\u8BBE\u7F6E\u6682\u4E0D\u53EF\u7528\u3002");
12806
+ return unwrapRpcResult3(await rpcCall(endpoint, payload, signal));
12807
+ }, [rpcCall]);
12808
+ const applySnapshot = React22.useCallback((value) => {
12809
+ const result = targetBotFromSnapshot(value, account.botId);
12810
+ if (mounted.current) setSettings(groupSettingsFrom(result.bot));
12811
+ return result;
12812
+ }, [account.botId]);
12813
+ const loadSettings = React22.useCallback(async ({ signal, restoreProvisioning = false } = {}) => {
12814
+ setRefreshing(true);
12815
+ setRefreshError(null);
12816
+ try {
12817
+ const value = await invoke(FEISHU_ENDPOINTS.status, {}, signal);
12818
+ if (signal?.aborted || !mounted.current) return void 0;
12819
+ const result = applySnapshot(value);
12820
+ if (restoreProvisioning) {
12821
+ const active = result.snapshot.provisioning;
12822
+ if (active?.operation === GROUP_MESSAGE_PERMISSION_OPERATION2 && active.botId === account.botId) {
12823
+ const timestamp7 = Date.now();
12824
+ setNow(timestamp7);
12825
+ setProvision({
12826
+ phase: active.submitted ? "connecting" : "qr",
12827
+ ...active,
12828
+ durationMs: Math.max(1, active.expiresAt - timestamp7),
12829
+ expired: !active.submitted && active.expiresAt <= timestamp7
12830
+ });
12831
+ }
12832
+ }
12833
+ return result.bot;
12834
+ } catch (cause) {
12835
+ if (signal?.aborted || cause?.name === "AbortError" || !mounted.current) return void 0;
12836
+ setRefreshError(presentError3(cause));
12837
+ return void 0;
12838
+ } finally {
12839
+ if (!signal?.aborted && mounted.current) setRefreshing(false);
12840
+ }
12841
+ }, [account.botId, applySnapshot, invoke]);
12842
+ React22.useEffect(() => {
12843
+ const controller = new AbortController();
12844
+ void loadSettings({ signal: controller.signal, restoreProvisioning: true });
12845
+ return () => controller.abort();
12846
+ }, [loadSettings]);
12847
+ const saveSetting = React22.useCallback(async (endpoint, payload) => {
12848
+ const value = await invoke(endpoint, { botId: account.botId, ...payload });
12849
+ applySnapshot(value);
12850
+ }, [account.botId, applySnapshot, invoke]);
12851
+ const startAuthorization = React22.useCallback(async ({ replace = false } = {}) => {
12852
+ if (provisionBusy) return;
12853
+ const previousAttemptId = provision?.attemptId;
12854
+ setProvisionBusy(true);
12855
+ setProvision({ phase: "creating", operation: GROUP_MESSAGE_PERMISSION_OPERATION2, botId: account.botId });
12856
+ try {
12857
+ if (replace && previousAttemptId) {
12858
+ try {
12859
+ await invoke(FEISHU_ENDPOINTS.cancelProvisioning, { attemptId: previousAttemptId });
12860
+ } catch {
12861
+ }
12862
+ }
12863
+ const normalized = normalizeProvisioning2(await invoke(
12864
+ FEISHU_ENDPOINTS.beginGroupMessagePermission,
12865
+ { botId: account.botId }
12866
+ ));
12867
+ if (normalized.operation !== GROUP_MESSAGE_PERMISSION_OPERATION2 || normalized.botId !== account.botId) {
12868
+ throw new Error("\u98DE\u4E66\u670D\u52A1\u8FD4\u56DE\u4E86\u4E0D\u5339\u914D\u7684\u7FA4\u6D88\u606F\u6743\u9650\u4E8C\u7EF4\u7801");
12869
+ }
12870
+ const timestamp7 = Date.now();
12871
+ if (!mounted.current) return;
12872
+ setNow(timestamp7);
12873
+ setProvision({
12874
+ phase: normalized.submitted ? "connecting" : "qr",
12875
+ ...normalized,
12876
+ durationMs: Math.max(1, normalized.expiresAt - timestamp7),
12877
+ expired: false
12878
+ });
12879
+ } catch (cause) {
12880
+ if (!mounted.current) return;
12881
+ setProvision({
12882
+ phase: "error",
12883
+ operation: GROUP_MESSAGE_PERMISSION_OPERATION2,
12884
+ botId: account.botId,
12885
+ ...previousAttemptId ? { attemptId: previousAttemptId } : {},
12886
+ error: presentError3(cause)
12887
+ });
12888
+ } finally {
12889
+ if (mounted.current) setProvisionBusy(false);
12890
+ }
12891
+ }, [account.botId, invoke, provision?.attemptId, provisionBusy]);
12892
+ const finishAuthorization = React22.useCallback(async (signal) => {
12893
+ const bot = await loadSettings({ signal, restoreProvisioning: false });
12894
+ if (signal?.aborted || !mounted.current) return;
12895
+ if (!bot) throw new Error("\u7FA4\u6D88\u606F\u6743\u9650\u5DF2\u66F4\u65B0\uFF0C\u4F46\u6682\u65F6\u65E0\u6CD5\u786E\u8BA4\u673A\u5668\u4EBA\u8FDE\u63A5\u72B6\u6001");
12896
+ setProvision(null);
12897
+ }, [loadSettings]);
12898
+ React22.useEffect(() => {
12899
+ if (!provision?.attemptId || !["qr", "connecting"].includes(provision.phase) || provision.expired) return void 0;
12900
+ const timerHost = globalThis.window ?? globalThis;
12901
+ const controller = new AbortController();
12902
+ const timer = timerHost.setTimeout(async () => {
12903
+ try {
12904
+ const result = normalizePollResult(await invoke(
12905
+ FEISHU_ENDPOINTS.pollProvisioning,
12906
+ { attemptId: provision.attemptId },
12907
+ controller.signal
12908
+ ));
12909
+ if (result.operation !== GROUP_MESSAGE_PERMISSION_OPERATION2 || result.botId !== account.botId) {
12910
+ throw new Error("\u98DE\u4E66\u670D\u52A1\u8FD4\u56DE\u4E86\u4E0D\u5339\u914D\u7684\u6CE8\u518C\u8FDB\u5EA6");
12911
+ }
12912
+ if (result.status === "connected") {
12913
+ await finishAuthorization(controller.signal);
12914
+ return;
12915
+ }
12916
+ if (result.status === "failed") {
12917
+ const error = new Error(result.message ?? "\u98DE\u4E66\u7FA4\u6D88\u606F\u6743\u9650\u5F00\u901A\u5931\u8D25");
12918
+ error.code = "FEISHU_PROVISION_FAILED";
12919
+ throw error;
12920
+ }
12921
+ if (result.status === "expired") {
12922
+ if (mounted.current) setProvision((current) => current?.attemptId === provision.attemptId ? { ...current, phase: "qr", expired: true } : current);
12923
+ return;
12924
+ }
12925
+ if (mounted.current) {
12926
+ setProvision((current) => current?.attemptId === provision.attemptId ? {
12927
+ ...current,
12928
+ ...result.provisioning ?? {},
12929
+ phase: ["scanned", "connecting"].includes(result.status) ? "connecting" : "qr"
12930
+ } : current);
12931
+ }
12932
+ } catch (cause) {
12933
+ if (controller.signal.aborted || cause?.name === "AbortError" || !mounted.current) return;
12934
+ setProvision((current) => current?.attemptId === provision.attemptId ? { ...current, phase: "error", error: presentError3(cause) } : current);
12935
+ }
12936
+ }, provision.pollIntervalMs ?? 1800);
12937
+ return () => {
12938
+ controller.abort();
12939
+ timerHost.clearTimeout(timer);
12940
+ };
12941
+ }, [account.botId, finishAuthorization, invoke, provision]);
12942
+ React22.useEffect(() => {
12943
+ if (!provision?.attemptId || provision.phase !== "qr" || provision.expired) return void 0;
12944
+ const timerHost = globalThis.window ?? globalThis;
12945
+ const tick = () => {
12946
+ const timestamp7 = Date.now();
12947
+ setNow(timestamp7);
12948
+ if (timestamp7 >= provision.expiresAt) {
12949
+ setProvision((current) => current?.attemptId === provision.attemptId ? { ...current, expired: true } : current);
12950
+ }
12951
+ };
12952
+ tick();
12953
+ const timer = timerHost.setInterval(tick, 1e3);
12954
+ return () => timerHost.clearInterval(timer);
12955
+ }, [provision?.attemptId, provision?.expired, provision?.expiresAt, provision?.phase]);
12956
+ const cancelAuthorization = React22.useCallback(async () => {
12957
+ if (!provision?.attemptId || provisionBusy) {
12958
+ setProvision(null);
12959
+ return;
12960
+ }
12961
+ setProvisionBusy(true);
12962
+ try {
12963
+ const result = normalizePollResult(await invoke(
12964
+ FEISHU_ENDPOINTS.cancelProvisioning,
12965
+ { attemptId: provision.attemptId }
12966
+ ));
12967
+ if (result.operation !== GROUP_MESSAGE_PERMISSION_OPERATION2 || result.botId !== account.botId) {
12968
+ throw new Error("\u98DE\u4E66\u670D\u52A1\u8FD4\u56DE\u4E86\u4E0D\u5339\u914D\u7684\u6CE8\u518C\u8FDB\u5EA6");
12969
+ }
12970
+ if (result.status === "connecting") {
12971
+ setProvision((current) => ({ ...current, phase: "connecting", submitted: true }));
12972
+ return;
12973
+ }
12974
+ if (result.status === "connected") {
12975
+ await finishAuthorization();
12976
+ return;
12977
+ }
12978
+ setProvision(null);
12979
+ await loadSettings({ restoreProvisioning: false });
12980
+ } catch (cause) {
12981
+ if (mounted.current) {
12982
+ setProvision((current) => ({ ...current, phase: "error", error: presentError3(cause) }));
12983
+ }
12984
+ } finally {
12985
+ if (mounted.current) setProvisionBusy(false);
12986
+ }
12987
+ }, [account.botId, finishAuthorization, invoke, loadSettings, provision, provisionBusy]);
12988
+ const disabled = refreshing || Boolean(provision);
12989
+ return h2(
12990
+ "section",
12991
+ {
12992
+ className: "dim-feishuGroupSettings",
12993
+ "aria-label": "\u7FA4\u804A\u8BBE\u7F6E"
12994
+ },
12995
+ refreshError ? h2("p", {
12996
+ className: "dim-feishuGroupRefreshError",
12997
+ role: "alert"
12998
+ }, refreshError.message) : null,
12999
+ h2(
13000
+ "div",
13001
+ { className: "dim-feishuGroupControls" },
13002
+ h2(GroupResponseModeEditor, {
13003
+ value: settings.groupResponseMode,
13004
+ permissionGranted: settings.groupMessagePermissionGranted,
13005
+ disabled,
13006
+ authorizationDisabled: provisionBusy,
13007
+ onSave: async (groupResponseMode) => {
13008
+ if (groupResponseMode === "all" && !settings.groupMessagePermissionGranted) {
13009
+ await startAuthorization();
13010
+ return;
13011
+ }
13012
+ await saveSetting(FEISHU_ENDPOINTS.setGroupResponseMode, { groupResponseMode });
13013
+ },
13014
+ onAuthorize: () => startAuthorization()
13015
+ }),
13016
+ h2(GroupTopicReplyEditor, {
13017
+ value: settings.groupTopicReply,
13018
+ disabled,
13019
+ onSave: (groupTopicReply) => saveSetting(
13020
+ FEISHU_ENDPOINTS.setGroupTopicReply,
13021
+ { groupTopicReply }
13022
+ )
13023
+ })
13024
+ ),
13025
+ h2(PermissionFlow, {
13026
+ provision,
13027
+ now,
13028
+ busy: provisionBusy,
13029
+ botName: account.botName || "\u5F53\u524D\u673A\u5668\u4EBA",
13030
+ onRetry: () => {
13031
+ void startAuthorization({ replace: true });
13032
+ },
13033
+ onCancel: () => {
13034
+ void cancelAuthorization();
13035
+ },
13036
+ onClose: () => {
13037
+ setProvision(null);
13038
+ void loadSettings({ restoreProvisioning: true });
13039
+ }
13040
+ })
13041
+ );
13042
+ }
13043
+
13044
+ // plugin-src/client/delivery-settings.js
13045
+ var DELIVERY_RPC_CHANNEL = "/dsh-im-delivery";
13046
+ var DELIVERY_DOCS_URL = Object.freeze({
13047
+ zh: "https://github.com/xmanrui/dsh-im/blob/main/PROACTIVE_DELIVERY.md",
13048
+ en: "https://github.com/xmanrui/dsh-im/blob/main/PROACTIVE_DELIVERY.en.md"
13049
+ });
13050
+ var DELIVERY_ENDPOINTS = Object.freeze({
13051
+ list: "target.list",
13052
+ listSuggestions: "target.suggestion.list",
13053
+ create: "target.create",
13054
+ update: "target.update",
13055
+ delete: "target.delete",
13056
+ sessionSync: "target.session-sync.set",
13057
+ test: "target.test"
13058
+ });
13059
+ var BOT_SETTINGS_TABS = Object.freeze([
13060
+ Object.freeze({ id: "delivery", label: "\u6295\u9012\u8BBE\u7F6E" }),
13061
+ Object.freeze({ id: "access", label: "\u8BBF\u95EE\u8BBE\u7F6E" })
13062
+ ]);
13063
+ var FEISHU_BOT_SETTINGS_TABS = Object.freeze([
13064
+ ...BOT_SETTINGS_TABS,
13065
+ Object.freeze({ id: "group", label: "\u7FA4\u804A" })
13066
+ ]);
13067
+ function botSettingsTabsForChannel(channel4) {
13068
+ return channel4 === "feishu" ? FEISHU_BOT_SETTINGS_TABS : BOT_SETTINGS_TABS;
13069
+ }
13070
+ var CHANNEL_DEFINITIONS = Object.freeze({
13071
+ weixin: {
13072
+ label: "\u5FAE\u4FE1",
13073
+ kinds: [{ value: "user", label: "\u7528\u6237" }],
13074
+ fields: { user: [{ key: "toUserId", label: "\u5FAE\u4FE1\u7528\u6237 ID", placeholder: "\u586B\u5199\u63A5\u6536\u6D88\u606F\u7684\u7528\u6237 ID" }] }
13075
+ },
13076
+ feishu: {
13077
+ label: "\u98DE\u4E66",
13078
+ kinds: [{ value: "user", label: "\u79C1\u804A" }, { value: "group", label: "\u7FA4\u804A" }],
13079
+ fields: {
13080
+ user: [{ key: "openId", label: "Open ID", placeholder: "ou_xxx" }],
13081
+ group: [{ key: "chatId", label: "\u7FA4 Chat ID", placeholder: "oc_xxx" }]
13082
+ }
13083
+ },
13084
+ dingtalk: {
13085
+ label: "\u9489\u9489",
13086
+ kinds: [{ value: "user", label: "\u7528\u6237" }, { value: "group", label: "\u7FA4\u804A" }],
13087
+ fields: {
13088
+ user: [{ key: "userId", label: "\u7528\u6237 ID", placeholder: "\u586B\u5199\u9489\u9489\u7528\u6237 ID" }],
13089
+ group: [{ key: "openConversationId", label: "\u7FA4 Open Conversation ID", placeholder: "cidxxx" }]
13090
+ }
13091
+ },
13092
+ wecom: {
13093
+ label: "\u4F01\u4E1A\u5FAE\u4FE1",
13094
+ kinds: [{ value: "user", label: "\u79C1\u804A" }, { value: "group", label: "\u7FA4\u804A" }],
13095
+ fields: {
13096
+ user: [{ key: "chatId", label: "\u7528\u6237 ID", placeholder: "\u586B\u5199\u4F01\u4E1A\u5FAE\u4FE1\u7528\u6237 ID" }],
13097
+ group: [{ key: "chatId", label: "\u7FA4 Chat ID", placeholder: "\u586B\u5199\u7FA4 chatid" }]
13098
+ }
13099
+ },
13100
+ qq: {
13101
+ label: "QQ",
13102
+ kinds: [{ value: "user", label: "\u5355\u804A" }, { value: "group", label: "\u7FA4\u804A" }],
13103
+ fields: {
13104
+ user: [{ key: "userOpenId", label: "\u7528\u6237 Open ID", placeholder: "\u586B\u5199 user_openid" }],
13105
+ group: [{ key: "groupOpenId", label: "\u7FA4 Open ID", placeholder: "\u586B\u5199 group_openid" }]
13106
+ }
13107
+ },
13108
+ slack: {
13109
+ label: "Slack",
13110
+ kinds: [{ value: "conversation", label: "\u4F1A\u8BDD" }, { value: "thread", label: "\u7EBF\u7A0B" }],
13111
+ fields: {
13112
+ conversation: [{ key: "channelId", label: "Channel ID", placeholder: "C0123456789" }],
13113
+ thread: [
13114
+ { key: "channelId", label: "Channel ID", placeholder: "C0123456789" },
13115
+ { key: "threadTs", label: "Thread \u65F6\u95F4\u6233", placeholder: "1712345678.123456" }
13116
+ ]
13117
+ }
13118
+ },
13119
+ telegram: {
13120
+ label: "Telegram",
13121
+ kinds: [{ value: "chat", label: "\u804A\u5929" }, { value: "topic", label: "\u8BDD\u9898" }],
13122
+ fields: {
13123
+ chat: [{ key: "chatId", label: "Chat ID", placeholder: "-1001234567890", inputMode: "numeric" }],
13124
+ topic: [
13125
+ { key: "chatId", label: "Chat ID", placeholder: "-1001234567890", inputMode: "numeric" },
12375
13126
  { key: "messageThreadId", label: "Topic ID", placeholder: "123", inputMode: "numeric", integer: true }
12376
13127
  ]
12377
13128
  }
@@ -12511,13 +13262,13 @@ function TargetForm({
12511
13262
  }) {
12512
13263
  const editing = mode === "edit";
12513
13264
  const initialKind = initialValue?.kind && definition.fields[initialValue.kind] ? initialValue.kind : definition.kinds[0].value;
12514
- const [targetId, setTargetId] = React21.useState(initialValue?.targetId ?? "");
12515
- const [name2, setName] = React21.useState(initialValue?.name ?? "");
12516
- const [kind, setKind] = React21.useState(initialKind);
12517
- const [route, setRoute] = React21.useState(initialValue?.route ?? {});
12518
- const [error, setError] = React21.useState(null);
12519
- const [testing, setTesting] = React21.useState(false);
12520
- const [testState, setTestState] = React21.useState(null);
13265
+ const [targetId, setTargetId] = React23.useState(initialValue?.targetId ?? "");
13266
+ const [name2, setName] = React23.useState(initialValue?.name ?? "");
13267
+ const [kind, setKind] = React23.useState(initialKind);
13268
+ const [route, setRoute] = React23.useState(initialValue?.route ?? {});
13269
+ const [error, setError] = React23.useState(null);
13270
+ const [testing, setTesting] = React23.useState(false);
13271
+ const [testState, setTestState] = React23.useState(null);
12521
13272
  const currentTarget = () => {
12522
13273
  const normalizedRoute = Object.fromEntries(fieldsFor(definition, kind).map((field) => {
12523
13274
  const raw = String(route[field.key] ?? "").trim();
@@ -12563,7 +13314,7 @@ function TargetForm({
12563
13314
  "div",
12564
13315
  { className: "dim-targetFormHeading" },
12565
13316
  h2("h3", null, editing ? "\u7F16\u8F91\u6295\u9012\u76EE\u6807" : "\u65B0\u5EFA\u6295\u9012\u76EE\u6807"),
12566
- editing ? null : h2("p", null, source === "suggestion" ? "\u5DF2\u4ECE\u4F1A\u8BDD\u81EA\u52A8\u586B\u5165\u76EE\u6807\u4FE1\u606F\uFF1B\u786E\u8BA4\u540E\u518D\u4FDD\u5B58\u3002" : "\u8BF7\u624B\u52A8\u586B\u5199\u4ECE\u5BF9\u5E94\u5E73\u53F0\u53D6\u5F97\u7684\u539F\u751F\u6807\u8BC6\u3002")
13317
+ editing ? initialValue?.sessionSync?.enabled ? h2("p", null, "\u4FEE\u6539\u63A5\u6536\u4F4D\u7F6E\u4F1A\u5173\u95ED\u4F1A\u8BDD\u53CC\u5411\u540C\u6B65\u3002") : null : h2("p", null, source === "suggestion" ? "\u5DF2\u4ECE\u4F1A\u8BDD\u81EA\u52A8\u586B\u5165\u76EE\u6807\u4FE1\u606F\uFF1B\u786E\u8BA4\u540E\u518D\u4FDD\u5B58\u3002" : "\u8BF7\u624B\u52A8\u586B\u5199\u4ECE\u5BF9\u5E94\u5E73\u53F0\u53D6\u5F97\u7684\u539F\u751F\u6807\u8BC6\u3002")
12567
13318
  ),
12568
13319
  h2(
12569
13320
  "div",
@@ -12734,7 +13485,7 @@ function TargetSuggestionPicker({
12734
13485
  suggestions.map((suggestion, index) => {
12735
13486
  const identity = routeIdentity(definition, suggestion);
12736
13487
  const added = configured.has(identity);
12737
- return React21.createElement("option", {
13488
+ return React23.createElement("option", {
12738
13489
  key: suggestion.id ?? suggestion.suggestionId ?? `${identity}:${index}`,
12739
13490
  value: String(index),
12740
13491
  disabled: added
@@ -12751,10 +13502,13 @@ function TargetSuggestionPicker({
12751
13502
  );
12752
13503
  }
12753
13504
  function TargetRow({ definition, target, botId, connected, rpcCall, onChanged, onEdit }) {
12754
- const [action, setAction] = React21.useState(null);
12755
- const [testState, setTestState] = React21.useState(null);
12756
- const [copyState, setCopyState] = React21.useState(null);
12757
- const [confirmDelete, setConfirmDelete] = React21.useState(false);
13505
+ const [action, setAction] = React23.useState(null);
13506
+ const [testState, setTestState] = React23.useState(null);
13507
+ const [syncFeedback, setSyncFeedback] = React23.useState(null);
13508
+ const [copyState, setCopyState] = React23.useState(null);
13509
+ const [confirmDelete, setConfirmDelete] = React23.useState(false);
13510
+ const sessionSync = target.sessionSync ?? { enabled: false, state: "unavailable" };
13511
+ const syncDescription = sessionSync.state === "active" ? "\u81EA\u52A8\u8DDF\u968F\u8BE5\u79C1\u804A\u7684\u5F53\u524D\u4F1A\u8BDD" : sessionSync.state === "waiting" ? "\u7B49\u5F85\u8BE5\u79C1\u804A\u5EFA\u7ACB\u65B0\u4F1A\u8BDD" : sessionSync.state === "unavailable" ? "\u4EC5\u652F\u6301\u5DF2\u7ECF\u804A\u8FC7\u3001\u5DF2\u5EFA\u7ACB\u5F53\u524D\u4F1A\u8BDD\u4E14\u4F7F\u7528\u5F53\u524D Host Harness \u7684\u79C1\u804A\u76EE\u6807" : "\u5173\u95ED\u65F6\u4E0D\u53D1\u9001 DSH \u4F1A\u8BDD\u6D88\u606F";
12758
13512
  const testTarget = async () => {
12759
13513
  setAction("test");
12760
13514
  setTestState(null);
@@ -12787,6 +13541,25 @@ function TargetRow({ definition, target, botId, connected, rpcCall, onChanged, o
12787
13541
  setConfirmDelete(false);
12788
13542
  }
12789
13543
  };
13544
+ const setSessionSync = async (enabled) => {
13545
+ setAction("sync");
13546
+ setSyncFeedback(null);
13547
+ try {
13548
+ await rpcCall(DELIVERY_ENDPOINTS.sessionSync, {
13549
+ botId,
13550
+ targetId: target.targetId,
13551
+ enabled
13552
+ });
13553
+ await onChanged();
13554
+ } catch (error) {
13555
+ setSyncFeedback({
13556
+ tone: "error",
13557
+ message: error?.code === "session-sync-unavailable" ? "\u4F1A\u8BDD\u540C\u6B65\u4EC5\u652F\u6301\u5DF2\u7ECF\u804A\u8FC7\u3001\u5DF2\u5EFA\u7ACB\u5F53\u524D\u4F1A\u8BDD\u4E14\u4F7F\u7528\u5F53\u524D Host Harness \u7684\u79C1\u804A\u76EE\u6807\u3002" : presentError10(error, "\u4F1A\u8BDD\u540C\u6B65\u8BBE\u7F6E\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002")
13558
+ });
13559
+ } finally {
13560
+ setAction(null);
13561
+ }
13562
+ };
12790
13563
  return h2(
12791
13564
  "li",
12792
13565
  { className: "dim-targetRow", "data-target-id": target.targetId },
@@ -12796,7 +13569,7 @@ function TargetRow({ definition, target, botId, connected, rpcCall, onChanged, o
12796
13569
  h2(
12797
13570
  "div",
12798
13571
  { className: "dim-targetTitle" },
12799
- React21.createElement("strong", null, target.name || target.targetId),
13572
+ React23.createElement("strong", null, target.name || target.targetId),
12800
13573
  h2("span", null, kindLabel(definition, target.kind))
12801
13574
  ),
12802
13575
  h2("code", null, `targetId: ${target.targetId}`)
@@ -12818,7 +13591,29 @@ function TargetRow({ definition, target, botId, connected, rpcCall, onChanged, o
12818
13591
  disabled: Boolean(action)
12819
13592
  }, "\u5220\u9664")
12820
13593
  ),
13594
+ h2(
13595
+ "label",
13596
+ { className: "dim-targetSessionSync" },
13597
+ h2(
13598
+ "span",
13599
+ { className: "dim-targetSessionSyncCopy" },
13600
+ h2("strong", null, "\u4F1A\u8BDD\u53CC\u5411\u540C\u6B65"),
13601
+ h2("small", null, syncDescription)
13602
+ ),
13603
+ h2("input", {
13604
+ type: "checkbox",
13605
+ checked: sessionSync.enabled === true,
13606
+ disabled: Boolean(action) || sessionSync.enabled !== true && sessionSync.state === "unavailable",
13607
+ onChange: (event) => void setSessionSync(event.target.checked),
13608
+ "aria-label": "\u4F1A\u8BDD\u53CC\u5411\u540C\u6B65"
13609
+ })
13610
+ ),
12821
13611
  copyState ? h2("p", { className: "dim-targetFeedback", role: "status" }, copyState) : null,
13612
+ syncFeedback ? h2("p", {
13613
+ className: "dim-targetFeedback",
13614
+ "data-tone": syncFeedback.tone,
13615
+ role: "alert"
13616
+ }, syncFeedback.message) : null,
12822
13617
  testState ? h2("p", {
12823
13618
  className: "dim-targetFeedback",
12824
13619
  "data-tone": testState.tone,
@@ -12833,7 +13628,8 @@ function TargetRow({ definition, target, botId, connected, rpcCall, onChanged, o
12833
13628
  null,
12834
13629
  "\u5220\u9664 ",
12835
13630
  h2("code", null, target.targetId),
12836
- "\uFF1F\u4F7F\u7528\u8FD9\u4E2A targetId \u7684\u5916\u90E8\u8C03\u7528\u5C06\u8FD4\u56DE unknown-target\u3002"
13631
+ "\uFF1F\u4F7F\u7528\u8FD9\u4E2A targetId \u7684\u5916\u90E8\u8C03\u7528\u5C06\u8FD4\u56DE unknown-target\u3002",
13632
+ sessionSync.enabled ? " \u4F1A\u8BDD\u540C\u6B65\u8BBE\u7F6E\u4E5F\u4F1A\u4E00\u5E76\u5220\u9664\u3002" : null
12837
13633
  ),
12838
13634
  h2(
12839
13635
  "div",
@@ -12859,26 +13655,26 @@ function DeliveryTargetSettingsPage({
12859
13655
  onBack
12860
13656
  }) {
12861
13657
  const definition = CHANNEL_DEFINITIONS[channel4];
12862
- const [activeTabId, setActiveTabId] = React21.useState(BOT_SETTINGS_TABS[0].id);
12863
- const [phase, setPhase] = React21.useState("loading");
12864
- const [targets, setTargets] = React21.useState([]);
12865
- const [suggestionPhase, setSuggestionPhase] = React21.useState("idle");
12866
- const [suggestions, setSuggestions] = React21.useState([]);
12867
- const [suggestionError, setSuggestionError] = React21.useState(null);
12868
- const [error, setError] = React21.useState(null);
12869
- const [editor, setEditor] = React21.useState(null);
12870
- const [saving, setSaving] = React21.useState(false);
12871
- const [botCopyState, setBotCopyState] = React21.useState(null);
12872
- const [accessPolicy, setAccessPolicy] = React21.useState(account.accessPolicy);
12873
- const mounted = React21.useRef(true);
12874
- React21.useEffect(() => {
13658
+ const [activeTabId, setActiveTabId] = React23.useState(BOT_SETTINGS_TABS[0].id);
13659
+ const [phase, setPhase] = React23.useState("loading");
13660
+ const [targets, setTargets] = React23.useState([]);
13661
+ const [suggestionPhase, setSuggestionPhase] = React23.useState("idle");
13662
+ const [suggestions, setSuggestions] = React23.useState([]);
13663
+ const [suggestionError, setSuggestionError] = React23.useState(null);
13664
+ const [error, setError] = React23.useState(null);
13665
+ const [editor, setEditor] = React23.useState(null);
13666
+ const [saving, setSaving] = React23.useState(false);
13667
+ const [botCopyState, setBotCopyState] = React23.useState(null);
13668
+ const [accessPolicy, setAccessPolicy] = React23.useState(account.accessPolicy);
13669
+ const mounted = React23.useRef(true);
13670
+ React23.useEffect(() => {
12875
13671
  setAccessPolicy(account.accessPolicy);
12876
13672
  }, [account.botId, account.accessPolicy]);
12877
- const invoke = React21.useCallback(async (endpoint, payload = {}, signal) => {
13673
+ const invoke = React23.useCallback(async (endpoint, payload = {}, signal) => {
12878
13674
  if (typeof rpcCall !== "function") throw new Error("\u6295\u9012\u76EE\u6807\u8BBE\u7F6E\u6682\u4E0D\u53EF\u7528\u3002");
12879
13675
  return unwrapRpcResult11(await rpcCall(endpoint, payload, signal));
12880
13676
  }, [rpcCall]);
12881
- const loadTargets = React21.useCallback(async ({ signal, silent = false } = {}) => {
13677
+ const loadTargets = React23.useCallback(async ({ signal, silent = false } = {}) => {
12882
13678
  if (!silent) setPhase("loading");
12883
13679
  setError(null);
12884
13680
  try {
@@ -12892,7 +13688,7 @@ function DeliveryTargetSettingsPage({
12892
13688
  setPhase("error");
12893
13689
  }
12894
13690
  }, [account.botId, invoke]);
12895
- const loadSuggestions = React21.useCallback(async () => {
13691
+ const loadSuggestions = React23.useCallback(async () => {
12896
13692
  setSuggestionPhase("loading");
12897
13693
  setSuggestionError(null);
12898
13694
  try {
@@ -12906,7 +13702,7 @@ function DeliveryTargetSettingsPage({
12906
13702
  setSuggestionPhase("error");
12907
13703
  }
12908
13704
  }, [account.botId, definition, invoke]);
12909
- React21.useEffect(() => {
13705
+ React23.useEffect(() => {
12910
13706
  mounted.current = true;
12911
13707
  const controller = new AbortController();
12912
13708
  void loadTargets({ signal: controller.signal });
@@ -12977,7 +13773,8 @@ function DeliveryTargetSettingsPage({
12977
13773
  setBotCopyState(presentError10(caught, "\u590D\u5236\u5931\u8D25\u3002"));
12978
13774
  }
12979
13775
  };
12980
- const activeTab = BOT_SETTINGS_TABS.find((tab) => tab.id === activeTabId) ?? BOT_SETTINGS_TABS[0];
13776
+ const settingsTabs = botSettingsTabsForChannel(channel4);
13777
+ const activeTab = settingsTabs.find((tab) => tab.id === activeTabId) ?? settingsTabs[0];
12981
13778
  const activeTabDomId = `dim-bot-settings-${activeTab.id}-tab`;
12982
13779
  const activePanelId = `dim-bot-settings-${activeTab.id}-panel`;
12983
13780
  return h2(
@@ -12998,7 +13795,7 @@ function DeliveryTargetSettingsPage({
12998
13795
  className: "dim-botSettingsTabs",
12999
13796
  role: "tablist",
13000
13797
  "aria-label": "\u673A\u5668\u4EBA\u8BBE\u7F6E\u9875\u7B7E"
13001
- }, BOT_SETTINGS_TABS.map((tab) => h2("button", {
13798
+ }, settingsTabs.map((tab) => h2("button", {
13002
13799
  key: tab.id,
13003
13800
  id: `dim-bot-settings-${tab.id}-tab`,
13004
13801
  type: "button",
@@ -13023,8 +13820,11 @@ function DeliveryTargetSettingsPage({
13023
13820
  account: { ...account, accessPolicy },
13024
13821
  rpcCall: accessRpcCall,
13025
13822
  onSaved: setAccessPolicy
13823
+ }) : activeTab.id === "group" && channel4 === "feishu" ? h2(FeishuGroupSettingsPage, {
13824
+ account,
13825
+ rpcCall: accessRpcCall
13026
13826
  }) : h2(
13027
- React21.Fragment,
13827
+ React23.Fragment,
13028
13828
  null,
13029
13829
  h2(
13030
13830
  "section",
@@ -13135,7 +13935,7 @@ function DeliveryTargetSettingsPage({
13135
13935
  }
13136
13936
 
13137
13937
  // plugin-src/client/global-settings.js
13138
- var React22 = __toESM(require("react"), 1);
13938
+ var React24 = __toESM(require("react"), 1);
13139
13939
 
13140
13940
  // src/channels/shared/inbound-ttl.mjs
13141
13941
  var DEFAULT_INBOUND_TTL_HOURS = 168;
@@ -13197,29 +13997,29 @@ function GlobalButton({ children, kind = "secondary", className = "", ...props }
13197
13997
  }, children);
13198
13998
  }
13199
13999
  function GlobalSettingsPanel({ rpcCall }) {
13200
- const [phase, setPhase] = React22.useState("loading");
13201
- const [loadError, setLoadError] = React22.useState(null);
13202
- const [ttlInput, setTtlInput] = React22.useState("");
13203
- const [savedTtl, setSavedTtl] = React22.useState(null);
13204
- const [ttlError, setTtlError] = React22.useState(false);
13205
- const [saveError, setSaveError] = React22.useState(null);
13206
- const [saveSucceeded, setSaveSucceeded] = React22.useState(false);
13207
- const [isSaving, setIsSaving] = React22.useState(false);
13208
- const [sweepConfirming, setSweepConfirming] = React22.useState(false);
13209
- const [sweeping, setSweeping] = React22.useState(false);
13210
- const ttlErrorId = React22.useId();
13211
- const ttlHintsId = React22.useId();
13212
- const sweepTriggerId = React22.useId();
13213
- const sweepConfirmId = React22.useId();
13214
- const sweepConfirmTextId = React22.useId();
13215
- const sweepConfirmButtonId = React22.useId();
13216
- const mounted = React22.useRef(true);
13217
- const saving = React22.useRef(false);
13218
- const invoke = React22.useCallback(async (endpoint, payload = {}, signal) => {
14000
+ const [phase, setPhase] = React24.useState("loading");
14001
+ const [loadError, setLoadError] = React24.useState(null);
14002
+ const [ttlInput, setTtlInput] = React24.useState("");
14003
+ const [savedTtl, setSavedTtl] = React24.useState(null);
14004
+ const [ttlError, setTtlError] = React24.useState(false);
14005
+ const [saveError, setSaveError] = React24.useState(null);
14006
+ const [saveSucceeded, setSaveSucceeded] = React24.useState(false);
14007
+ const [isSaving, setIsSaving] = React24.useState(false);
14008
+ const [sweepConfirming, setSweepConfirming] = React24.useState(false);
14009
+ const [sweeping, setSweeping] = React24.useState(false);
14010
+ const ttlErrorId = React24.useId();
14011
+ const ttlHintsId = React24.useId();
14012
+ const sweepTriggerId = React24.useId();
14013
+ const sweepConfirmId = React24.useId();
14014
+ const sweepConfirmTextId = React24.useId();
14015
+ const sweepConfirmButtonId = React24.useId();
14016
+ const mounted = React24.useRef(true);
14017
+ const saving = React24.useRef(false);
14018
+ const invoke = React24.useCallback(async (endpoint, payload = {}, signal) => {
13219
14019
  if (typeof rpcCall !== "function") throw new Error("\u901A\u7528\u8BBE\u7F6E\u6682\u4E0D\u53EF\u7528\u3002");
13220
14020
  return unwrapRpcResult12(await rpcCall(endpoint, payload, signal));
13221
14021
  }, [rpcCall]);
13222
- const loadSettings = React22.useCallback(async ({ signal } = {}) => {
14022
+ const loadSettings = React24.useCallback(async ({ signal } = {}) => {
13223
14023
  setPhase("loading");
13224
14024
  setLoadError(null);
13225
14025
  try {
@@ -13243,7 +14043,7 @@ function GlobalSettingsPanel({ rpcCall }) {
13243
14043
  setPhase("error");
13244
14044
  }
13245
14045
  }, [invoke]);
13246
- React22.useEffect(() => {
14046
+ React24.useEffect(() => {
13247
14047
  mounted.current = true;
13248
14048
  const controller = new AbortController();
13249
14049
  void loadSettings({ signal: controller.signal });
@@ -13252,7 +14052,7 @@ function GlobalSettingsPanel({ rpcCall }) {
13252
14052
  controller.abort();
13253
14053
  };
13254
14054
  }, [loadSettings]);
13255
- React22.useEffect(() => {
14055
+ React24.useEffect(() => {
13256
14056
  if (!sweepConfirming) return;
13257
14057
  globalThis.document?.getElementById(sweepConfirmButtonId)?.focus();
13258
14058
  }, [sweepConfirmButtonId, sweepConfirming]);
@@ -13950,6 +14750,45 @@ var CSS11 = String.raw`
13950
14750
  .dim-botSettingsTab[aria-selected="true"]::after { background: var(--dsw-alias-state-business-primary, #3370ff); transform: scaleX(1); }
13951
14751
  .dim-botSettingsTab:focus-visible { outline: 2px solid var(--dsw-alias-state-business-primary, #3370ff); outline-offset: -2px; border-radius: 5px; }
13952
14752
  .dim-botSettingsTabPanel { min-width: 0; display: grid; gap: 14px; padding-top: 14px; }
14753
+ .dim-feishuGroupSettings { min-width: 0; display: grid; gap: 14px; }
14754
+ .dim-feishuGroupControls { min-width: 0; display: grid; gap: 12px; }
14755
+ .dim-feishuGroupControl { min-width: 0; display: grid; grid-template-columns: minmax(0, 1fr); gap: 10px; padding: 16px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 12px; background: var(--dsw-alias-bg-layer-3, #fff); }
14756
+ .dim-feishuGroupControlHeader { min-width: 0; display: flex; align-items: center; justify-content: space-between; gap: 12px; }
14757
+ .dim-feishuGroupControlHeader h3 { min-width: 0; margin: 0; color: var(--dsw-alias-label-primary, #1f2329); font-size: 14px; line-height: 21px; font-weight: 650; }
14758
+ .dim-feishuGroupControlStatus { flex: none; color: var(--dsw-alias-label-tertiary, #8f959e); font-size: 11px; line-height: 17px; }
14759
+ .dim-feishuGroupSelect { min-width: 0; width: 100%; height: 38px; padding: 0 34px 0 11px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 8px; color: var(--dsw-alias-label-primary, #1f2329); background: var(--dsw-alias-bg-layer-1, #fff); font: inherit; font-size: 13px; line-height: 38px; cursor: pointer; transition: border-color .15s ease, box-shadow .15s ease; }
14760
+ .dim-feishuGroupSelect:hover:not(:disabled) { border-color: color-mix(in srgb, var(--dsw-alias-state-business-primary, #3370ff) 42%, var(--dsw-alias-border-l2, #dfe1e5)); }
14761
+ .dim-feishuGroupSelect:focus-visible { outline: none; border-color: var(--dsw-alias-state-business-primary, #3370ff); box-shadow: 0 0 0 2px color-mix(in srgb, var(--dsw-alias-state-business-primary, #3370ff) 15%, transparent); }
14762
+ .dim-feishuGroupSelect:disabled { cursor: not-allowed; opacity: .55; }
14763
+ .dim-feishuGroupHelp { margin: -2px 0 0; color: var(--dsw-alias-label-tertiary, #8f959e); font-size: 12px; line-height: 19px; }
14764
+ .dim-feishuGroupPermissionAction { display: flex; justify-content: flex-start; }
14765
+ .dim-feishuGroupPermissionAction .dim-deliveryButton { color: var(--dsw-alias-state-business-primary, #3370ff); border-color: color-mix(in srgb, var(--dsw-alias-state-business-primary, #3370ff) 30%, var(--dsw-alias-border-l2, #dfe1e5)); }
14766
+ .dim-feishuGroupError, .dim-feishuGroupRefreshError { margin: 0; padding: 9px 11px; border-radius: 8px; font-size: 12px; line-height: 18px; }
14767
+ .dim-feishuGroupError, .dim-feishuGroupRefreshError { color: var(--dsw-alias-state-error-primary, #d54941); background: color-mix(in srgb, var(--dsw-alias-state-error-primary, #d54941) 7%, var(--dsw-alias-bg-layer-1, #fff)); }
14768
+ .dim-feishuGroupAuthorization { min-width: 0; display: grid; grid-template-columns: 184px minmax(0, 1fr); align-items: start; gap: 24px; padding: 18px; border: 1px solid color-mix(in srgb, var(--dsw-alias-state-business-primary, #3370ff) 30%, var(--dsw-alias-border-l2, #dfe1e5)); border-radius: 12px; background: color-mix(in srgb, var(--dsw-alias-state-business-primary, #3370ff) 2.5%, var(--dsw-alias-bg-layer-3, #fff)); }
14769
+ .dim-feishuGroupAuthorizationState { min-height: 126px; grid-template-columns: 32px minmax(0, 1fr); align-items: center; }
14770
+ .dim-feishuGroupAuthorizationState h3, .dim-feishuGroupAuthorizationError h3, .dim-feishuGroupAuthorizationCopy h3 { margin: 0; color: var(--dsw-alias-label-primary, #1f2329); font-size: 16px; line-height: 24px; font-weight: 650; }
14771
+ .dim-feishuGroupAuthorizationState p, .dim-feishuGroupAuthorizationError p, .dim-feishuGroupAuthorizationCopy > p { margin: 5px 0 0; color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; line-height: 19px; }
14772
+ .dim-feishuGroupSpinner { width: 24px; height: 24px; border: 3px solid var(--dsw-alias-border-l2, #dfe1e5); border-top-color: var(--dsw-alias-state-business-primary, #3370ff); border-radius: 50%; animation: dim-spin .8s linear infinite; }
14773
+ .dim-feishuGroupAuthorizationError { grid-template-columns: minmax(0, 1fr) max-content; align-items: center; border-color: color-mix(in srgb, var(--dsw-alias-state-error-primary, #d54941) 28%, var(--dsw-alias-border-l2, #dfe1e5)); background: color-mix(in srgb, var(--dsw-alias-state-error-primary, #d54941) 4%, var(--dsw-alias-bg-layer-3, #fff)); }
14774
+ .dim-feishuGroupAuthorizationError code { display: inline-block; margin-top: 6px; color: var(--dsw-alias-label-tertiary, #8f959e); font: 11px/17px ui-monospace, SFMono-Regular, Menlo, monospace; }
14775
+ .dim-feishuGroupQrColumn { min-width: 0; }
14776
+ .dim-feishuGroupQrFrame { position: relative; width: 176px; height: 176px; display: grid; place-items: center; padding: 10px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 11px; background: #fff; box-shadow: 0 6px 18px rgb(31 35 41 / 7%); }
14777
+ .dim-feishuGroupQrFrame img { width: 100%; height: 100%; display: block; object-fit: contain; }
14778
+ .dim-feishuGroupQrFallback { width: 100%; height: 100%; display: grid; place-content: center; justify-items: center; gap: 7px; border-radius: 7px; color: var(--dsw-alias-state-business-primary, #3370ff); background: #f7f9ff; text-align: center; }
14779
+ .dim-feishuGroupQrFallback span { color: var(--dsw-alias-label-secondary, #646a73); font-size: 11px; line-height: 17px; }
14780
+ .dim-feishuGroupQrExpired { position: absolute; inset: 10px; display: grid; place-content: center; gap: 2px; border-radius: 8px; color: var(--dsw-alias-label-primary, #1f2329); background: rgb(255 255 255 / 94%); backdrop-filter: blur(3px); text-align: center; }
14781
+ .dim-feishuGroupQrExpired span { font-size: 13px; line-height: 20px; font-weight: 650; }
14782
+ .dim-feishuGroupQrExpired small { color: var(--dsw-alias-label-secondary, #646a73); font-size: 11px; line-height: 17px; }
14783
+ .dim-feishuGroupCountdown { width: 176px; display: grid; grid-template-columns: minmax(0, 1fr) max-content; gap: 5px 10px; margin-top: 9px; color: var(--dsw-alias-label-tertiary, #8f959e); font-size: 11px; line-height: 17px; font-variant-numeric: tabular-nums; }
14784
+ .dim-feishuGroupCountdown strong { color: var(--dsw-alias-label-secondary, #646a73); font-weight: 600; }
14785
+ .dim-feishuGroupProgress { grid-column: 1 / -1; height: 3px; overflow: hidden; border-radius: 99px; background: var(--dsw-alias-bg-module-platform, #f2f3f5); }
14786
+ .dim-feishuGroupProgress > span { display: block; height: 100%; border-radius: inherit; background: var(--dsw-alias-state-business-primary, #3370ff); transition: width 1s linear; }
14787
+ .dim-feishuGroupAuthorizationCopy { min-width: 0; }
14788
+ .dim-feishuGroupAuthorizationEyebrow { display: block; margin-bottom: 7px; color: var(--dsw-alias-label-secondary, #646a73); font-size: 11px; line-height: 17px; font-weight: 600; }
14789
+ .dim-feishuGroupAuthorizationCopy ol { display: grid; gap: 6px; margin: 13px 0 0; padding-left: 18px; color: var(--dsw-alias-label-secondary, #646a73); font-size: 11px; line-height: 18px; }
14790
+ .dim-feishuGroupAuthorizationActions { display: flex; align-items: center; justify-content: flex-start; flex-wrap: wrap; gap: 8px; margin-top: 14px; }
14791
+ .dim-feishuGroupAuthorizationLink { text-decoration: none; }
13953
14792
  .dim-deliveryDocsLink { min-height: 30px; flex: none; display: inline-flex; align-items: center; gap: 4px; padding: 5px 8px; border-radius: 7px; color: var(--dsw-alias-state-business-primary, #3370ff); font-size: 12px; line-height: 18px; text-decoration: none; white-space: nowrap; }
13954
14793
  .dim-deliveryDocsLink:hover { background: color-mix(in srgb, var(--dsw-alias-state-business-primary, #3370ff) 8%, transparent); }
13955
14794
  .dim-deliveryDocsLink:focus-visible { outline: 2px solid var(--dsw-alias-state-business-primary, #3370ff); outline-offset: 2px; }
@@ -13979,6 +14818,12 @@ var CSS11 = String.raw`
13979
14818
  .dim-targetTitle span { flex: none; padding: 1px 6px; border-radius: 5px; color: var(--dsw-alias-state-business-primary, #3370ff); background: color-mix(in srgb, var(--dsw-alias-state-business-primary, #3370ff) 9%, transparent); font-size: 10px; line-height: 16px; }
13980
14819
  .dim-targetSummary code { display: block; overflow: hidden; margin-top: 3px; color: var(--dsw-alias-label-secondary, #646a73); font: 11px/17px ui-monospace, SFMono-Regular, Menlo, monospace; text-overflow: ellipsis; white-space: nowrap; }
13981
14820
  .dim-targetActions { display: flex; align-items: center; justify-content: flex-end; gap: 6px; flex-wrap: wrap; }
14821
+ .dim-targetSessionSync { grid-column: 1 / -1; min-width: 0; display: flex; align-items: center; justify-content: space-between; gap: 12px; padding-top: 9px; border-top: 1px solid var(--dsw-alias-border-l1, #eef0f3); cursor: pointer; }
14822
+ .dim-targetSessionSyncCopy { min-width: 0; display: grid; gap: 1px; }
14823
+ .dim-targetSessionSyncCopy strong { color: var(--dsw-alias-label-primary, #1f2329); font-size: 12px; line-height: 18px; font-weight: 600; }
14824
+ .dim-targetSessionSyncCopy small { color: var(--dsw-alias-label-secondary, #646a73); font-size: 11px; line-height: 17px; }
14825
+ .dim-targetSessionSync input { width: 16px; height: 16px; flex: none; margin: 0; accent-color: var(--dsw-alias-state-business-primary, #3370ff); }
14826
+ .dim-targetSessionSync:has(input:disabled) { cursor: default; }
13982
14827
  .dim-targetFeedback { grid-column: 1 / -1; margin: 0; color: var(--dsw-alias-label-secondary, #646a73); font-size: 11px; line-height: 17px; }
13983
14828
  .dim-targetFeedback[data-tone="success"] { color: var(--dsw-alias-state-success-primary, #20a162); }
13984
14829
  .dim-targetFeedback[data-tone="error"], .dim-targetFormError { color: var(--dsw-alias-state-error-primary, #d54941); }
@@ -14102,6 +14947,10 @@ var CSS11 = String.raw`
14102
14947
  .dim-accessControls { grid-template-columns: minmax(0, 1fr); }
14103
14948
  .dim-accessUserRow { grid-template-columns: minmax(0, 1fr); }
14104
14949
  .dim-accessDeleteUser { justify-self: start; }
14950
+ .dim-feishuGroupAuthorization { grid-template-columns: minmax(0, 1fr); justify-items: center; }
14951
+ .dim-feishuGroupAuthorizationCopy { width: 100%; }
14952
+ .dim-feishuGroupAuthorizationState { grid-template-columns: 32px minmax(0, 1fr); justify-items: stretch; }
14953
+ .dim-feishuGroupAuthorizationError { justify-items: stretch; }
14105
14954
  }
14106
14955
  @media (max-width: 840px) {
14107
14956
  .dim-title { align-items: flex-start; }
@@ -14131,6 +14980,7 @@ var CSS11 = String.raw`
14131
14980
  .dim-deliveryBotId > span { grid-column: 1 / -1; }
14132
14981
  .dim-targetActions .dim-deliveryButton { flex: 1 1 auto; }
14133
14982
  .dim-accessActions .dim-deliveryButton { width: 100%; }
14983
+ .dim-feishuGroupAuthorizationActions .dim-deliveryButton { flex: 1 1 auto; }
14134
14984
  .dim-directoryPickerBackdrop { padding: 10px; }
14135
14985
  .dim-directoryPicker { height: calc(100vh - 20px); min-height: 0; border-radius: 14px; }
14136
14986
  .dim-directoryPickerHeader { padding: 18px 17px 14px; }
@@ -14160,7 +15010,7 @@ function installImStyles() {
14160
15010
  }
14161
15011
 
14162
15012
  // plugin-src/client/update-panel.js
14163
- var React23 = __toESM(require("react"), 1);
15013
+ var React25 = __toESM(require("react"), 1);
14164
15014
  var import_react_dom3 = require("react-dom");
14165
15015
  var import_valid = __toESM(require_valid(), 1);
14166
15016
  var import_rcompare = __toESM(require_rcompare(), 1);
@@ -14250,11 +15100,11 @@ function manualUpdateCommand(snapshot) {
14250
15100
  return `dsh plugin --profile ${profileArgument} add -w @xmanrui/dsh-im@${version}`;
14251
15101
  }
14252
15102
  function ManualUpdateCommand({ command, disabled, sourceInstall, desktop }) {
14253
- const [copyState, setCopyState] = React23.useState("idle");
14254
- const commandRef = React23.useRef(null);
14255
- const mounted = React23.useRef(false);
14256
- const copying = React23.useRef(false);
14257
- React23.useEffect(() => {
15103
+ const [copyState, setCopyState] = React25.useState("idle");
15104
+ const commandRef = React25.useRef(null);
15105
+ const mounted = React25.useRef(false);
15106
+ const copying = React25.useRef(false);
15107
+ React25.useEffect(() => {
14258
15108
  mounted.current = true;
14259
15109
  return () => {
14260
15110
  mounted.current = false;
@@ -14286,7 +15136,7 @@ function ManualUpdateCommand({ command, disabled, sourceInstall, desktop }) {
14286
15136
  { className: "dim-updateManual", "aria-label": "\u624B\u5DE5\u66F4\u65B0" },
14287
15137
  h2("h4", { className: "dim-updateManualHeading" }, "\u624B\u5DE5\u66F4\u65B0"),
14288
15138
  command ? h2(
14289
- React23.Fragment,
15139
+ React25.Fragment,
14290
15140
  null,
14291
15141
  h2("p", { className: "dim-updateManualHint" }, "\u81EA\u52A8\u66F4\u65B0\u5931\u8D25\u53EF\u4EE5\u4F7F\u7528\u547D\u4EE4\u66F4\u65B0\uFF1A"),
14292
15142
  h2(
@@ -14322,7 +15172,7 @@ function ManualUpdateCommand({ command, disabled, sourceInstall, desktop }) {
14322
15172
  "aria-hidden": "true",
14323
15173
  focusable: "false"
14324
15174
  }, copyState === "copied" ? h2("path", { d: "m4 10 4 4 8-8" }) : h2(
14325
- React23.Fragment,
15175
+ React25.Fragment,
14326
15176
  null,
14327
15177
  h2("rect", { x: 7, y: 7, width: 10, height: 11, rx: 1.5 }),
14328
15178
  h2("path", { d: "M5 13H3.5A1.5 1.5 0 0 1 2 11.5v-8A1.5 1.5 0 0 1 3.5 2h8A1.5 1.5 0 0 1 13 3.5V5" })
@@ -14347,10 +15197,10 @@ function ManualUpdateCommand({ command, disabled, sourceInstall, desktop }) {
14347
15197
  );
14348
15198
  }
14349
15199
  function UpdateDialog({ children, onClose }) {
14350
- const dialogRef = React23.useRef(null);
14351
- const titleId = React23.useId();
14352
- const descriptionId = React23.useId();
14353
- React23.useEffect(() => {
15200
+ const dialogRef = React25.useRef(null);
15201
+ const titleId = React25.useId();
15202
+ const descriptionId = React25.useId();
15203
+ React25.useEffect(() => {
14354
15204
  const previous = globalThis.document?.activeElement;
14355
15205
  dialogRef.current?.focus?.();
14356
15206
  return () => {
@@ -14408,19 +15258,19 @@ function UpdateDialog({ children, onClose }) {
14408
15258
  return typeof document !== "undefined" && document.body ? (0, import_react_dom3.createPortal)(content, document.body) : content;
14409
15259
  }
14410
15260
  function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
14411
- const [snapshot, setSnapshot] = React23.useState(null);
14412
- const [action, setAction] = React23.useState("status");
14413
- const [error, setError] = React23.useState(null);
14414
- const [open, setOpen] = React23.useState(false);
14415
- const [uncertainInstall, setUncertainInstall] = React23.useState(false);
14416
- const mounted = React23.useRef(false);
14417
- const busy = React23.useRef(false);
14418
- const readController = React23.useRef(null);
14419
- const pollReadController = React23.useRef(null);
14420
- const installRequest = React23.useRef(null);
14421
- const onStatusRef = React23.useRef(onStatus);
15261
+ const [snapshot, setSnapshot] = React25.useState(null);
15262
+ const [action, setAction] = React25.useState("status");
15263
+ const [error, setError] = React25.useState(null);
15264
+ const [open, setOpen] = React25.useState(false);
15265
+ const [uncertainInstall, setUncertainInstall] = React25.useState(false);
15266
+ const mounted = React25.useRef(false);
15267
+ const busy = React25.useRef(false);
15268
+ const readController = React25.useRef(null);
15269
+ const pollReadController = React25.useRef(null);
15270
+ const installRequest = React25.useRef(null);
15271
+ const onStatusRef = React25.useRef(onStatus);
14422
15272
  onStatusRef.current = onStatus;
14423
- const accept = React23.useCallback((next) => {
15273
+ const accept = React25.useCallback((next) => {
14424
15274
  setSnapshot(next);
14425
15275
  onStatusRef.current?.(next);
14426
15276
  }, []);
@@ -14429,7 +15279,7 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
14429
15279
  setUncertainInstall(false);
14430
15280
  accept(next);
14431
15281
  };
14432
- const invoke = React23.useCallback(async (endpoint, payload = {}, signal) => {
15282
+ const invoke = React25.useCallback(async (endpoint, payload = {}, signal) => {
14433
15283
  if (typeof rpcCall !== "function") {
14434
15284
  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");
14435
15285
  unavailable.code = "update-unavailable";
@@ -14437,7 +15287,7 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
14437
15287
  }
14438
15288
  return unwrapSnapshot(await rpcCall(endpoint, payload, signal));
14439
15289
  }, [rpcCall]);
14440
- React23.useEffect(() => {
15290
+ React25.useEffect(() => {
14441
15291
  mounted.current = true;
14442
15292
  busy.current = true;
14443
15293
  const controller = new AbortController();
@@ -14460,7 +15310,7 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
14460
15310
  const activeJob = ACTIVE_STATES4.has(snapshot?.job?.state);
14461
15311
  const restartRequired = snapshot?.job?.state === "restart-required" || snapshot?.blockedReason === "pending-restart";
14462
15312
  const shouldPoll = activeJob || uncertainInstall;
14463
- React23.useEffect(() => {
15313
+ React25.useEffect(() => {
14464
15314
  if (!shouldPoll) return void 0;
14465
15315
  let controller;
14466
15316
  const scheduler = createPollScheduler({
@@ -14558,7 +15408,7 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
14558
15408
  const manualCommand = manualUpdateCommand(snapshot);
14559
15409
  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";
14560
15410
  return h2(
14561
- React23.Fragment,
15411
+ React25.Fragment,
14562
15412
  null,
14563
15413
  h2("button", {
14564
15414
  type: "button",
@@ -14583,13 +15433,13 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
14583
15433
  h2("dt", null, "\u8FD0\u884C\u7248\u672C"),
14584
15434
  h2("dd", null, `v${snapshot?.runningVersion ?? clientVersion}`),
14585
15435
  snapshot?.installedVersion && snapshot.installedVersion !== snapshot.runningVersion ? h2(
14586
- React23.Fragment,
15436
+ React25.Fragment,
14587
15437
  null,
14588
15438
  h2("dt", null, "\u5DF2\u5B89\u88C5\u7248\u672C"),
14589
15439
  h2("dd", null, `v${snapshot.installedVersion}`)
14590
15440
  ) : null,
14591
15441
  targetVersion ? h2(
14592
- React23.Fragment,
15442
+ React25.Fragment,
14593
15443
  null,
14594
15444
  h2("dt", null, "\u76EE\u6807\u7248\u672C"),
14595
15445
  h2("dd", null, `v${targetVersion}`)
@@ -14794,23 +15644,23 @@ function IMSettingsTab({
14794
15644
  browserLocation = globalThis.location,
14795
15645
  navigateToRecoveryUrl = replacePageLocation
14796
15646
  }) {
14797
- const [selected, setSelected] = React24.useState("weixin");
14798
- const [loopbackRecovery, setLoopbackRecovery] = React24.useState(null);
14799
- const [runningVersion, setRunningVersion] = React24.useState(IM_PLUGIN_VERSION);
14800
- const [deliverySettings, setDeliverySettings] = React24.useState(null);
14801
- const githubTooltipId = React24.useId();
14802
- const generalSettingsTooltipId = React24.useId();
15647
+ const [selected, setSelected] = React26.useState("weixin");
15648
+ const [loopbackRecovery, setLoopbackRecovery] = React26.useState(null);
15649
+ const [runningVersion, setRunningVersion] = React26.useState(IM_PLUGIN_VERSION);
15650
+ const [deliverySettings, setDeliverySettings] = React26.useState(null);
15651
+ const githubTooltipId = React26.useId();
15652
+ const generalSettingsTooltipId = React26.useId();
14803
15653
  const globalSettingsSelected = selected === GLOBAL_SETTINGS_TAB_ID;
14804
15654
  const active = CHANNELS.find((channel4) => channel4.id === selected) ?? CHANNELS[0];
14805
15655
  const activeTabId = globalSettingsSelected ? "dim-general-settings-trigger" : `dim-tab-${active.id}`;
14806
15656
  const activePanelId = globalSettingsSelected ? `dim-panel-${GLOBAL_SETTINGS_TAB_ID}` : `dim-panel-${active.id}`;
14807
- const reportLoopbackRecovery = React24.useCallback((recovery) => {
15657
+ const reportLoopbackRecovery = React26.useCallback((recovery) => {
14808
15658
  setLoopbackRecovery((current) => current?.url === recovery.url ? current : recovery);
14809
15659
  }, []);
14810
- const reportUpdateStatus = React24.useCallback((snapshot) => {
15660
+ const reportUpdateStatus = React26.useCallback((snapshot) => {
14811
15661
  setRunningVersion(snapshot.runningVersion);
14812
15662
  }, []);
14813
- const rpcCalls = React24.useMemo(() => createLoopbackAwareRpcCalls({
15663
+ const rpcCalls = React26.useMemo(() => createLoopbackAwareRpcCalls({
14814
15664
  dingtalkRpcCall,
14815
15665
  discordRpcCall,
14816
15666
  feishuRpcCall,
@@ -14844,7 +15694,7 @@ function IMSettingsTab({
14844
15694
  weixinRpcCall,
14845
15695
  whatsappRpcCall
14846
15696
  ]);
14847
- const botSettingsContext = React24.useMemo(() => Object.freeze({
15697
+ const botSettingsContext = React26.useMemo(() => Object.freeze({
14848
15698
  openBotSettings: setDeliverySettings
14849
15699
  }), []);
14850
15700
  return h2(