diff --git a/CHANGELOG.md b/CHANGELOG.md index c04d494..e807c20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,11 @@ This file records the notable changes in each dsh-im release. Its format follows ## [Unreleased] +### Changed / 变更 + +- 机器人卡片的设置页改为可扩展的横向 Tab 布局,现有 Bot ID、投递目标管理与专属使用文档统一归入“投递设置”页签。 + Bot-card settings now use an extensible horizontal tab layout, with the existing Bot ID, delivery-target management, and dedicated guide grouped under the Delivery settings tab. + ## [4.3.0] - 2026-09-01 ### Added / 新增 diff --git a/lib/client.js b/lib/client.js index 2228335..274079b 100644 --- a/lib/client.js +++ b/lib/client.js @@ -841,6 +841,9 @@ var EN = Object.freeze({ "IM\u673A\u5668\u4EBA": "IM bots", "IM\u673A\u5668\u4EBA\u8BBE\u7F6E": "IM bot settings", "\u66F4\u591A\u673A\u5668\u4EBA\u8BBE\u7F6E": "More bot settings", + "\u673A\u5668\u4EBA\u8BBE\u7F6E": "Bot settings", + "\u673A\u5668\u4EBA\u8BBE\u7F6E\u9875\u7B7E": "Bot settings tabs", + "\u6295\u9012\u8BBE\u7F6E": "Delivery settings", "IM \u6E20\u9053": "IM channels", "\u8BA9 DeepSeek Harness \u89E6\u624B\u53EF\u53CA": "Connecting DeepSeek Harness", "\u5F53\u524D\u7248\u672C": "Current version", @@ -11870,6 +11873,9 @@ var DELIVERY_ENDPOINTS = Object.freeze({ delete: "target.delete", test: "target.test" }); +var BOT_SETTINGS_TABS = Object.freeze([ + Object.freeze({ id: "delivery", label: "\u6295\u9012\u8BBE\u7F6E" }) +]); var CHANNEL_DEFINITIONS = Object.freeze({ weixin: { label: "\u5FAE\u4FE1", @@ -12520,6 +12526,9 @@ function DeliveryTargetSettingsPage({ channel: channel4, account, rpcCall, onBac setBotCopyState(presentError10(caught, "\u590D\u5236\u5931\u8D25\u3002")); } }; + const deliveryTab = BOT_SETTINGS_TABS[0]; + const deliveryTabId = `dim-bot-settings-${deliveryTab.id}-tab`; + const deliveryPanelId = `dim-bot-settings-${deliveryTab.id}-panel`; return h2( "section", { @@ -12529,114 +12538,137 @@ function DeliveryTargetSettingsPage({ channel: channel4, account, rpcCall, onBac h2( "header", { className: "dim-deliveryHeader" }, - h2(DeliveryButton, { className: "dim-deliveryBack", onClick: onBack }, "\u2190 \u8FD4\u56DE\u673A\u5668\u4EBA\u5217\u8868"), - h2( - "div", - { className: "dim-deliveryHeaderMeta" }, - h2( - "a", - { - className: "dim-deliveryDocsLink", - href: isEnglish() ? DELIVERY_DOCS_URL.en : DELIVERY_DOCS_URL.zh, - target: "_blank", - rel: "noopener noreferrer", - "aria-label": "\u6253\u5F00\u4E3B\u52A8\u6295\u9012\u4F7F\u7528\u6587\u6863" - }, - h2("span", null, "\u4F7F\u7528\u6587\u6863"), - h2("span", { "aria-hidden": "true" }, "\u2197") - ) - ) + h2(DeliveryButton, { className: "dim-deliveryBack", onClick: onBack }, "\u2190 \u8FD4\u56DE\u673A\u5668\u4EBA\u5217\u8868") ), h2( - "section", - { className: "dim-deliveryIdentity", "aria-labelledby": "dim-delivery-bot-title" }, - h2( - "div", - null, - h2( - "h2", - { id: "dim-delivery-bot-title", className: "dim-deliveryBotName" }, - account.botName || "\u673A\u5668\u4EBA\u8BBE\u7F6E" - ) - ), - h2( - "div", - { className: "dim-deliveryBotId" }, - h2("span", null, "Bot ID"), - h2("code", { title: account.botId }, account.botId), - h2(DeliveryButton, { onClick: () => void copyBotId() }, "\u590D\u5236") - ), - botCopyState ? h2("p", { className: "dim-targetFeedback", role: "status" }, botCopyState) : null + "div", + { className: "dim-botSettingsTabsBar" }, + h2("nav", { + className: "dim-botSettingsTabs", + role: "tablist", + "aria-label": "\u673A\u5668\u4EBA\u8BBE\u7F6E\u9875\u7B7E" + }, BOT_SETTINGS_TABS.map((tab) => h2("button", { + key: tab.id, + id: `dim-bot-settings-${tab.id}-tab`, + type: "button", + role: "tab", + className: "dim-botSettingsTab", + "aria-selected": tab.id === deliveryTab.id, + "aria-controls": `dim-bot-settings-${tab.id}-panel`, + tabIndex: tab.id === deliveryTab.id ? 0 : -1 + }, tab.label))) ), h2( - "section", - { className: "dim-deliveryTargets", "aria-labelledby": "dim-delivery-targets-title" }, + "div", + { + id: deliveryPanelId, + className: "dim-botSettingsTabPanel", + role: "tabpanel", + "aria-labelledby": deliveryTabId + }, h2( - "div", - { className: "dim-deliverySectionHeading" }, + "section", + { className: "dim-deliveryIdentity", "aria-labelledby": "dim-delivery-bot-title" }, h2( "div", - null, - h2("h3", { id: "dim-delivery-targets-title" }, "\u6295\u9012\u76EE\u6807"), - account.connected ? null : h2("p", null, "\u673A\u5668\u4EBA\u5F53\u524D\u79BB\u7EBF\uFF1B\u4ECD\u53EF\u914D\u7F6E\u76EE\u6807\uFF0C\u6062\u590D\u8FDE\u63A5\u540E\u518D\u6D4B\u8BD5\u3002") + { className: "dim-deliveryIdentityHeading" }, + h2( + "h2", + { id: "dim-delivery-bot-title", className: "dim-deliveryBotName" }, + account.botName || "\u673A\u5668\u4EBA\u8BBE\u7F6E" + ), + h2( + "a", + { + className: "dim-deliveryDocsLink", + href: isEnglish() ? DELIVERY_DOCS_URL.en : DELIVERY_DOCS_URL.zh, + target: "_blank", + rel: "noopener noreferrer", + "aria-label": "\u6253\u5F00\u4E3B\u52A8\u6295\u9012\u4F7F\u7528\u6587\u6863" + }, + h2("span", null, "\u4F7F\u7528\u6587\u6863"), + h2("span", { "aria-hidden": "true" }, "\u2197") + ) ), - h2(DeliveryButton, { - kind: "primary", - onClick: openSuggestionPicker, - disabled: Boolean(editor) || phase !== "ready", - title: phase === "ready" ? void 0 : "\u8BF7\u5148\u5B8C\u6210\u6295\u9012\u76EE\u6807\u8BFB\u53D6" - }, "\u65B0\u5EFA\u76EE\u6807") + h2( + "div", + { className: "dim-deliveryBotId" }, + h2("span", null, "Bot ID"), + h2("code", { title: account.botId }, account.botId), + h2(DeliveryButton, { onClick: () => void copyBotId() }, "\u590D\u5236") + ), + botCopyState ? h2("p", { className: "dim-targetFeedback", role: "status" }, botCopyState) : null ), - editor?.mode === "suggestions" ? h2(TargetSuggestionPicker, { - definition, - phase: suggestionPhase, - suggestions, - error: suggestionError, - targets, - onRefresh: loadSuggestions, - onSelect: selectSuggestion, - onManual: () => setEditor({ - mode: "create", - source: "manual", - draftKey: "manual", - initialValue: { targetId: randomTargetId(targets) } - }), - onCancel: () => setEditor(null) - }) : editor ? h2(TargetForm, { - key: editor.mode === "edit" ? editor.target.targetId : editor.draftKey, - definition, - mode: editor.mode, - initialValue: editor.mode === "edit" ? editor.target : editor.initialValue, - source: editor.source, - busy: saving, - connected: account.connected, - onCancel: () => setEditor(editor.source === "suggestion" ? { mode: "suggestions" } : null), - onSave: saveTarget, - onTest: (target) => invoke(DELIVERY_ENDPOINTS.test, { + h2( + "section", + { className: "dim-deliveryTargets", "aria-labelledby": "dim-delivery-targets-title" }, + h2( + "div", + { className: "dim-deliverySectionHeading" }, + h2( + "div", + null, + h2("h3", { id: "dim-delivery-targets-title" }, "\u6295\u9012\u76EE\u6807"), + account.connected ? null : h2("p", null, "\u673A\u5668\u4EBA\u5F53\u524D\u79BB\u7EBF\uFF1B\u4ECD\u53EF\u914D\u7F6E\u76EE\u6807\uFF0C\u6062\u590D\u8FDE\u63A5\u540E\u518D\u6D4B\u8BD5\u3002") + ), + h2(DeliveryButton, { + kind: "primary", + onClick: openSuggestionPicker, + disabled: Boolean(editor) || phase !== "ready", + title: phase === "ready" ? void 0 : "\u8BF7\u5148\u5B8C\u6210\u6295\u9012\u76EE\u6807\u8BFB\u53D6" + }, "\u65B0\u5EFA\u76EE\u6807") + ), + editor?.mode === "suggestions" ? h2(TargetSuggestionPicker, { + definition, + phase: suggestionPhase, + suggestions, + error: suggestionError, + targets, + onRefresh: loadSuggestions, + onSelect: selectSuggestion, + onManual: () => setEditor({ + mode: "create", + source: "manual", + draftKey: "manual", + initialValue: { targetId: randomTargetId(targets) } + }), + onCancel: () => setEditor(null) + }) : editor ? h2(TargetForm, { + key: editor.mode === "edit" ? editor.target.targetId : editor.draftKey, + definition, + mode: editor.mode, + initialValue: editor.mode === "edit" ? editor.target : editor.initialValue, + source: editor.source, + busy: saving, + connected: account.connected, + onCancel: () => setEditor(editor.source === "suggestion" ? { mode: "suggestions" } : null), + onSave: saveTarget, + onTest: (target) => invoke(DELIVERY_ENDPOINTS.test, { + botId: account.botId, + target + }) + }) : null, + phase === "loading" ? h2("div", { className: "dim-deliveryState", "aria-busy": "true" }, "\u6B63\u5728\u8BFB\u53D6\u6295\u9012\u76EE\u6807\u2026") : phase === "error" ? h2( + "div", + { className: "dim-deliveryState", role: "alert" }, + h2("p", null, error), + h2(DeliveryButton, { onClick: () => void loadTargets() }, "\u91CD\u65B0\u8BFB\u53D6") + ) : targets.length === 0 ? h2( + "div", + { className: "dim-deliveryState dim-deliveryEmpty" }, + h2("strong", null, "\u5C1A\u672A\u914D\u7F6E\u6295\u9012\u76EE\u6807"), + h2("p", null, "\u70B9\u51FB\u201C\u65B0\u5EFA\u76EE\u6807\u201D\u53EF\u4ECE\u5DF2\u804A\u8FC7\u7684\u4F1A\u8BDD\u9009\u62E9\uFF0C\u4E5F\u53EF\u624B\u52A8\u586B\u5199\u3002") + ) : h2("ul", { className: "dim-targetList" }, targets.map((target) => h2(TargetRow, { + key: target.targetId, + definition, + target, botId: account.botId, - target - }) - }) : null, - phase === "loading" ? h2("div", { className: "dim-deliveryState", "aria-busy": "true" }, "\u6B63\u5728\u8BFB\u53D6\u6295\u9012\u76EE\u6807\u2026") : phase === "error" ? h2( - "div", - { className: "dim-deliveryState", role: "alert" }, - h2("p", null, error), - h2(DeliveryButton, { onClick: () => void loadTargets() }, "\u91CD\u65B0\u8BFB\u53D6") - ) : targets.length === 0 ? h2( - "div", - { className: "dim-deliveryState dim-deliveryEmpty" }, - h2("strong", null, "\u5C1A\u672A\u914D\u7F6E\u6295\u9012\u76EE\u6807"), - h2("p", null, "\u70B9\u51FB\u201C\u65B0\u5EFA\u76EE\u6807\u201D\u53EF\u4ECE\u5DF2\u804A\u8FC7\u7684\u4F1A\u8BDD\u9009\u62E9\uFF0C\u4E5F\u53EF\u624B\u52A8\u586B\u5199\u3002") - ) : h2("ul", { className: "dim-targetList" }, targets.map((target) => h2(TargetRow, { - key: target.targetId, - definition, - target, - botId: account.botId, - connected: account.connected, - rpcCall: invoke, - onChanged: () => loadTargets({ silent: true }), - onEdit: () => setEditor({ mode: "edit", target, source: "edit" }) - }))) + connected: account.connected, + rpcCall: invoke, + onChanged: () => loadTargets({ silent: true }), + onEdit: () => setEditor({ mode: "edit", target, source: "edit" }) + }))) + ) ) ); } @@ -13067,9 +13099,18 @@ var CSS11 = String.raw` .dim-botSettingsButton:focus-visible { outline: 2px solid var(--dsw-alias-state-business-primary, #3370ff); outline-offset: 2px; } .dim-botSettingsTooltip { position: absolute; top: calc(100% + 6px); right: 0; z-index: 30; width: max-content; padding: 6px 9px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 7px; color: var(--dsw-alias-label-primary, #1f2329); background: var(--dsw-alias-bg-layer-3, #fff); box-shadow: 0 8px 24px rgb(31 35 41 / 14%); font-size: 11px; line-height: 16px; opacity: 0; visibility: hidden; transform: translateY(-3px); pointer-events: none; transition: opacity .15s ease, transform .15s ease, visibility .15s ease; } .dim-botSettingsAction:hover .dim-botSettingsTooltip, .dim-botSettingsAction:focus-within .dim-botSettingsTooltip { opacity: 1; visibility: visible; transform: translateY(0); } -.dim-deliveryPage { min-width: 0; display: grid; gap: 14px; } +.dim-deliveryPage { min-width: 0; display: grid; } .dim-deliveryHeader { min-width: 0; display: flex; align-items: center; justify-content: space-between; gap: 16px; } -.dim-deliveryHeaderMeta { min-width: 0; display: flex; align-items: center; justify-content: flex-end; gap: 10px; text-align: right; } +.dim-botSettingsTabsBar { min-width: 0; margin-top: 10px; border-bottom: 1px solid var(--dsw-alias-border-l2, #dfe1e5); } +.dim-botSettingsTabs { min-width: 0; display: flex; align-items: flex-end; gap: 24px; overflow-x: auto; scrollbar-width: none; } +.dim-botSettingsTabs::-webkit-scrollbar { display: none; } +.dim-botSettingsTab { position: relative; min-height: 38px; flex: none; display: inline-flex; align-items: center; justify-content: center; padding: 6px 2px 9px; border: 0; color: var(--dsw-alias-label-secondary, #646a73); background: transparent; font: inherit; font-size: 13px; line-height: 20px; font-weight: 560; white-space: nowrap; cursor: pointer; transition: color .15s ease; } +.dim-botSettingsTab::after { content: ''; position: absolute; right: 0; bottom: -1px; left: 0; height: 2px; border-radius: 2px 2px 0 0; background: transparent; transform: scaleX(.45); transition: background .15s ease, transform .15s ease; } +.dim-botSettingsTab:hover:not([aria-selected="true"]) { color: var(--dsw-alias-label-primary, #1f2329); } +.dim-botSettingsTab[aria-selected="true"] { color: var(--dsw-alias-state-business-primary, #3370ff); font-weight: 650; } +.dim-botSettingsTab[aria-selected="true"]::after { background: var(--dsw-alias-state-business-primary, #3370ff); transform: scaleX(1); } +.dim-botSettingsTab:focus-visible { outline: 2px solid var(--dsw-alias-state-business-primary, #3370ff); outline-offset: -2px; border-radius: 5px; } +.dim-botSettingsTabPanel { min-width: 0; display: grid; gap: 14px; padding-top: 14px; } .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; } .dim-deliveryDocsLink:hover { background: color-mix(in srgb, var(--dsw-alias-state-business-primary, #3370ff) 8%, transparent); } .dim-deliveryDocsLink:focus-visible { outline: 2px solid var(--dsw-alias-state-business-primary, #3370ff); outline-offset: 2px; } @@ -13081,6 +13122,7 @@ var CSS11 = String.raw` .dim-deliveryButton[data-kind="danger"] { color: var(--dsw-alias-state-error-primary, #d54941); } .dim-deliveryBack { flex: none; border-color: transparent; background: transparent; } .dim-deliveryIdentity, .dim-deliveryTargets { min-width: 0; padding: 16px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 12px; background: var(--dsw-alias-bg-layer-3, #fff); } +.dim-deliveryIdentityHeading { min-width: 0; display: flex; align-items: center; justify-content: space-between; gap: 12px; } .dim-deliveryBotName, .dim-deliverySectionHeading h3 { min-width: 0; overflow: hidden; margin: 0; font-size: 15px; line-height: 22px; font-weight: 650; text-overflow: ellipsis; white-space: nowrap; } .dim-deliveryIdentity > div:first-child p, .dim-deliverySectionHeading p { margin: 2px 0 0; color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; line-height: 18px; } .dim-deliveryBotId { min-width: 0; display: grid; grid-template-columns: max-content minmax(0, 1fr) max-content; align-items: center; gap: 10px; margin-top: 12px; padding: 10px 12px; border-radius: 9px; background: var(--dsw-alias-bg-module-platform, #f7f8fa); } @@ -13169,8 +13211,8 @@ var CSS11 = String.raw` .dim-rail { grid-template-columns: minmax(0, 1fr); } .dim-loopbackRecovery { align-items: stretch; flex-direction: column; gap: 12px; } .dim-loopbackRecoveryAction { width: 100%; } - .dim-deliveryHeader, .dim-deliverySectionHeading { align-items: stretch; flex-direction: column; } - .dim-deliveryHeaderMeta { justify-content: space-between; text-align: left; } + .dim-deliverySectionHeading { align-items: stretch; flex-direction: column; } + .dim-botSettingsTabs { gap: 18px; } .dim-deliveryBotId { grid-template-columns: minmax(0, 1fr) max-content; } .dim-deliveryBotId > span { grid-column: 1 / -1; } .dim-targetActions .dim-deliveryButton { flex: 1 1 auto; } diff --git a/lib/index.js b/lib/index.js index dc67ef2..8f82743 100644 --- a/lib/index.js +++ b/lib/index.js @@ -361,7 +361,7 @@ ID\uFF1A{id}`,{title:A,id:a?.sessionId??i}),w("\u53D1\u9001 /history \u67E5\u770 ${w("\u5185\u5BB9\u8F83\u957F\uFF0C\u751F\u6210\u5B8C\u6210\u540E\u5C06\u5206\u6BB5\u53D1\u9001\u5B8C\u6574\u56DE\u7B54\u3002")}`;return Zv(n,uu-t.length)+t}function CRe(n){let t=[],o=n;for(;o.length>uu;){let i=Zv(o,uu),e=i.lastIndexOf(` `)+1;e{let c=String(g??"")||"\u2026";await this.#n(a,VW(c)),A=c}};await o.markdown(r);let s=CRe(A);for(let[g,c]of s.entries()){let I=g===0?a:await this.#a(t,i.replyTo);g>0&&e.push(I),await this.#n(I,c),await this.#i(I)}return{messageId:a.messageId,providerMessageIds:e.map(g=>g.messageId)}}catch(a){for(let A of e)await this.#l(A.messageId);throw a}}async sendFile(t,o,{replyTo:i,signal:e}={}){return this.#g(t,o,{replyTo:i,signal:e,messageType:"file",presentation:"feishu-file"})}async sendImage(t,o,{replyTo:i,signal:e}={}){return this.#g(t,o,{replyTo:i,signal:e,messageType:"image",presentation:"feishu-image"})}async#g(t,o,{replyTo:i,signal:e,messageType:a,presentation:A}){if(e?.throwIfAborted(),typeof t!="string"||!t)throw new TypeError("chatId is required");if(!o||typeof o!="object"||typeof o.artifactId!="string"||!o.artifactId||typeof o.deliveryKey!="string"||!o.deliveryKey||typeof o.fileName!="string"||!o.fileName||!Buffer.isBuffer(o.bytes))throw new TypeError("A materialized result file is required");let r;try{r=await zW(Q=>{Q.throwIfAborted();let y=a==="image"?this.#e.im.v1.image.create({data:{image_type:"message",image:o.bytes}}):this.#e.im.v1.file.create({data:{file_type:"stream",file_name:o.fileName,file:o.bytes}});return In(o,y),y},{signal:e,timeoutMs:this.#t,stage:"upload"})}catch(Q){throw e?.aborted?Wv(e):Cu("upload",Q)}e?.throwIfAborted();let s=a==="image"?r?.image_key??r?.data?.image_key:r?.file_key??r?.data?.file_key;if(typeof s!="string"||!s)throw Cu("upload",void 0,r?.code);let g=hRe(o,t,a),c=JSON.stringify(a==="image"?{image_key:s}:{file_key:s}),I=i?{path:{message_id:i},data:{msg_type:a,content:c,uuid:g}}:{params:{receive_id_type:"chat_id"},data:{receive_id:t,msg_type:a,content:c,uuid:g}},d=()=>{let Q=i?this.#e.im.v1.message.reply(I):this.#e.im.v1.message.create(I);return In(o,Q),Q},B;try{B=await zW(async Q=>{Q.throwIfAborted();let y;try{y=await d()}catch(m){if(XW(m)!==230049)throw m;y={code:230049}}return Q.throwIfAborted(),Number(y?.code)===230049&&(y=await d(),Q.throwIfAborted()),y},{signal:e,timeoutMs:this.#r,stage:"message send"})}catch(Q){throw e?.aborted?Wv(e):Cu("message send",Q,void 0,{uncertain:!0})}if(Number.isFinite(Number(B?.code))&&Number(B.code)!==0)throw Cu("message send",void 0,B.code,{uncertain:!0});let f=B?.data?.message_id;if(typeof f!="string"||!f)throw Cu("message send",void 0,void 0,{uncertain:!0});return Wt({deliveryId:o.deliveryKey,presentation:A,providerMessageIds:[f],artifacts:[{artifactId:o.artifactId,outcome:"sent"}]})}async#a(t,o){let i=VW(this.#A),a=il("Feishu card.create",await this.#e.cardkit.v1.card.create({data:{type:"card_json",data:JSON.stringify(uRe(i))}}))?.data?.card_id;if(!a)throw new Error("Feishu card.create returned no card_id");let A=await this.#I(t,a,o);return{cardId:a,messageId:A,content:i,sequence:0}}async#n(t,o){if(o===t.content)return;let i=await this.#e.cardkit.v1.cardElement.content({path:{card_id:t.cardId,element_id:WW},data:{content:o,sequence:++t.sequence,uuid:`content_${t.cardId}_${t.sequence}`}});il("Feishu cardElement.content",i),t.content=o}async#i(t){let o=await this.#e.cardkit.v1.card.settings({path:{card_id:t.cardId},data:{settings:JSON.stringify({config:{streaming_mode:!1,summary:{content:ERe(t.content)||w("\u56DE\u7B54\u5B8C\u6210")}}}),sequence:++t.sequence,uuid:`settings_${t.cardId}_${t.sequence}`}});il("Feishu card.settings",o)}async#I(t,o,i){let e=JSON.stringify({type:"card",data:{card_id:o}}),a=i?await this.#e.im.v1.message.reply({path:{message_id:i},data:{msg_type:"interactive",content:e}}):await this.#e.im.v1.message.create({params:{receive_id_type:"chat_id"},data:{receive_id:t,msg_type:"interactive",content:e}});il("Feishu message send",a);let A=a?.data?.message_id;if(!A)throw new Error("Feishu message send returned no message_id");return A}async#l(t){try{let o=await this.#e.im.v1.message.delete({path:{message_id:t}});il("Feishu message delete",o)}catch(o){console.warn("[bridge] unable to recall a failed streaming card:",o.message)}}async addReaction(t,o){let e=il("Feishu reaction.create",await this.#e.im.v1.messageReaction.create({path:{message_id:t},data:{reaction_type:{emoji_type:o}}}))?.data?.reaction_id;if(!e)throw new Error("Feishu reaction.create returned no reaction_id");return e}async removeReaction(t,o){il("Feishu reaction.delete",await this.#e.im.v1.messageReaction.delete({path:{message_id:t,reaction_id:o}}))}};var e6="/open-apis/application/v7/app_slash_commands",BRe=new Set(["99991640","99991672"]),rS=Object.freeze(["application:app_slash_command:read","application:app_slash_command:write"]),A6="ai-agent_outlined",Xv=Object.freeze([{command:"menu",icon:"skill_outlined",default:"\u6253\u5F00\u529F\u80FD\u83DC\u5355",en_us:"Open the feature menu"},{command:"new",icon:"ai-deepthink_outlined",default:"\u5F00\u542F\u5168\u65B0\u4F1A\u8BDD",en_us:"Start a fresh session"},{command:"help",icon:"promptword_outlined",default:"\u67E5\u770B\u5E2E\u52A9",en_us:"Show help"},{command:"status",icon:"ai-functions_outlined",default:"\u67E5\u770B\u673A\u5668\u4EBA\u72B6\u6001",en_us:"Show bot status"},{command:"compact",icon:"ai-block_outlined",default:"\u538B\u7F29\u5F53\u524D\u4F1A\u8BDD\u4E0A\u4E0B\u6587",en_us:"Compact the current session"},{command:"sessionlist",icon:"chat-ai_outlined",default:"\u5217\u51FA\u4F1A\u8BDD",en_us:"List sessions"},{command:"workspacelist",icon:"folder_outlined",default:"\u5217\u51FA\u5DE5\u4F5C\u533A",en_us:"List workspaces"},{command:"watch",icon:"flag_outlined",default:"\u5173\u6CE8\u4E00\u4E2A\u4F1A\u8BDD",en_us:"Watch a session"},{command:"unwatch",icon:"clear_outlined",default:"\u53D6\u6D88\u5173\u6CE8\u4F1A\u8BDD",en_us:"Unwatch a session"},{command:"watchlist",icon:"flag_outlined",default:"\u67E5\u770B\u5173\u6CE8\u5217\u8868",en_us:"List watched sessions"},{command:"archived",icon:"folder_outlined",default:"\u8BBE\u7F6E\u5F52\u6863\u4F1A\u8BDD\u663E\u9690\uFF08on/off\uFF09",en_us:"Show or hide archived sessions (on/off)"}]);function $v(n,t){let o=n==="lark"?"https://open.larksuite.com":"https://open.feishu.cn";return new URL(t,o)}function $W(n,t){if(!n||typeof n!="object"||Array.isArray(n))throw new Error(`${t} returned a non-JSON response`);if(n.code!==0){let o=new Error(`${t} failed: ${n.msg||`code ${n.code}`}`);throw o.code=String(n.code),o.msg=n.msg,o}return n}function eP(n,t){let o=AbortSignal.timeout(t);return n?AbortSignal.any([n,o]):o}async function AP(n,t,o){try{return $W(await n.request(t),o)}catch(i){let e=i?.response?.data;if(e&&typeof e=="object"&&!Array.isArray(e)&&Object.hasOwn(e,"code"))return $W(e,o);throw i}}async function fRe({appId:n,appSecret:t,domain:o,httpInstance:i,timeoutMs:e,signal:a}){if(!n||!t)throw new Error("Feishu slash registration requires app credentials");if(!i||typeof i.request!="function")throw new TypeError("Feishu slash registration requires an HTTP instance");let A=await AP(i,{method:"POST",url:$v(o,"/open-apis/auth/v3/tenant_access_token/internal").href,headers:{"content-type":"application/json; charset=utf-8"},data:{app_id:n,app_secret:t},signal:eP(a,e),timeout:e},"Feishu authentication");if(!A.tenant_access_token)throw new Error("Feishu authentication returned no tenant access token");return A.tenant_access_token}async function QRe({tenantAccessToken:n,domain:t,httpInstance:o,timeoutMs:i,signal:e}){let a=await AP(o,{method:"GET",url:$v(t,e6).href,headers:{authorization:`Bearer ${n}`,"content-type":"application/json; charset=utf-8"},signal:eP(e,i),timeout:i},"Feishu slash command list");return Array.isArray(a.data?.items)?a.data.items:[]}async function yRe({tenantAccessToken:n,domain:t,httpInstance:o,timeoutMs:i,signal:e,command:a,description:A,icon:r=A6}){let s={command:a};return A&&(A.default_value||A.i18n)?s.description=A:typeof A=="string"&&A.trim()&&(s.description={default_value:A.trim()}),r&&(s.description={...s.description??{},icon:{icon_key:r}}),(await AP(o,{method:"POST",url:$v(t,e6).href,headers:{authorization:`Bearer ${n}`,"content-type":"application/json; charset=utf-8"},data:s,signal:eP(e,i),timeout:i},`Feishu slash command create (/${a})`)).data?.command_id??null}async function t6({appId:n,appSecret:t,domain:o="feishu",httpInstance:i,timeoutMs:e=15e3,signal:a,manifest:A=Xv}){let r=await fRe({appId:n,appSecret:t,domain:o,httpInstance:i,timeoutMs:e,signal:a}),s=new Set((await QRe({tenantAccessToken:r,domain:o,httpInstance:i,timeoutMs:e,signal:a})).map(I=>I.command)),g=[],c=[];for(let I of A){let d=String(I.command??"").replace(/^\//,"");if(d&&!s.has(d))try{let B={default_value:I.default??I.en_us??d,i18n:{zh_cn:I.default??d,en_us:I.en_us??I.default??d}},f=await yRe({tenantAccessToken:r,domain:o,httpInstance:i,timeoutMs:e,signal:a,command:d,description:B,icon:I.icon??A6});g.push({command:d,command_id:f})}catch(B){if(B?.code==="40000000"&&/already exists/i.test(B?.msg??"")){s.add(d);continue}if(BRe.has(B?.code)||/(?:lacks permission|access denied)/i.test(B?.msg??"")){c.push({command:d,error:B});break}c.push({command:d,error:B?.message??String(B)})}}return{created:g,existing:[...s].filter(I=>I!=null),failed:c}}var SRe=15e3,wRe="\u2705 \u4FEE\u590D\u5B8C\u6210\uFF1A\u5DF2\u5B9E\u6D4B\u6536\u5230 card.action.trigger\uFF0C\u83DC\u5355\u6309\u94AE\u73B0\u5728\u53EF\u7528\u3002",DRe="\u26A0\uFE0F \u4FEE\u590D\u9A8C\u8BC1\u8D85\u65F6\uFF1A\u672A\u6536\u5230\u6D4B\u8BD5\u5361\u6309\u94AE\u7684 card.action.trigger\uFF0C\u4E0D\u80FD\u786E\u8BA4\u6309\u94AE\u5DF2\u4FEE\u590D\u3002\u8BF7\u4E0D\u8981\u91CD\u590D\u6388\u6743\uFF1B\u5148\u68C0\u67E5\u98DE\u4E66\u5F00\u653E\u5E73\u53F0\u7684\u5361\u7247\u56DE\u8C03\u914D\u7F6E\uFF0C\u786E\u8BA4\u540E\u518D\u53D1\u9001 /repair\u3002",tP="\u26A0\uFE0F \u4FEE\u590D\u9A8C\u8BC1\u5931\u8D25\uFF1A\u65E0\u6CD5\u53D1\u9001\u4E13\u7528\u6D4B\u8BD5\u5361\uFF0C\u4E0D\u80FD\u786E\u8BA4 card.action.trigger \u5DF2\u6062\u590D\u3002\u8BF7\u4E0D\u8981\u91CD\u590D\u6388\u6743\uFF1B\u5148\u68C0\u67E5\u673A\u5668\u4EBA\u6D88\u606F\u6743\u9650\u548C\u8FDE\u63A5\u72B6\u6001\u3002",ORe="\u26A0\uFE0F \u4FEE\u590D\u9A8C\u8BC1\u4E2D\u65AD\uFF1ARuntime \u5DF2\u505C\u6B62\uFF0C\u672A\u5B8C\u6210 card.action.trigger \u5B9E\u6D4B\uFF0C\u4E0D\u80FD\u786E\u8BA4\u4FEE\u590D\u6210\u529F\u3002\u8BF7\u4E0D\u8981\u91CD\u590D\u6388\u6743\uFF1B\u5148\u7B49\u5F85\u673A\u5668\u4EBA\u6062\u590D\u8FDE\u63A5\u3002",bRe=new Set(["connected","connecting","reconnecting"]);function yn(n){return typeof n=="string"&&n.trim()?n.trim():null}function MRe(n){return yn(n?.operator?.open_id)??yn(n?.operator?.operator_id?.open_id)}function _Re(n,t){try{let o=n?.getConnectionStatus?.()?.state;return typeof o=="string"&&o?o:t}catch{return t}}function Md(n,t){let o=new Error(t);return o.code=n,o}function TRe(n,t){if(!n||typeof n.request!="function")return;let o=i=>({...i??{},timeout:i?.timeout??t});return{request:i=>n.request(o(i)),get:(i,e)=>n.get(i,o(e)),delete:(i,e)=>n.delete(i,o(e)),head:(i,e)=>n.head(i,o(e)),options:(i,e)=>n.options(i,o(e)),post:(i,e,a)=>n.post(i,e,o(a)),put:(i,e,a)=>n.put(i,e,o(a)),patch:(i,e,a)=>n.patch(i,e,o(a))}}function NRe({allowedSenderCount:n=1}={}){return{startedAt:null,ready:!1,feishuLongConnectionState:"idle",harnessReachable:!1,messagesReceived:0,messagesReplied:0,messagesRejected:0,reactionsAdded:0,reactionsRemoved:0,reactionErrors:0,streamResponses:0,streamUpdates:0,streamFallbacks:0,streamErrors:0,cardActionsReceived:0,cardActionProbesVerified:0,lastMessageAt:null,lastReplyAt:null,lastRejectedAt:null,lastCardActionAt:null,lastError:null,agentPreset:"standard",authorizationMode:"sender-open-id-allowlist",allowedSenderCount:n,slashCommandRegistration:"idle",slashCommandsRegistered:0,slashCommandsExisting:0,slashCommandsFailed:0,slashCommandsError:null}}var aS=class{#e;#A;#t;#r;#g;#a;#n;#i;#I;#l;#o;#s;#c;#p;#d;#h;#E;#C=null;#f=null;#B=null;#y=null;#S=null;#Q=null;#m=new Map;#u;#b=!0;constructor({lark:t,botId:o,appId:i,appSecret:e,domain:a="feishu",botOpenId:A,groupResponseMode:r,ownerOpenId:s,ownerOpenIds:g,harness:c,state:I,contextEnhancement:d,repair:B,replyTimeoutMs:f=6e5,connectTimeoutMs:Q=15e3,requestTimeoutMs:y=SRe,slashCommands:m=!0,wsAgent:_,logger:T=console}){if(!t)throw new Error("FeishuRuntime requires the Feishu SDK");if(!i||!e)throw new Error("FeishuRuntime requires app credentials");let R=Array.isArray(g)?g:[s],H=[...new Set(R.filter(K=>typeof K=="string"&&K))];if(H.length===0)throw new Error("FeishuRuntime requires at least one owner open_id");if(!c)throw new Error("FeishuRuntime requires a Harness client");if(!I)throw new Error("FeishuRuntime requires a state store");if(B!=null&&!yn(o))throw new TypeError("FeishuRuntime repair capability requires a botId");if(!Number.isFinite(y)||y<=0)throw new TypeError("FeishuRuntime requestTimeoutMs must be a positive number");this.#e=t,this.#A=yn(o),this.#t=i,this.#r=e,this.#g=a,this.#a=yn(A),this.#n=ro(r),this.#i=H,this.#I=c,this.#l=I,this.#o=d,this.#E=B??null,this.#s=f,this.#c=Q,this.#p=y,this.#b=!!m,this.#d=_,this.#h=T,this.#u=NRe({allowedSenderCount:H.length})}get status(){return structuredClone(this.#u)}setGroupResponseMode(t){this.#n=ro(t),this.#f?.setGroupResponseMode(this.#n)}async start(){for(;;){for(;this.#S;)await this.#S;if(this.#y)return this.#y;let o=this.#B;if(o){let i=_Re(o,this.#u.feishuLongConnectionState);if(bRe.has(i))return this.status;await this.stop({preserveError:i==="failed"});continue}if(this.#C||this.#f||this.#Q){await this.stop({preserveError:this.#u.feishuLongConnectionState==="failed"});continue}break}let t;return t=this.#R().finally(()=>{this.#y===t&&(this.#y=null)}),this.#y=t,t}async#R(){let t=new AbortController;this.#Q=t;let{signal:o}=t,i=()=>o.reason??new DOMException("Feishu runtime stopped","AbortError"),e=()=>!o.aborted&&this.#Q===t,a=()=>{if(!e())throw i()};this.#u.startedAt=new Date().toISOString(),this.#u.feishuLongConnectionState="connecting",this.#u.lastError=null;try{await this.#I.ensureRunning({signal:o}),a(),this.#u.harnessReachable=!0;let A=this.#g==="lark"?this.#e.Domain.Lark:this.#e.Domain.Feishu,r={appId:this.#t,appSecret:this.#r,domain:A},s=TRe(this.#e.defaultHttpInstance,this.#p);s&&(r.httpInstance=s);let g=new this.#e.Client(r);this.#C=g;let c=new tS({client:g,initialText:w("\u5DF2\u8FDE\u63A5 DeepSeek Harness\uFF0C\u6B63\u5728\u601D\u8003\u2026")}),I=new eS({client:g,channel:c,harness:this.#I,state:this.#l,contextEnhancement:this.#o,status:this.#u,allowedSenderOpenIds:new Set(this.#i),botId:this.#A,appId:this.#t,botOpenId:this.#a,groupResponseMode:this.#n,repair:this.#E,replyTimeoutMs:this.#s,signal:o,logger:this.#h});this.#f=I;let d=new this.#e.EventDispatcher({}).register({"im.message.receive_v1":_=>{e()&&I.accept(_)},"im.message.reaction.created_v1":()=>{},"im.message.reaction.deleted_v1":()=>{},"card.action.trigger":_=>{e()&&(this.#u.cardActionsReceived+=1,this.#u.lastCardActionAt=new Date().toISOString(),this.#D(_)||I.onCardAction(_))}}),B,f,Q=new Promise((_,T)=>{let R=!1,H=()=>{f(i())},K=(q,z)=>{R||(R=!0,clearTimeout(F),o.removeEventListener("abort",H),q(z))},F=setTimeout(()=>{K(T,new Error(`Feishu WebSocket handshake timed out after ${this.#c}ms`))},this.#c);B=()=>{K(_)},f=q=>{K(T,q)},o.addEventListener("abort",H,{once:!0}),o.aborted&&H()});Q.catch(()=>{});let y=new this.#e.WSClient({...r,...this.#d?{agent:this.#d}:{},loggerLevel:this.#e.LoggerLevel.info,handshakeTimeoutMs:this.#c,onReady:()=>{e()&&(this.#u.feishuLongConnectionState="connected",this.#u.ready=!0,this.#u.lastError=null,B())},onError:_=>{e()&&(this.#u.feishuLongConnectionState="failed",this.#u.ready=!1,this.#u.lastError=_?.message??String(_),this.#h.error("[dsh-feishu] Feishu long connection failed:",this.#u.lastError),f(_))},onReconnecting:()=>{e()&&(this.#u.feishuLongConnectionState="reconnecting",this.#u.ready=!1)},onReconnected:()=>{e()&&(this.#u.feishuLongConnectionState="connected",this.#u.ready=!0,this.#u.lastError=null)}});this.#B=y;let m=Promise.resolve().then(()=>y.start({eventDispatcher:d})).catch(_=>{throw f(_),_});return await Promise.all([m,Q]),a(),this.#b&&s&&this.#T(s,e,o),this.status}catch(A){throw o.aborted||(this.#u.ready=!1,this.#u.feishuLongConnectionState="failed",this.#u.lastError=A?.message??String(A),await this.#M({preserveError:!0,abortController:t})),A}}async beginCardActionProbe({expectedOperatorOpenId:t,timeoutMs:o=9e4}={}){if(!this.#u.ready||!this.#C)throw Md("card_action_probe_unavailable","\u98DE\u4E66\u673A\u5668\u4EBA\u5C1A\u672A\u8FDE\u63A5");let i=yn(t);if(!i||i==="*")throw new TypeError("A precise Feishu operator open_id is required");if(!Number.isFinite(o)||o<=0||o>10*6e4)throw new TypeError("Card-action probe timeout must be between 1 and 600000ms");let e=mRe().replaceAll("-",""),a;try{a=await this.#C.im.v1.message.create({params:{receive_id_type:"open_id"},data:{receive_id:i,msg_type:"interactive",content:NW(e)}})}catch{throw this.#w(i,w(tP),"failure"),Md("card_action_probe_send_failed","\u65E0\u6CD5\u53D1\u9001\u98DE\u4E66\u5361\u7247\u56DE\u8C03\u6D4B\u8BD5")}if(a?.code&&a.code!==0)throw this.#w(i,w(tP),"failure"),Md("card_action_probe_send_failed","\u65E0\u6CD5\u53D1\u9001\u98DE\u4E66\u5361\u7247\u56DE\u8C03\u6D4B\u8BD5");let A=yn(a?.data?.message_id)??yn(a?.message_id);if(!A)throw this.#w(i,w(tP),"failure"),Md("card_action_probe_send_failed","\u98DE\u4E66\u672A\u8FD4\u56DE\u6D4B\u8BD5\u5361\u7247\u7684\u6D88\u606F ID");return new Promise((r,s)=>{let g=setTimeout(()=>{let c=this.#m.get(A);!c||c.nonce!==e||(this.#m.delete(A),this.#w(i,w(DRe),"timeout"),s(Md("card_action_probe_timeout","\u5728\u89C4\u5B9A\u65F6\u95F4\u5185\u672A\u6536\u5230\u98DE\u4E66\u5361\u7247\u6309\u94AE\u56DE\u8C03")))},o);g.unref?.(),this.#m.set(A,{messageId:A,nonce:e,expectedOperatorOpenId:i,timeout:g,resolve:r,reject:s})})}#D(t){let o=yn(t?.context?.open_message_id);if(!o)return!1;let i=this.#m.get(o);if(!i)return!1;let e=t?.action?.value,a=MRe(t);return e?.action!=="repair_verify"||e?.nonce!==i.nonce||a!==i.expectedOperatorOpenId?!1:(clearTimeout(i.timeout),this.#m.delete(o),this.#u.cardActionProbesVerified+=1,this.#w(a,w(wRe),"success").finally(()=>{i.resolve({verified:!0,messageId:o,operatorOpenId:a})}),!0)}#w(t,o,i){let e=this.#C;return e?Promise.resolve().then(async()=>{let a=await e.im.v1.message.create({params:{receive_id_type:"open_id"},data:{receive_id:t,msg_type:"text",content:JSON.stringify({text:o})}});if(a?.code&&a.code!==0)throw new Error("Feishu callback repair notice failed");return!0}).catch(()=>(this.#h.warn?.(`[dsh-feishu] unable to send the callback repair ${i} notice`),!1)):(this.#h.warn?.(`[dsh-feishu] unable to send the callback repair ${i} notice`),Promise.resolve(!1))}async sendConnectionTest(t){if(!this.#u.ready||!this.#C){let e=new Error("\u98DE\u4E66\u673A\u5668\u4EBA\u5C1A\u672A\u8FDE\u63A5");throw e.code="test-target-unavailable",e}if(typeof t!="string"||!t.trim())throw new TypeError("Feishu connection test text is required");let o=async(e,a,A)=>{let r=await this.#C.im.v1.message.create({params:{receive_id_type:e},data:{receive_id:a,msg_type:"text",content:JSON.stringify({text:A})}});if(r?.code&&r.code!==0)throw new Error(`Feishu connection test failed: ${r.msg||r.code}`)},i=this.#i.find(e=>e!=="*");return i?(await o("open_id",i,t),{sent:!0}):Xg({state:this.#l,text:t,channelLabel:w("\u98DE\u4E66\u673A\u5668\u4EBA"),send:async(e,a)=>{let A=typeof e?.chatId=="string"?e.chatId.trim():"";if(!A)throw lr(w("\u98DE\u4E66\u673A\u5668\u4EBA"));await o("chat_id",A,a)}})}async sendProactiveText(t,o,{signal:i}={}){if(!this.#u.ready||!this.#C){let r=new Error("\u98DE\u4E66\u673A\u5668\u4EBA\u5C1A\u672A\u8FDE\u63A5");throw r.code="bot-not-connected",r}let e=t?.kind==="user"?"open_id":t?.kind==="group"?"chat_id":null,a=t?.kind==="user"?yn(t?.route?.openId):t?.kind==="group"?yn(t?.route?.chatId):null;if(!e||!a){let r=new TypeError("Invalid Feishu proactive delivery target");throw r.code="invalid-target",r}i?.throwIfAborted();let A=await this.#C.im.v1.message.create({params:{receive_id_type:e},data:{receive_id:a,msg_type:"text",content:JSON.stringify({text:o})}});if(A?.code&&A.code!==0){let r=new Error(`Feishu proactive delivery failed: ${A.msg||A.code}`);throw r.code="target-rejected",r}return{sent:!0}}async#T(t,o,i){this.#u.slashCommandRegistration="registering",this.#u.slashCommandsError=null;try{let e=await t6({appId:this.#t,appSecret:this.#r,domain:this.#g,httpInstance:t,signal:i,manifest:Xv});if(!o())return;this.#u.slashCommandRegistration="done",this.#u.slashCommandsRegistered=e.created.length,this.#u.slashCommandsExisting=e.existing.length,this.#u.slashCommandsFailed=e.failed.length,this.#u.slashCommandsError=e.failed.length>0?e.failed.map(a=>`/${a.command}: ${a.error?.message??String(a.error)}`).join("; "):null,e.created.length>0&&this.#h.info?.(`[dsh-feishu] registered ${e.created.length} slash command(s)`),e.failed.length>0&&this.#h.warn?.(`[dsh-feishu] ${e.failed.length} slash command(s) failed to register: ${this.#u.slashCommandsError}`)}catch(e){if(!o())return;this.#u.slashCommandRegistration="failed",this.#u.slashCommandsError=e?.message??String(e),this.#h.warn?.(`[dsh-feishu] slash command registration skipped: ${this.#u.slashCommandsError}`)}}stop(t={}){if(this.#S)return this.#S;let o;return o=this.#k(t).finally(()=>{this.#S===o&&(this.#S=null)}),this.#S=o,o}async#k({preserveError:t=!1}={}){let o=this.#Q;this.#Q===o&&(this.#Q=null),o?.abort(new DOMException("Feishu runtime stopped","AbortError"));let i=this.#y;return i&&await i.catch(()=>{}),this.#M({preserveError:t,abortController:o})}async#M({preserveError:t=!1,abortController:o}={}){let i=t?this.#u.lastError:null;this.#Q===o&&(this.#Q=null),o?.abort(new DOMException("Feishu runtime stopped","AbortError"));for(let A of this.#m.values())clearTimeout(A.timeout),this.#w(A.expectedOperatorOpenId,w(ORe),"abort"),A.reject(Md("abort","\u98DE\u4E66\u8FD0\u884C\u65F6\u5DF2\u505C\u6B62"));this.#m.clear(),this.#u.ready=!1;let e=this.#B;this.#B=null,e?.close({force:!0});let a=this.#f;return this.#f=null,a&&await a.waitForIdle(),this.#C=null,this.#u.feishuLongConnectionState=t?"failed":"idle",this.#u.slashCommandRegistration="idle",this.#u.lastError=i,this.status}};var iS=class extends pr{constructor(t){super({...t,rpcIdPrefix:"feishu",logPrefix:"dsh-feishu"})}};import{createHash as RRe}from"node:crypto";import{mkdir as kRe,readFile as vRe,rename as PRe,unlink as GRe,writeFile as FRe}from"node:fs/promises";import{dirname as LRe}from"node:path";var sS="DSH_FEISHU_APP_SECRET";function ps(n){return typeof n=="string"&&n.trim()?n.trim():null}function r6(n){let t=ps(n);return t&&/^[A-Za-z0-9_-]{1,128}$/.test(t)?t:null}function a6(n){return`bot_${RRe("sha256").update(n).digest("hex").slice(0,24)}`}function URe(n){let t=Array.isArray(n.ownerOpenIds)?n.ownerOpenIds:[n.ownerOpenId];return[...new Set(t.map(ps).filter(Boolean))]}function oS(n,{legacy:t=!1}={}){if(!n||typeof n!="object")return null;let o=ps(n.appId),i=URe(n);if(!o||i.length===0)return null;let e=r6(n.id)??(t?a6(o):null),a=ps(n.secretRef)??(t?sS:null);if(!e||!a||!/^[A-Za-z_][A-Za-z0-9_]*$/.test(a))return null;let A=n.domain==="lark"?"lark":"feishu";return Object.freeze({id:e,appId:o,secretRef:a,ownerOpenIds:Object.freeze(i),domain:A,botName:ps(n.botName),botOpenId:ps(n.botOpenId),activated:n.activated??null,groupResponseMode:ro(n.groupResponseMode),groupMessagePermissionGranted:n.groupMessagePermissionGranted===!0,deletionPending:n.deletionPending===!0,connectedAt:ps(n.connectedAt),createdAt:ps(n.createdAt)??ps(n.connectedAt)})}function HRe(n){if(!n||typeof n!="object")return null;if(n.version===2&&Array.isArray(n.bots)){let o=n.bots.map(A=>oS(A));if(o.some(A=>A===null))throw new Error("dsh-feishu config contains an invalid bot entry");let i=new Set,e=new Set,a=new Set;for(let A of o){if(i.has(A.id)||e.has(A.secretRef)||a.has(A.appId))throw new Error("dsh-feishu config contains duplicate bot identities");i.add(A.id),e.add(A.secretRef),a.add(A.appId)}return{value:Object.freeze({version:2,bots:Object.freeze(o)}),migrated:!1}}let t=oS(n,{legacy:!0});return t?{value:Object.freeze({version:2,bots:Object.freeze([t])}),migrated:!0}:null}var nS=class{#e;#A=Object.freeze({version:2,bots:Object.freeze([])});#t=Promise.resolve();constructor(t){this.#e=t}async load(){try{let t=JSON.parse(await vRe(this.#e,"utf8")),o=HRe(t);if(!o)throw new Error("dsh-feishu config is incomplete or invalid");this.#A=o.value,o.migrated&&await this.#a(this.#A)}catch(t){if(t?.code!=="ENOENT")throw t;this.#A=Object.freeze({version:2,bots:Object.freeze([])})}return this}get(){let t=this.#A.bots[0];if(!t)return null;let o=structuredClone(t);return o.ownerOpenId=o.ownerOpenIds[0],o}list(){return structuredClone(this.#A.bots)}getBot(t){let o=this.#A.bots.find(i=>i.id===t);return o?structuredClone(o):null}async save(t){let o={...t};o.id||(o.id=a6(ps(o.appId)??"invalid")),o.secretRef||(o.secretRef=sS);let i=oS(o);if(!i)throw new Error("Refusing to persist incomplete dsh-feishu configuration");return await this.#r([i]),this.get()}async saveBot(t){let o=oS(t);if(!o)throw new Error("Refusing to persist incomplete dsh-feishu bot configuration");return this.#g(i=>{if(i.find(r=>r.secretRef===o.secretRef&&r.id!==o.id))throw new Error("Refusing to share a credential reference between Feishu bots");if(i.find(r=>r.appId===o.appId&&r.id!==o.id))throw new Error("Refusing to persist the same Feishu app twice");let A=i.findIndex(r=>r.id===o.id);return A===-1?i.push(o):i[A]=o,structuredClone(o)})}async removeBot(t){if(!r6(t))throw new TypeError("Invalid Feishu bot id");return this.#g(o=>{let i=o.findIndex(a=>a.id===t);if(i===-1)return null;let[e]=o.splice(i,1);return structuredClone(e)})}async clear(){let t=this.#t.then(async()=>{try{await GRe(this.#e)}catch(o){if(o?.code!=="ENOENT")throw o}this.#A=Object.freeze({version:2,bots:Object.freeze([])})});this.#t=t.then(()=>{},()=>{}),await t}async#r(t){let o=Object.freeze({version:2,bots:Object.freeze([...t])}),i=this.#t.then(async()=>{await this.#a(o),this.#A=o});this.#t=i.then(()=>{},()=>{}),await i}async#g(t){let o,i=this.#t.then(async()=>{let e=[...this.#A.bots];o=t(e);let a=Object.freeze({version:2,bots:Object.freeze(e)});await this.#a(a),this.#A=a});return this.#t=i.then(()=>{},()=>{}),await i,o}async#a(t){await kRe(LRe(this.#e),{recursive:!0,mode:448});let o=`${this.#e}.tmp`;await FRe(o,`${JSON.stringify(t,null,2)} -`,{encoding:"utf8",mode:384}),await PRe(o,this.#e)}};import{randomUUID as c6}from"node:crypto";var rP=new Set(["starting","qr_ready","polling","slow_down","domain_switched","saving"]),KRe=new Set(["polling","slow_down","domain_switched"]),Ri=Object.freeze({IDLE:"idle",STARTING:"starting",QR_READY:"qr_ready",POLLING:"polling",SLOW_DOWN:"slow_down",DOMAIN_SWITCHED:"domain_switched",SAVING:"saving",SUCCEEDED:"succeeded",EXPIRED:"expired",CANCELLED:"cancelled",ERROR:"error"});function i6(n){return["access_denied","expired_token","abort"].includes(n?.code)?n.code:"registration_failed"}function aP(n){let t=i6(n);return{code:t,message:{access_denied:"Registration was denied.",abort:"Registration was cancelled.",expired_token:"The registration QR code expired."}[t]??"Unable to register the Feishu app."}}function xRe(n){let t=Number(n);if(!Number.isFinite(t)||t<=0)throw new TypeError("registerApp onQRCodeReady returned an invalid expireIn");return t}function YRe(n){if(n!==void 0){if(n===null||typeof n!="object"||Array.isArray(n))throw new TypeError("registerApp returned invalid user_info");return{...n}}}var Qg=class{#e;#A;#t;#r;#g;#a=0;#n=null;#i;constructor({registerApp:t,onCredentials:o,now:i=Date.now,setTimeout:e=globalThis.setTimeout,clearTimeout:a=globalThis.clearTimeout}={}){if(typeof t!="function")throw new TypeError("RegistrationManager requires a registerApp function");if(typeof o!="function")throw new TypeError("RegistrationManager requires an onCredentials function");if(typeof i!="function"||typeof e!="function"||typeof a!="function")throw new TypeError("RegistrationManager clock dependencies must be functions");this.#e=t,this.#A=o,this.#t=i,this.#r=e,this.#g=a,this.#i=this.#l(null,Ri.IDLE)}start(t={}){if(t===null||typeof t!="object"||Array.isArray(t))throw new TypeError("Registration options must be an object");this.#B();let o={id:++this.#a,controller:new AbortController,qrCodeUrl:null,expiresAt:null,pollIntervalSeconds:null,expiryTimer:null};this.#n=o,this.#i=this.#l(o,Ri.STARTING);let i={...t,signal:o.controller.signal,onQRCodeReady:a=>this.#s(o,a),onStatusChange:a=>this.#c(o,a)};return Promise.resolve().then(()=>this.#e(i)).then(a=>this.#p(o,a),a=>this.#d(o,a)),this.status()}status(){this.#h();let t={...this.#i};t.error&&(t.error={...t.error});let o=this.#n;return o&&o.expiresAt!==null&&rP.has(t.state)&&(t.remainingSeconds=Math.max(0,Math.ceil((o.expiresAt-this.#t())/1e3))),t}cancel(){let t=this.#n;return t?(this.#C(t,Ri.CANCELLED,{error:{code:"abort",message:"Registration was cancelled."}}),t.controller.abort(),this.status()):this.status()}#I(t){return this.#n===t}#l(t,o,i={}){let e={state:o,attempt:t?.id??this.#a,updatedAt:this.#t(),...i};return t?.qrCodeUrl&&rP.has(o)&&(e.qrCodeUrl=t.qrCodeUrl,e.expiresAt=t.expiresAt),t?.pollIntervalSeconds!==null&&rP.has(o)&&(e.pollIntervalSeconds=t.pollIntervalSeconds),e}#o(t,o,i={}){this.#I(t)&&(this.#i=this.#l(t,o,i))}#s(t,o){if(!this.#I(t))return;if(typeof o?.url!="string"||!o.url)throw new TypeError("registerApp onQRCodeReady returned an invalid URL");let i=xRe(o.expireIn);t.qrCodeUrl=o.url,t.expiresAt=this.#t()+i*1e3,this.#f(t),t.expiryTimer=this.#r(()=>this.#E(t),i*1e3),t.expiryTimer?.unref?.(),this.#o(t,Ri.QR_READY)}#c(t,o){!this.#I(t)||!KRe.has(o?.status)||(o.status===Ri.SLOW_DOWN&&Number.isFinite(Number(o.interval))&&(t.pollIntervalSeconds=Number(o.interval)),this.#o(t,o.status))}async#p(t,o){if(!this.#I(t))return;let i=o?.client_id,e=o?.client_secret;if(typeof i!="string"||!i||typeof e!="string"||!e){this.#C(t,Ri.ERROR,{error:{code:"invalid_credentials",message:"Feishu registration returned invalid credentials."}});return}let a;try{a=YRe(o.user_info)}catch{this.#C(t,Ri.ERROR,{error:{code:"invalid_credentials",message:"Feishu registration returned invalid credentials."}});return}this.#f(t),t.qrCodeUrl=null,t.expiresAt=null,t.pollIntervalSeconds=null,this.#o(t,Ri.SAVING);try{await this.#A({client_id:i,client_secret:e,user_info:a})}catch{this.#I(t)&&this.#C(t,Ri.ERROR,{error:{code:"credentials_callback_failed",message:"Unable to store the Feishu credentials."}});return}this.#I(t)&&this.#C(t,Ri.SUCCEEDED)}#d(t,o){if(!this.#I(t))return;let i=i6(o);if(i==="expired_token"){this.#C(t,Ri.EXPIRED,{error:aP(o)});return}if(i==="abort"){this.#C(t,Ri.CANCELLED,{error:aP(o)});return}this.#C(t,Ri.ERROR,{error:aP(o)})}#h(){let t=this.#n;t&&t.expiresAt!==null&&this.#t()>=t.expiresAt&&this.#E(t)}#E(t){this.#I(t)&&(this.#C(t,Ri.EXPIRED,{error:{code:"expired_token",message:"The registration QR code expired."}}),t.controller.abort())}#C(t,o,i={}){this.#I(t)&&(this.#f(t),this.#i=this.#l(t,o,i),this.#n=null)}#f(t){t.expiryTimer!==null&&(this.#g(t.expiryTimer),t.expiryTimer=null)}#B(){let t=this.#n;t&&(this.#f(t),this.#n=null,t.controller.abort())}};var jRe="card.action.trigger",JRe="im:message:readonly",qRe="im:resource",Bu="callback_repair";function zRe(n){return n==="lark"?"accounts.larksuite.com":"accounts.feishu.cn"}function VRe(n){return n==="lark"?"open.larksuite.com":"open.feishu.cn"}function iP(n,t,o="feishu",i="Feishu app update"){let e;try{e=new URL(n)}catch{throw new Error(`${i} returned an invalid verification URL`)}let a=o==="feishu"||o==="lark",A=e.searchParams.getAll("clientID"),r=e.searchParams.getAll("tp"),s=e.searchParams.getAll("addons"),g=String(t).includes("{{")||String(t).includes("}}")||[...e.searchParams.values()].some(c=>c.includes("{{")||c.includes("}}"));if(!a||e.protocol!=="https:"||e.hostname!==VRe(o)||e.port!==""||e.username!==""||e.password!==""||r.length!==1||r[0]!=="sdk"||A.length!==1||A[0]!==t||e.searchParams.has("createOnly")||s.length!==1||!s[0]?.trim()||g)throw new Error(`${i} returned an unsafe verification URL`);return e.toString()}function WRe(n,t,o="feishu"){return iP(n,t,o,"Feishu callback repair")}var gS=class{#e;#A;#t;constructor({registerApp:t,onCredentials:o,appId:i,domain:e="feishu"}={}){if(typeof t!="function")throw new TypeError("registerApp is required");if(typeof o!="function")throw new TypeError("onCredentials is required");if(typeof i!="string"||!i.trim())throw new TypeError("appId is required");if(e!=="feishu"&&e!=="lark")throw new TypeError("domain is invalid");this.#A=i.trim(),this.#t=e,this.#e=new Qg({registerApp:a=>t({...a,onQRCodeReady:A=>{WRe(A?.url,this.#A,this.#t),a.onQRCodeReady(A)}}),onCredentials:o})}start(){return this.#e.start({source:"deepseek-harness-card-action-repair",domain:zRe(this.#t),appId:this.#A,addons:{preset:!1,scopes:{tenant:[JRe,qRe,...rS]},callbacks:{items:[jRe]}}})}status(){return this.#e.status()}cancel(){return this.#e.cancel()}};var ZRe="im:message.group_msg",IS="group_message_permission";function XRe(n){return n==="lark"?"accounts.larksuite.com":"accounts.feishu.cn"}function $Re(n,t,o="feishu"){return iP(n,t,o,"Feishu group message permission update")}var cS=class{#e;#A;#t;constructor({registerApp:t,onCredentials:o,appId:i,domain:e="feishu"}={}){if(typeof t!="function")throw new TypeError("registerApp is required");if(typeof o!="function")throw new TypeError("onCredentials is required");if(typeof i!="string"||!i.trim())throw new TypeError("appId is required");if(e!=="feishu"&&e!=="lark")throw new TypeError("domain is invalid");this.#A=i.trim(),this.#t=e,this.#e=new Qg({registerApp:a=>t({...a,onQRCodeReady:A=>{$Re(A?.url,this.#A,this.#t),a.onQRCodeReady(A)}}),onCredentials:o})}start(){return this.#e.start({source:"deepseek-harness-group-message-permission",domain:XRe(this.#t),appId:this.#A,addons:{preset:!1,scopes:{tenant:[ZRe]}}})}status(){return this.#e.status()}cancel(){return this.#e.cancel()}};var o6=Object.freeze(["im:message.p2p_msg:readonly","im:message.group_at_msg:readonly","im:message:readonly","im:message:send_as_bot","im:message.reactions:write_only","im:message:recall","im:resource","cardkit:card:write",...rS]);var oP=new Set(["starting","qr_ready","polling","slow_down","domain_switched"]),lS=new Set([...oP,"saving"]),n6=new Set([Bu,IS]),s6="*",eke=12e4,Ake=6e5;function I6(){return{ready:!1,feishuLongConnectionState:"idle",harnessReachable:!1}}function fu(n){return n?n.status:I6()}function Qu(n){return n.ready===!0&&n.feishuLongConnectionState==="connected"&&n.harnessReachable===!0}function l6(n){return n.length>12?`${n.slice(0,8)}\u2022\u2022\u2022\u2022${n.slice(-4)}`:"cli_\u2022\u2022\u2022\u2022"}function tke(n){return{name:n.botName,appIdMasked:l6(n.appId),activated:n.activated,domain:n.domain}}function rke({connected:n,error:t,connection:o}){return n?"connected":t||o.feishuLongConnectionState==="failed"?"error":"disconnected"}function ake(){return`bot_${c6().replaceAll("-","")}`}function ike(){return`reg_${c6().replaceAll("-","")}`}function g6(n){return`DSH_FEISHU_APP_SECRET_${n.slice(4).toUpperCase()}`}function pS(n){return JSON.stringify({id:n.id,appId:n.appId,secretRef:n.secretRef,ownerOpenIds:n.ownerOpenIds,domain:n.domain,botName:n.botName,botOpenId:n.botOpenId,activated:n.activated,groupResponseMode:ro(n.groupResponseMode),groupMessagePermissionGranted:n.groupMessagePermissionGranted===!0,deletionPending:n.deletionPending===!0,connectedAt:n.connectedAt??null,createdAt:n.createdAt??null})}function dS(n){return typeof n=="string"&&n.trim()?n.trim():null}var yu=class{#e;#A;#t;#r;#g;#a;#n;#i;#I=new Map;#l=new Map;#o=new Map;#s=new Map;#c=new Map;#p=null;#d=Promise.resolve();#h=new Map;#E=1;#C;#f=!1;constructor({registerApp:t,verifyApp:o,credentials:i,configStore:e,createRuntime:a,deleteState:A=async()=>{},createBotId:r=ake,createRegistrationId:s=ike,callbackProbeTimeoutMs:g=eke}){if(typeof t!="function")throw new Error("registerApp is required");if(typeof o!="function")throw new Error("verifyApp is required");if(!i)throw new Error("credentials service is required");if(!e||typeof e.list!="function")throw new Error("multi-bot config store is required");if(typeof a!="function")throw new Error("createRuntime is required");if(typeof A!="function")throw new Error("deleteState must be a function");if(!Number.isFinite(g)||g<=0||g>Ake)throw new TypeError("callbackProbeTimeoutMs must be between 1 and 600000ms");this.#e=t,this.#A=o,this.#t=i,this.#r=e,this.#g=a,this.#a=A,this.#n=r,this.#i=s,this.#C=g}async initialize(){if(this.#f)return this.status();let t=this.#r.list(),o=!1;return await Promise.allSettled(t.map(i=>this.#_(i.id,async()=>{let e=fu(this.#I.get(i.id));if(Qu(e)||e.feishuLongConnectionState==="connecting"||e.feishuLongConnectionState==="reconnecting")return;if(o=!0,i.deletionPending){this.#l.set(i.id,{code:"deletion_pending",message:"\u673A\u5668\u4EBA\u6B63\u5728\u7B49\u5F85\u5B8C\u6210\u672C\u5730\u5220\u9664\uFF0C\u8BF7\u91CD\u8BD5\u79FB\u9664\u3002"});return}let a;try{a=await this.#t.resolve(i.secretRef)}catch{this.#l.set(i.id,{code:"missing_credentials",message:"\u65E0\u6CD5\u8BFB\u53D6\u673A\u5668\u4EBA\u51ED\u636E\uFF0C\u8BF7\u68C0\u67E5\u51ED\u636E\u5B58\u50A8\u3002"});return}if(!a?.value){this.#l.set(i.id,{code:"missing_credentials",message:"\u673A\u5668\u4EBA\u51ED\u636E\u7F3A\u5931\uFF0C\u8BF7\u5220\u9664\u540E\u91CD\u65B0\u626B\u7801\u63A5\u5165\u3002"});return}try{await this.#b(i,a.value),this.#l.delete(i.id)}catch{this.#l.set(i.id,{code:"connection_failed",message:"\u673A\u5668\u4EBA\u6682\u65F6\u65E0\u6CD5\u8FDE\u63A5\u98DE\u4E66\uFF0C\u8BF7\u91CD\u8BD5\u3002"})}}))),o&&this.#v(),this.status()}startRegistration(){this.#M();let t=this.#i();if(typeof t!="string"||!/^[A-Za-z0-9_-]{1,128}$/.test(t)||this.#o.has(t))throw new Error("Registration id generator returned an invalid or duplicate id");let o={id:t,manager:null,botId:null,createdNew:!1,cancelled:!1};return o.manager=new Qg({registerApp:this.#e,onCredentials:i=>this.#L(()=>this.#u(o,i))}),this.#o.set(t,o),this.#p=t,this.#U(),o.manager.start({source:"deepseek-harness",createOnly:!0,appPreset:{name:"{user} \u7684\u5317\u6C47\u661F\u6CB3 AI \u52A9\u624B",desc:"\u8FDE\u63A5\u98DE\u4E66\u4E0E DeepSeek Harness\uFF0C\u5728\u804A\u5929\u4E2D\u4F7F\u7528\u4F01\u4E1A AI \u52A9\u624B\u3002"},addons:{preset:!1,scopes:{tenant:[...o6]},events:{items:{tenant:["im.message.receive_v1"]}},callbacks:{items:["card.action.trigger"]}}}),this.#v(),this.registrationStatus(t)}startCallbackRepair(t,{actorOpenId:o,chatId:i}={}){this.#M();let e=this.#k(t);if(e.deletionPending)throw new Error("Cannot repair a Feishu bot pending deletion");let a=this.#s.get(t),A=a?this.#o.get(a):null;if(A&&lS.has(A.manager.status().state)){if(A.operation!==Bu)throw new Error("Another Feishu app update is already active for this bot");return this.registrationStatus(A.id)}this.#s.delete(t);let r=this.#i();if(typeof r!="string"||!/^[A-Za-z0-9_-]{1,128}$/.test(r)||this.#o.has(r))throw new Error("Registration id generator returned an invalid or duplicate id");let s={id:r,operation:Bu,manager:null,botId:t,createdNew:!1,cancelled:!1,remoteCommitted:!1,processing:null,publicError:null,stage:"authorizing",target:structuredClone(e),targetFingerprint:pS(e),initiator:{actorOpenId:dS(o),chatId:dS(i)}};return s.manager=new gS({registerApp:this.#e,appId:e.appId,domain:e.domain,onCredentials:g=>{s.remoteCommitted=!0;let I=this.#Q(s,g).finally(()=>{s.processing===I&&(s.processing=null)});return s.processing=I,I}}),this.#o.set(r,s),this.#s.set(t,r),this.#p=r,this.#U(),s.manager.start(),this.#v(),this.registrationStatus(r)}startGroupMessagePermission(t){this.#M();let o=this.#k(t);if(o.deletionPending)throw new Error("Cannot update permissions for a Feishu bot pending deletion");let i=this.#s.get(t),e=i?this.#o.get(i):null;if(e&&lS.has(e.manager.status().state)){if(e.operation!==IS)throw new Error("Another Feishu app update is already active for this bot");return this.registrationStatus(e.id)}this.#s.delete(t);let a=this.#i();if(typeof a!="string"||!/^[A-Za-z0-9_-]{1,128}$/.test(a)||this.#o.has(a))throw new Error("Registration id generator returned an invalid or duplicate id");let A={id:a,operation:IS,manager:null,botId:t,createdNew:!1,cancelled:!1,remoteCommitted:!1,processing:null,publicError:null,stage:"authorizing",target:structuredClone(o),targetFingerprint:pS(o),initiator:{actorOpenId:null,chatId:null}};return A.manager=new cS({registerApp:this.#e,appId:o.appId,domain:o.domain,onCredentials:r=>{A.remoteCommitted=!0;let g=this.#S(A,r).finally(()=>{A.processing===g&&(A.processing=null)});return A.processing=g,g}}),this.#o.set(a,A),this.#s.set(t,a),this.#p=a,this.#U(),A.manager.start(),this.#v(),this.registrationStatus(a)}hasRegistration(t){return this.#o.has(t)}registrationStatus(t){let o=this.#o.get(t);return o?this.#B({registration:o,selectedBotId:o.botId}):null}async cancelRegistration(t=this.#p){let o=this.#o.get(t);if(!o)return this.status();let i=o.manager.status().state;return n6.has(o.operation)&&i==="saving"?this.registrationStatus(t):lS.has(i)?(o.cancelled=!0,o.manager.cancel(),await this.#L(async()=>{o.createdNew&&o.botId&&this.#c.get(o.botId)===o.id&&this.#r.getBot(o.botId)&&await this.#_(o.botId,()=>this.#w(o.botId))}),this.#v(),this.registrationStatus(t)??this.status()):this.registrationStatus(t)}status(t){return this.#B({registration:this.#o.get(this.#p)??null,selectedBotId:t})}async bindCredentials({appId:t,appSecret:o,domain:i="feishu"}={}){this.#M();let e=typeof t=="string"?t.trim():"",a=typeof o=="string"?o.trim():"",A=i==="lark"?"lark":"feishu";if(!e||!a)throw new TypeError("Feishu App ID and App Secret are required");return this.#L(async()=>{this.#M();let r=await this.#A({appId:e,appSecret:a,domain:A});this.#M();let s=this.#r.list().find(f=>f.appId===e),g=s?.id??this.#n();if(typeof g!="string"||!/^[A-Za-z0-9_-]{1,128}$/.test(g)||!s&&this.#r.getBot(g))throw new Error("Bot id generator returned an invalid or duplicate id");let c=s?.secretRef??g6(g),I=await this.#t.resolve(c).catch(()=>{}),d={...s,id:g,appId:e,secretRef:c,ownerOpenIds:s?.ownerOpenIds?.length?s.ownerOpenIds:[s6],domain:A,botName:r.name,botOpenId:r.openId,activated:r.activated,deletionPending:!1,connectedAt:new Date().toISOString(),createdAt:s?.createdAt??new Date().toISOString()};await this.#t.set(c,a);let B;try{B=await this.#r.saveBot(d)}catch(f){throw await this.#T(c,I),f}return await this.#_(g,async()=>{try{await this.#b(B,a),this.#l.delete(g)}catch{this.#l.set(g,{code:"connection_failed",message:"\u673A\u5668\u4EBA\u5DF2\u7ECF\u7ED1\u5B9A\uFF0C\u4F46\u957F\u8FDE\u63A5\u672A\u5C31\u7EEA\uFF0C\u8BF7\u70B9\u51FB\u91CD\u8BD5\u3002"})}}),this.#v(),this.status(g)})}async reconnectBot(t){return this.#M(),this.#_(t,async()=>{let o=this.#k(t);if(o.deletionPending)return this.#l.set(t,{code:"deletion_pending",message:"\u673A\u5668\u4EBA\u6B63\u5728\u7B49\u5F85\u5B8C\u6210\u672C\u5730\u5220\u9664\uFF0C\u8BF7\u91CD\u8BD5\u79FB\u9664\u3002"}),this.status(t);if(Qu(fu(this.#I.get(t))))return this.status(t);let i;try{i=await this.#t.resolve(o.secretRef)}catch{i=null}if(!i?.value)return this.#l.set(t,{code:"missing_credentials",message:"\u673A\u5668\u4EBA\u51ED\u636E\u7F3A\u5931\uFF0C\u8BF7\u5220\u9664\u540E\u91CD\u65B0\u626B\u7801\u63A5\u5165\u3002"}),this.#v(),this.status(t);try{await this.#b(o,i.value),this.#l.delete(t)}catch{this.#l.set(t,{code:"connection_failed",message:"\u673A\u5668\u4EBA\u6682\u65F6\u65E0\u6CD5\u8FDE\u63A5\u98DE\u4E66\uFF0C\u8BF7\u91CD\u8BD5\u3002"})}return this.#v(),this.status(t)})}async sendConnectionTest(t){return this.#M(),this.#_(t,async()=>{let o=this.#k(t),i=this.#I.get(t);if(!Qu(fu(i))||typeof i.sendConnectionTest!="function"){let e=new Error("\u98DE\u4E66\u673A\u5668\u4EBA\u5C1A\u672A\u8FDE\u63A5");throw e.code="test-target-unavailable",e}return i.sendConnectionTest(ba(`${o.botName}\uFF08${l6(o.appId)}\uFF09`,"\u98DE\u4E66\u673A\u5668\u4EBA"))})}async sendProactiveText(t,o,i,e={}){return this.#M(),this.#_(t,async()=>{this.#k(t);let a=this.#I.get(t);if(!Qu(fu(a))||typeof a.sendProactiveText!="function"){let A=new Error("\u98DE\u4E66\u673A\u5668\u4EBA\u5C1A\u672A\u8FDE\u63A5");throw A.code="bot-not-connected",A}return a.sendProactiveText(o,i,e)})}async disconnectBot(t){return this.#M(),this.#_(t,async()=>(this.#k(t),await this.#D(t),this.#l.delete(t),this.#v(),this.status(t)))}async updateGroupResponseMode(t,o){if(this.#M(),!zm(o))throw new TypeError("Invalid Feishu group response mode");return this.#L(()=>this.#_(t,async()=>{let i=this.#k(t);if(o==="all"&&i.groupMessagePermissionGranted!==!0){let a=new Error("Authorize im:message.group_msg before enabling all group messages");throw a.code="group_message_permission_required",a}let e=await this.#r.saveBot({...i,groupResponseMode:o});return this.#I.get(t)?.setGroupResponseMode?.(e.groupResponseMode),this.#v(),this.status(t)}))}async deleteBot(t){return this.#M(),this.#L(()=>this.#_(t,async()=>(this.#k(t),await this.#w(t),this.#v(),this.status())))}async reconnect(){let t=this.#r.list()[0];return t?this.reconnectBot(t.id):this.status()}async disconnect(){let t=this.#r.list()[0];return t?this.deleteBot(t.id):this.status()}async close(){if(this.#f)return;this.#f=!0;let t=[];for(let o of this.#o.values()){let i=o.manager.status().state;n6.has(o.operation)&&i==="saving"?(o.manager.cancel(),o.processing&&t.push(o.processing)):lS.has(i)&&(o.cancelled=!0,o.manager.cancel())}await this.#d,await Promise.allSettled([...this.#h.values()]),await Promise.allSettled([...this.#I.keys()].map(o=>this.#D(o))),await Promise.allSettled(t),await this.#d,await Promise.allSettled([...this.#h.values()]),await Promise.allSettled([...this.#I.keys()].map(o=>this.#D(o)))}#B({registration:t,selectedBotId:o}={}){let i=this.#r.list().map(I=>{let d=fu(this.#I.get(I.id)),B=Qu(d),f=this.#l.get(I.id)??null;return{botId:I.id,phase:rke({connected:B,error:f,connection:d}),connected:B,configured:!0,groupResponseMode:ro(I.groupResponseMode),groupMessagePermissionGranted:I.groupMessagePermissionGranted===!0,bot:tke(I),connection:d,lastMessageError:Vr(d.lastMessageError),error:f}}),e=t?this.#y(t):{state:"idle",attempt:0,updatedAt:Date.now()},a=oP.has(e.state),A=e.state==="saving",r=!!t&&(a||A),s=i.find(I=>I.botId===o)??(r?null:i[0]??null),g=i.some(I=>I.connected),c=s?.phase??"unconfigured";return a?c="registering":A?c="connecting":e.state==="error"&&!s&&(c="error"),{schemaVersion:2,revision:this.#E,phase:c,connected:s?.connected??!1,configured:i.length>0,bot:s?.bot??null,connection:s?.connection??I6(),error:s?.error??e.error??null,registration:e,bots:i,totals:{configured:i.length,connected:i.filter(I=>I.connected).length},anyConnected:g}}#y(t){let o=t.manager.status();return{...o,attempt:t.id,...t.botId?{botId:t.botId}:{},...t.operation?{operation:t.operation}:{},...t.stage?{stage:t.stage}:{},...o.state==="error"&&t.publicError?{error:{...t.publicError}}:{}}}async#S(t,o){let i=o.client_id,e=o.client_secret,a=dS(o.user_info?.open_id),A=o.user_info?.tenant_brand,r=t.target;if(t.cancelled)throw this.#m(t,"abort","Group message permission update was cancelled before local activation.");if(i!==r.appId)throw this.#m(t,"repair_app_mismatch","Feishu returned credentials for a different app.");if(!a)throw this.#m(t,"repair_owner_missing","Feishu returned no permission operator identity.");if(A!==void 0&&A!==r.domain)throw this.#m(t,"repair_domain_mismatch","Feishu returned credentials for a different account domain.");if(!r.ownerOpenIds.includes(s6)&&!r.ownerOpenIds.includes(a))throw this.#m(t,"repair_owner_mismatch","The Feishu permission operator is not an owner of this configured bot.");t.stage="verifying_identity";let s;try{s=await this.#A({appId:i,appSecret:e,domain:r.domain})}catch(g){throw this.#m(t,"repair_credentials_invalid","Feishu could not verify the updated app credentials.",g)}if(r.botOpenId&&s?.openId!==r.botOpenId)throw this.#m(t,"repair_bot_mismatch","The updated Feishu app belongs to a different bot identity.");await this.#L(()=>this.#_(t.botId,async()=>{let g=this.#r.getBot(t.botId);if(!g||g.deletionPending||pS(g)!==t.targetFingerprint)throw this.#m(t,"repair_target_changed","The Feishu bot changed while its permission update was in progress.");let c;try{c=await this.#t.resolve(g.secretRef)}catch(d){throw this.#m(t,"credential_update_failed","Unable to read the current Feishu credential.",d)}if(c?.value!==e){t.stage="persisting_secret";try{await this.#t.set(g.secretRef,e)}catch(B){if((await this.#t.resolve(g.secretRef).catch(()=>null))?.value!==e)throw this.#m(t,"credential_update_failed","Unable to store the updated Feishu credential.",B)}if((await this.#t.resolve(g.secretRef).catch(()=>null))?.value!==e)throw this.#m(t,"credential_state_unknown","The updated Feishu credential could not be confirmed after writing.")}t.stage="enabling_all_messages";let I;try{I=await this.#r.saveBot({...g,groupMessagePermissionGranted:!0,groupResponseMode:"all"})}catch(d){throw this.#m(t,"group_message_permission_save_failed","The permission was accepted, but all-message mode could not be saved.",d)}t.stage="restarting";try{await this.#b(I,e)}catch(d){throw this.#l.set(t.botId,{code:"connection_failed",message:"\u7FA4\u6D88\u606F\u6743\u9650\u5DF2\u5F00\u901A\uFF0C\u4F46\u673A\u5668\u4EBA\u957F\u8FDE\u63A5\u672A\u5C31\u7EEA\uFF0C\u8BF7\u70B9\u51FB\u91CD\u8BD5\u3002"}),this.#v(),this.#m(t,"group_message_permission_connection_failed","The permission was accepted, but the Feishu runtime could not restart.",d)}this.#l.delete(t.botId),t.stage="verified",t.publicError=null,this.#v()}))}async#Q(t,o){let i=o.client_id,e=o.client_secret,a=dS(o.user_info?.open_id),A=o.user_info?.tenant_brand,r=t.target;if(t.cancelled)throw this.#m(t,"abort","Callback repair was cancelled before local activation.");if(i!==r.appId)throw this.#m(t,"repair_app_mismatch","Feishu returned credentials for a different app.");if(!a)throw this.#m(t,"repair_owner_missing","Feishu returned no repair operator identity.");if(A!==void 0&&A!==r.domain)throw this.#m(t,"repair_domain_mismatch","Feishu returned credentials for a different account domain.");t.stage="verifying_identity";let s;try{s=await this.#A({appId:i,appSecret:e,domain:r.domain})}catch(c){throw this.#m(t,"repair_credentials_invalid","Feishu could not verify the repaired app credentials.",c)}if(r.botOpenId&&s?.openId!==r.botOpenId)throw this.#m(t,"repair_bot_mismatch","The repaired Feishu app belongs to a different bot identity.");let g=await this.#L(()=>this.#_(t.botId,async()=>{let c=this.#r.getBot(t.botId);if(!c||c.deletionPending||pS(c)!==t.targetFingerprint)throw this.#m(t,"repair_target_changed","The Feishu bot changed while its callback repair was in progress.");let I;try{I=await this.#t.resolve(c.secretRef)}catch(f){throw this.#m(t,"credential_update_failed","Unable to read the current Feishu credential.",f)}if(I?.value!==e){t.stage="persisting_secret";try{await this.#t.set(c.secretRef,e)}catch(Q){if((await this.#t.resolve(c.secretRef).catch(()=>null))?.value!==e)throw this.#m(t,"credential_update_failed","Unable to store the repaired Feishu credential.",Q)}if((await this.#t.resolve(c.secretRef).catch(()=>null))?.value!==e)throw this.#m(t,"credential_state_unknown","The repaired Feishu credential could not be confirmed after writing.")}let B;t.stage="restarting";try{await this.#b(c,e),B=this.#I.get(t.botId)}catch(f){throw this.#l.set(t.botId,{code:"connection_failed",message:"\u673A\u5668\u4EBA\u56DE\u8C03\u4FEE\u590D\u5DF2\u4FDD\u5B58\uFF0C\u4F46\u957F\u8FDE\u63A5\u672A\u5C31\u7EEA\uFF0C\u8BF7\u70B9\u51FB\u91CD\u8BD5\u3002"}),this.#v(),this.#m(t,"repair_connection_failed","The repaired Feishu runtime could not be started.",f)}if(!B)throw this.#m(t,"repair_connection_failed","The repaired Feishu runtime is unavailable.");return this.#l.delete(t.botId),this.#v(),B}));if(typeof g.beginCardActionProbe!="function")throw this.#m(t,"card_action_probe_unavailable","The Feishu runtime cannot verify card callbacks.");t.stage="awaiting_callback";try{if((await g.beginCardActionProbe({expectedOperatorOpenId:t.initiator.actorOpenId??a,timeoutMs:this.#C,...t.initiator.chatId?{chatId:t.initiator.chatId}:{}}))?.verified!==!0){let I=new Error("Feishu runtime returned no callback proof");throw I.code="card_action_probe_failed",I}}catch(c){let I=c?.code==="card_action_probe_timeout"?"card_action_probe_timeout":c?.code==="card_action_probe_unavailable"?"card_action_probe_unavailable":c?.code==="card_action_probe_send_failed"?"card_action_probe_send_failed":"card_action_probe_failed";throw this.#m(t,I,I==="card_action_probe_timeout"?"Timed out waiting for the Feishu callback verification button.":"The Feishu card callback probe failed.",c)}t.stage="verified",t.publicError=null,this.#v()}#m(t,o,i,e){t.publicError={code:o,message:i};let a=new Error(i,e?{cause:e}:void 0);return a.code=o,a}async#u(t,o){if(t.cancelled)throw new Error("Registration was cancelled");let i=o.client_id,e=o.client_secret,a=o.user_info?.open_id,A=o.user_info?.tenant_brand==="lark"?"lark":"feishu";if(!a)throw new Error("Feishu registration returned no owner open_id");let r=await this.#A({appId:i,appSecret:e,domain:A});if(t.cancelled)throw new Error("Registration was cancelled");let s=this.#r.list().find(Q=>Q.appId===i),g=s?.id??this.#n();if(typeof g!="string"||!/^[A-Za-z0-9_-]{1,128}$/.test(g)||!s&&this.#r.getBot(g))throw new Error("Bot id generator returned an invalid or duplicate id");let c=s?.secretRef??g6(g),I=this.#c.get(g),d=await this.#t.resolve(c).catch(()=>{});await this.#t.set(c,e);let B;try{B=await this.#r.saveBot({...s,id:g,appId:i,secretRef:c,ownerOpenIds:[...new Set([...s?.ownerOpenIds??[],a])],domain:A,botName:r.name,botOpenId:r.openId,activated:r.activated,deletionPending:!1,connectedAt:new Date().toISOString(),createdAt:s?.createdAt??new Date().toISOString()}),t.botId=g,t.createdNew=!s,this.#c.set(g,t.id)}catch(Q){try{await this.#T(c,d)}catch(y){throw new Error("Unable to restore the Feishu credential after a config failure.",{cause:y})}throw Q}if(t.cancelled)throw t.createdNew?await this.#_(g,()=>this.#w(g)):(await this.#r.saveBot(s),await this.#T(c,d),I?this.#c.set(g,I):this.#c.delete(g)),new Error("Registration was cancelled");let f=!1;try{if(await this.#_(g,()=>this.#b(B,e)),t.cancelled)throw t.createdNew?await this.#_(g,()=>this.#w(g)):(await this.#r.saveBot(s),await this.#T(c,d),I?this.#c.set(g,I):this.#c.delete(g),d?.value&&!s.deletionPending?await this.#_(g,()=>this.#b(s,d.value)):await this.#_(g,()=>this.#D(g))),f=!0,new Error("Registration was cancelled");this.#l.delete(g),this.#v()}catch(Q){if(t.cancelled)throw!f&&t.createdNew&&this.#r.getBot(g)?await this.#_(g,()=>this.#w(g)):!f&&s&&(await this.#r.saveBot(s),await this.#T(c,d),I?this.#c.set(g,I):this.#c.delete(g),!this.#f&&d?.value&&!s.deletionPending?await this.#_(g,()=>this.#b(s,d.value)):await this.#_(g,()=>this.#D(g))),this.#v(),Q;if(s&&d?.value)try{throw await this.#r.saveBot(s),await this.#T(c,d),I?this.#c.set(g,I):this.#c.delete(g),s.deletionPending?(await this.#_(g,()=>this.#D(g)),this.#l.set(g,{code:"deletion_pending",message:"\u673A\u5668\u4EBA\u6B63\u5728\u7B49\u5F85\u5B8C\u6210\u672C\u5730\u5220\u9664\uFF0C\u8BF7\u91CD\u8BD5\u79FB\u9664\u3002"})):(await this.#_(g,()=>this.#b(s,d.value)),this.#l.delete(g)),this.#v(),Q}catch(y){throw y===Q?Q:(this.#l.set(g,{code:"connection_failed",message:"\u673A\u5668\u4EBA\u8FDE\u63A5\u66F4\u65B0\u5931\u8D25\uFF0C\u4E14\u539F\u8FDE\u63A5\u65E0\u6CD5\u6062\u590D\uFF0C\u8BF7\u91CD\u8BD5\u3002"}),this.#v(),new Error("Unable to restore the previous Feishu bot connection.",{cause:y}))}throw this.#l.set(g,{code:"connection_failed",message:"\u673A\u5668\u4EBA\u5DF2\u7ECF\u521B\u5EFA\uFF0C\u4F46\u957F\u8FDE\u63A5\u672A\u5C31\u7EEA\uFF0C\u8BF7\u70B9\u51FB\u91CD\u8BD5\u3002"}),this.#v(),Q}}async#b(t,o){await this.#D(t.id);let i=await this.#g({botId:t.id,config:t,appSecret:o,repair:this.#R(t.id)});this.#I.set(t.id,i);try{await i.start()}catch(e){throw this.#I.get(t.id)===i&&this.#I.delete(t.id),await i.stop({preserveError:!0}).catch(()=>{}),e}}#R(t){let o=i=>{let e=this.#o.get(i);return e?.operation===Bu&&e.botId===t?e:null};return Object.freeze({start:({actorOpenId:i,chatId:e}={})=>this.startCallbackRepair(t,{actorOpenId:i,chatId:e}),status:({attemptId:i}={})=>o(i)?this.registrationStatus(i):null,cancel:async({attemptId:i}={})=>o(i)?this.cancelRegistration(i):this.status(t)})}async#D(t){let o=this.#I.get(t);this.#I.delete(t),o&&await o.stop()}async#w(t){let o=this.#r.getBot(t);if(o){o.deletionPending||(o=await this.#r.saveBot({...o,deletionPending:!0})),await this.#D(t);try{await this.#t.unset(o.secretRef)}catch(i){throw this.#l.set(t,{code:"credential_removal_failed",message:"\u65E0\u6CD5\u5220\u9664\u673A\u5668\u4EBA\u51ED\u636E\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002"}),new Error("Unable to remove the Feishu credential.",{cause:i})}try{await this.#a({botId:t,config:o})}catch(i){throw this.#l.set(t,{code:"state_cleanup_failed",message:"\u65E0\u6CD5\u5220\u9664\u673A\u5668\u4EBA\u7684\u672C\u5730\u4F1A\u8BDD\u6570\u636E\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002"}),new Error("Unable to remove the Feishu bot session state.",{cause:i})}await this.#r.removeBot(t),this.#l.delete(t),this.#c.delete(t)}}async#T(t,o){o?.value?await this.#t.set(t,o.value):await this.#t.unset(t)}#k(t){let o=this.#r.getBot(t);if(!o)throw new Error("Unknown Feishu bot");return o}#M(){if(this.#f)throw new Error("The Feishu controller is closed")}#L(t){let o=this.#d.then(t,t);return this.#d=o.then(()=>{},()=>{}),o}#_(t,o){let e=(this.#h.get(t)??Promise.resolve()).then(o,o),a=e.then(()=>{},()=>{});return this.#h.set(t,a),a.finally(()=>{this.#h.get(t)===a&&this.#h.delete(t)}),e}#U(){if(!(this.#o.size<=32))for(let[t,o]of this.#o){if(t===this.#p)continue;let i=o.manager.status().state;if(!oP.has(i)&&i!=="saving"&&this.#o.delete(t),this.#o.size<=32)break}}#v(){this.#E+=1}};function ske(n){for(let t of["https_proxy","HTTPS_PROXY","http_proxy","HTTP_PROXY"]){let o=n?.[t];if(typeof o=="string"&&o.trim())return o.trim()}}function gke(n,t=o=>new p6.default(o)){let o=ske(n);return o?t(o):void 0}function cke(n){let t=mc(n.dshHome??process.env.DSH_HOME??_d(oke(),".dsh")),o=mc(n.dataDir??_d(t,"integrations","dsh-feishu"));return{root:o,config:mc(n.configPath??_d(o,"config.json")),legacyState:mc(n.statePath??_d(o,"state.json")),bots:mc(n.botsDir??_d(o,"bots")),workspaces:mc(n.workspacesPath??_d(o,"workspaces.json"))}}async function nP(n,t={},o={}){if(!n?.credentials)throw new TypeError("dsh-feishu requires ctx.credentials");let i=Aa(n,t),e=o.lark??Hv,a=o.Controller??yu,A=o.ConfigStore??nS,r=o.StateStore??Jm,s=o.HarnessClient??iS,g=o.FeishuRuntime??aS,c=o.verifyFeishuApp??QW,I=fe=>c({...fe,httpInstance:e.defaultHttpInstance}),d=o.createConnectionSupervisor??Kv,B=typeof n.logger=="function"?n.logger("dsh-feishu"):n.logger??console,f=()=>Ra(n),Q=cke(t),y=await new A(Q.config).load(),m=mc(t.workspace??process.cwd()),_=o.WorkspaceStore??ea,T=o.workspaces??await new _(Q.workspaces,{defaultWorkspace:m}).load(),R=typeof y.list=="function",H=()=>R?y.list():[],K=H();R&&await T.reconcile(K.map(fe=>fe.id)),await Promise.all(K.map(fe=>T.ensure(fe.id,{defaultAgentPreset:t.agentPreset})));let F=typeof y.removeBot=="function"?Pa(y,{workspaces:T,method:"removeBot",botIdFromRemoved:fe=>fe.id,saveMethod:"saveBot",botIdFromSave:fe=>fe?.id}):y,q=new Map,z=fe=>!fe.id||!fe.secretRef||fe.secretRef===sS?Q.legacyState:mc(Q.bots,fe.id,"state.json"),x=async fe=>{let Se=fe.id??"__legacy__",Ke=q.get(Se);return Ke||(Ke=await new r(z(fe)).load(),q.set(Se,Ke)),Ke},G=async fe=>{let Se=H().find(Ke=>Ke.id===fe);if(!Se)throw new Error("Unknown Feishu bot");return x(Se)},J=Ua(n,o.commandExecutor),{controlExecutor:L,sessionMaintenanceExecutor:N,fileIngressExecutor:k}=Ha(n,{controlExecutor:o.controlExecutor,sessionMaintenanceExecutor:o.sessionMaintenanceExecutor,fileIngressExecutor:o.fileIngressExecutor}),V=new s({...i,workspace:m,autostart:!1,dshBin:t.dshBin??"dsh",...J?{commandExecutor:J}:{},...L?{controlExecutor:L}:{},...N?{sessionMaintenanceExecutor:N}:{},...k?{fileIngressExecutor:k}:{}}),te=o.proxyEnv??process.env,ie=gke(te,o.createProxyAgent),we=new a({registerApp:fe=>e.registerApp(fe),verifyApp:I,credentials:n.credentials,configStore:F,createRuntime:async({botId:fe,config:Se,appSecret:Ke,repair:rA})=>{let ve=await x(Se),aA=fe??Se.id??Se.appId;await T.ensure(aA,{defaultAgentPreset:t.agentPreset});let Ce=Ga(V,{botId:aA,workspaces:T,state:ve,agentPresetCatalog:f});return new g({lark:e,botId:aA,repair:rA,appId:Se.appId,appSecret:Ke,domain:Se.domain,botOpenId:Se.botOpenId,groupResponseMode:Se.groupResponseMode,ownerOpenIds:Se.ownerOpenIds??[Se.ownerOpenId],harness:Ce.harness,state:Ce.state,contextEnhancement:{botId:aA,getSettings:()=>T.contextEnhancementFor(aA)},replyTimeoutMs:t.replyTimeoutMs??6e5,slashCommands:t.slashCommands!==!1,...ie?{wsAgent:ie}:{},logger:{error:(...he)=>B.error?.(`[${fe??Se.id}]`,...he),warn:(...he)=>B.warn?.(`[${fe??Se.id}]`,...he),info:(...he)=>B.info?.(`[${fe??Se.id}]`,...he),debug:(...he)=>B.debug?.(`[${fe??Se.id}]`,...he)}})},deleteState:async({botId:fe,config:Se})=>{q.delete(fe);try{await nke(z(Se))}catch(Ke){if(Ke?.code!=="ENOENT")throw Ke}}}),se=Fa(we,{workspaces:T,stateFor:G,agentPresetCatalog:f}),Ne=d({controller:se,harness:V,logger:B,retryDelaysMs:t.retryDelaysMs,healthyIntervalMs:t.healthyIntervalMs}).start();return{controller:se,deliveryAdapter:La({channel:"feishu",workspaces:T,coreController:we,stateFor:G}),ready:Ne.ready,async close(){await Ne.close(),await se.close(),V.stopManagedProcess(),ie?.destroy?.()}}}import Mke from"qrcode";var Nd=qe(dP(),1);var Dke=qe(dP(),1);var Oke=Object.freeze({$locale:"en",IM\u673A\u5668\u4EBA:"IM bots",IM\u673A\u5668\u4EBA\u8BBE\u7F6E:"IM bot settings",\u66F4\u591A\u673A\u5668\u4EBA\u8BBE\u7F6E:"More bot settings","IM \u6E20\u9053":"IM channels","\u8BA9 DeepSeek Harness \u89E6\u624B\u53EF\u53CA":"Connecting DeepSeek Harness",\u5F53\u524D\u7248\u672C:"Current version","DSH-IM \u66F4\u65B0":"DSH-IM update",\u68C0\u67E5\u66F4\u65B0:"Check for updates",\u91CD\u65B0\u68C0\u67E5:"Check again",\u5237\u65B0\u72B6\u6001:"Refresh status",\u624B\u5DE5\u66F4\u65B0:"Manual update",\u624B\u5DE5\u66F4\u65B0\u547D\u4EE4:"Manual update command",\u590D\u5236\u547D\u4EE4:"Copy command","\u590D\u5236\u4E2D\u2026":"Copying\u2026",\u5DF2\u590D\u5236:"Copied","\u547D\u4EE4\u5DF2\u590D\u5236\u3002":"Command copied.","\u590D\u5236\u5931\u8D25\uFF0C\u8BF7\u9009\u4E2D\u547D\u4EE4\u540E\u6309 Ctrl+C \u6216 \u2318C \u590D\u5236\u3002":"Copy failed. Select the command and press Ctrl+C or \u2318C to copy it.","\u81EA\u52A8\u66F4\u65B0\u5931\u8D25\u53EF\u4EE5\u4F7F\u7528\u547D\u4EE4\u66F4\u65B0\uFF1A":"If the automatic update fails, update with this command:","\u5C1A\u672A\u786E\u8BA4\u76EE\u6807\u7248\u672C\uFF0C\u6B64\u547D\u4EE4\u5B89\u88C5\u6267\u884C\u65F6 npm \u7684 latest \u7248\u672C\u3002":"No target version has been confirmed. This command installs the npm latest version available when you run it.","\u8BF7\u5728\u5F53\u524D Desktop \u7684\u5185\u7F6E\u7EC8\u7AEF\u6267\u884C\uFF0C\u5B8C\u6210\u540E\u624B\u52A8\u91CD\u542F\u3002":"Run this in the current Desktop\u2019s built-in terminal, then restart manually.","\u8BF7\u5728\u8FD0\u884C\u5F53\u524D Harness \u7684\u540C\u4E00\u73AF\u5883\u6267\u884C\uFF0C\u4FDD\u6301 DSH_HOME \u4E00\u81F4\uFF0C\u5B8C\u6210\u540E\u624B\u52A8\u91CD\u542F\u3002":"Run this in the same environment as the current Harness, with the same DSH_HOME, then restart manually.","\u8BF7\u7B49\u5F85\u5F53\u524D\u64CD\u4F5C\u7ED3\u675F\uFF0C\u786E\u8BA4\u6CA1\u6709\u5B89\u88C5\u8FDB\u7A0B\u8FD0\u884C\u540E\u518D\u6267\u884C\u547D\u4EE4\u3002":"Wait for the current operation to finish and confirm no installer is running before using this command.","\u6E90\u7801\u6216\u94FE\u63A5\u5B89\u88C5\u8BF7\u6309\u539F\u5B89\u88C5\u65B9\u5F0F\u66F4\u65B0\uFF0C\u4E0D\u63D0\u4F9B\u8986\u76D6\u6E90\u7801\u7684 npm \u547D\u4EE4\u3002":"Update source or linked installations using their original method. No npm command is provided to overwrite the source.","\u65E0\u6CD5\u5B89\u5168\u751F\u6210\u5F53\u524D profile \u7684\u547D\u4EE4\uFF0C\u8BF7\u5728\u7EC8\u7AEF\u4E2D\u624B\u52A8\u786E\u8BA4 profile \u540E\u66F4\u65B0\u3002":"A safe command cannot be generated for this profile. Confirm the profile in your terminal before updating manually.",\u66F4\u65B0\u81F3:"Update to",\u5B89\u88C5\u66F4\u65B0:"Install update","\u6B63\u5728\u66F4\u65B0\u2026":"Updating\u2026",\u5F85\u624B\u52A8\u91CD\u542F:"Restart needed",\u8FD0\u884C\u7248\u672C:"Running version",\u5DF2\u5B89\u88C5\u7248\u672C:"Installed version","\u76EE\u6807 profile":"Target profile",\u76EE\u6807\u7248\u672C:"Target version",\u65E0\u6CD5\u786E\u8BA4:"Unknown",\u53D1\u73B0\u65B0\u7248\u672C:"Update available",\u5DF2\u662F\u6700\u65B0\u7248\u672C:"Up to date",\u5F53\u524D\u7248\u672C\u65E0\u9700\u66F4\u65B0:"No newer version available","\u5DF2\u83B7\u53D6 npm \u6700\u65B0\u7248\u672C":"Latest npm version retrieved","\u68C0\u67E5 npm \u6700\u65B0\u7248\u672C\uFF0C\u4E0D\u4F1A\u81EA\u52A8\u5B89\u88C5\u3002":"Check the latest npm version. Nothing is installed automatically.","\u6B63\u5728\u4ECE npm \u68C0\u67E5\u6700\u65B0\u7248\u672C\u2026":"Checking npm for the latest version\u2026","\u6B63\u5728\u5B89\u88C5\uFF0C\u8BF7\u7A0D\u5019\u2026":"Installing, please wait\u2026","\u6B63\u5728\u6821\u9A8C\u5B89\u88C5\u7ED3\u679C\u2026":"Verifying the installation\u2026","\u5DF2\u5B89\u88C5\uFF0C\u5F85\u624B\u52A8\u91CD\u542F":"Installed \u2014 manual restart needed",\u66F4\u65B0\u5DF2\u751F\u6548:"Updated version is active",\u66F4\u65B0\u5931\u8D25:"Update failed",\u66F4\u65B0\u8BF7\u6C42\u5931\u8D25:"Update request failed","\u4EC5\u66F4\u65B0 DSH-IM\u3002\u5B89\u88C5\u5B8C\u6210\u540E\u9700\u624B\u52A8\u91CD\u542F\u540E\u53F0\uFF1B\u672C\u529F\u80FD\u4E0D\u4F1A\u81EA\u52A8\u91CD\u542F\u6216\u4E3B\u52A8\u5237\u65B0\u9875\u9762\u3002":"Only DSH-IM will be updated. Restart the backend manually after installation. This feature does not automatically restart it or initiate a page refresh.","\u5F53\u524D\u662F\u6E90\u7801\u6216\u94FE\u63A5\u5B89\u88C5\uFF0C\u53EA\u80FD\u68C0\u67E5\u7248\u672C\uFF1B\u8BF7\u624B\u52A8\u66F4\u65B0\u6E90\u7801\uFF0C\u6216\u8FC1\u79FB\u5230 npm \u5B89\u88C5\u3002":"This is a source or linked installation. You can check versions, but must update the source manually or migrate to an npm installation.","\u65E0\u6CD5\u786E\u8BA4\u5F53\u524D profile\uFF0C\u8BF7\u5728\u5BF9\u5E94\u7684 Harness \u73AF\u5883\u4E2D\u624B\u52A8\u66F4\u65B0\u3002":"The current profile could not be identified. Update manually in the corresponding Harness environment.","\u5F53\u524D\u8FD0\u884C\u73AF\u5883\u4E0D\u652F\u6301\u6309\u94AE\u5B89\u88C5\uFF0C\u8BF7\u624B\u52A8\u66F4\u65B0\u63D2\u4EF6\u3002":"This runtime cannot install through this button. Update the plugin manually.","\u5F53\u524D npm \u6E90\u914D\u7F6E\u4E0E\u5B98\u65B9\u6E90\u4E0D\u4E00\u81F4\uFF0C\u8BF7\u5148\u68C0\u67E5 registry \u914D\u7F6E\u3002":"The npm registry configuration differs from the official registry. Check your registry settings first.","\u5F53\u524D Host \u7684 Node.js \u7248\u672C\u4E0D\u6EE1\u8DB3\u65B0\u7248\u8981\u6C42\uFF0C\u8BF7\u5148\u66F4\u65B0\u8FD0\u884C\u73AF\u5883\u3002":"The Host Node.js version does not meet the new version\u2019s requirements. Update the runtime first.","\u65B0\u7248\u672C\u5DF2\u5B89\u88C5\uFF0C\u8BF7\u5728\u65B9\u4FBF\u65F6\u624B\u52A8\u91CD\u542F\u5F53\u524D Harness \u6216 Desktop\u3002":"The new version is installed. Manually restart this Harness or Desktop when convenient.","\u4E0A\u6B21\u5B89\u88C5\u7ED3\u679C\u65E0\u6CD5\u786E\u8BA4\uFF0C\u8BF7\u5148\u68C0\u67E5\u6B64 profile \u7684\u63D2\u4EF6\u5B89\u88C5\u72B6\u6001\u3002":"The previous installation result is uncertain. Check this profile\u2019s plugin installation first.","\u65E0\u6CD5\u8BBF\u95EE npm \u6216\u8BF7\u6C42\u8D85\u65F6\uFF0C\u8BF7\u7A0D\u540E\u91CD\u65B0\u68C0\u67E5\u3002":"npm is unreachable or the request timed out. Check again later.","npm \u8FD4\u56DE\u7684\u7248\u672C\u4FE1\u606F\u65E0\u6548\uFF0C\u6682\u65F6\u65E0\u6CD5\u66F4\u65B0\u3002":"npm returned invalid release information. Updating is unavailable.","\u7248\u672C\u786E\u8BA4\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u91CD\u65B0\u68C0\u67E5\u540E\u518D\u5B89\u88C5\u3002":"The version confirmation expired. Check again before installing.","\u63D2\u4EF6\u5B89\u88C5\u72B6\u6001\u5DF2\u53D1\u751F\u53D8\u5316\uFF0C\u8BF7\u91CD\u65B0\u68C0\u67E5\u3002":"The plugin installation changed. Check again.","\u6B64 profile \u6B63\u5728\u66F4\u65B0\uFF0C\u8BF7\u7A0D\u540E\u67E5\u770B\u72B6\u6001\u3002":"This profile is already updating. Check its status shortly.","\u5B89\u88C5\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u5F53\u524D\u5B89\u88C5\u72B6\u6001\u540E\u91CD\u8BD5\u3002":"Installation failed. Check the current installation before retrying.","\u5B89\u88C5\u7ED3\u679C\u6821\u9A8C\u5931\u8D25\uFF0C\u8BF7\u624B\u52A8\u68C0\u67E5\u63D2\u4EF6\u7248\u672C\u3002":"Installation verification failed. Check the plugin version manually.","\u65E0\u6CD5\u5B89\u5168\u4FDD\u5B58\u66F4\u65B0\u72B6\u6001\uFF0C\u8BF7\u5148\u68C0\u67E5\u5F53\u524D\u5B89\u88C5\u7ED3\u679C\u3002":"Update state could not be saved safely. Check the current installation result before retrying.","\u4E0A\u6B21\u66F4\u65B0\u5DF2\u4E2D\u65AD\uFF0C\u8BF7\u68C0\u67E5\u5B89\u88C5\u72B6\u6001\u540E\u91CD\u8BD5\u3002":"The previous update was interrupted. Check the installation before retrying.","\u66F4\u65B0\u670D\u52A1\u5DF2\u5173\u95ED\uFF0C\u8BF7\u624B\u52A8\u91CD\u65B0\u6253\u5F00\u8BBE\u7F6E\u9875\u3002":"The update service has closed. Reopen settings manually.","\u66F4\u65B0\u8BF7\u6C42\u65E0\u6548\uFF0C\u8BF7\u91CD\u65B0\u68C0\u67E5\u7248\u672C\u3002":"The update request is invalid. Check the version again.","\u5F53\u524D\u63D2\u4EF6\u5B89\u88C5\u4E0D\u5B8C\u6574\u6216\u4E0E profile \u4E0D\u7B26\uFF0C\u8BF7\u624B\u52A8\u68C0\u67E5\u5B89\u88C5\u914D\u7F6E\u3002":"The plugin installation is incomplete or does not match this profile. Check the installation configuration manually.","\u65E0\u6CD5\u786E\u8BA4\u5F53\u524D npm \u6E90\u914D\u7F6E\uFF0C\u6682\u65F6\u4E0D\u80FD\u5B89\u88C5\u66F4\u65B0\u3002":"The npm registry configuration could not be verified. Installation is unavailable.","\u5B89\u88C5\u8D85\u65F6\uFF0C\u8BF7\u5148\u786E\u8BA4\u5F53\u524D\u5B89\u88C5\u72B6\u6001\uFF0C\u518D\u51B3\u5B9A\u662F\u5426\u91CD\u8BD5\u3002":"Installation timed out. Check the current installation before retrying.","\u66F4\u65B0\u8BF7\u6C42\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002":"The update request failed. Try again.","\u66F4\u65B0\u670D\u52A1\u8FD4\u56DE\u4E86\u65E0\u6CD5\u8BC6\u522B\u7684\u54CD\u5E94\u3002":"The update service returned an unrecognized response.","\u5F53\u524D Host \u4E0D\u652F\u6301\u66F4\u65B0\u63A5\u53E3\uFF0C\u8BF7\u5148\u624B\u52A8\u66F4\u65B0\u63D2\u4EF6\u5E76\u91CD\u542F\u3002":"This Host does not support the update API. Update the plugin manually and restart first.","\u5173\u95ED\u7A97\u53E3\u4E0D\u4F1A\u53D6\u6D88\u5B89\u88C5\u3002\u8BF7\u52FF\u540C\u65F6\u5728\u5176\u4ED6\u7A97\u53E3\u7BA1\u7406\u6B64 profile \u7684\u63D2\u4EF6\u3002":"Closing this window does not cancel installation. Do not manage this profile\u2019s plugins in another window at the same time.","\u9875\u9762\u7248\u672C\u4E0E\u8FD0\u884C\u7248\u672C\u4E0D\u540C\uFF0C\u8BF7\u624B\u52A8\u5237\u65B0\u9875\u9762\uFF1B\u82E5\u4ECD\u4E0D\u4E00\u81F4\uFF0C\u8BF7\u624B\u52A8\u91CD\u542F Harness \u6216 Desktop\u3002":"The page and running versions differ. Refresh the page manually; if they still differ, manually restart Harness or Desktop.","\u8BF7\u5728\u673A\u5668\u4EBA\u7A7A\u95F2\u65F6\u5B89\u88C5\uFF1B\u5B89\u88C5\u4F1A\u4FEE\u6539\u5F53\u524D profile \u7684\u4F9D\u8D56\uFF0C\u5B8C\u6210\u540E\u9700\u624B\u52A8\u91CD\u542F\u3002":"Install while bots are idle. This changes the current profile\u2019s dependencies and requires a manual restart afterward.","AI Office":"AI Office","\uFF08\u5B9E\u9A8C\u529F\u80FD\uFF09":"(Experimental)","AI Office \u8BBE\u7F6E":"AI Office settings","AI Office \u8BBE\u7F6E\u9875\u7F3A\u5C11 RPC \u8FDE\u63A5":"AI Office settings are missing an RPC connection","\u6B63\u5728\u8BFB\u53D6 AI Office Connector\u2026":"Loading AI Office Connector\u2026","\u672C\u673A\u4E3B\u52A8\u8FDE\u63A5\u516C\u7F51 Office\uFF1BHarness \u4E0D\u5F00\u653E\u7AEF\u53E3\u3002\u534F\u8BAE Hook \u56FA\u5B9A\u4E3A ":"This machine connects outward to the public Office; Harness exposes no port. Protocol hooks: ",\u5C1A\u672A\u914D\u7F6E:"Not configured","\u5DF2\u8FDE\u63A5 Office":"Connected to Office",\u5DF2\u914D\u7F6E:"Configured",\u7B49\u5F85\u91CD\u8FDE:"Waiting to reconnect",\u51ED\u636E\u7F3A\u5931:"Credential missing",\u6700\u8FD1\u5FC3\u8DF3:"Last heartbeat",\u6700\u8FD1\u4E8B\u4EF6:"Last event",\u91CD\u8FDE\u6B21\u6570:"Reconnects","\u8FD0\u884C Job":"Running Jobs","\u5B8C\u6210 Job":"Completed Jobs",\u5C1A\u65E0:"None yet",\u8BBE\u5907\u8FDE\u63A5:"Device connection","Token \u53EA\u5199\u5165\u672C\u673A\u51ED\u636E\u5B58\u50A8":"Token is written only to the local credential store","\u7C98\u8D34 Office \u4E00\u6B21\u6027\u51ED\u636E":"Paste the one-time Office credential","\u5DF2\u5B89\u5168\u4FDD\u5B58\uFF1B\u7559\u7A7A\u4FDD\u6301\u4E0D\u53D8":"Stored securely; leave blank to keep it",\u6700\u5927\u5E76\u53D1:"Max concurrency","Heartbeat \u79D2\u6570":"Heartbeat seconds","Workspace \u6620\u5C04":"Workspace mappings","\u6BCF\u884C alias=/\u672C\u673A/\u7EDD\u5BF9\u8DEF\u5F84\uFF1BOffice \u53EA\u80FD\u770B\u5230 alias\u3002":"One alias=/local/absolute/path per line; Office sees only aliases.","Instruction Preset \u6620\u5C04":"Instruction preset mappings","\u6BCF\u884C alias=\u6307\u4EE4\uFF1B\u65B0\u589E preset \u4E0D\u9700\u8981\u6539 Office \u4EE3\u7801\u3002":"One alias=instruction per line; new presets require no Office code change.",\u4FDD\u5B58\u5E76\u8FDE\u63A5:"Save and connect",\u6D4B\u8BD5\u8FDE\u63A5:"Test connection","\u6D4B\u8BD5\u4E2D\u2026":"Testing\u2026",\u91CD\u65B0\u8FDE\u63A5:"Reconnect",\u79FB\u9664\u8FDE\u63A5:"Remove connection","\u8FDE\u63A5\u6D4B\u8BD5\u901A\u8FC7\u3002":"Connection test passed.","\u914D\u7F6E\u5DF2\u4FDD\u5B58\u3002":"Configuration saved.","\u534F\u8BAE Hook \u9884\u89C8":"Protocol hook preview","\u7531 Base URL \u81EA\u52A8\u6D3E\u751F\uFF0C\u4E0D\u5355\u72EC\u586B\u5199":"Derived from Base URL; no separate input","Base URL \u65E0\u6548":"Invalid Base URL","Office Hook \u5C1A\u672A\u90E8\u7F72\u65F6\uFF0C\u914D\u7F6E\u4F1A\u5B89\u5168\u4FDD\u5B58\u5E76\u81EA\u52A8\u91CD\u8BD5\uFF1B\u51FA\u73B0 HTTP 404 \u4EE3\u8868\u534F\u8BAE\u7AEF\u70B9\u5F85\u4E0A\u7EBF\uFF0C\u4E0D\u4EE3\u8868 Harness \u6545\u969C\u3002":"Configuration is saved and retried while Office hooks are unavailable; HTTP 404 means the protocol endpoint is pending, not a Harness failure.","Workspace \u6620\u5C04\u6BCF\u884C\u5FC5\u987B\u4F7F\u7528 alias=value":"Each workspace mapping must use alias=value","Instruction Preset \u6620\u5C04\u6BCF\u884C\u5FC5\u987B\u4F7F\u7528 alias=value":"Each instruction preset mapping must use alias=value","action-items=\u8F6C\u6362\u4E3A\u8D1F\u8D23\u4EBA\u3001\u622A\u6B62\u548C\u9A8C\u6536\u660E\u786E\u7684\u5DE5\u5355":"action-items=Turn this into accountable tasks with deadlines and acceptance criteria","AI Office \u62D2\u7EDD\u4E86 Device Token\u3002":"AI Office rejected the Device Token.","AI Office Connector Hook \u5C1A\u672A\u5C31\u7EEA\u3002":"AI Office Connector hooks are not available yet.","AI Office Connector \u534F\u8BAE\u7248\u672C\u4E0D\u517C\u5BB9\u3002":"The AI Office Connector protocol is incompatible.","\u672C\u673A\u6682\u65F6\u65E0\u6CD5\u8BBF\u95EE AI Office\u3002":"AI Office cannot currently be reached from this machine.","AI Office \u8FDE\u63A5\u5DF2\u4E2D\u65AD\u3002":"The AI Office connection was interrupted.","\u5E2E\u52A9\u4E0E\u53CD\u9988 \xB7 \u524D\u5F80 GitHub":"Help & feedback \xB7 Open GitHub","\u8BF7\u6539\u7528 localhost \u91CD\u65B0\u6253\u5F00":"Reopen with localhost","\u9875\u9762\u4F1A\u5728\u5F53\u524D\u7AEF\u53E3\u91CD\u65B0\u6253\u5F00\uFF0C\u673A\u5668\u4EBA\u914D\u7F6E\u4E0D\u4F1A\u6539\u53D8\u3002":"The page will reopen on the current port. Your bot configuration will not change.","\u4F7F\u7528 localhost \u91CD\u65B0\u6253\u5F00":"Reopen with localhost","\u5F53\u524D\u5730\u5740\u4E0E\u6D4F\u89C8\u5668\u7684\u672C\u673A\u8BF7\u6C42\u6821\u9A8C\u4E0D\u517C\u5BB9\u3002\u8BF7\u4F7F\u7528\u4E0A\u65B9\u6309\u94AE\u6539\u7528 localhost \u91CD\u65B0\u6253\u5F00\u3002":"This address is incompatible with the browser\u2019s local-request checks. Use the button above to reopen with localhost.",\u5FAE\u4FE1:"WeChat",\u98DE\u4E66:"Feishu",\u9489\u9489:"DingTalk",\u4F01\u4E1A\u5FAE\u4FE1:"WeCom",\u5FAE\u4FE1\u673A\u5668\u4EBA:"WeChat bot",\u98DE\u4E66\u673A\u5668\u4EBA:"Feishu bot",\u9489\u9489\u673A\u5668\u4EBA:"DingTalk bot",\u4F01\u4E1A\u5FAE\u4FE1\u673A\u5668\u4EBA:"WeCom bot",QQ\u673A\u5668\u4EBA:"QQ bot",WhatsApp\u673A\u5668\u4EBA:"WhatsApp bot",WhatsApp\u8D26\u53F7:"WhatsApp account",\u5FAE\u4FE1\u8BBE\u7F6E:"WeChat settings",\u98DE\u4E66\u673A\u5668\u4EBA\u8BBE\u7F6E:"Feishu bot settings",\u7FA4\u804A\u54CD\u5E94\u65B9\u5F0F:"Group response mode","\u4EC5\u5728 @\u673A\u5668\u4EBA\u65F6\u54CD\u5E94\uFF08\u63A8\u8350\uFF09":"Only respond when @mentioned (recommended)",\u54CD\u5E94\u6240\u6709\u7FA4\u6D88\u606F:"Respond to all group messages","\u54CD\u5E94\u6240\u6709\u7FA4\u6D88\u606F\uFF08\u9700\u98DE\u4E66\u654F\u611F\u6743\u9650\uFF09":"Respond to all group messages (requires a sensitive Feishu scope)",\u91CD\u65B0\u6388\u6743:"Reauthorize",\u53BB\u6388\u6743:"Authorize",\u91CD\u65B0\u6388\u6743\u7FA4\u6D88\u606F\u6743\u9650:"Reauthorize group-message permission",\u6388\u6743\u7FA4\u6D88\u606F\u6743\u9650:"Authorize group-message permission","\u6B63\u5728\u51C6\u5907\u6388\u6743\u2026":"Preparing authorization\u2026","\u6B63\u5728\u51C6\u5907\u2026":"Preparing\u2026","\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":"Direct messages always work; group chats require an explicit @mention of this bot. The group-message permission is already granted, so switching again needs no authorization.","\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":"Direct messages always work; group chats require an explicit @mention of this bot. Selecting all messages opens the official Feishu authorization flow.","\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":"The \u201CRead all messages in associated group chat\u201D scope (im:message.group_msg) is granted; the bot processes every visible group message.","\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":"The \u201CRead all messages in associated group chat\u201D scope has not been confirmed. Complete Feishu authorization.","\u79C1\u804A\u59CB\u7EC8\u54CD\u5E94\uFF1B\u7FA4\u804A\u4EC5\u5904\u7406\u660E\u786E @\u5F53\u524D\u673A\u5668\u4EBA\u7684\u6D88\u606F\u3002":"Direct messages always work; group chats require an explicit @mention of this bot.","\u9700\u5728\u98DE\u4E66\u4E3A\u8BE5\u673A\u5668\u4EBA\u5F00\u901A\u201C\u83B7\u53D6\u7FA4\u7EC4\u4E2D\u6240\u6709\u6D88\u606F\u201D\u6743\u9650\uFF08im:message.group_msg\uFF09\uFF1B\u5F00\u901A\u540E\uFF0C\u673A\u5668\u4EBA\u4F1A\u5904\u7406\u7FA4\u804A\u4E2D\u7684\u6240\u6709\u53EF\u89C1\u6D88\u606F\u3002":"Grant this bot the \u201CRead all messages in associated group chat\u201D Feishu scope (im:message.group_msg); once granted, it will process every visible group message.","\u7FA4\u804A\u54CD\u5E94\u65B9\u5F0F\u4FEE\u6539\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002":"Could not update the group response mode. Try again.","\u7FA4\u6D88\u606F\u6743\u9650\u6388\u6743\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002":"Could not authorize group-message permission. Try again.",\u9489\u9489\u8BBE\u7F6E:"DingTalk settings",\u4F01\u4E1A\u5FAE\u4FE1\u8BBE\u7F6E:"WeCom settings",\u626B\u7801\u63A5\u5165\u673A\u5668\u4EBA:"Scan QR code",\u6B63\u5728\u63A5\u5165:"Connecting",\u624B\u52A8\u63A5\u5165:"Manual setup",\u6536\u8D77\u51ED\u636E:"Hide credentials",\u6536\u8D77\u63A5\u5165:"Hide setup",\u63A5\u5165\u673A\u5668\u4EBA:"Connect bot",\u5F00\u59CB\u63A5\u5165:"Start setup",\u5728\u7EBF:"online",\u8FD0\u884C\u6B63\u5E38:"Connected",\u6B63\u5728\u8FDE\u63A5:"Connecting","\u6B63\u5728\u8FDE\u63A5\u2026":"Connecting\u2026",\u8FDE\u63A5\u672A\u5C31\u7EEA:"Not connected",\u8FDE\u63A5\u4E2D:"Connecting",\u8FDE\u63A5\u4E2D\u65AD:"Disconnected",\u9700\u8981\u5904\u7406:"Needs attention",\u72B6\u6001\u672A\u77E5:"Unknown status",\u79BB\u7EBF:"Offline",\u5DF2\u65AD\u5F00:"Disconnected",\u6D88\u606F\u901A\u9053:"Message channel",\u67E5\u770B\u6D88\u606F\u901A\u9053\u8BF4\u660E:"View message channel details",\u6700\u8FD1\u68C0\u67E5:"Last checked",\u6700\u8FD1\u4E00\u6761\u6D88\u606F\u5904\u7406\u5931\u8D25:"Latest message failed",\u9519\u8BEF\u7801:"Code",\u53C2\u8003\u53F7:"Reference",\u5F53\u524D\u5DE5\u4F5C\u533A:"Current workspace",\u9009\u62E9\u76EE\u5F55:"Choose folder",\u9009\u62E9\u673A\u5668\u4EBA\u5DE5\u4F5C\u533A\u76EE\u5F55:"Select bot workspace folder",\u5F53\u524D\u76EE\u5F55:"Current folder",\u4E3B\u76EE\u5F55:"Home",\u76F4\u63A5\u8F93\u5165\u8DEF\u5F84:"Enter a path directly","\u652F\u6301 Windows \u76D8\u7B26\u3001UNC \u4E0E POSIX \u7EDD\u5BF9\u8DEF\u5F84\u3002":"Supports Windows drives, UNC paths, and POSIX absolute paths.","\u8F93\u5165 Host \u4E0A\u7684\u5B8C\u6574\u7EDD\u5BF9\u8DEF\u5F84":"Enter a full absolute path on the Host",\u524D\u5F80:"Go","\u8BFB\u53D6\u4E2D\u2026":"Loading\u2026","\u6B63\u5728\u51C6\u5907\u76EE\u5F55\u9009\u62E9\u5668\u2026":"Preparing folder picker\u2026","\u6B63\u5728\u8BFB\u53D6\u76EE\u5F55\u2026":"Loading folders\u2026","\u8FD9\u4E2A\u76EE\u5F55\u4E2D\u6CA1\u6709\u5B50\u6587\u4EF6\u5939\u3002":"This folder has no subfolders.","\u6B64\u76EE\u5F55\u7684\u5B50\u6587\u4EF6\u5939\u8FC7\u591A\uFF0C\u4EC5\u663E\u793A\u524D\u4E00\u90E8\u5206\u3002":"This folder has too many subfolders; only the first group is shown.","\u65E0\u6CD5\u8BFB\u53D6\u76EE\u5F55\uFF0C\u8BF7\u91CD\u8BD5\u3002":"Could not load the folder. Try again.",\u91CD\u8BD5:"Retry",\u663E\u793A\u9690\u85CF\u6587\u4EF6\u5939:"Show hidden folders","\u5207\u6362\u540E\u4F1A\u6E05\u9664\u8FD9\u4E2A\u673A\u5668\u4EBA\u7684\u65E7\u4F1A\u8BDD\u6620\u5C04\u3002":"Switching clears this bot\u2019s previous session mappings.","\u5207\u6362\u4E2D\u2026":"Switching\u2026",\u9009\u62E9\u6B64\u76EE\u5F55:"Select this folder",\u5DE5\u4F5C\u533A\u7EDD\u5BF9\u8DEF\u5F84:"Absolute workspace path","/\u7EDD\u5BF9\u8DEF\u5F84/\u5230/\u5DE5\u4F5C\u533A":"/absolute/path/to/workspace",\u4FEE\u6539:"Change",\u4FDD\u5B58:"Save",\u4E0A\u4E0B\u6587\u589E\u5F3A:"Context enhancement",\u672A\u5F00\u542F:"Not enabled",\u4EC5\u7FA4\u804A:"Groups only",\u4EC5\u79C1\u804A:"Direct only",\u7FA4\u804A\u548C\u79C1\u804A:"Groups and direct",\u5173\u95ED\u5F39\u7A97:"Close dialog",\u67E5\u770B\u4E0A\u4E0B\u6587\u589E\u5F3A\u8BF4\u660E:"View context enhancement details",\u4E0A\u4E0B\u6587\u589E\u5F3A\u8303\u56F4:"Context enhancement scope",\u542F\u7528\u8303\u56F4:"Enable in",\u542F\u7528:"Enabled","\uFF08\u5F53\u524D\u6E20\u9053\u4E0D\u652F\u6301\u7FA4\u804A\uFF09":"(This channel does not support group chats)",\u6765\u6E90\u5B57\u6BB5:"Source fields",\u67E5\u770B\u6765\u6E90\u5B57\u6BB5\u8BF4\u660E:"View source field details",\u67E5\u770B\u7FA4\u804A\u6765\u6E90\u5B57\u6BB5\u8BF4\u660E:"View group source field details",\u67E5\u770B\u79C1\u804A\u6765\u6E90\u5B57\u6BB5\u8BF4\u660E:"View direct source field details",\u6E20\u9053:"Channel",\u4F1A\u8BDD\u7C7B\u578B:"Conversation type",\u53D1\u9001\u8005\u6807\u8BC6:"Sender ID",\u53D1\u9001\u8005\u6635\u79F0:"Sender name",\u67E5\u770B\u53D1\u9001\u8005\u6635\u79F0\u5B57\u6BB5\u8BF4\u660E:"View sender name field details",\u67E5\u770B\u7FA4\u804A\u53D1\u9001\u8005\u6635\u79F0\u5B57\u6BB5\u8BF4\u660E:"View group sender name field details",\u67E5\u770B\u79C1\u804A\u53D1\u9001\u8005\u6635\u79F0\u5B57\u6BB5\u8BF4\u660E:"View direct sender name field details","\u8BE5\u5B57\u6BB5\u4E0D\u662F\u6BCF\u4E2A\u6E20\u9053\u90FD\u80FD\u63D0\u4F9B\u3002\u5F53\u524D\u6D88\u606F\u6CA1\u6709\u53D1\u9001\u8005\u6635\u79F0\u65F6\uFF0C\u5373\u4F7F\u5DF2\u9009\u62E9\u8BE5\u5B57\u6BB5\uFF0C \u4E2D\u4E5F\u4F1A\u7701\u7565 senderName\u3002":"This field is not available on every channel. If the current message has no sender name, omits senderName even when the field is selected.",\u673A\u5668\u4EBA\u6807\u8BC6:"Bot ID",\u589E\u5F3A\u63D0\u793A\u8BCD:"Guidance",\u67E5\u770B\u589E\u5F3A\u63D0\u793A\u8BCD\u4F7F\u7528\u8BF4\u660E:"View guidance instructions",\u67E5\u770B\u7FA4\u804A\u589E\u5F3A\u63D0\u793A\u8BCD\u4F7F\u7528\u8BF4\u660E:"View group guidance instructions",\u67E5\u770B\u79C1\u804A\u589E\u5F3A\u63D0\u793A\u8BCD\u4F7F\u7528\u8BF4\u660E:"View direct guidance instructions",\u4F7F\u7528\u8BF4\u660E:"How to use","\u7528\u4E8E\u544A\u8BC9\u6A21\u578B\u5982\u4F55\u4F7F\u7528 \u4E2D\u5DF2\u9009\u62E9\u7684\u6765\u6E90\u5B57\u6BB5\u3002\u53EA\u586B\u5199\u6B63\u6587\uFF0C\u63D2\u4EF6\u4F1A\u81EA\u52A8\u6DFB\u52A0 \u6210\u5BF9\u6807\u7B7E\u3002":"Tell the model how to use the selected fields in . Enter only the body; the plugin adds paired tags.",\u751F\u6548\u89C4\u5219:"Behavior",\u9690\u79C1\u63D0\u793A:"Privacy",\u4F7F\u7528\u793A\u4F8B:"Example","\u4EC5\u4F9D\u636E\u5F53\u524D\u6D88\u606F\u7684 \u4E2D\u5B9E\u9645\u63D0\u4F9B\u7684\u5B57\u6BB5\u7406\u89E3\u6765\u6E90\uFF1B\u6CA1\u6709\u63D0\u4F9B\u7684\u5B57\u6BB5\u4E0D\u8981\u731C\u6D4B\u6216\u8865\u5168\u3002\nconversationType\u662F\u7FA4\u804A\u65F6\u56DE\u590D\u4E25\u8083\u4E00\u70B9\uFF0CconversationType\u662F\u79C1\u804A\u65F6\u56DE\u590D\u4E00\u5B9A\u8981\u5E7D\u9ED8\u641E\u7B11\uFF0C\u50CF\u5468\u661F\u9A70\u7684\u7535\u5F71\u4E00\u6837\u641E\u7B11":`Understand the source only from fields actually present in for the current message; do not guess or fill in missing fields. +`,{encoding:"utf8",mode:384}),await PRe(o,this.#e)}};import{randomUUID as c6}from"node:crypto";var rP=new Set(["starting","qr_ready","polling","slow_down","domain_switched","saving"]),KRe=new Set(["polling","slow_down","domain_switched"]),Ri=Object.freeze({IDLE:"idle",STARTING:"starting",QR_READY:"qr_ready",POLLING:"polling",SLOW_DOWN:"slow_down",DOMAIN_SWITCHED:"domain_switched",SAVING:"saving",SUCCEEDED:"succeeded",EXPIRED:"expired",CANCELLED:"cancelled",ERROR:"error"});function i6(n){return["access_denied","expired_token","abort"].includes(n?.code)?n.code:"registration_failed"}function aP(n){let t=i6(n);return{code:t,message:{access_denied:"Registration was denied.",abort:"Registration was cancelled.",expired_token:"The registration QR code expired."}[t]??"Unable to register the Feishu app."}}function xRe(n){let t=Number(n);if(!Number.isFinite(t)||t<=0)throw new TypeError("registerApp onQRCodeReady returned an invalid expireIn");return t}function YRe(n){if(n!==void 0){if(n===null||typeof n!="object"||Array.isArray(n))throw new TypeError("registerApp returned invalid user_info");return{...n}}}var Qg=class{#e;#A;#t;#r;#g;#a=0;#n=null;#i;constructor({registerApp:t,onCredentials:o,now:i=Date.now,setTimeout:e=globalThis.setTimeout,clearTimeout:a=globalThis.clearTimeout}={}){if(typeof t!="function")throw new TypeError("RegistrationManager requires a registerApp function");if(typeof o!="function")throw new TypeError("RegistrationManager requires an onCredentials function");if(typeof i!="function"||typeof e!="function"||typeof a!="function")throw new TypeError("RegistrationManager clock dependencies must be functions");this.#e=t,this.#A=o,this.#t=i,this.#r=e,this.#g=a,this.#i=this.#l(null,Ri.IDLE)}start(t={}){if(t===null||typeof t!="object"||Array.isArray(t))throw new TypeError("Registration options must be an object");this.#B();let o={id:++this.#a,controller:new AbortController,qrCodeUrl:null,expiresAt:null,pollIntervalSeconds:null,expiryTimer:null};this.#n=o,this.#i=this.#l(o,Ri.STARTING);let i={...t,signal:o.controller.signal,onQRCodeReady:a=>this.#s(o,a),onStatusChange:a=>this.#c(o,a)};return Promise.resolve().then(()=>this.#e(i)).then(a=>this.#p(o,a),a=>this.#d(o,a)),this.status()}status(){this.#h();let t={...this.#i};t.error&&(t.error={...t.error});let o=this.#n;return o&&o.expiresAt!==null&&rP.has(t.state)&&(t.remainingSeconds=Math.max(0,Math.ceil((o.expiresAt-this.#t())/1e3))),t}cancel(){let t=this.#n;return t?(this.#C(t,Ri.CANCELLED,{error:{code:"abort",message:"Registration was cancelled."}}),t.controller.abort(),this.status()):this.status()}#I(t){return this.#n===t}#l(t,o,i={}){let e={state:o,attempt:t?.id??this.#a,updatedAt:this.#t(),...i};return t?.qrCodeUrl&&rP.has(o)&&(e.qrCodeUrl=t.qrCodeUrl,e.expiresAt=t.expiresAt),t?.pollIntervalSeconds!==null&&rP.has(o)&&(e.pollIntervalSeconds=t.pollIntervalSeconds),e}#o(t,o,i={}){this.#I(t)&&(this.#i=this.#l(t,o,i))}#s(t,o){if(!this.#I(t))return;if(typeof o?.url!="string"||!o.url)throw new TypeError("registerApp onQRCodeReady returned an invalid URL");let i=xRe(o.expireIn);t.qrCodeUrl=o.url,t.expiresAt=this.#t()+i*1e3,this.#f(t),t.expiryTimer=this.#r(()=>this.#E(t),i*1e3),t.expiryTimer?.unref?.(),this.#o(t,Ri.QR_READY)}#c(t,o){!this.#I(t)||!KRe.has(o?.status)||(o.status===Ri.SLOW_DOWN&&Number.isFinite(Number(o.interval))&&(t.pollIntervalSeconds=Number(o.interval)),this.#o(t,o.status))}async#p(t,o){if(!this.#I(t))return;let i=o?.client_id,e=o?.client_secret;if(typeof i!="string"||!i||typeof e!="string"||!e){this.#C(t,Ri.ERROR,{error:{code:"invalid_credentials",message:"Feishu registration returned invalid credentials."}});return}let a;try{a=YRe(o.user_info)}catch{this.#C(t,Ri.ERROR,{error:{code:"invalid_credentials",message:"Feishu registration returned invalid credentials."}});return}this.#f(t),t.qrCodeUrl=null,t.expiresAt=null,t.pollIntervalSeconds=null,this.#o(t,Ri.SAVING);try{await this.#A({client_id:i,client_secret:e,user_info:a})}catch{this.#I(t)&&this.#C(t,Ri.ERROR,{error:{code:"credentials_callback_failed",message:"Unable to store the Feishu credentials."}});return}this.#I(t)&&this.#C(t,Ri.SUCCEEDED)}#d(t,o){if(!this.#I(t))return;let i=i6(o);if(i==="expired_token"){this.#C(t,Ri.EXPIRED,{error:aP(o)});return}if(i==="abort"){this.#C(t,Ri.CANCELLED,{error:aP(o)});return}this.#C(t,Ri.ERROR,{error:aP(o)})}#h(){let t=this.#n;t&&t.expiresAt!==null&&this.#t()>=t.expiresAt&&this.#E(t)}#E(t){this.#I(t)&&(this.#C(t,Ri.EXPIRED,{error:{code:"expired_token",message:"The registration QR code expired."}}),t.controller.abort())}#C(t,o,i={}){this.#I(t)&&(this.#f(t),this.#i=this.#l(t,o,i),this.#n=null)}#f(t){t.expiryTimer!==null&&(this.#g(t.expiryTimer),t.expiryTimer=null)}#B(){let t=this.#n;t&&(this.#f(t),this.#n=null,t.controller.abort())}};var jRe="card.action.trigger",JRe="im:message:readonly",qRe="im:resource",Bu="callback_repair";function zRe(n){return n==="lark"?"accounts.larksuite.com":"accounts.feishu.cn"}function VRe(n){return n==="lark"?"open.larksuite.com":"open.feishu.cn"}function iP(n,t,o="feishu",i="Feishu app update"){let e;try{e=new URL(n)}catch{throw new Error(`${i} returned an invalid verification URL`)}let a=o==="feishu"||o==="lark",A=e.searchParams.getAll("clientID"),r=e.searchParams.getAll("tp"),s=e.searchParams.getAll("addons"),g=String(t).includes("{{")||String(t).includes("}}")||[...e.searchParams.values()].some(c=>c.includes("{{")||c.includes("}}"));if(!a||e.protocol!=="https:"||e.hostname!==VRe(o)||e.port!==""||e.username!==""||e.password!==""||r.length!==1||r[0]!=="sdk"||A.length!==1||A[0]!==t||e.searchParams.has("createOnly")||s.length!==1||!s[0]?.trim()||g)throw new Error(`${i} returned an unsafe verification URL`);return e.toString()}function WRe(n,t,o="feishu"){return iP(n,t,o,"Feishu callback repair")}var gS=class{#e;#A;#t;constructor({registerApp:t,onCredentials:o,appId:i,domain:e="feishu"}={}){if(typeof t!="function")throw new TypeError("registerApp is required");if(typeof o!="function")throw new TypeError("onCredentials is required");if(typeof i!="string"||!i.trim())throw new TypeError("appId is required");if(e!=="feishu"&&e!=="lark")throw new TypeError("domain is invalid");this.#A=i.trim(),this.#t=e,this.#e=new Qg({registerApp:a=>t({...a,onQRCodeReady:A=>{WRe(A?.url,this.#A,this.#t),a.onQRCodeReady(A)}}),onCredentials:o})}start(){return this.#e.start({source:"deepseek-harness-card-action-repair",domain:zRe(this.#t),appId:this.#A,addons:{preset:!1,scopes:{tenant:[JRe,qRe,...rS]},callbacks:{items:[jRe]}}})}status(){return this.#e.status()}cancel(){return this.#e.cancel()}};var ZRe="im:message.group_msg",IS="group_message_permission";function XRe(n){return n==="lark"?"accounts.larksuite.com":"accounts.feishu.cn"}function $Re(n,t,o="feishu"){return iP(n,t,o,"Feishu group message permission update")}var cS=class{#e;#A;#t;constructor({registerApp:t,onCredentials:o,appId:i,domain:e="feishu"}={}){if(typeof t!="function")throw new TypeError("registerApp is required");if(typeof o!="function")throw new TypeError("onCredentials is required");if(typeof i!="string"||!i.trim())throw new TypeError("appId is required");if(e!=="feishu"&&e!=="lark")throw new TypeError("domain is invalid");this.#A=i.trim(),this.#t=e,this.#e=new Qg({registerApp:a=>t({...a,onQRCodeReady:A=>{$Re(A?.url,this.#A,this.#t),a.onQRCodeReady(A)}}),onCredentials:o})}start(){return this.#e.start({source:"deepseek-harness-group-message-permission",domain:XRe(this.#t),appId:this.#A,addons:{preset:!1,scopes:{tenant:[ZRe]}}})}status(){return this.#e.status()}cancel(){return this.#e.cancel()}};var o6=Object.freeze(["im:message.p2p_msg:readonly","im:message.group_at_msg:readonly","im:message:readonly","im:message:send_as_bot","im:message.reactions:write_only","im:message:recall","im:resource","cardkit:card:write",...rS]);var oP=new Set(["starting","qr_ready","polling","slow_down","domain_switched"]),lS=new Set([...oP,"saving"]),n6=new Set([Bu,IS]),s6="*",eke=12e4,Ake=6e5;function I6(){return{ready:!1,feishuLongConnectionState:"idle",harnessReachable:!1}}function fu(n){return n?n.status:I6()}function Qu(n){return n.ready===!0&&n.feishuLongConnectionState==="connected"&&n.harnessReachable===!0}function l6(n){return n.length>12?`${n.slice(0,8)}\u2022\u2022\u2022\u2022${n.slice(-4)}`:"cli_\u2022\u2022\u2022\u2022"}function tke(n){return{name:n.botName,appIdMasked:l6(n.appId),activated:n.activated,domain:n.domain}}function rke({connected:n,error:t,connection:o}){return n?"connected":t||o.feishuLongConnectionState==="failed"?"error":"disconnected"}function ake(){return`bot_${c6().replaceAll("-","")}`}function ike(){return`reg_${c6().replaceAll("-","")}`}function g6(n){return`DSH_FEISHU_APP_SECRET_${n.slice(4).toUpperCase()}`}function pS(n){return JSON.stringify({id:n.id,appId:n.appId,secretRef:n.secretRef,ownerOpenIds:n.ownerOpenIds,domain:n.domain,botName:n.botName,botOpenId:n.botOpenId,activated:n.activated,groupResponseMode:ro(n.groupResponseMode),groupMessagePermissionGranted:n.groupMessagePermissionGranted===!0,deletionPending:n.deletionPending===!0,connectedAt:n.connectedAt??null,createdAt:n.createdAt??null})}function dS(n){return typeof n=="string"&&n.trim()?n.trim():null}var yu=class{#e;#A;#t;#r;#g;#a;#n;#i;#I=new Map;#l=new Map;#o=new Map;#s=new Map;#c=new Map;#p=null;#d=Promise.resolve();#h=new Map;#E=1;#C;#f=!1;constructor({registerApp:t,verifyApp:o,credentials:i,configStore:e,createRuntime:a,deleteState:A=async()=>{},createBotId:r=ake,createRegistrationId:s=ike,callbackProbeTimeoutMs:g=eke}){if(typeof t!="function")throw new Error("registerApp is required");if(typeof o!="function")throw new Error("verifyApp is required");if(!i)throw new Error("credentials service is required");if(!e||typeof e.list!="function")throw new Error("multi-bot config store is required");if(typeof a!="function")throw new Error("createRuntime is required");if(typeof A!="function")throw new Error("deleteState must be a function");if(!Number.isFinite(g)||g<=0||g>Ake)throw new TypeError("callbackProbeTimeoutMs must be between 1 and 600000ms");this.#e=t,this.#A=o,this.#t=i,this.#r=e,this.#g=a,this.#a=A,this.#n=r,this.#i=s,this.#C=g}async initialize(){if(this.#f)return this.status();let t=this.#r.list(),o=!1;return await Promise.allSettled(t.map(i=>this.#_(i.id,async()=>{let e=fu(this.#I.get(i.id));if(Qu(e)||e.feishuLongConnectionState==="connecting"||e.feishuLongConnectionState==="reconnecting")return;if(o=!0,i.deletionPending){this.#l.set(i.id,{code:"deletion_pending",message:"\u673A\u5668\u4EBA\u6B63\u5728\u7B49\u5F85\u5B8C\u6210\u672C\u5730\u5220\u9664\uFF0C\u8BF7\u91CD\u8BD5\u79FB\u9664\u3002"});return}let a;try{a=await this.#t.resolve(i.secretRef)}catch{this.#l.set(i.id,{code:"missing_credentials",message:"\u65E0\u6CD5\u8BFB\u53D6\u673A\u5668\u4EBA\u51ED\u636E\uFF0C\u8BF7\u68C0\u67E5\u51ED\u636E\u5B58\u50A8\u3002"});return}if(!a?.value){this.#l.set(i.id,{code:"missing_credentials",message:"\u673A\u5668\u4EBA\u51ED\u636E\u7F3A\u5931\uFF0C\u8BF7\u5220\u9664\u540E\u91CD\u65B0\u626B\u7801\u63A5\u5165\u3002"});return}try{await this.#b(i,a.value),this.#l.delete(i.id)}catch{this.#l.set(i.id,{code:"connection_failed",message:"\u673A\u5668\u4EBA\u6682\u65F6\u65E0\u6CD5\u8FDE\u63A5\u98DE\u4E66\uFF0C\u8BF7\u91CD\u8BD5\u3002"})}}))),o&&this.#v(),this.status()}startRegistration(){this.#M();let t=this.#i();if(typeof t!="string"||!/^[A-Za-z0-9_-]{1,128}$/.test(t)||this.#o.has(t))throw new Error("Registration id generator returned an invalid or duplicate id");let o={id:t,manager:null,botId:null,createdNew:!1,cancelled:!1};return o.manager=new Qg({registerApp:this.#e,onCredentials:i=>this.#L(()=>this.#u(o,i))}),this.#o.set(t,o),this.#p=t,this.#U(),o.manager.start({source:"deepseek-harness",createOnly:!0,appPreset:{name:"{user} \u7684\u5317\u6C47\u661F\u6CB3 AI \u52A9\u624B",desc:"\u8FDE\u63A5\u98DE\u4E66\u4E0E DeepSeek Harness\uFF0C\u5728\u804A\u5929\u4E2D\u4F7F\u7528\u4F01\u4E1A AI \u52A9\u624B\u3002"},addons:{preset:!1,scopes:{tenant:[...o6]},events:{items:{tenant:["im.message.receive_v1"]}},callbacks:{items:["card.action.trigger"]}}}),this.#v(),this.registrationStatus(t)}startCallbackRepair(t,{actorOpenId:o,chatId:i}={}){this.#M();let e=this.#k(t);if(e.deletionPending)throw new Error("Cannot repair a Feishu bot pending deletion");let a=this.#s.get(t),A=a?this.#o.get(a):null;if(A&&lS.has(A.manager.status().state)){if(A.operation!==Bu)throw new Error("Another Feishu app update is already active for this bot");return this.registrationStatus(A.id)}this.#s.delete(t);let r=this.#i();if(typeof r!="string"||!/^[A-Za-z0-9_-]{1,128}$/.test(r)||this.#o.has(r))throw new Error("Registration id generator returned an invalid or duplicate id");let s={id:r,operation:Bu,manager:null,botId:t,createdNew:!1,cancelled:!1,remoteCommitted:!1,processing:null,publicError:null,stage:"authorizing",target:structuredClone(e),targetFingerprint:pS(e),initiator:{actorOpenId:dS(o),chatId:dS(i)}};return s.manager=new gS({registerApp:this.#e,appId:e.appId,domain:e.domain,onCredentials:g=>{s.remoteCommitted=!0;let I=this.#Q(s,g).finally(()=>{s.processing===I&&(s.processing=null)});return s.processing=I,I}}),this.#o.set(r,s),this.#s.set(t,r),this.#p=r,this.#U(),s.manager.start(),this.#v(),this.registrationStatus(r)}startGroupMessagePermission(t){this.#M();let o=this.#k(t);if(o.deletionPending)throw new Error("Cannot update permissions for a Feishu bot pending deletion");let i=this.#s.get(t),e=i?this.#o.get(i):null;if(e&&lS.has(e.manager.status().state)){if(e.operation!==IS)throw new Error("Another Feishu app update is already active for this bot");return this.registrationStatus(e.id)}this.#s.delete(t);let a=this.#i();if(typeof a!="string"||!/^[A-Za-z0-9_-]{1,128}$/.test(a)||this.#o.has(a))throw new Error("Registration id generator returned an invalid or duplicate id");let A={id:a,operation:IS,manager:null,botId:t,createdNew:!1,cancelled:!1,remoteCommitted:!1,processing:null,publicError:null,stage:"authorizing",target:structuredClone(o),targetFingerprint:pS(o),initiator:{actorOpenId:null,chatId:null}};return A.manager=new cS({registerApp:this.#e,appId:o.appId,domain:o.domain,onCredentials:r=>{A.remoteCommitted=!0;let g=this.#S(A,r).finally(()=>{A.processing===g&&(A.processing=null)});return A.processing=g,g}}),this.#o.set(a,A),this.#s.set(t,a),this.#p=a,this.#U(),A.manager.start(),this.#v(),this.registrationStatus(a)}hasRegistration(t){return this.#o.has(t)}registrationStatus(t){let o=this.#o.get(t);return o?this.#B({registration:o,selectedBotId:o.botId}):null}async cancelRegistration(t=this.#p){let o=this.#o.get(t);if(!o)return this.status();let i=o.manager.status().state;return n6.has(o.operation)&&i==="saving"?this.registrationStatus(t):lS.has(i)?(o.cancelled=!0,o.manager.cancel(),await this.#L(async()=>{o.createdNew&&o.botId&&this.#c.get(o.botId)===o.id&&this.#r.getBot(o.botId)&&await this.#_(o.botId,()=>this.#w(o.botId))}),this.#v(),this.registrationStatus(t)??this.status()):this.registrationStatus(t)}status(t){return this.#B({registration:this.#o.get(this.#p)??null,selectedBotId:t})}async bindCredentials({appId:t,appSecret:o,domain:i="feishu"}={}){this.#M();let e=typeof t=="string"?t.trim():"",a=typeof o=="string"?o.trim():"",A=i==="lark"?"lark":"feishu";if(!e||!a)throw new TypeError("Feishu App ID and App Secret are required");return this.#L(async()=>{this.#M();let r=await this.#A({appId:e,appSecret:a,domain:A});this.#M();let s=this.#r.list().find(f=>f.appId===e),g=s?.id??this.#n();if(typeof g!="string"||!/^[A-Za-z0-9_-]{1,128}$/.test(g)||!s&&this.#r.getBot(g))throw new Error("Bot id generator returned an invalid or duplicate id");let c=s?.secretRef??g6(g),I=await this.#t.resolve(c).catch(()=>{}),d={...s,id:g,appId:e,secretRef:c,ownerOpenIds:s?.ownerOpenIds?.length?s.ownerOpenIds:[s6],domain:A,botName:r.name,botOpenId:r.openId,activated:r.activated,deletionPending:!1,connectedAt:new Date().toISOString(),createdAt:s?.createdAt??new Date().toISOString()};await this.#t.set(c,a);let B;try{B=await this.#r.saveBot(d)}catch(f){throw await this.#T(c,I),f}return await this.#_(g,async()=>{try{await this.#b(B,a),this.#l.delete(g)}catch{this.#l.set(g,{code:"connection_failed",message:"\u673A\u5668\u4EBA\u5DF2\u7ECF\u7ED1\u5B9A\uFF0C\u4F46\u957F\u8FDE\u63A5\u672A\u5C31\u7EEA\uFF0C\u8BF7\u70B9\u51FB\u91CD\u8BD5\u3002"})}}),this.#v(),this.status(g)})}async reconnectBot(t){return this.#M(),this.#_(t,async()=>{let o=this.#k(t);if(o.deletionPending)return this.#l.set(t,{code:"deletion_pending",message:"\u673A\u5668\u4EBA\u6B63\u5728\u7B49\u5F85\u5B8C\u6210\u672C\u5730\u5220\u9664\uFF0C\u8BF7\u91CD\u8BD5\u79FB\u9664\u3002"}),this.status(t);if(Qu(fu(this.#I.get(t))))return this.status(t);let i;try{i=await this.#t.resolve(o.secretRef)}catch{i=null}if(!i?.value)return this.#l.set(t,{code:"missing_credentials",message:"\u673A\u5668\u4EBA\u51ED\u636E\u7F3A\u5931\uFF0C\u8BF7\u5220\u9664\u540E\u91CD\u65B0\u626B\u7801\u63A5\u5165\u3002"}),this.#v(),this.status(t);try{await this.#b(o,i.value),this.#l.delete(t)}catch{this.#l.set(t,{code:"connection_failed",message:"\u673A\u5668\u4EBA\u6682\u65F6\u65E0\u6CD5\u8FDE\u63A5\u98DE\u4E66\uFF0C\u8BF7\u91CD\u8BD5\u3002"})}return this.#v(),this.status(t)})}async sendConnectionTest(t){return this.#M(),this.#_(t,async()=>{let o=this.#k(t),i=this.#I.get(t);if(!Qu(fu(i))||typeof i.sendConnectionTest!="function"){let e=new Error("\u98DE\u4E66\u673A\u5668\u4EBA\u5C1A\u672A\u8FDE\u63A5");throw e.code="test-target-unavailable",e}return i.sendConnectionTest(ba(`${o.botName}\uFF08${l6(o.appId)}\uFF09`,"\u98DE\u4E66\u673A\u5668\u4EBA"))})}async sendProactiveText(t,o,i,e={}){return this.#M(),this.#_(t,async()=>{this.#k(t);let a=this.#I.get(t);if(!Qu(fu(a))||typeof a.sendProactiveText!="function"){let A=new Error("\u98DE\u4E66\u673A\u5668\u4EBA\u5C1A\u672A\u8FDE\u63A5");throw A.code="bot-not-connected",A}return a.sendProactiveText(o,i,e)})}async disconnectBot(t){return this.#M(),this.#_(t,async()=>(this.#k(t),await this.#D(t),this.#l.delete(t),this.#v(),this.status(t)))}async updateGroupResponseMode(t,o){if(this.#M(),!zm(o))throw new TypeError("Invalid Feishu group response mode");return this.#L(()=>this.#_(t,async()=>{let i=this.#k(t);if(o==="all"&&i.groupMessagePermissionGranted!==!0){let a=new Error("Authorize im:message.group_msg before enabling all group messages");throw a.code="group_message_permission_required",a}let e=await this.#r.saveBot({...i,groupResponseMode:o});return this.#I.get(t)?.setGroupResponseMode?.(e.groupResponseMode),this.#v(),this.status(t)}))}async deleteBot(t){return this.#M(),this.#L(()=>this.#_(t,async()=>(this.#k(t),await this.#w(t),this.#v(),this.status())))}async reconnect(){let t=this.#r.list()[0];return t?this.reconnectBot(t.id):this.status()}async disconnect(){let t=this.#r.list()[0];return t?this.deleteBot(t.id):this.status()}async close(){if(this.#f)return;this.#f=!0;let t=[];for(let o of this.#o.values()){let i=o.manager.status().state;n6.has(o.operation)&&i==="saving"?(o.manager.cancel(),o.processing&&t.push(o.processing)):lS.has(i)&&(o.cancelled=!0,o.manager.cancel())}await this.#d,await Promise.allSettled([...this.#h.values()]),await Promise.allSettled([...this.#I.keys()].map(o=>this.#D(o))),await Promise.allSettled(t),await this.#d,await Promise.allSettled([...this.#h.values()]),await Promise.allSettled([...this.#I.keys()].map(o=>this.#D(o)))}#B({registration:t,selectedBotId:o}={}){let i=this.#r.list().map(I=>{let d=fu(this.#I.get(I.id)),B=Qu(d),f=this.#l.get(I.id)??null;return{botId:I.id,phase:rke({connected:B,error:f,connection:d}),connected:B,configured:!0,groupResponseMode:ro(I.groupResponseMode),groupMessagePermissionGranted:I.groupMessagePermissionGranted===!0,bot:tke(I),connection:d,lastMessageError:Vr(d.lastMessageError),error:f}}),e=t?this.#y(t):{state:"idle",attempt:0,updatedAt:Date.now()},a=oP.has(e.state),A=e.state==="saving",r=!!t&&(a||A),s=i.find(I=>I.botId===o)??(r?null:i[0]??null),g=i.some(I=>I.connected),c=s?.phase??"unconfigured";return a?c="registering":A?c="connecting":e.state==="error"&&!s&&(c="error"),{schemaVersion:2,revision:this.#E,phase:c,connected:s?.connected??!1,configured:i.length>0,bot:s?.bot??null,connection:s?.connection??I6(),error:s?.error??e.error??null,registration:e,bots:i,totals:{configured:i.length,connected:i.filter(I=>I.connected).length},anyConnected:g}}#y(t){let o=t.manager.status();return{...o,attempt:t.id,...t.botId?{botId:t.botId}:{},...t.operation?{operation:t.operation}:{},...t.stage?{stage:t.stage}:{},...o.state==="error"&&t.publicError?{error:{...t.publicError}}:{}}}async#S(t,o){let i=o.client_id,e=o.client_secret,a=dS(o.user_info?.open_id),A=o.user_info?.tenant_brand,r=t.target;if(t.cancelled)throw this.#m(t,"abort","Group message permission update was cancelled before local activation.");if(i!==r.appId)throw this.#m(t,"repair_app_mismatch","Feishu returned credentials for a different app.");if(!a)throw this.#m(t,"repair_owner_missing","Feishu returned no permission operator identity.");if(A!==void 0&&A!==r.domain)throw this.#m(t,"repair_domain_mismatch","Feishu returned credentials for a different account domain.");if(!r.ownerOpenIds.includes(s6)&&!r.ownerOpenIds.includes(a))throw this.#m(t,"repair_owner_mismatch","The Feishu permission operator is not an owner of this configured bot.");t.stage="verifying_identity";let s;try{s=await this.#A({appId:i,appSecret:e,domain:r.domain})}catch(g){throw this.#m(t,"repair_credentials_invalid","Feishu could not verify the updated app credentials.",g)}if(r.botOpenId&&s?.openId!==r.botOpenId)throw this.#m(t,"repair_bot_mismatch","The updated Feishu app belongs to a different bot identity.");await this.#L(()=>this.#_(t.botId,async()=>{let g=this.#r.getBot(t.botId);if(!g||g.deletionPending||pS(g)!==t.targetFingerprint)throw this.#m(t,"repair_target_changed","The Feishu bot changed while its permission update was in progress.");let c;try{c=await this.#t.resolve(g.secretRef)}catch(d){throw this.#m(t,"credential_update_failed","Unable to read the current Feishu credential.",d)}if(c?.value!==e){t.stage="persisting_secret";try{await this.#t.set(g.secretRef,e)}catch(B){if((await this.#t.resolve(g.secretRef).catch(()=>null))?.value!==e)throw this.#m(t,"credential_update_failed","Unable to store the updated Feishu credential.",B)}if((await this.#t.resolve(g.secretRef).catch(()=>null))?.value!==e)throw this.#m(t,"credential_state_unknown","The updated Feishu credential could not be confirmed after writing.")}t.stage="enabling_all_messages";let I;try{I=await this.#r.saveBot({...g,groupMessagePermissionGranted:!0,groupResponseMode:"all"})}catch(d){throw this.#m(t,"group_message_permission_save_failed","The permission was accepted, but all-message mode could not be saved.",d)}t.stage="restarting";try{await this.#b(I,e)}catch(d){throw this.#l.set(t.botId,{code:"connection_failed",message:"\u7FA4\u6D88\u606F\u6743\u9650\u5DF2\u5F00\u901A\uFF0C\u4F46\u673A\u5668\u4EBA\u957F\u8FDE\u63A5\u672A\u5C31\u7EEA\uFF0C\u8BF7\u70B9\u51FB\u91CD\u8BD5\u3002"}),this.#v(),this.#m(t,"group_message_permission_connection_failed","The permission was accepted, but the Feishu runtime could not restart.",d)}this.#l.delete(t.botId),t.stage="verified",t.publicError=null,this.#v()}))}async#Q(t,o){let i=o.client_id,e=o.client_secret,a=dS(o.user_info?.open_id),A=o.user_info?.tenant_brand,r=t.target;if(t.cancelled)throw this.#m(t,"abort","Callback repair was cancelled before local activation.");if(i!==r.appId)throw this.#m(t,"repair_app_mismatch","Feishu returned credentials for a different app.");if(!a)throw this.#m(t,"repair_owner_missing","Feishu returned no repair operator identity.");if(A!==void 0&&A!==r.domain)throw this.#m(t,"repair_domain_mismatch","Feishu returned credentials for a different account domain.");t.stage="verifying_identity";let s;try{s=await this.#A({appId:i,appSecret:e,domain:r.domain})}catch(c){throw this.#m(t,"repair_credentials_invalid","Feishu could not verify the repaired app credentials.",c)}if(r.botOpenId&&s?.openId!==r.botOpenId)throw this.#m(t,"repair_bot_mismatch","The repaired Feishu app belongs to a different bot identity.");let g=await this.#L(()=>this.#_(t.botId,async()=>{let c=this.#r.getBot(t.botId);if(!c||c.deletionPending||pS(c)!==t.targetFingerprint)throw this.#m(t,"repair_target_changed","The Feishu bot changed while its callback repair was in progress.");let I;try{I=await this.#t.resolve(c.secretRef)}catch(f){throw this.#m(t,"credential_update_failed","Unable to read the current Feishu credential.",f)}if(I?.value!==e){t.stage="persisting_secret";try{await this.#t.set(c.secretRef,e)}catch(Q){if((await this.#t.resolve(c.secretRef).catch(()=>null))?.value!==e)throw this.#m(t,"credential_update_failed","Unable to store the repaired Feishu credential.",Q)}if((await this.#t.resolve(c.secretRef).catch(()=>null))?.value!==e)throw this.#m(t,"credential_state_unknown","The repaired Feishu credential could not be confirmed after writing.")}let B;t.stage="restarting";try{await this.#b(c,e),B=this.#I.get(t.botId)}catch(f){throw this.#l.set(t.botId,{code:"connection_failed",message:"\u673A\u5668\u4EBA\u56DE\u8C03\u4FEE\u590D\u5DF2\u4FDD\u5B58\uFF0C\u4F46\u957F\u8FDE\u63A5\u672A\u5C31\u7EEA\uFF0C\u8BF7\u70B9\u51FB\u91CD\u8BD5\u3002"}),this.#v(),this.#m(t,"repair_connection_failed","The repaired Feishu runtime could not be started.",f)}if(!B)throw this.#m(t,"repair_connection_failed","The repaired Feishu runtime is unavailable.");return this.#l.delete(t.botId),this.#v(),B}));if(typeof g.beginCardActionProbe!="function")throw this.#m(t,"card_action_probe_unavailable","The Feishu runtime cannot verify card callbacks.");t.stage="awaiting_callback";try{if((await g.beginCardActionProbe({expectedOperatorOpenId:t.initiator.actorOpenId??a,timeoutMs:this.#C,...t.initiator.chatId?{chatId:t.initiator.chatId}:{}}))?.verified!==!0){let I=new Error("Feishu runtime returned no callback proof");throw I.code="card_action_probe_failed",I}}catch(c){let I=c?.code==="card_action_probe_timeout"?"card_action_probe_timeout":c?.code==="card_action_probe_unavailable"?"card_action_probe_unavailable":c?.code==="card_action_probe_send_failed"?"card_action_probe_send_failed":"card_action_probe_failed";throw this.#m(t,I,I==="card_action_probe_timeout"?"Timed out waiting for the Feishu callback verification button.":"The Feishu card callback probe failed.",c)}t.stage="verified",t.publicError=null,this.#v()}#m(t,o,i,e){t.publicError={code:o,message:i};let a=new Error(i,e?{cause:e}:void 0);return a.code=o,a}async#u(t,o){if(t.cancelled)throw new Error("Registration was cancelled");let i=o.client_id,e=o.client_secret,a=o.user_info?.open_id,A=o.user_info?.tenant_brand==="lark"?"lark":"feishu";if(!a)throw new Error("Feishu registration returned no owner open_id");let r=await this.#A({appId:i,appSecret:e,domain:A});if(t.cancelled)throw new Error("Registration was cancelled");let s=this.#r.list().find(Q=>Q.appId===i),g=s?.id??this.#n();if(typeof g!="string"||!/^[A-Za-z0-9_-]{1,128}$/.test(g)||!s&&this.#r.getBot(g))throw new Error("Bot id generator returned an invalid or duplicate id");let c=s?.secretRef??g6(g),I=this.#c.get(g),d=await this.#t.resolve(c).catch(()=>{});await this.#t.set(c,e);let B;try{B=await this.#r.saveBot({...s,id:g,appId:i,secretRef:c,ownerOpenIds:[...new Set([...s?.ownerOpenIds??[],a])],domain:A,botName:r.name,botOpenId:r.openId,activated:r.activated,deletionPending:!1,connectedAt:new Date().toISOString(),createdAt:s?.createdAt??new Date().toISOString()}),t.botId=g,t.createdNew=!s,this.#c.set(g,t.id)}catch(Q){try{await this.#T(c,d)}catch(y){throw new Error("Unable to restore the Feishu credential after a config failure.",{cause:y})}throw Q}if(t.cancelled)throw t.createdNew?await this.#_(g,()=>this.#w(g)):(await this.#r.saveBot(s),await this.#T(c,d),I?this.#c.set(g,I):this.#c.delete(g)),new Error("Registration was cancelled");let f=!1;try{if(await this.#_(g,()=>this.#b(B,e)),t.cancelled)throw t.createdNew?await this.#_(g,()=>this.#w(g)):(await this.#r.saveBot(s),await this.#T(c,d),I?this.#c.set(g,I):this.#c.delete(g),d?.value&&!s.deletionPending?await this.#_(g,()=>this.#b(s,d.value)):await this.#_(g,()=>this.#D(g))),f=!0,new Error("Registration was cancelled");this.#l.delete(g),this.#v()}catch(Q){if(t.cancelled)throw!f&&t.createdNew&&this.#r.getBot(g)?await this.#_(g,()=>this.#w(g)):!f&&s&&(await this.#r.saveBot(s),await this.#T(c,d),I?this.#c.set(g,I):this.#c.delete(g),!this.#f&&d?.value&&!s.deletionPending?await this.#_(g,()=>this.#b(s,d.value)):await this.#_(g,()=>this.#D(g))),this.#v(),Q;if(s&&d?.value)try{throw await this.#r.saveBot(s),await this.#T(c,d),I?this.#c.set(g,I):this.#c.delete(g),s.deletionPending?(await this.#_(g,()=>this.#D(g)),this.#l.set(g,{code:"deletion_pending",message:"\u673A\u5668\u4EBA\u6B63\u5728\u7B49\u5F85\u5B8C\u6210\u672C\u5730\u5220\u9664\uFF0C\u8BF7\u91CD\u8BD5\u79FB\u9664\u3002"})):(await this.#_(g,()=>this.#b(s,d.value)),this.#l.delete(g)),this.#v(),Q}catch(y){throw y===Q?Q:(this.#l.set(g,{code:"connection_failed",message:"\u673A\u5668\u4EBA\u8FDE\u63A5\u66F4\u65B0\u5931\u8D25\uFF0C\u4E14\u539F\u8FDE\u63A5\u65E0\u6CD5\u6062\u590D\uFF0C\u8BF7\u91CD\u8BD5\u3002"}),this.#v(),new Error("Unable to restore the previous Feishu bot connection.",{cause:y}))}throw this.#l.set(g,{code:"connection_failed",message:"\u673A\u5668\u4EBA\u5DF2\u7ECF\u521B\u5EFA\uFF0C\u4F46\u957F\u8FDE\u63A5\u672A\u5C31\u7EEA\uFF0C\u8BF7\u70B9\u51FB\u91CD\u8BD5\u3002"}),this.#v(),Q}}async#b(t,o){await this.#D(t.id);let i=await this.#g({botId:t.id,config:t,appSecret:o,repair:this.#R(t.id)});this.#I.set(t.id,i);try{await i.start()}catch(e){throw this.#I.get(t.id)===i&&this.#I.delete(t.id),await i.stop({preserveError:!0}).catch(()=>{}),e}}#R(t){let o=i=>{let e=this.#o.get(i);return e?.operation===Bu&&e.botId===t?e:null};return Object.freeze({start:({actorOpenId:i,chatId:e}={})=>this.startCallbackRepair(t,{actorOpenId:i,chatId:e}),status:({attemptId:i}={})=>o(i)?this.registrationStatus(i):null,cancel:async({attemptId:i}={})=>o(i)?this.cancelRegistration(i):this.status(t)})}async#D(t){let o=this.#I.get(t);this.#I.delete(t),o&&await o.stop()}async#w(t){let o=this.#r.getBot(t);if(o){o.deletionPending||(o=await this.#r.saveBot({...o,deletionPending:!0})),await this.#D(t);try{await this.#t.unset(o.secretRef)}catch(i){throw this.#l.set(t,{code:"credential_removal_failed",message:"\u65E0\u6CD5\u5220\u9664\u673A\u5668\u4EBA\u51ED\u636E\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002"}),new Error("Unable to remove the Feishu credential.",{cause:i})}try{await this.#a({botId:t,config:o})}catch(i){throw this.#l.set(t,{code:"state_cleanup_failed",message:"\u65E0\u6CD5\u5220\u9664\u673A\u5668\u4EBA\u7684\u672C\u5730\u4F1A\u8BDD\u6570\u636E\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002"}),new Error("Unable to remove the Feishu bot session state.",{cause:i})}await this.#r.removeBot(t),this.#l.delete(t),this.#c.delete(t)}}async#T(t,o){o?.value?await this.#t.set(t,o.value):await this.#t.unset(t)}#k(t){let o=this.#r.getBot(t);if(!o)throw new Error("Unknown Feishu bot");return o}#M(){if(this.#f)throw new Error("The Feishu controller is closed")}#L(t){let o=this.#d.then(t,t);return this.#d=o.then(()=>{},()=>{}),o}#_(t,o){let e=(this.#h.get(t)??Promise.resolve()).then(o,o),a=e.then(()=>{},()=>{});return this.#h.set(t,a),a.finally(()=>{this.#h.get(t)===a&&this.#h.delete(t)}),e}#U(){if(!(this.#o.size<=32))for(let[t,o]of this.#o){if(t===this.#p)continue;let i=o.manager.status().state;if(!oP.has(i)&&i!=="saving"&&this.#o.delete(t),this.#o.size<=32)break}}#v(){this.#E+=1}};function ske(n){for(let t of["https_proxy","HTTPS_PROXY","http_proxy","HTTP_PROXY"]){let o=n?.[t];if(typeof o=="string"&&o.trim())return o.trim()}}function gke(n,t=o=>new p6.default(o)){let o=ske(n);return o?t(o):void 0}function cke(n){let t=mc(n.dshHome??process.env.DSH_HOME??_d(oke(),".dsh")),o=mc(n.dataDir??_d(t,"integrations","dsh-feishu"));return{root:o,config:mc(n.configPath??_d(o,"config.json")),legacyState:mc(n.statePath??_d(o,"state.json")),bots:mc(n.botsDir??_d(o,"bots")),workspaces:mc(n.workspacesPath??_d(o,"workspaces.json"))}}async function nP(n,t={},o={}){if(!n?.credentials)throw new TypeError("dsh-feishu requires ctx.credentials");let i=Aa(n,t),e=o.lark??Hv,a=o.Controller??yu,A=o.ConfigStore??nS,r=o.StateStore??Jm,s=o.HarnessClient??iS,g=o.FeishuRuntime??aS,c=o.verifyFeishuApp??QW,I=fe=>c({...fe,httpInstance:e.defaultHttpInstance}),d=o.createConnectionSupervisor??Kv,B=typeof n.logger=="function"?n.logger("dsh-feishu"):n.logger??console,f=()=>Ra(n),Q=cke(t),y=await new A(Q.config).load(),m=mc(t.workspace??process.cwd()),_=o.WorkspaceStore??ea,T=o.workspaces??await new _(Q.workspaces,{defaultWorkspace:m}).load(),R=typeof y.list=="function",H=()=>R?y.list():[],K=H();R&&await T.reconcile(K.map(fe=>fe.id)),await Promise.all(K.map(fe=>T.ensure(fe.id,{defaultAgentPreset:t.agentPreset})));let F=typeof y.removeBot=="function"?Pa(y,{workspaces:T,method:"removeBot",botIdFromRemoved:fe=>fe.id,saveMethod:"saveBot",botIdFromSave:fe=>fe?.id}):y,q=new Map,z=fe=>!fe.id||!fe.secretRef||fe.secretRef===sS?Q.legacyState:mc(Q.bots,fe.id,"state.json"),x=async fe=>{let Se=fe.id??"__legacy__",Ke=q.get(Se);return Ke||(Ke=await new r(z(fe)).load(),q.set(Se,Ke)),Ke},G=async fe=>{let Se=H().find(Ke=>Ke.id===fe);if(!Se)throw new Error("Unknown Feishu bot");return x(Se)},J=Ua(n,o.commandExecutor),{controlExecutor:L,sessionMaintenanceExecutor:N,fileIngressExecutor:k}=Ha(n,{controlExecutor:o.controlExecutor,sessionMaintenanceExecutor:o.sessionMaintenanceExecutor,fileIngressExecutor:o.fileIngressExecutor}),V=new s({...i,workspace:m,autostart:!1,dshBin:t.dshBin??"dsh",...J?{commandExecutor:J}:{},...L?{controlExecutor:L}:{},...N?{sessionMaintenanceExecutor:N}:{},...k?{fileIngressExecutor:k}:{}}),te=o.proxyEnv??process.env,ie=gke(te,o.createProxyAgent),we=new a({registerApp:fe=>e.registerApp(fe),verifyApp:I,credentials:n.credentials,configStore:F,createRuntime:async({botId:fe,config:Se,appSecret:Ke,repair:rA})=>{let ve=await x(Se),aA=fe??Se.id??Se.appId;await T.ensure(aA,{defaultAgentPreset:t.agentPreset});let Ce=Ga(V,{botId:aA,workspaces:T,state:ve,agentPresetCatalog:f});return new g({lark:e,botId:aA,repair:rA,appId:Se.appId,appSecret:Ke,domain:Se.domain,botOpenId:Se.botOpenId,groupResponseMode:Se.groupResponseMode,ownerOpenIds:Se.ownerOpenIds??[Se.ownerOpenId],harness:Ce.harness,state:Ce.state,contextEnhancement:{botId:aA,getSettings:()=>T.contextEnhancementFor(aA)},replyTimeoutMs:t.replyTimeoutMs??6e5,slashCommands:t.slashCommands!==!1,...ie?{wsAgent:ie}:{},logger:{error:(...he)=>B.error?.(`[${fe??Se.id}]`,...he),warn:(...he)=>B.warn?.(`[${fe??Se.id}]`,...he),info:(...he)=>B.info?.(`[${fe??Se.id}]`,...he),debug:(...he)=>B.debug?.(`[${fe??Se.id}]`,...he)}})},deleteState:async({botId:fe,config:Se})=>{q.delete(fe);try{await nke(z(Se))}catch(Ke){if(Ke?.code!=="ENOENT")throw Ke}}}),se=Fa(we,{workspaces:T,stateFor:G,agentPresetCatalog:f}),Ne=d({controller:se,harness:V,logger:B,retryDelaysMs:t.retryDelaysMs,healthyIntervalMs:t.healthyIntervalMs}).start();return{controller:se,deliveryAdapter:La({channel:"feishu",workspaces:T,coreController:we,stateFor:G}),ready:Ne.ready,async close(){await Ne.close(),await se.close(),V.stopManagedProcess(),ie?.destroy?.()}}}import Mke from"qrcode";var Nd=qe(dP(),1);var Dke=qe(dP(),1);var Oke=Object.freeze({$locale:"en",IM\u673A\u5668\u4EBA:"IM bots",IM\u673A\u5668\u4EBA\u8BBE\u7F6E:"IM bot settings",\u66F4\u591A\u673A\u5668\u4EBA\u8BBE\u7F6E:"More bot settings",\u673A\u5668\u4EBA\u8BBE\u7F6E:"Bot settings",\u673A\u5668\u4EBA\u8BBE\u7F6E\u9875\u7B7E:"Bot settings tabs",\u6295\u9012\u8BBE\u7F6E:"Delivery settings","IM \u6E20\u9053":"IM channels","\u8BA9 DeepSeek Harness \u89E6\u624B\u53EF\u53CA":"Connecting DeepSeek Harness",\u5F53\u524D\u7248\u672C:"Current version","DSH-IM \u66F4\u65B0":"DSH-IM update",\u68C0\u67E5\u66F4\u65B0:"Check for updates",\u91CD\u65B0\u68C0\u67E5:"Check again",\u5237\u65B0\u72B6\u6001:"Refresh status",\u624B\u5DE5\u66F4\u65B0:"Manual update",\u624B\u5DE5\u66F4\u65B0\u547D\u4EE4:"Manual update command",\u590D\u5236\u547D\u4EE4:"Copy command","\u590D\u5236\u4E2D\u2026":"Copying\u2026",\u5DF2\u590D\u5236:"Copied","\u547D\u4EE4\u5DF2\u590D\u5236\u3002":"Command copied.","\u590D\u5236\u5931\u8D25\uFF0C\u8BF7\u9009\u4E2D\u547D\u4EE4\u540E\u6309 Ctrl+C \u6216 \u2318C \u590D\u5236\u3002":"Copy failed. Select the command and press Ctrl+C or \u2318C to copy it.","\u81EA\u52A8\u66F4\u65B0\u5931\u8D25\u53EF\u4EE5\u4F7F\u7528\u547D\u4EE4\u66F4\u65B0\uFF1A":"If the automatic update fails, update with this command:","\u5C1A\u672A\u786E\u8BA4\u76EE\u6807\u7248\u672C\uFF0C\u6B64\u547D\u4EE4\u5B89\u88C5\u6267\u884C\u65F6 npm \u7684 latest \u7248\u672C\u3002":"No target version has been confirmed. This command installs the npm latest version available when you run it.","\u8BF7\u5728\u5F53\u524D Desktop \u7684\u5185\u7F6E\u7EC8\u7AEF\u6267\u884C\uFF0C\u5B8C\u6210\u540E\u624B\u52A8\u91CD\u542F\u3002":"Run this in the current Desktop\u2019s built-in terminal, then restart manually.","\u8BF7\u5728\u8FD0\u884C\u5F53\u524D Harness \u7684\u540C\u4E00\u73AF\u5883\u6267\u884C\uFF0C\u4FDD\u6301 DSH_HOME \u4E00\u81F4\uFF0C\u5B8C\u6210\u540E\u624B\u52A8\u91CD\u542F\u3002":"Run this in the same environment as the current Harness, with the same DSH_HOME, then restart manually.","\u8BF7\u7B49\u5F85\u5F53\u524D\u64CD\u4F5C\u7ED3\u675F\uFF0C\u786E\u8BA4\u6CA1\u6709\u5B89\u88C5\u8FDB\u7A0B\u8FD0\u884C\u540E\u518D\u6267\u884C\u547D\u4EE4\u3002":"Wait for the current operation to finish and confirm no installer is running before using this command.","\u6E90\u7801\u6216\u94FE\u63A5\u5B89\u88C5\u8BF7\u6309\u539F\u5B89\u88C5\u65B9\u5F0F\u66F4\u65B0\uFF0C\u4E0D\u63D0\u4F9B\u8986\u76D6\u6E90\u7801\u7684 npm \u547D\u4EE4\u3002":"Update source or linked installations using their original method. No npm command is provided to overwrite the source.","\u65E0\u6CD5\u5B89\u5168\u751F\u6210\u5F53\u524D profile \u7684\u547D\u4EE4\uFF0C\u8BF7\u5728\u7EC8\u7AEF\u4E2D\u624B\u52A8\u786E\u8BA4 profile \u540E\u66F4\u65B0\u3002":"A safe command cannot be generated for this profile. Confirm the profile in your terminal before updating manually.",\u66F4\u65B0\u81F3:"Update to",\u5B89\u88C5\u66F4\u65B0:"Install update","\u6B63\u5728\u66F4\u65B0\u2026":"Updating\u2026",\u5F85\u624B\u52A8\u91CD\u542F:"Restart needed",\u8FD0\u884C\u7248\u672C:"Running version",\u5DF2\u5B89\u88C5\u7248\u672C:"Installed version","\u76EE\u6807 profile":"Target profile",\u76EE\u6807\u7248\u672C:"Target version",\u65E0\u6CD5\u786E\u8BA4:"Unknown",\u53D1\u73B0\u65B0\u7248\u672C:"Update available",\u5DF2\u662F\u6700\u65B0\u7248\u672C:"Up to date",\u5F53\u524D\u7248\u672C\u65E0\u9700\u66F4\u65B0:"No newer version available","\u5DF2\u83B7\u53D6 npm \u6700\u65B0\u7248\u672C":"Latest npm version retrieved","\u68C0\u67E5 npm \u6700\u65B0\u7248\u672C\uFF0C\u4E0D\u4F1A\u81EA\u52A8\u5B89\u88C5\u3002":"Check the latest npm version. Nothing is installed automatically.","\u6B63\u5728\u4ECE npm \u68C0\u67E5\u6700\u65B0\u7248\u672C\u2026":"Checking npm for the latest version\u2026","\u6B63\u5728\u5B89\u88C5\uFF0C\u8BF7\u7A0D\u5019\u2026":"Installing, please wait\u2026","\u6B63\u5728\u6821\u9A8C\u5B89\u88C5\u7ED3\u679C\u2026":"Verifying the installation\u2026","\u5DF2\u5B89\u88C5\uFF0C\u5F85\u624B\u52A8\u91CD\u542F":"Installed \u2014 manual restart needed",\u66F4\u65B0\u5DF2\u751F\u6548:"Updated version is active",\u66F4\u65B0\u5931\u8D25:"Update failed",\u66F4\u65B0\u8BF7\u6C42\u5931\u8D25:"Update request failed","\u4EC5\u66F4\u65B0 DSH-IM\u3002\u5B89\u88C5\u5B8C\u6210\u540E\u9700\u624B\u52A8\u91CD\u542F\u540E\u53F0\uFF1B\u672C\u529F\u80FD\u4E0D\u4F1A\u81EA\u52A8\u91CD\u542F\u6216\u4E3B\u52A8\u5237\u65B0\u9875\u9762\u3002":"Only DSH-IM will be updated. Restart the backend manually after installation. This feature does not automatically restart it or initiate a page refresh.","\u5F53\u524D\u662F\u6E90\u7801\u6216\u94FE\u63A5\u5B89\u88C5\uFF0C\u53EA\u80FD\u68C0\u67E5\u7248\u672C\uFF1B\u8BF7\u624B\u52A8\u66F4\u65B0\u6E90\u7801\uFF0C\u6216\u8FC1\u79FB\u5230 npm \u5B89\u88C5\u3002":"This is a source or linked installation. You can check versions, but must update the source manually or migrate to an npm installation.","\u65E0\u6CD5\u786E\u8BA4\u5F53\u524D profile\uFF0C\u8BF7\u5728\u5BF9\u5E94\u7684 Harness \u73AF\u5883\u4E2D\u624B\u52A8\u66F4\u65B0\u3002":"The current profile could not be identified. Update manually in the corresponding Harness environment.","\u5F53\u524D\u8FD0\u884C\u73AF\u5883\u4E0D\u652F\u6301\u6309\u94AE\u5B89\u88C5\uFF0C\u8BF7\u624B\u52A8\u66F4\u65B0\u63D2\u4EF6\u3002":"This runtime cannot install through this button. Update the plugin manually.","\u5F53\u524D npm \u6E90\u914D\u7F6E\u4E0E\u5B98\u65B9\u6E90\u4E0D\u4E00\u81F4\uFF0C\u8BF7\u5148\u68C0\u67E5 registry \u914D\u7F6E\u3002":"The npm registry configuration differs from the official registry. Check your registry settings first.","\u5F53\u524D Host \u7684 Node.js \u7248\u672C\u4E0D\u6EE1\u8DB3\u65B0\u7248\u8981\u6C42\uFF0C\u8BF7\u5148\u66F4\u65B0\u8FD0\u884C\u73AF\u5883\u3002":"The Host Node.js version does not meet the new version\u2019s requirements. Update the runtime first.","\u65B0\u7248\u672C\u5DF2\u5B89\u88C5\uFF0C\u8BF7\u5728\u65B9\u4FBF\u65F6\u624B\u52A8\u91CD\u542F\u5F53\u524D Harness \u6216 Desktop\u3002":"The new version is installed. Manually restart this Harness or Desktop when convenient.","\u4E0A\u6B21\u5B89\u88C5\u7ED3\u679C\u65E0\u6CD5\u786E\u8BA4\uFF0C\u8BF7\u5148\u68C0\u67E5\u6B64 profile \u7684\u63D2\u4EF6\u5B89\u88C5\u72B6\u6001\u3002":"The previous installation result is uncertain. Check this profile\u2019s plugin installation first.","\u65E0\u6CD5\u8BBF\u95EE npm \u6216\u8BF7\u6C42\u8D85\u65F6\uFF0C\u8BF7\u7A0D\u540E\u91CD\u65B0\u68C0\u67E5\u3002":"npm is unreachable or the request timed out. Check again later.","npm \u8FD4\u56DE\u7684\u7248\u672C\u4FE1\u606F\u65E0\u6548\uFF0C\u6682\u65F6\u65E0\u6CD5\u66F4\u65B0\u3002":"npm returned invalid release information. Updating is unavailable.","\u7248\u672C\u786E\u8BA4\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u91CD\u65B0\u68C0\u67E5\u540E\u518D\u5B89\u88C5\u3002":"The version confirmation expired. Check again before installing.","\u63D2\u4EF6\u5B89\u88C5\u72B6\u6001\u5DF2\u53D1\u751F\u53D8\u5316\uFF0C\u8BF7\u91CD\u65B0\u68C0\u67E5\u3002":"The plugin installation changed. Check again.","\u6B64 profile \u6B63\u5728\u66F4\u65B0\uFF0C\u8BF7\u7A0D\u540E\u67E5\u770B\u72B6\u6001\u3002":"This profile is already updating. Check its status shortly.","\u5B89\u88C5\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u5F53\u524D\u5B89\u88C5\u72B6\u6001\u540E\u91CD\u8BD5\u3002":"Installation failed. Check the current installation before retrying.","\u5B89\u88C5\u7ED3\u679C\u6821\u9A8C\u5931\u8D25\uFF0C\u8BF7\u624B\u52A8\u68C0\u67E5\u63D2\u4EF6\u7248\u672C\u3002":"Installation verification failed. Check the plugin version manually.","\u65E0\u6CD5\u5B89\u5168\u4FDD\u5B58\u66F4\u65B0\u72B6\u6001\uFF0C\u8BF7\u5148\u68C0\u67E5\u5F53\u524D\u5B89\u88C5\u7ED3\u679C\u3002":"Update state could not be saved safely. Check the current installation result before retrying.","\u4E0A\u6B21\u66F4\u65B0\u5DF2\u4E2D\u65AD\uFF0C\u8BF7\u68C0\u67E5\u5B89\u88C5\u72B6\u6001\u540E\u91CD\u8BD5\u3002":"The previous update was interrupted. Check the installation before retrying.","\u66F4\u65B0\u670D\u52A1\u5DF2\u5173\u95ED\uFF0C\u8BF7\u624B\u52A8\u91CD\u65B0\u6253\u5F00\u8BBE\u7F6E\u9875\u3002":"The update service has closed. Reopen settings manually.","\u66F4\u65B0\u8BF7\u6C42\u65E0\u6548\uFF0C\u8BF7\u91CD\u65B0\u68C0\u67E5\u7248\u672C\u3002":"The update request is invalid. Check the version again.","\u5F53\u524D\u63D2\u4EF6\u5B89\u88C5\u4E0D\u5B8C\u6574\u6216\u4E0E profile \u4E0D\u7B26\uFF0C\u8BF7\u624B\u52A8\u68C0\u67E5\u5B89\u88C5\u914D\u7F6E\u3002":"The plugin installation is incomplete or does not match this profile. Check the installation configuration manually.","\u65E0\u6CD5\u786E\u8BA4\u5F53\u524D npm \u6E90\u914D\u7F6E\uFF0C\u6682\u65F6\u4E0D\u80FD\u5B89\u88C5\u66F4\u65B0\u3002":"The npm registry configuration could not be verified. Installation is unavailable.","\u5B89\u88C5\u8D85\u65F6\uFF0C\u8BF7\u5148\u786E\u8BA4\u5F53\u524D\u5B89\u88C5\u72B6\u6001\uFF0C\u518D\u51B3\u5B9A\u662F\u5426\u91CD\u8BD5\u3002":"Installation timed out. Check the current installation before retrying.","\u66F4\u65B0\u8BF7\u6C42\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002":"The update request failed. Try again.","\u66F4\u65B0\u670D\u52A1\u8FD4\u56DE\u4E86\u65E0\u6CD5\u8BC6\u522B\u7684\u54CD\u5E94\u3002":"The update service returned an unrecognized response.","\u5F53\u524D Host \u4E0D\u652F\u6301\u66F4\u65B0\u63A5\u53E3\uFF0C\u8BF7\u5148\u624B\u52A8\u66F4\u65B0\u63D2\u4EF6\u5E76\u91CD\u542F\u3002":"This Host does not support the update API. Update the plugin manually and restart first.","\u5173\u95ED\u7A97\u53E3\u4E0D\u4F1A\u53D6\u6D88\u5B89\u88C5\u3002\u8BF7\u52FF\u540C\u65F6\u5728\u5176\u4ED6\u7A97\u53E3\u7BA1\u7406\u6B64 profile \u7684\u63D2\u4EF6\u3002":"Closing this window does not cancel installation. Do not manage this profile\u2019s plugins in another window at the same time.","\u9875\u9762\u7248\u672C\u4E0E\u8FD0\u884C\u7248\u672C\u4E0D\u540C\uFF0C\u8BF7\u624B\u52A8\u5237\u65B0\u9875\u9762\uFF1B\u82E5\u4ECD\u4E0D\u4E00\u81F4\uFF0C\u8BF7\u624B\u52A8\u91CD\u542F Harness \u6216 Desktop\u3002":"The page and running versions differ. Refresh the page manually; if they still differ, manually restart Harness or Desktop.","\u8BF7\u5728\u673A\u5668\u4EBA\u7A7A\u95F2\u65F6\u5B89\u88C5\uFF1B\u5B89\u88C5\u4F1A\u4FEE\u6539\u5F53\u524D profile \u7684\u4F9D\u8D56\uFF0C\u5B8C\u6210\u540E\u9700\u624B\u52A8\u91CD\u542F\u3002":"Install while bots are idle. This changes the current profile\u2019s dependencies and requires a manual restart afterward.","AI Office":"AI Office","\uFF08\u5B9E\u9A8C\u529F\u80FD\uFF09":"(Experimental)","AI Office \u8BBE\u7F6E":"AI Office settings","AI Office \u8BBE\u7F6E\u9875\u7F3A\u5C11 RPC \u8FDE\u63A5":"AI Office settings are missing an RPC connection","\u6B63\u5728\u8BFB\u53D6 AI Office Connector\u2026":"Loading AI Office Connector\u2026","\u672C\u673A\u4E3B\u52A8\u8FDE\u63A5\u516C\u7F51 Office\uFF1BHarness \u4E0D\u5F00\u653E\u7AEF\u53E3\u3002\u534F\u8BAE Hook \u56FA\u5B9A\u4E3A ":"This machine connects outward to the public Office; Harness exposes no port. Protocol hooks: ",\u5C1A\u672A\u914D\u7F6E:"Not configured","\u5DF2\u8FDE\u63A5 Office":"Connected to Office",\u5DF2\u914D\u7F6E:"Configured",\u7B49\u5F85\u91CD\u8FDE:"Waiting to reconnect",\u51ED\u636E\u7F3A\u5931:"Credential missing",\u6700\u8FD1\u5FC3\u8DF3:"Last heartbeat",\u6700\u8FD1\u4E8B\u4EF6:"Last event",\u91CD\u8FDE\u6B21\u6570:"Reconnects","\u8FD0\u884C Job":"Running Jobs","\u5B8C\u6210 Job":"Completed Jobs",\u5C1A\u65E0:"None yet",\u8BBE\u5907\u8FDE\u63A5:"Device connection","Token \u53EA\u5199\u5165\u672C\u673A\u51ED\u636E\u5B58\u50A8":"Token is written only to the local credential store","\u7C98\u8D34 Office \u4E00\u6B21\u6027\u51ED\u636E":"Paste the one-time Office credential","\u5DF2\u5B89\u5168\u4FDD\u5B58\uFF1B\u7559\u7A7A\u4FDD\u6301\u4E0D\u53D8":"Stored securely; leave blank to keep it",\u6700\u5927\u5E76\u53D1:"Max concurrency","Heartbeat \u79D2\u6570":"Heartbeat seconds","Workspace \u6620\u5C04":"Workspace mappings","\u6BCF\u884C alias=/\u672C\u673A/\u7EDD\u5BF9\u8DEF\u5F84\uFF1BOffice \u53EA\u80FD\u770B\u5230 alias\u3002":"One alias=/local/absolute/path per line; Office sees only aliases.","Instruction Preset \u6620\u5C04":"Instruction preset mappings","\u6BCF\u884C alias=\u6307\u4EE4\uFF1B\u65B0\u589E preset \u4E0D\u9700\u8981\u6539 Office \u4EE3\u7801\u3002":"One alias=instruction per line; new presets require no Office code change.",\u4FDD\u5B58\u5E76\u8FDE\u63A5:"Save and connect",\u6D4B\u8BD5\u8FDE\u63A5:"Test connection","\u6D4B\u8BD5\u4E2D\u2026":"Testing\u2026",\u91CD\u65B0\u8FDE\u63A5:"Reconnect",\u79FB\u9664\u8FDE\u63A5:"Remove connection","\u8FDE\u63A5\u6D4B\u8BD5\u901A\u8FC7\u3002":"Connection test passed.","\u914D\u7F6E\u5DF2\u4FDD\u5B58\u3002":"Configuration saved.","\u534F\u8BAE Hook \u9884\u89C8":"Protocol hook preview","\u7531 Base URL \u81EA\u52A8\u6D3E\u751F\uFF0C\u4E0D\u5355\u72EC\u586B\u5199":"Derived from Base URL; no separate input","Base URL \u65E0\u6548":"Invalid Base URL","Office Hook \u5C1A\u672A\u90E8\u7F72\u65F6\uFF0C\u914D\u7F6E\u4F1A\u5B89\u5168\u4FDD\u5B58\u5E76\u81EA\u52A8\u91CD\u8BD5\uFF1B\u51FA\u73B0 HTTP 404 \u4EE3\u8868\u534F\u8BAE\u7AEF\u70B9\u5F85\u4E0A\u7EBF\uFF0C\u4E0D\u4EE3\u8868 Harness \u6545\u969C\u3002":"Configuration is saved and retried while Office hooks are unavailable; HTTP 404 means the protocol endpoint is pending, not a Harness failure.","Workspace \u6620\u5C04\u6BCF\u884C\u5FC5\u987B\u4F7F\u7528 alias=value":"Each workspace mapping must use alias=value","Instruction Preset \u6620\u5C04\u6BCF\u884C\u5FC5\u987B\u4F7F\u7528 alias=value":"Each instruction preset mapping must use alias=value","action-items=\u8F6C\u6362\u4E3A\u8D1F\u8D23\u4EBA\u3001\u622A\u6B62\u548C\u9A8C\u6536\u660E\u786E\u7684\u5DE5\u5355":"action-items=Turn this into accountable tasks with deadlines and acceptance criteria","AI Office \u62D2\u7EDD\u4E86 Device Token\u3002":"AI Office rejected the Device Token.","AI Office Connector Hook \u5C1A\u672A\u5C31\u7EEA\u3002":"AI Office Connector hooks are not available yet.","AI Office Connector \u534F\u8BAE\u7248\u672C\u4E0D\u517C\u5BB9\u3002":"The AI Office Connector protocol is incompatible.","\u672C\u673A\u6682\u65F6\u65E0\u6CD5\u8BBF\u95EE AI Office\u3002":"AI Office cannot currently be reached from this machine.","AI Office \u8FDE\u63A5\u5DF2\u4E2D\u65AD\u3002":"The AI Office connection was interrupted.","\u5E2E\u52A9\u4E0E\u53CD\u9988 \xB7 \u524D\u5F80 GitHub":"Help & feedback \xB7 Open GitHub","\u8BF7\u6539\u7528 localhost \u91CD\u65B0\u6253\u5F00":"Reopen with localhost","\u9875\u9762\u4F1A\u5728\u5F53\u524D\u7AEF\u53E3\u91CD\u65B0\u6253\u5F00\uFF0C\u673A\u5668\u4EBA\u914D\u7F6E\u4E0D\u4F1A\u6539\u53D8\u3002":"The page will reopen on the current port. Your bot configuration will not change.","\u4F7F\u7528 localhost \u91CD\u65B0\u6253\u5F00":"Reopen with localhost","\u5F53\u524D\u5730\u5740\u4E0E\u6D4F\u89C8\u5668\u7684\u672C\u673A\u8BF7\u6C42\u6821\u9A8C\u4E0D\u517C\u5BB9\u3002\u8BF7\u4F7F\u7528\u4E0A\u65B9\u6309\u94AE\u6539\u7528 localhost \u91CD\u65B0\u6253\u5F00\u3002":"This address is incompatible with the browser\u2019s local-request checks. Use the button above to reopen with localhost.",\u5FAE\u4FE1:"WeChat",\u98DE\u4E66:"Feishu",\u9489\u9489:"DingTalk",\u4F01\u4E1A\u5FAE\u4FE1:"WeCom",\u5FAE\u4FE1\u673A\u5668\u4EBA:"WeChat bot",\u98DE\u4E66\u673A\u5668\u4EBA:"Feishu bot",\u9489\u9489\u673A\u5668\u4EBA:"DingTalk bot",\u4F01\u4E1A\u5FAE\u4FE1\u673A\u5668\u4EBA:"WeCom bot",QQ\u673A\u5668\u4EBA:"QQ bot",WhatsApp\u673A\u5668\u4EBA:"WhatsApp bot",WhatsApp\u8D26\u53F7:"WhatsApp account",\u5FAE\u4FE1\u8BBE\u7F6E:"WeChat settings",\u98DE\u4E66\u673A\u5668\u4EBA\u8BBE\u7F6E:"Feishu bot settings",\u7FA4\u804A\u54CD\u5E94\u65B9\u5F0F:"Group response mode","\u4EC5\u5728 @\u673A\u5668\u4EBA\u65F6\u54CD\u5E94\uFF08\u63A8\u8350\uFF09":"Only respond when @mentioned (recommended)",\u54CD\u5E94\u6240\u6709\u7FA4\u6D88\u606F:"Respond to all group messages","\u54CD\u5E94\u6240\u6709\u7FA4\u6D88\u606F\uFF08\u9700\u98DE\u4E66\u654F\u611F\u6743\u9650\uFF09":"Respond to all group messages (requires a sensitive Feishu scope)",\u91CD\u65B0\u6388\u6743:"Reauthorize",\u53BB\u6388\u6743:"Authorize",\u91CD\u65B0\u6388\u6743\u7FA4\u6D88\u606F\u6743\u9650:"Reauthorize group-message permission",\u6388\u6743\u7FA4\u6D88\u606F\u6743\u9650:"Authorize group-message permission","\u6B63\u5728\u51C6\u5907\u6388\u6743\u2026":"Preparing authorization\u2026","\u6B63\u5728\u51C6\u5907\u2026":"Preparing\u2026","\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":"Direct messages always work; group chats require an explicit @mention of this bot. The group-message permission is already granted, so switching again needs no authorization.","\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":"Direct messages always work; group chats require an explicit @mention of this bot. Selecting all messages opens the official Feishu authorization flow.","\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":"The \u201CRead all messages in associated group chat\u201D scope (im:message.group_msg) is granted; the bot processes every visible group message.","\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":"The \u201CRead all messages in associated group chat\u201D scope has not been confirmed. Complete Feishu authorization.","\u79C1\u804A\u59CB\u7EC8\u54CD\u5E94\uFF1B\u7FA4\u804A\u4EC5\u5904\u7406\u660E\u786E @\u5F53\u524D\u673A\u5668\u4EBA\u7684\u6D88\u606F\u3002":"Direct messages always work; group chats require an explicit @mention of this bot.","\u9700\u5728\u98DE\u4E66\u4E3A\u8BE5\u673A\u5668\u4EBA\u5F00\u901A\u201C\u83B7\u53D6\u7FA4\u7EC4\u4E2D\u6240\u6709\u6D88\u606F\u201D\u6743\u9650\uFF08im:message.group_msg\uFF09\uFF1B\u5F00\u901A\u540E\uFF0C\u673A\u5668\u4EBA\u4F1A\u5904\u7406\u7FA4\u804A\u4E2D\u7684\u6240\u6709\u53EF\u89C1\u6D88\u606F\u3002":"Grant this bot the \u201CRead all messages in associated group chat\u201D Feishu scope (im:message.group_msg); once granted, it will process every visible group message.","\u7FA4\u804A\u54CD\u5E94\u65B9\u5F0F\u4FEE\u6539\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002":"Could not update the group response mode. Try again.","\u7FA4\u6D88\u606F\u6743\u9650\u6388\u6743\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002":"Could not authorize group-message permission. Try again.",\u9489\u9489\u8BBE\u7F6E:"DingTalk settings",\u4F01\u4E1A\u5FAE\u4FE1\u8BBE\u7F6E:"WeCom settings",\u626B\u7801\u63A5\u5165\u673A\u5668\u4EBA:"Scan QR code",\u6B63\u5728\u63A5\u5165:"Connecting",\u624B\u52A8\u63A5\u5165:"Manual setup",\u6536\u8D77\u51ED\u636E:"Hide credentials",\u6536\u8D77\u63A5\u5165:"Hide setup",\u63A5\u5165\u673A\u5668\u4EBA:"Connect bot",\u5F00\u59CB\u63A5\u5165:"Start setup",\u5728\u7EBF:"online",\u8FD0\u884C\u6B63\u5E38:"Connected",\u6B63\u5728\u8FDE\u63A5:"Connecting","\u6B63\u5728\u8FDE\u63A5\u2026":"Connecting\u2026",\u8FDE\u63A5\u672A\u5C31\u7EEA:"Not connected",\u8FDE\u63A5\u4E2D:"Connecting",\u8FDE\u63A5\u4E2D\u65AD:"Disconnected",\u9700\u8981\u5904\u7406:"Needs attention",\u72B6\u6001\u672A\u77E5:"Unknown status",\u79BB\u7EBF:"Offline",\u5DF2\u65AD\u5F00:"Disconnected",\u6D88\u606F\u901A\u9053:"Message channel",\u67E5\u770B\u6D88\u606F\u901A\u9053\u8BF4\u660E:"View message channel details",\u6700\u8FD1\u68C0\u67E5:"Last checked",\u6700\u8FD1\u4E00\u6761\u6D88\u606F\u5904\u7406\u5931\u8D25:"Latest message failed",\u9519\u8BEF\u7801:"Code",\u53C2\u8003\u53F7:"Reference",\u5F53\u524D\u5DE5\u4F5C\u533A:"Current workspace",\u9009\u62E9\u76EE\u5F55:"Choose folder",\u9009\u62E9\u673A\u5668\u4EBA\u5DE5\u4F5C\u533A\u76EE\u5F55:"Select bot workspace folder",\u5F53\u524D\u76EE\u5F55:"Current folder",\u4E3B\u76EE\u5F55:"Home",\u76F4\u63A5\u8F93\u5165\u8DEF\u5F84:"Enter a path directly","\u652F\u6301 Windows \u76D8\u7B26\u3001UNC \u4E0E POSIX \u7EDD\u5BF9\u8DEF\u5F84\u3002":"Supports Windows drives, UNC paths, and POSIX absolute paths.","\u8F93\u5165 Host \u4E0A\u7684\u5B8C\u6574\u7EDD\u5BF9\u8DEF\u5F84":"Enter a full absolute path on the Host",\u524D\u5F80:"Go","\u8BFB\u53D6\u4E2D\u2026":"Loading\u2026","\u6B63\u5728\u51C6\u5907\u76EE\u5F55\u9009\u62E9\u5668\u2026":"Preparing folder picker\u2026","\u6B63\u5728\u8BFB\u53D6\u76EE\u5F55\u2026":"Loading folders\u2026","\u8FD9\u4E2A\u76EE\u5F55\u4E2D\u6CA1\u6709\u5B50\u6587\u4EF6\u5939\u3002":"This folder has no subfolders.","\u6B64\u76EE\u5F55\u7684\u5B50\u6587\u4EF6\u5939\u8FC7\u591A\uFF0C\u4EC5\u663E\u793A\u524D\u4E00\u90E8\u5206\u3002":"This folder has too many subfolders; only the first group is shown.","\u65E0\u6CD5\u8BFB\u53D6\u76EE\u5F55\uFF0C\u8BF7\u91CD\u8BD5\u3002":"Could not load the folder. Try again.",\u91CD\u8BD5:"Retry",\u663E\u793A\u9690\u85CF\u6587\u4EF6\u5939:"Show hidden folders","\u5207\u6362\u540E\u4F1A\u6E05\u9664\u8FD9\u4E2A\u673A\u5668\u4EBA\u7684\u65E7\u4F1A\u8BDD\u6620\u5C04\u3002":"Switching clears this bot\u2019s previous session mappings.","\u5207\u6362\u4E2D\u2026":"Switching\u2026",\u9009\u62E9\u6B64\u76EE\u5F55:"Select this folder",\u5DE5\u4F5C\u533A\u7EDD\u5BF9\u8DEF\u5F84:"Absolute workspace path","/\u7EDD\u5BF9\u8DEF\u5F84/\u5230/\u5DE5\u4F5C\u533A":"/absolute/path/to/workspace",\u4FEE\u6539:"Change",\u4FDD\u5B58:"Save",\u4E0A\u4E0B\u6587\u589E\u5F3A:"Context enhancement",\u672A\u5F00\u542F:"Not enabled",\u4EC5\u7FA4\u804A:"Groups only",\u4EC5\u79C1\u804A:"Direct only",\u7FA4\u804A\u548C\u79C1\u804A:"Groups and direct",\u5173\u95ED\u5F39\u7A97:"Close dialog",\u67E5\u770B\u4E0A\u4E0B\u6587\u589E\u5F3A\u8BF4\u660E:"View context enhancement details",\u4E0A\u4E0B\u6587\u589E\u5F3A\u8303\u56F4:"Context enhancement scope",\u542F\u7528\u8303\u56F4:"Enable in",\u542F\u7528:"Enabled","\uFF08\u5F53\u524D\u6E20\u9053\u4E0D\u652F\u6301\u7FA4\u804A\uFF09":"(This channel does not support group chats)",\u6765\u6E90\u5B57\u6BB5:"Source fields",\u67E5\u770B\u6765\u6E90\u5B57\u6BB5\u8BF4\u660E:"View source field details",\u67E5\u770B\u7FA4\u804A\u6765\u6E90\u5B57\u6BB5\u8BF4\u660E:"View group source field details",\u67E5\u770B\u79C1\u804A\u6765\u6E90\u5B57\u6BB5\u8BF4\u660E:"View direct source field details",\u6E20\u9053:"Channel",\u4F1A\u8BDD\u7C7B\u578B:"Conversation type",\u53D1\u9001\u8005\u6807\u8BC6:"Sender ID",\u53D1\u9001\u8005\u6635\u79F0:"Sender name",\u67E5\u770B\u53D1\u9001\u8005\u6635\u79F0\u5B57\u6BB5\u8BF4\u660E:"View sender name field details",\u67E5\u770B\u7FA4\u804A\u53D1\u9001\u8005\u6635\u79F0\u5B57\u6BB5\u8BF4\u660E:"View group sender name field details",\u67E5\u770B\u79C1\u804A\u53D1\u9001\u8005\u6635\u79F0\u5B57\u6BB5\u8BF4\u660E:"View direct sender name field details","\u8BE5\u5B57\u6BB5\u4E0D\u662F\u6BCF\u4E2A\u6E20\u9053\u90FD\u80FD\u63D0\u4F9B\u3002\u5F53\u524D\u6D88\u606F\u6CA1\u6709\u53D1\u9001\u8005\u6635\u79F0\u65F6\uFF0C\u5373\u4F7F\u5DF2\u9009\u62E9\u8BE5\u5B57\u6BB5\uFF0C \u4E2D\u4E5F\u4F1A\u7701\u7565 senderName\u3002":"This field is not available on every channel. If the current message has no sender name, omits senderName even when the field is selected.",\u673A\u5668\u4EBA\u6807\u8BC6:"Bot ID",\u589E\u5F3A\u63D0\u793A\u8BCD:"Guidance",\u67E5\u770B\u589E\u5F3A\u63D0\u793A\u8BCD\u4F7F\u7528\u8BF4\u660E:"View guidance instructions",\u67E5\u770B\u7FA4\u804A\u589E\u5F3A\u63D0\u793A\u8BCD\u4F7F\u7528\u8BF4\u660E:"View group guidance instructions",\u67E5\u770B\u79C1\u804A\u589E\u5F3A\u63D0\u793A\u8BCD\u4F7F\u7528\u8BF4\u660E:"View direct guidance instructions",\u4F7F\u7528\u8BF4\u660E:"How to use","\u7528\u4E8E\u544A\u8BC9\u6A21\u578B\u5982\u4F55\u4F7F\u7528 \u4E2D\u5DF2\u9009\u62E9\u7684\u6765\u6E90\u5B57\u6BB5\u3002\u53EA\u586B\u5199\u6B63\u6587\uFF0C\u63D2\u4EF6\u4F1A\u81EA\u52A8\u6DFB\u52A0 \u6210\u5BF9\u6807\u7B7E\u3002":"Tell the model how to use the selected fields in . Enter only the body; the plugin adds paired tags.",\u751F\u6548\u89C4\u5219:"Behavior",\u9690\u79C1\u63D0\u793A:"Privacy",\u4F7F\u7528\u793A\u4F8B:"Example","\u4EC5\u4F9D\u636E\u5F53\u524D\u6D88\u606F\u7684 \u4E2D\u5B9E\u9645\u63D0\u4F9B\u7684\u5B57\u6BB5\u7406\u89E3\u6765\u6E90\uFF1B\u6CA1\u6709\u63D0\u4F9B\u7684\u5B57\u6BB5\u4E0D\u8981\u731C\u6D4B\u6216\u8865\u5168\u3002\nconversationType\u662F\u7FA4\u804A\u65F6\u56DE\u590D\u4E25\u8083\u4E00\u70B9\uFF0CconversationType\u662F\u79C1\u804A\u65F6\u56DE\u590D\u4E00\u5B9A\u8981\u5E7D\u9ED8\u641E\u7B11\uFF0C\u50CF\u5468\u661F\u9A70\u7684\u7535\u5F71\u4E00\u6837\u641E\u7B11":`Understand the source only from fields actually present in for the current message; do not guess or fill in missing fields. When conversationType is group, reply more seriously. When conversationType is direct, make the reply humorous and funny.`,"\u4EC5\u4F9D\u636E\u5F53\u524D\u6D88\u606F\u7684 \u4E2D\u5B9E\u9645\u63D0\u4F9B\u7684\u5B57\u6BB5\u7406\u89E3\u6765\u6E90\uFF1B\u6CA1\u6709\u63D0\u4F9B\u7684\u5B57\u6BB5\u4E0D\u8981\u731C\u6D4B\u6216\u8865\u5168\u3002\n\u5F53\u524D\u6D88\u606F\u6765\u81EA\u7FA4\u804A\uFF0C\u8BF7\u4F7F\u7528\u4E25\u8083\u3001\u514B\u5236\u3001\u7B80\u6D01\u7684\u8868\u8FBE\u65B9\u5F0F\u3002":`Understand the source only from fields actually present in for the current message; do not guess or fill in missing fields. This message is from a group chat. Use a serious, restrained, and concise style.`,"\u4EC5\u4F9D\u636E\u5F53\u524D\u6D88\u606F\u7684 \u4E2D\u5B9E\u9645\u63D0\u4F9B\u7684\u5B57\u6BB5\u7406\u89E3\u6765\u6E90\uFF1B\u6CA1\u6709\u63D0\u4F9B\u7684\u5B57\u6BB5\u4E0D\u8981\u731C\u6D4B\u6216\u8865\u5168\u3002\n\u5F53\u524D\u6D88\u606F\u6765\u81EA\u79C1\u804A\uFF0C\u53EF\u4EE5\u4F7F\u7528\u66F4\u8F7B\u677E\u3001\u5E7D\u9ED8\u3001\u8BE6\u7EC6\u7684\u8868\u8FBE\u65B9\u5F0F\u3002":`Understand the source only from fields actually present in for the current message; do not guess or fill in missing fields. This message is from a direct chat. You may use a lighter, more humorous, and detailed style.`,\u586B\u5165\u793A\u4F8B:"Use example",\u6E05\u7A7A:"Clear","\u9009\u62E9\u5728\u54EA\u4E9B\u4F1A\u8BDD\u4E2D\u542F\u7528\u3001\u63D0\u4F9B\u54EA\u4E9B\u6765\u6E90\u5B57\u6BB5\uFF0C\u4EE5\u53CA\u5982\u4F55\u4F7F\u7528\u8FD9\u4E9B\u4FE1\u606F\u3002\u4EC5\u4F7F\u7528\u5DF2\u6709\u6D88\u606F\u5143\u6570\u636E\uFF0C\u4E0D\u67E5\u8BE2\u5E73\u53F0 API\u3002":"Choose which conversations to enhance, which source fields to include, and how to use them. Only existing message metadata is used; no platform APIs are queried.","\u589E\u5F3A\u63D0\u793A\u8BCD\u4E2D\u8BF7\u4F7F\u7528\u5B57\u6BB5\u540D\uFF08\u5982 senderId\u3001conversationType\uFF09\u5F15\u7528\u8FD9\u4E9B\u4FE1\u606F\u3002\u53EA\u53D1\u9001\u52FE\u9009\u4E14\u5F53\u524D\u6D88\u606F\u4E2D\u53EF\u7528\u7684\u5B57\u6BB5\uFF0C\u4E0D\u4F1A\u989D\u5916\u67E5\u8BE2\u6216\u8865\u5168\u3002":"Reference these values by field name (such as senderId or conversationType) in the guidance. Only selected fields available in the current message are sent; no extra lookups are made.","\u589E\u5F3A\u63D0\u793A\u8BCD\u4E2D\u8BF7\u4F7F\u7528\u5B57\u6BB5\u540D\uFF08\u5982 senderId\u3001conversationType\uFF09\u5F15\u7528\u8FD9\u4E9B\u4FE1\u606F\u3002\u53EA\u53D1\u9001\u5F53\u524D\u4F1A\u8BDD\u4E2D\u52FE\u9009\u4E14\u53EF\u7528\u7684\u5B57\u6BB5\uFF0C\u4E0D\u4F1A\u989D\u5916\u67E5\u8BE2\u6216\u8865\u5168\u3002":"Reference these values by field name (such as senderId or conversationType) in the guidance. Only fields selected and available in the current conversation are sent; no extra lookups are made.","\u7528\u4E8E\u544A\u8BC9\u6A21\u578B\u5982\u4F55\u4F7F\u7528\u5F53\u524D\u7FA4\u804A\u6D88\u606F\u7684 \u6765\u6E90\u5B57\u6BB5\u3002\u53EA\u586B\u5199\u6B63\u6587\uFF0C\u63D2\u4EF6\u4F1A\u81EA\u52A8\u6DFB\u52A0 \u6210\u5BF9\u6807\u7B7E\u3002":"Tell the model how to use fields for the current group message. Enter only the body; the plugin adds paired tags.","\u7528\u4E8E\u544A\u8BC9\u6A21\u578B\u5982\u4F55\u4F7F\u7528\u5F53\u524D\u79C1\u804A\u6D88\u606F\u7684 \u6765\u6E90\u5B57\u6BB5\u3002\u53EA\u586B\u5199\u6B63\u6587\uFF0C\u63D2\u4EF6\u4F1A\u81EA\u52A8\u6DFB\u52A0 \u6210\u5BF9\u6807\u7B7E\u3002":"Tell the model how to use fields for the current direct message. Enter only the body; the plugin adds paired tags.","\u4EC5\u5728\u7FA4\u804A\u5F00\u5173\u5F00\u542F\u65F6\u4F7F\u7528\u3002\u6E05\u7A7A\u5E76\u4FDD\u5B58\u540E\u4E0D\u518D\u9644\u52A0\u7FA4\u804A\u589E\u5F3A\u63D0\u793A\u8BCD\uFF1B\u6240\u9009\u6765\u6E90\u5B57\u6BB5\u4ECD\u6309\u5F53\u524D\u573A\u666F\u8BBE\u7F6E\u53D1\u9001\u3002":"Used only when group enhancement is enabled. Clear and save to omit group guidance; selected source fields still follow the group settings.","\u4EC5\u5728\u79C1\u804A\u5F00\u5173\u5F00\u542F\u65F6\u4F7F\u7528\u3002\u6E05\u7A7A\u5E76\u4FDD\u5B58\u540E\u4E0D\u518D\u9644\u52A0\u79C1\u804A\u589E\u5F3A\u63D0\u793A\u8BCD\uFF1B\u6240\u9009\u6765\u6E90\u5B57\u6BB5\u4ECD\u6309\u5F53\u524D\u573A\u666F\u8BBE\u7F6E\u53D1\u9001\u3002":"Used only when direct enhancement is enabled. Clear and save to omit direct guidance; selected source fields still follow the direct settings.","\u53EA\u9700\u586B\u5199\u6B63\u6587\uFF0C\u63D2\u4EF6\u81EA\u52A8\u6DFB\u52A0 \u6210\u5BF9\u6807\u7B7E\u3002\u6E05\u7A7A\u5E76\u4FDD\u5B58\u540E\uFF0C\u4E0D\u518D\u9644\u52A0\u589E\u5F3A\u63D0\u793A\u8BCD\uFF0C\u5DF2\u9009\u6765\u6E90\u5B57\u6BB5\u4ECD\u6309\u5F00\u5173\u8BBE\u7F6E\u53D1\u9001\u3002":"Enter only the body; the plugin adds paired tags. Clear and save to omit guidance. Selected source fields still follow the conversation switches.","\u53D1\u9001\u8005\u6807\u8BC6\u53EF\u80FD\u5305\u542B\u5E73\u53F0\u7528\u6237 ID \u6216\u7535\u8BDD\u53F7\u7801\u5F62\u5F0F\u7684\u6807\u8BC6\u3002\u5173\u95ED\u5F00\u5173\u4E0D\u4F1A\u5220\u9664\u5DF2\u7ECF\u5199\u5165\u4F1A\u8BDD\u5386\u53F2\u7684\u4FE1\u606F\u3002":"Sender IDs may contain platform user IDs or phone-number-like identifiers. Turning this off does not remove information already saved in conversation history.","\u4E0A\u4E0B\u6587\u589E\u5F3A\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002":"Could not save context enhancement. Try again.","\u8BF7\u63D0\u4EA4\u5B8C\u6574\u7684\u4E0A\u4E0B\u6587\u589E\u5F3A\u8BBE\u7F6E\u3002":"Submit the complete context enhancement settings.","\u7FA4\u804A\u548C\u79C1\u804A\u5F00\u5173\u5FC5\u987B\u662F\u5E03\u5C14\u503C\u3002":"Group and direct switches must be booleans.","\u6765\u6E90\u5B57\u6BB5\u53EA\u80FD\u9009\u62E9\u5DF2\u5B9A\u4E49\u7684\u4E94\u4E2A\u5B57\u6BB5\u3002":"Source fields must be chosen from the five defined fields.","\u4FDD\u5B58\u4E2D\u2026":"Saving\u2026",\u672A\u8BBE\u7F6E:"Not set","\u5DE5\u4F5C\u533A\u4FEE\u6539\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002":"Could not update the workspace. Try again.","\u8BF7\u8F93\u5165\u5DE5\u4F5C\u533A\u7EDD\u5BF9\u8DEF\u5F84\u3002":"Enter an absolute workspace path.","\u5DE5\u4F5C\u533A\u5FC5\u987B\u662F\u7EDD\u5BF9\u8DEF\u5F84\u3002":"The workspace must be an absolute path.","\u5DE5\u4F5C\u533A\u8DEF\u5F84\u4E0D\u5B58\u5728\u3002":"The workspace path does not exist.","\u5DE5\u4F5C\u533A\u8DEF\u5F84\u5FC5\u987B\u6307\u5411\u4E00\u4E2A\u76EE\u5F55\u3002":"The workspace path must point to a directory.","\u627E\u4E0D\u5230\u8981\u4FEE\u6539\u7684\u673A\u5668\u4EBA\u3002":"The bot could not be found.","Agent Preset":"Agent Preset","\u67E5\u770B Agent Preset \u8BF4\u660E":"View Agent Preset help","\u8DDF\u968F Host \u9ED8\u8BA4":"Follow the Host default","\uFF08\u5DF2\u4E0D\u53EF\u7528\uFF09":" (unavailable)","\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":"This affects only new sessions. If the current chat already has a session, send /new, then send a regular message to apply it.","\u5F53\u524D Agent Preset \u5DF2\u4E0D\u53EF\u7528\uFF0C\u8BF7\u9009\u62E9\u5176\u4ED6 Preset \u6216\u8DDF\u968F Host \u9ED8\u8BA4\u3002":"The current Agent Preset is unavailable. Choose another preset or follow the Host default.","Agent Preset \u4FEE\u6539\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002":"Could not update the Agent Preset. Try again.","\u8BF7\u9009\u62E9 Agent Preset\u3002":"Choose an Agent Preset.","Agent Preset \u65E0\u6548\u3002":"The Agent Preset is invalid.","Agent Preset \u4E0D\u5B58\u5728\u6216\u4E0D\u53EF\u7528\u3002":"The Agent Preset does not exist or is unavailable.",\u5C1A\u672A\u68C0\u67E5:"Not checked yet",\u521A\u521A:"Just now",\u68C0\u67E5\u8FDE\u63A5:"Check connection","\u68C0\u67E5\u4E2D\u2026":"Checking\u2026","\u8FDE\u63A5\u68C0\u67E5\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002":"Connection check failed. Try again later.","\u6D4B\u8BD5\u6D88\u606F\u5DF2\u53D1\u9001\uFF0C\u8BF7\u5230\u5BF9\u5E94\u673A\u5668\u4EBA\u4F1A\u8BDD\u4E2D\u786E\u8BA4\u3002":"Test message sent. Check the matching bot conversation.","\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\u3002\u673A\u5668\u4EBA\u5C1A\u672A\u6536\u5230\u53EF\u7528\u4E8E\u6D4B\u8BD5\u7684\u79C1\u804A\u6D88\u606F\u3002":"Connection check completed. The bot has not received a direct message it can use for testing.","\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\uFF0C\u4F46\u6D4B\u8BD5\u6D88\u606F\u53D1\u9001\u5931\u8D25\u3002":"Connection check completed, but the test message could not be sent.","\u6D4B\u8BD5\u6D88\u606F\u5DF2\u53D1\u9001\uFF0C\u8BF7\u5230\u98DE\u4E66\u4F1A\u8BDD\u4E2D\u786E\u8BA4\u3002":"Test message sent. Check the Feishu conversation.","\u6D4B\u8BD5\u6D88\u606F\u5DF2\u53D1\u9001\uFF0C\u8BF7\u5230 WhatsApp \u81EA\u804A\u4F1A\u8BDD\u4E2D\u786E\u8BA4\u3002":"Test message sent. Check the WhatsApp self-chat.","\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\uFF0C\u4F46\u5F53\u524D\u6CA1\u6709\u53EF\u7528\u7684 WhatsApp \u81EA\u804A\u76EE\u6807\u3002":"Connection check completed, but no WhatsApp self-chat target is available.","\u9489\u9489\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\uFF0C\u6D4B\u8BD5\u6D88\u606F\u5DF2\u53D1\u9001\u3002":"DingTalk connection check completed and the test message was sent.","\u9489\u9489\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\uFF0C\u4F46\u6D4B\u8BD5\u6D88\u606F\u53D1\u9001\u5931\u8D25\u3002":"DingTalk connection check completed, but the test message could not be sent.","\u5FAE\u4FE1\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\uFF0C\u6D4B\u8BD5\u6D88\u606F\u5DF2\u53D1\u9001\u3002":"WeChat connection check completed and the test message was sent.","\u5FAE\u4FE1\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\uFF0C\u4F46\u6D4B\u8BD5\u6D88\u606F\u53D1\u9001\u5931\u8D25\u3002":"WeChat connection check completed, but the test message could not be sent.","\u4F01\u4E1A\u5FAE\u4FE1\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\uFF0C\u6D4B\u8BD5\u6D88\u606F\u5DF2\u53D1\u9001\u3002":"WeCom connection check completed and the test message was sent.","\u4F01\u4E1A\u5FAE\u4FE1\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\uFF0C\u4F46\u6D4B\u8BD5\u6D88\u606F\u53D1\u9001\u5931\u8D25\u3002":"WeCom connection check completed, but the test message could not be sent.",\u91CD\u8BD5\u8FDE\u63A5:"Reconnect","\u91CD\u8BD5\u4E2D\u2026":"Retrying\u2026",\u79FB\u9664\u63A5\u5165:"Remove connection",\u786E\u8BA4\u79FB\u9664\u63A5\u5165:"Remove connection",\u786E\u8BA4\u79FB\u9664:"Remove","\u6B63\u5728\u79FB\u9664\u2026":"Removing\u2026",\u4FDD\u7559\u673A\u5668\u4EBA:"Keep bot",\u4FDD\u7559\u8D26\u53F7:"Keep account",\u53D6\u6D88:"Cancel",\u5173\u95ED:"Close",\u7ACB\u5373\u91CD\u8BD5:"Retry now",\u91CD\u65B0\u8BFB\u53D6:"Reload",\u91CD\u65B0\u751F\u6210\u4E8C\u7EF4\u7801:"Generate a new QR code",\u91CD\u65B0\u751F\u6210\u4E8C\u7EF4\u7801\u540E\u7EE7\u7EED:"Generate a new QR code",\u5237\u65B0\u4E8C\u7EF4\u7801:"Refresh QR code","\u5237\u65B0\u4E2D\u2026":"Refreshing\u2026",\u6362\u4E00\u4E2A\u4E8C\u7EF4\u7801:"Get another QR code",\u7EE7\u7EED\u8FDE\u63A5:"Continue connecting",\u7ED1\u5B9A\u5E76\u8FDE\u63A5:"Connect","\u6B63\u5728\u7ED1\u5B9A\u2026":"Connecting\u2026",\u9A8C\u8BC1\u5E76\u8FDE\u63A5:"Verify and connect","\u6B63\u5728\u9A8C\u8BC1\u5E76\u8FDE\u63A5\u2026":"Verifying and connecting\u2026","\u6B63\u5728\u9A8C\u8BC1\u2026":"Verifying\u2026","\u64CD\u4F5C\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5":"The operation failed. Try again later.",\u8BF7\u7A0D\u540E\u91CD\u8BD5:"Try again later.",\u5F53\u524D\u4E8C\u7EF4\u7801\u6709\u6548\u65F6\u95F4:"QR code expires in",\u4E8C\u7EF4\u7801\u6709\u6548\u65F6\u95F4:"QR code expires in",\u4E8C\u7EF4\u7801\u5DF2\u8FC7\u671F:"QR code expired",\u4E8C\u7EF4\u7801\u5DF2\u5931\u6548:"QR code expired","\u4E8C\u7EF4\u7801\u5DF2\u8FC7\u671F\n\u8BF7\u91CD\u65B0\u751F\u6210":`QR code expired diff --git a/plugin-src/client/delivery-settings.js b/plugin-src/client/delivery-settings.js index b6dcae0..c184106 100644 --- a/plugin-src/client/delivery-settings.js +++ b/plugin-src/client/delivery-settings.js @@ -18,6 +18,10 @@ export const DELIVERY_ENDPOINTS = Object.freeze({ test: 'target.test', }); +export const BOT_SETTINGS_TABS = Object.freeze([ + Object.freeze({ id: 'delivery', label: '投递设置' }), +]); + const CHANNEL_DEFINITIONS = Object.freeze({ weixin: { label: '微信', @@ -654,13 +658,41 @@ export function DeliveryTargetSettingsPage({ channel, account, rpcCall, onBack } } }; + const deliveryTab = BOT_SETTINGS_TABS[0]; + const deliveryTabId = `dim-bot-settings-${deliveryTab.id}-tab`; + const deliveryPanelId = `dim-bot-settings-${deliveryTab.id}-panel`; + return h('section', { className: 'dim-deliveryPage', 'aria-label': `${account.botName || definition.label}机器人设置`, }, h('header', { className: 'dim-deliveryHeader' }, - h(DeliveryButton, { className: 'dim-deliveryBack', onClick: onBack }, '← 返回机器人列表'), - h('div', { className: 'dim-deliveryHeaderMeta' }, + h(DeliveryButton, { className: 'dim-deliveryBack', onClick: onBack }, '← 返回机器人列表')), + h('div', { className: 'dim-botSettingsTabsBar' }, + h('nav', { + className: 'dim-botSettingsTabs', + role: 'tablist', + 'aria-label': '机器人设置页签', + }, BOT_SETTINGS_TABS.map((tab) => h('button', { + key: tab.id, + id: `dim-bot-settings-${tab.id}-tab`, + type: 'button', + role: 'tab', + className: 'dim-botSettingsTab', + 'aria-selected': tab.id === deliveryTab.id, + 'aria-controls': `dim-bot-settings-${tab.id}-panel`, + tabIndex: tab.id === deliveryTab.id ? 0 : -1, + }, tab.label)))), + h('div', { + id: deliveryPanelId, + className: 'dim-botSettingsTabPanel', + role: 'tabpanel', + 'aria-labelledby': deliveryTabId, + }, + h('section', { className: 'dim-deliveryIdentity', 'aria-labelledby': 'dim-delivery-bot-title' }, + h('div', { className: 'dim-deliveryIdentityHeading' }, + h('h2', { id: 'dim-delivery-bot-title', className: 'dim-deliveryBotName' }, + account.botName || '机器人设置'), h('a', { className: 'dim-deliveryDocsLink', href: isEnglish() ? DELIVERY_DOCS_URL.en : DELIVERY_DOCS_URL.zh, @@ -669,11 +701,7 @@ export function DeliveryTargetSettingsPage({ channel, account, rpcCall, onBack } 'aria-label': '打开主动投递使用文档', }, h('span', null, '使用文档'), - h('span', { 'aria-hidden': 'true' }, '↗')))), - h('section', { className: 'dim-deliveryIdentity', 'aria-labelledby': 'dim-delivery-bot-title' }, - h('div', null, - h('h2', { id: 'dim-delivery-bot-title', className: 'dim-deliveryBotName' }, - account.botName || '机器人设置')), + h('span', { 'aria-hidden': 'true' }, '↗'))), h('div', { className: 'dim-deliveryBotId' }, h('span', null, 'Bot ID'), h('code', { title: account.botId }, account.botId), @@ -745,5 +773,5 @@ export function DeliveryTargetSettingsPage({ channel, account, rpcCall, onBack } rpcCall: invoke, onChanged: () => loadTargets({ silent: true }), onEdit: () => setEditor({ mode: 'edit', target, source: 'edit' }), - }))))); + })))))); } diff --git a/plugin-src/client/i18n.js b/plugin-src/client/i18n.js index 5702d3f..947c3b6 100644 --- a/plugin-src/client/i18n.js +++ b/plugin-src/client/i18n.js @@ -7,6 +7,9 @@ const EN = Object.freeze({ 'IM机器人': 'IM bots', 'IM机器人设置': 'IM bot settings', '更多机器人设置': 'More bot settings', + '机器人设置': 'Bot settings', + '机器人设置页签': 'Bot settings tabs', + '投递设置': 'Delivery settings', 'IM 渠道': 'IM channels', '让 DeepSeek Harness 触手可及': 'Connecting DeepSeek Harness', '当前版本': 'Current version', diff --git a/plugin-src/client/styles.js b/plugin-src/client/styles.js index 20c8396..bfc30d4 100644 --- a/plugin-src/client/styles.js +++ b/plugin-src/client/styles.js @@ -369,9 +369,18 @@ const CSS = String.raw` .dim-botSettingsButton:focus-visible { outline: 2px solid var(--dsw-alias-state-business-primary, #3370ff); outline-offset: 2px; } .dim-botSettingsTooltip { position: absolute; top: calc(100% + 6px); right: 0; z-index: 30; width: max-content; padding: 6px 9px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 7px; color: var(--dsw-alias-label-primary, #1f2329); background: var(--dsw-alias-bg-layer-3, #fff); box-shadow: 0 8px 24px rgb(31 35 41 / 14%); font-size: 11px; line-height: 16px; opacity: 0; visibility: hidden; transform: translateY(-3px); pointer-events: none; transition: opacity .15s ease, transform .15s ease, visibility .15s ease; } .dim-botSettingsAction:hover .dim-botSettingsTooltip, .dim-botSettingsAction:focus-within .dim-botSettingsTooltip { opacity: 1; visibility: visible; transform: translateY(0); } -.dim-deliveryPage { min-width: 0; display: grid; gap: 14px; } +.dim-deliveryPage { min-width: 0; display: grid; } .dim-deliveryHeader { min-width: 0; display: flex; align-items: center; justify-content: space-between; gap: 16px; } -.dim-deliveryHeaderMeta { min-width: 0; display: flex; align-items: center; justify-content: flex-end; gap: 10px; text-align: right; } +.dim-botSettingsTabsBar { min-width: 0; margin-top: 10px; border-bottom: 1px solid var(--dsw-alias-border-l2, #dfe1e5); } +.dim-botSettingsTabs { min-width: 0; display: flex; align-items: flex-end; gap: 24px; overflow-x: auto; scrollbar-width: none; } +.dim-botSettingsTabs::-webkit-scrollbar { display: none; } +.dim-botSettingsTab { position: relative; min-height: 38px; flex: none; display: inline-flex; align-items: center; justify-content: center; padding: 6px 2px 9px; border: 0; color: var(--dsw-alias-label-secondary, #646a73); background: transparent; font: inherit; font-size: 13px; line-height: 20px; font-weight: 560; white-space: nowrap; cursor: pointer; transition: color .15s ease; } +.dim-botSettingsTab::after { content: ''; position: absolute; right: 0; bottom: -1px; left: 0; height: 2px; border-radius: 2px 2px 0 0; background: transparent; transform: scaleX(.45); transition: background .15s ease, transform .15s ease; } +.dim-botSettingsTab:hover:not([aria-selected="true"]) { color: var(--dsw-alias-label-primary, #1f2329); } +.dim-botSettingsTab[aria-selected="true"] { color: var(--dsw-alias-state-business-primary, #3370ff); font-weight: 650; } +.dim-botSettingsTab[aria-selected="true"]::after { background: var(--dsw-alias-state-business-primary, #3370ff); transform: scaleX(1); } +.dim-botSettingsTab:focus-visible { outline: 2px solid var(--dsw-alias-state-business-primary, #3370ff); outline-offset: -2px; border-radius: 5px; } +.dim-botSettingsTabPanel { min-width: 0; display: grid; gap: 14px; padding-top: 14px; } .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; } .dim-deliveryDocsLink:hover { background: color-mix(in srgb, var(--dsw-alias-state-business-primary, #3370ff) 8%, transparent); } .dim-deliveryDocsLink:focus-visible { outline: 2px solid var(--dsw-alias-state-business-primary, #3370ff); outline-offset: 2px; } @@ -383,6 +392,7 @@ const CSS = String.raw` .dim-deliveryButton[data-kind="danger"] { color: var(--dsw-alias-state-error-primary, #d54941); } .dim-deliveryBack { flex: none; border-color: transparent; background: transparent; } .dim-deliveryIdentity, .dim-deliveryTargets { min-width: 0; padding: 16px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 12px; background: var(--dsw-alias-bg-layer-3, #fff); } +.dim-deliveryIdentityHeading { min-width: 0; display: flex; align-items: center; justify-content: space-between; gap: 12px; } .dim-deliveryBotName, .dim-deliverySectionHeading h3 { min-width: 0; overflow: hidden; margin: 0; font-size: 15px; line-height: 22px; font-weight: 650; text-overflow: ellipsis; white-space: nowrap; } .dim-deliveryIdentity > div:first-child p, .dim-deliverySectionHeading p { margin: 2px 0 0; color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; line-height: 18px; } .dim-deliveryBotId { min-width: 0; display: grid; grid-template-columns: max-content minmax(0, 1fr) max-content; align-items: center; gap: 10px; margin-top: 12px; padding: 10px 12px; border-radius: 9px; background: var(--dsw-alias-bg-module-platform, #f7f8fa); } @@ -471,8 +481,8 @@ const CSS = String.raw` .dim-rail { grid-template-columns: minmax(0, 1fr); } .dim-loopbackRecovery { align-items: stretch; flex-direction: column; gap: 12px; } .dim-loopbackRecoveryAction { width: 100%; } - .dim-deliveryHeader, .dim-deliverySectionHeading { align-items: stretch; flex-direction: column; } - .dim-deliveryHeaderMeta { justify-content: space-between; text-align: left; } + .dim-deliverySectionHeading { align-items: stretch; flex-direction: column; } + .dim-botSettingsTabs { gap: 18px; } .dim-deliveryBotId { grid-template-columns: minmax(0, 1fr) max-content; } .dim-deliveryBotId > span { grid-column: 1 / -1; } .dim-targetActions .dim-deliveryButton { flex: 1 1 auto; } diff --git a/test/client-delivery-settings.test.mjs b/test/client-delivery-settings.test.mjs index 9a6b985..b832205 100644 --- a/test/client-delivery-settings.test.mjs +++ b/test/client-delivery-settings.test.mjs @@ -6,6 +6,7 @@ import { renderToStaticMarkup } from 'react-dom/server'; import TestRenderer from 'react-test-renderer'; import { + BOT_SETTINGS_TABS, DELIVERY_CHANNEL_DEFINITIONS, DELIVERY_ENDPOINTS, DELIVERY_RPC_CHANNEL, @@ -71,6 +72,7 @@ const connectedAccount = Object.freeze({ }); test('delivery settings define only the nine supported IM channel routes', () => { + assert.deepEqual(BOT_SETTINGS_TABS, [{ id: 'delivery', label: '投递设置' }]); assert.equal(DELIVERY_RPC_CHANNEL, '/dsh-im-delivery'); assert.deepEqual(Object.keys(DELIVERY_CHANNEL_DEFINITIONS), [ 'weixin', 'feishu', 'dingtalk', 'wecom', 'qq', @@ -183,19 +185,26 @@ test('the card gear opens a bot-scoped page in the current channel panel and ret const page = renderer.root.findByProps({ className: 'dim-deliveryPage' }); assert.match(textOf(page), /微信通知助手/); assert.doesNotMatch(textOf(page), /调用标识/); - assert.equal( - page.findByProps({ className: 'dim-deliveryHeader' }).findAllByType('span').length, - 2, - ); assert.equal( page.findByProps({ className: 'dim-deliveryHeader' }).findAllByType('h2').length, 0, ); + const settingsTab = page.findByProps({ role: 'tab' }); + const settingsPanel = page.findByProps({ role: 'tabpanel' }); + assert.equal(textOf(settingsTab), '投递设置'); + assert.equal(settingsTab.props['aria-selected'], true); + assert.equal(settingsTab.props.tabIndex, 0); + assert.equal(settingsTab.props['aria-controls'], settingsPanel.props.id); + assert.equal(settingsPanel.props['aria-labelledby'], settingsTab.props.id); + const identity = settingsPanel.findByProps({ className: 'dim-deliveryIdentity' }); + assert.ok(identity); + assert.ok(settingsPanel.findByProps({ className: 'dim-deliveryTargets' })); + assert.equal(page.findByProps({ className: 'dim-botSettingsTabsBar' }).findAllByType('a').length, 0); assert.equal( - textOf(page.findByProps({ className: 'dim-deliveryIdentity' }).findByType('h2')), + textOf(identity.findByType('h2')), '微信通知助手', ); - const docsLink = page.findByProps({ className: 'dim-deliveryDocsLink' }); + const docsLink = identity.findByProps({ className: 'dim-deliveryDocsLink' }); assert.equal(textOf(docsLink), '使用文档↗'); assert.equal( docsLink.props.href, @@ -471,6 +480,7 @@ test('recent conversation names remain platform data in the English UI', async ( docsLink.props.href, 'https://github.com/xmanrui/dsh-im/blob/main/PROACTIVE_DELIVERY.en.md', ); + assert.equal(textOf(renderer.root.findByProps({ role: 'tab' })), 'Delivery settings'); }); test('target create, edit, copy, and delete use the minimal RPC payloads', async (t) => {