diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b87a1a..f68ae51 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,13 @@ This file records the notable changes in each dsh-im release. Its format follows ## [Unreleased] +## [4.0.1] - 2026-08-30 + +### Fixed / 修复 + +- 新版 DSH 中,机器人卡片的工作区目录选择器现在使用 `uiWorkspace` 目录服务,不再调用已从 Workspace Controller 移除的 `workspaces.listDirectory` / `pickDirectory`;旧版 Host 仍保留原接口回退。 + On current DSH releases, the bot-card workspace picker now uses the `uiWorkspace` directory service instead of the removed Workspace Controller `workspaces.listDirectory` / `pickDirectory` methods, while retaining the legacy Host fallback. + ## [4.0.0] - 2026-08-29 ### Fixed / 修复 @@ -468,7 +475,8 @@ This file records the notable changes in each dsh-im release. Its format follows - 改进 npm 发布包结构,保留 CLI 入口并避免安装脚本拦截。 Improved npm package contents to preserve the CLI entry point and avoid install-script blocking. -[Unreleased]: https://github.com/xmanrui/dsh-im/compare/v4.0.0...HEAD +[Unreleased]: https://github.com/xmanrui/dsh-im/compare/v4.0.1...HEAD +[4.0.1]: https://github.com/xmanrui/dsh-im/compare/v4.0.0...v4.0.1 [4.0.0]: https://github.com/xmanrui/dsh-im/compare/v3.2.0...v4.0.0 [3.2.0]: https://github.com/xmanrui/dsh-im/compare/v3.1.1...v3.2.0 [3.1.1]: https://github.com/xmanrui/dsh-im/compare/v3.1.0...v3.1.1 diff --git a/lib/client.js b/lib/client.js index d982ef0..a6f571e 100644 --- a/lib/client.js +++ b/lib/client.js @@ -571,7 +571,7 @@ var React22 = __toESM(require("react"), 1); // package.json var package_default = { name: "@xmanrui/dsh-im", - version: "4.0.0", + version: "4.0.1", description: "\u628A\u4E5D\u79CD IM \u673A\u5668\u4EBA\u548C\u516C\u7F51 AI Office \u63A5\u5165\u672C\u673A DeepSeek Harness\u3002 Connect nine IM channels and a public AI Office to a local DeepSeek Harness.", keywords: [ "deepseek-harness", @@ -12487,6 +12487,14 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) { var name = "im-settings"; var inject = ["slots", "connection", "locale", "workspaces"]; var IM_PLUGIN_VERSION = package_default.version; +function callWorkspaceDirectoryApi(ctx, method, ...args) { + const uiWorkspace = typeof ctx.get === "function" ? ctx.get("uiWorkspace") : void 0; + const service = typeof uiWorkspace?.[method] === "function" ? uiWorkspace : ctx.workspaces; + if (typeof service?.[method] !== "function") { + throw new Error("\u65E0\u6CD5\u8BFB\u53D6\u76EE\u5F55\uFF0C\u8BF7\u91CD\u8BD5\u3002"); + } + return service[method](...args); +} var CHANNELS = Object.freeze([ { id: "weixin", label: "\u5FAE\u4FE1" }, { id: "feishu", label: "\u98DE\u4E66" }, @@ -12785,8 +12793,8 @@ function apply(ctx) { const officeRpcCall = (endpoint, payload, signal) => ctx.connection.rpc.call(OFFICE_RPC_CHANNEL, endpoint, payload, signal); const updateRpcCall = (endpoint, payload, signal) => ctx.connection.rpc.call(UPDATE_RPC_CHANNEL, endpoint, payload, signal); const workspaceDirectoryPicker = Object.freeze({ - listDirectory: (path, signal) => ctx.workspaces.listDirectory(path, signal), - pickDirectory: () => ctx.workspaces.pickDirectory() + listDirectory: (path, signal) => callWorkspaceDirectoryApi(ctx, "listDirectory", path, signal), + pickDirectory: () => callWorkspaceDirectoryApi(ctx, "pickDirectory") }); ctx.slots.inject("settings.section", () => ctx.slots.register({ name: "settings.section", diff --git a/lib/index.js b/lib/index.js index 6537fe3..96045ba 100644 --- a/lib/index.js +++ b/lib/index.js @@ -166,7 +166,7 @@ ${n}`:n)}function gM(n,t,o){return{cardParamMap:{flowStatus:t,msgContent:cE(n,o) ${cle(I.text,s)}`);g=Wg([a,...c,A.join(` `)].join(` -`)),s=Math.floor(s/2)}while(s>0&&(g.message.length>3e3||g.messages.length>3));return g}function lle(n){let t=n?.code??n?.failure?.code;return t==="session-not-found"?D("\u5F53\u524D\u804A\u5929\u7ED1\u5B9A\u7684\u4F1A\u8BDD\u5DF2\u4E0D\u5B58\u5728\uFF0C\u8BF7\u91CD\u65B0\u7ED1\u5B9A\u4F1A\u8BDD\u3002"):t===Ta||t==="workspace-bot-not-found"||t==="session-binding-changed"?D("\u4F1A\u8BDD\u3001\u5DE5\u4F5C\u533A\u6216\u673A\u5668\u4EBA\u72B6\u6001\u5DF2\u53D1\u751F\u53D8\u5316\uFF0C\u8BF7\u91CD\u65B0\u6267\u884C /history\u3002"):t==="harness-api-not-found"?D("\u5F53\u524D Harness \u6682\u4E0D\u652F\u6301\u8BFB\u53D6\u4F1A\u8BDD\u5386\u53F2\u3002"):n?.name==="AbortError"?D("\u5386\u53F2\u8BFB\u53D6\u5DF2\u53D6\u6D88\u3002"):n?.name==="TimeoutError"||t==="harness-timeout"?D("\u8BFB\u53D6\u5386\u53F2\u8D85\u65F6\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002"):D("\u6682\u65F6\u65E0\u6CD5\u8BFB\u53D6\u4F1A\u8BDD\u5386\u53F2\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002")}function Ei(n){return typeof n=="string"&&xK.test(n.trim())}async function xi(n,t,o,i,{signal:e,isDirect:a=!1,hasImages:A=!1,hasFiles:r=!1}={}){if(!Ei(n))return null;let s=xK.exec(n.trim())[1].trim();if(s&&(!/^\d+$/u.test(s)||/^0+$/u.test(s)))return Wg(D(ile));let g=s?Math.min(Number(s),ole):3;if(!a)return Wg(D("\u8BF7\u5728\u4E0E\u673A\u5668\u4EBA\u7684\u79C1\u804A\u4E2D\u4F7F\u7528 /history\u3002"));if(A||r)return Wg(D("/history \u4EC5\u652F\u6301\u6587\u5B57\u547D\u4EE4\uFF0C\u8BF7\u79FB\u9664\u56FE\u7247\u6216\u6587\u4EF6\u540E\u91CD\u8BD5\u3002"));let c=o?.sessionFor?.(i);if(typeof c!="string"||!c)return Wg(D("\u5F53\u524D\u804A\u5929\u5C1A\u672A\u7ED1\u5B9A\u4F1A\u8BDD\uFF0C\u8BF7\u5148\u53D1\u9001\u6D88\u606F\u6216\u4F7F\u7528 /session \u7ED1\u5B9A\u4F1A\u8BDD\u3002"));try{let I=t?.workspaceSession?.(c);if(typeof I?.readHistory!="function")return Wg(D("\u5F53\u524D Harness \u6682\u4E0D\u652F\u6301\u8BFB\u53D6\u4F1A\u8BDD\u5386\u53F2\u3002"));let d=AbortSignal.timeout(KK),B=e?AbortSignal.any([e,d]):d,f=Date.now()+KK,Q=new Map,y,m,_=[],T=!1;for(let R=0;R=f)throw new DOMException("History read timed out","TimeoutError");let F=await I.readHistory({maxMessages:nle,...y===void 0?{}:{beforeSeq:y},timeoutMs:Math.max(1,f-Date.now()),signal:B});if(B.throwIfAborted(),Date.now()>=f)throw new DOMException("History read timed out","TimeoutError");if(o.sessionFor(i)!==c){let z=new Error("Session binding changed during history read");throw z.code="session-binding-changed",z}if(!F||!Array.isArray(F.events)||typeof F.hasMore!="boolean")throw new TypeError("Invalid history page");let j=y??1/0;for(let z of F.events){let q=z?.event;if(!q||!Number.isSafeInteger(q.seq)||q.seq<0||typeof q.type!="string"||!q.type)throw new TypeError("Invalid history event");if(m!==void 0&&q.seq>m)continue;let x=Q.get(q.seq);if(x){if(!ale(x,q))throw new TypeError("Conflicting history events")}else{if(y!==void 0&&q.seq>=y)throw new TypeError("Invalid history cursor");Q.set(q.seq,q)}j=Math.min(j,q.seq)}let U=[...Q.values()].sort((z,q)=>z.seq-q.seq);if(m??=U.at(-1)?.seq,T=F.hasMore,T&&(!Number.isFinite(j)||j===y))throw new TypeError("History cursor did not advance");if(_=gle(U).slice(-g),_.length>=g||!T)break;y=j}return _.length===0?Wg(T?D("\u672C\u6B21\u6709\u9650\u8BFB\u53D6\u4E2D\u672A\u627E\u5230\u53EF\u9884\u89C8\u7684\u5386\u53F2\u6D88\u606F\u3002"):D("\u5F53\u524D\u4F1A\u8BDD\u6682\u65E0\u53EF\u9884\u89C8\u7684\u5386\u53F2\u6D88\u606F\u3002")):Ile(c,_,g,T)}catch(I){return Wg(lle(I))}}var Hf={name:"@xmanrui/dsh-im",version:"4.0.0",description:"\u628A\u4E5D\u79CD IM \u673A\u5668\u4EBA\u548C\u516C\u7F51 AI Office \u63A5\u5165\u672C\u673A DeepSeek Harness\u3002 Connect nine IM channels and a public AI Office to a local DeepSeek Harness.",keywords:["deepseek-harness","dsh","dsh-plugin","ai-agent","im","instant-messaging","chatbot","feishu","lark","wechat","wecom","dingtalk","qq","slack","telegram","discord","whatsapp","ai-office"],author:{name:"xmanrui",url:"https://github.com/xmanrui"},contributors:[{name:"C3H3-AI",url:"https://github.com/C3H3-AI"},{name:"divingleee",url:"https://github.com/divingleee"}],license:"MIT",type:"module",repository:{type:"git",url:"git+https://github.com/xmanrui/dsh-im.git"},homepage:"https://github.com/xmanrui/dsh-im#readme",bugs:"https://github.com/xmanrui/dsh-im/issues",publishConfig:{access:"public"},bin:{"dsh-im":"bin/dsh-im.mjs"},main:"./lib/index.js",exports:{".":"./lib/index.js","./client":"./lib/client.js","./package.json":"./package.json"},files:["assets","bin","cordis.patch.yml","lib","plugin-src","scripts","src","LICENSE","README.md","README.en.md","THIRD_PARTY_NOTICES.md"],dsh:{bundle:{patch:"./cordis.patch.yml"},client:{inject:["@deepseek-ai/dsh-client-connection","@deepseek-ai/dsh-client-runtime","@deepseek-ai/dsh-client-ui-settings","@deepseek-ai/dsh-client-ui-slots","@deepseek-ai/dsh-client-locale"],platform:"web"}},scripts:{build:"node plugin-src/client/build.mjs && node plugin-src/host/build.mjs",test:"node --test test/*.test.mjs test/channels/*/*.test.mjs",check:"npm run build && npm test && node scripts/verify-package.mjs"},engines:{node:">=22.19"},dependencies:{"@tencent-connect/qqbot-connector":"1.2.0","@tencent-connect/qqbot-nodejs":"1.0.4","@wecom/aibot-node-sdk":"1.0.7","dingtalk-stream":"2.1.4",qrcode:"1.5.4"},devDependencies:{"@larksuiteoapi/node-sdk":"1.73.0","@whiskeysockets/baileys":"7.0.0-rc14",esbuild:"0.25.9","https-proxy-agent":"5.0.1",react:"18.3.1","react-dom":"18.3.1","react-test-renderer":"18.3.1",semver:"7.8.5"}};var dle=/^\/(?:stop|steer|version)(?=$|\s)/iu,hle=/^\/stop(?=$|\s)/iu,Ele=/^\/version(?=$|\s)/iu,Cle="\u7528\u6CD5\uFF1A/stop\uFF08\u4E0D\u5E26\u53C2\u6570\uFF09",ule="\u7528\u6CD5\uFF1A/version\uFF08\u4E0D\u5E26\u53C2\u6570\uFF09",Ble="\u7528\u6CD5\uFF1A/steer <\u8865\u5145\u6307\u4EE4>",fle="\u63A7\u5236\u547D\u4EE4\u4EC5\u652F\u6301\u7EAF\u6587\u5B57\uFF0C\u8BF7\u79FB\u9664\u56FE\u7247\u540E\u91CD\u8BD5\u3002";function Bo(n,t={}){return{message:n,...t}}function jK(n){return n?{signal:n}:{}}function JK(n,t,o){if(typeof t?.sessionFor!="function")return null;let i=t.sessionFor(o);if(typeof i!="string"||!i)return null;if(typeof n?.workspaceSession!="function")throw new TypeError("Harness does not support workspace sessions");let e=n.workspaceSession(i);if(!e||typeof e!="object")throw new TypeError("Harness returned an invalid workspace session");return e}function Yi(n){return typeof n=="string"&&dle.test(n.trim())}async function Ci(n,t,o,i,{signal:e,hasImages:a=!1,pendingInteraction:A=!1,control:r}={}){if(!Yi(n))return null;let s=n.trim(),g=hle.test(s),c=Ele.test(s);if(a)return Bo(D(fle));if(c)return/^\/version$/iu.test(s)?Bo(`dsh-im v${Hf.version}`):Bo(D(ule));if(g){if(!/^\/stop$/iu.test(s))return Bo(D(Cle));let Q=JK(t,o,i);if(!Q)return Bo(D("\u5F53\u524D\u804A\u5929\u6CA1\u6709\u6B63\u5728\u8FD0\u884C\u7684\u4EFB\u52A1\u3002"));if(typeof Q.stopActiveTurn!="function")throw new TypeError("Harness session does not support stopping active turns");return await Q.stopActiveTurn(r,jK(e))?Bo(D("\u5DF2\u8BF7\u6C42\u505C\u6B62\u5F53\u524D\u4EFB\u52A1\u3002"),{stopped:!0}):Bo(D("\u5F53\u524D\u804A\u5929\u6CA1\u6709\u6B63\u5728\u8FD0\u884C\u7684\u4EFB\u52A1\u3002"))}let d=/^\/steer(?:\s+([\s\S]*))?$/iu.exec(s)?.[1]?.trim()??"";if(!d)return Bo(D(Ble));if(A)return Bo([D("\u5F53\u524D\u4EFB\u52A1\u6B63\u5728\u7B49\u5F85\u4F60\u7684\u56DE\u7B54\u6216\u5BA1\u6279\u3002"),"",D("\u8BF7\u5148\u5904\u7406\u5F53\u524D\u8BF7\u6C42\uFF0C\u6216\u8005\u53D1\u9001 /stop \u505C\u6B62\u4EFB\u52A1\u3002")].join(` +`)),s=Math.floor(s/2)}while(s>0&&(g.message.length>3e3||g.messages.length>3));return g}function lle(n){let t=n?.code??n?.failure?.code;return t==="session-not-found"?D("\u5F53\u524D\u804A\u5929\u7ED1\u5B9A\u7684\u4F1A\u8BDD\u5DF2\u4E0D\u5B58\u5728\uFF0C\u8BF7\u91CD\u65B0\u7ED1\u5B9A\u4F1A\u8BDD\u3002"):t===Ta||t==="workspace-bot-not-found"||t==="session-binding-changed"?D("\u4F1A\u8BDD\u3001\u5DE5\u4F5C\u533A\u6216\u673A\u5668\u4EBA\u72B6\u6001\u5DF2\u53D1\u751F\u53D8\u5316\uFF0C\u8BF7\u91CD\u65B0\u6267\u884C /history\u3002"):t==="harness-api-not-found"?D("\u5F53\u524D Harness \u6682\u4E0D\u652F\u6301\u8BFB\u53D6\u4F1A\u8BDD\u5386\u53F2\u3002"):n?.name==="AbortError"?D("\u5386\u53F2\u8BFB\u53D6\u5DF2\u53D6\u6D88\u3002"):n?.name==="TimeoutError"||t==="harness-timeout"?D("\u8BFB\u53D6\u5386\u53F2\u8D85\u65F6\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002"):D("\u6682\u65F6\u65E0\u6CD5\u8BFB\u53D6\u4F1A\u8BDD\u5386\u53F2\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002")}function Ei(n){return typeof n=="string"&&xK.test(n.trim())}async function xi(n,t,o,i,{signal:e,isDirect:a=!1,hasImages:A=!1,hasFiles:r=!1}={}){if(!Ei(n))return null;let s=xK.exec(n.trim())[1].trim();if(s&&(!/^\d+$/u.test(s)||/^0+$/u.test(s)))return Wg(D(ile));let g=s?Math.min(Number(s),ole):3;if(!a)return Wg(D("\u8BF7\u5728\u4E0E\u673A\u5668\u4EBA\u7684\u79C1\u804A\u4E2D\u4F7F\u7528 /history\u3002"));if(A||r)return Wg(D("/history \u4EC5\u652F\u6301\u6587\u5B57\u547D\u4EE4\uFF0C\u8BF7\u79FB\u9664\u56FE\u7247\u6216\u6587\u4EF6\u540E\u91CD\u8BD5\u3002"));let c=o?.sessionFor?.(i);if(typeof c!="string"||!c)return Wg(D("\u5F53\u524D\u804A\u5929\u5C1A\u672A\u7ED1\u5B9A\u4F1A\u8BDD\uFF0C\u8BF7\u5148\u53D1\u9001\u6D88\u606F\u6216\u4F7F\u7528 /session \u7ED1\u5B9A\u4F1A\u8BDD\u3002"));try{let I=t?.workspaceSession?.(c);if(typeof I?.readHistory!="function")return Wg(D("\u5F53\u524D Harness \u6682\u4E0D\u652F\u6301\u8BFB\u53D6\u4F1A\u8BDD\u5386\u53F2\u3002"));let d=AbortSignal.timeout(KK),B=e?AbortSignal.any([e,d]):d,f=Date.now()+KK,Q=new Map,y,m,_=[],T=!1;for(let R=0;R=f)throw new DOMException("History read timed out","TimeoutError");let F=await I.readHistory({maxMessages:nle,...y===void 0?{}:{beforeSeq:y},timeoutMs:Math.max(1,f-Date.now()),signal:B});if(B.throwIfAborted(),Date.now()>=f)throw new DOMException("History read timed out","TimeoutError");if(o.sessionFor(i)!==c){let z=new Error("Session binding changed during history read");throw z.code="session-binding-changed",z}if(!F||!Array.isArray(F.events)||typeof F.hasMore!="boolean")throw new TypeError("Invalid history page");let j=y??1/0;for(let z of F.events){let q=z?.event;if(!q||!Number.isSafeInteger(q.seq)||q.seq<0||typeof q.type!="string"||!q.type)throw new TypeError("Invalid history event");if(m!==void 0&&q.seq>m)continue;let x=Q.get(q.seq);if(x){if(!ale(x,q))throw new TypeError("Conflicting history events")}else{if(y!==void 0&&q.seq>=y)throw new TypeError("Invalid history cursor");Q.set(q.seq,q)}j=Math.min(j,q.seq)}let U=[...Q.values()].sort((z,q)=>z.seq-q.seq);if(m??=U.at(-1)?.seq,T=F.hasMore,T&&(!Number.isFinite(j)||j===y))throw new TypeError("History cursor did not advance");if(_=gle(U).slice(-g),_.length>=g||!T)break;y=j}return _.length===0?Wg(T?D("\u672C\u6B21\u6709\u9650\u8BFB\u53D6\u4E2D\u672A\u627E\u5230\u53EF\u9884\u89C8\u7684\u5386\u53F2\u6D88\u606F\u3002"):D("\u5F53\u524D\u4F1A\u8BDD\u6682\u65E0\u53EF\u9884\u89C8\u7684\u5386\u53F2\u6D88\u606F\u3002")):Ile(c,_,g,T)}catch(I){return Wg(lle(I))}}var Hf={name:"@xmanrui/dsh-im",version:"4.0.1",description:"\u628A\u4E5D\u79CD IM \u673A\u5668\u4EBA\u548C\u516C\u7F51 AI Office \u63A5\u5165\u672C\u673A DeepSeek Harness\u3002 Connect nine IM channels and a public AI Office to a local DeepSeek Harness.",keywords:["deepseek-harness","dsh","dsh-plugin","ai-agent","im","instant-messaging","chatbot","feishu","lark","wechat","wecom","dingtalk","qq","slack","telegram","discord","whatsapp","ai-office"],author:{name:"xmanrui",url:"https://github.com/xmanrui"},contributors:[{name:"C3H3-AI",url:"https://github.com/C3H3-AI"},{name:"divingleee",url:"https://github.com/divingleee"}],license:"MIT",type:"module",repository:{type:"git",url:"git+https://github.com/xmanrui/dsh-im.git"},homepage:"https://github.com/xmanrui/dsh-im#readme",bugs:"https://github.com/xmanrui/dsh-im/issues",publishConfig:{access:"public"},bin:{"dsh-im":"bin/dsh-im.mjs"},main:"./lib/index.js",exports:{".":"./lib/index.js","./client":"./lib/client.js","./package.json":"./package.json"},files:["assets","bin","cordis.patch.yml","lib","plugin-src","scripts","src","LICENSE","README.md","README.en.md","THIRD_PARTY_NOTICES.md"],dsh:{bundle:{patch:"./cordis.patch.yml"},client:{inject:["@deepseek-ai/dsh-client-connection","@deepseek-ai/dsh-client-runtime","@deepseek-ai/dsh-client-ui-settings","@deepseek-ai/dsh-client-ui-slots","@deepseek-ai/dsh-client-locale"],platform:"web"}},scripts:{build:"node plugin-src/client/build.mjs && node plugin-src/host/build.mjs",test:"node --test test/*.test.mjs test/channels/*/*.test.mjs",check:"npm run build && npm test && node scripts/verify-package.mjs"},engines:{node:">=22.19"},dependencies:{"@tencent-connect/qqbot-connector":"1.2.0","@tencent-connect/qqbot-nodejs":"1.0.4","@wecom/aibot-node-sdk":"1.0.7","dingtalk-stream":"2.1.4",qrcode:"1.5.4"},devDependencies:{"@larksuiteoapi/node-sdk":"1.73.0","@whiskeysockets/baileys":"7.0.0-rc14",esbuild:"0.25.9","https-proxy-agent":"5.0.1",react:"18.3.1","react-dom":"18.3.1","react-test-renderer":"18.3.1",semver:"7.8.5"}};var dle=/^\/(?:stop|steer|version)(?=$|\s)/iu,hle=/^\/stop(?=$|\s)/iu,Ele=/^\/version(?=$|\s)/iu,Cle="\u7528\u6CD5\uFF1A/stop\uFF08\u4E0D\u5E26\u53C2\u6570\uFF09",ule="\u7528\u6CD5\uFF1A/version\uFF08\u4E0D\u5E26\u53C2\u6570\uFF09",Ble="\u7528\u6CD5\uFF1A/steer <\u8865\u5145\u6307\u4EE4>",fle="\u63A7\u5236\u547D\u4EE4\u4EC5\u652F\u6301\u7EAF\u6587\u5B57\uFF0C\u8BF7\u79FB\u9664\u56FE\u7247\u540E\u91CD\u8BD5\u3002";function Bo(n,t={}){return{message:n,...t}}function jK(n){return n?{signal:n}:{}}function JK(n,t,o){if(typeof t?.sessionFor!="function")return null;let i=t.sessionFor(o);if(typeof i!="string"||!i)return null;if(typeof n?.workspaceSession!="function")throw new TypeError("Harness does not support workspace sessions");let e=n.workspaceSession(i);if(!e||typeof e!="object")throw new TypeError("Harness returned an invalid workspace session");return e}function Yi(n){return typeof n=="string"&&dle.test(n.trim())}async function Ci(n,t,o,i,{signal:e,hasImages:a=!1,pendingInteraction:A=!1,control:r}={}){if(!Yi(n))return null;let s=n.trim(),g=hle.test(s),c=Ele.test(s);if(a)return Bo(D(fle));if(c)return/^\/version$/iu.test(s)?Bo(`dsh-im v${Hf.version}`):Bo(D(ule));if(g){if(!/^\/stop$/iu.test(s))return Bo(D(Cle));let Q=JK(t,o,i);if(!Q)return Bo(D("\u5F53\u524D\u804A\u5929\u6CA1\u6709\u6B63\u5728\u8FD0\u884C\u7684\u4EFB\u52A1\u3002"));if(typeof Q.stopActiveTurn!="function")throw new TypeError("Harness session does not support stopping active turns");return await Q.stopActiveTurn(r,jK(e))?Bo(D("\u5DF2\u8BF7\u6C42\u505C\u6B62\u5F53\u524D\u4EFB\u52A1\u3002"),{stopped:!0}):Bo(D("\u5F53\u524D\u804A\u5929\u6CA1\u6709\u6B63\u5728\u8FD0\u884C\u7684\u4EFB\u52A1\u3002"))}let d=/^\/steer(?:\s+([\s\S]*))?$/iu.exec(s)?.[1]?.trim()??"";if(!d)return Bo(D(Ble));if(A)return Bo([D("\u5F53\u524D\u4EFB\u52A1\u6B63\u5728\u7B49\u5F85\u4F60\u7684\u56DE\u7B54\u6216\u5BA1\u6279\u3002"),"",D("\u8BF7\u5148\u5904\u7406\u5F53\u524D\u8BF7\u6C42\uFF0C\u6216\u8005\u53D1\u9001 /stop \u505C\u6B62\u4EFB\u52A1\u3002")].join(` `));let B=JK(t,o,i);if(!B)return Bo(D("\u5F53\u524D\u804A\u5929\u6CA1\u6709\u7ED1\u5B9A\u4F1A\u8BDD\uFF0C\u65E0\u6CD5\u8865\u5145\u6307\u4EE4\u3002\u8BF7\u5148\u7ED1\u5B9A\u4F1A\u8BDD\u3002"));if(typeof B.steerActiveTurn!="function")throw new TypeError("Harness session does not support steering active turns");let f=await B.steerActiveTurn(d,r,jK(e));return Bo(f?D("\u5DF2\u63D0\u4EA4\u8865\u5145\u6307\u4EE4\uFF0CAgent \u4F1A\u5728\u4E0B\u4E00\u6B65\u8BFB\u53D6\u3002"):D("\u4EFB\u52A1\u5DF2\u7ED3\u675F\uFF0C\u6CA1\u6709\u6B63\u5728\u8FD0\u884C\u7684\u4EFB\u52A1\uFF0C\u65E0\u6CD5\u8865\u5145\u6307\u4EE4\u3002\u8BF7\u76F4\u63A5\u53D1\u9001\u6D88\u606F\u5F00\u59CB\u65B0\u4EFB\u52A1\u3002"))}var Qle=/^\/model(?=$|\s)/i,Ax=/^\/models(?=$|\s)/i,tx=/^\/reasoning(?=$|\s)/i,rx=/^\/reasonings(?=$|\s)/i,ax=/^\/reasoninglist(?=$|\s)/i,qK="\u7528\u6CD5\uFF1A/model <\u5E8F\u53F7\u6216 provider/model> [\u63A8\u7406\u7B49\u7EA7ID]",yle="\u7528\u6CD5\uFF1A/models\uFF08\u4E0D\u5E26\u53C2\u6570\uFF09",mle="\u7528\u6CD5\uFF1A/reasoning [\u5E8F\u53F7\u3001\u7B49\u7EA7ID\u6216 --default]",Sle="\u7528\u6CD5\uFF1A/reasoninglist \u6216 /reasonings\uFF08\u4E0D\u5E26\u53C2\u6570\uFF09",ix="session-binding-changed",ox="model-selection-mismatch",Dle=/[\p{Cc}\p{Cf}\p{Zl}\p{Zp}]+/gu;function ht(n){return{handled:!0,message:n,messages:Js(n)}}function Wr(n){return typeof n!="string"?"":n.replace(Dle," ").replace(/\s+/gu," ").trim()}function wle(n){return n?{signal:n}:{}}function Ole(n){if(n===void 0)return;if(!n||typeof n!="object"||!Array.isArray(n.efforts)||n.efforts.length===0)throw new TypeError("Harness returned invalid model reasoning metadata");let t=n.efforts.map(o=>{if(!o||typeof o!="object"||typeof o.id!="string"||!o.id||typeof o.name!="string"||!o.name||o.description!==void 0&&typeof o.description!="string")throw new TypeError("Harness returned an invalid reasoning effort");return{id:o.id,name:o.name,...o.description===void 0?{}:{description:o.description}}});if(n.defaultEffort!==void 0&&(typeof n.defaultEffort!="string"||!n.defaultEffort))throw new TypeError("Harness returned an invalid default reasoning effort");return{efforts:t,...n.defaultEffort===void 0?{}:{defaultEffort:n.defaultEffort}}}function nx(n,{requireCurrent:t=!1}={}){if(!n||typeof n!="object"||!Array.isArray(n.groups)||!Array.isArray(n.failures))throw new TypeError("Harness returned an invalid model catalog");let o=n.groups.map(a=>{if(!a||typeof a!="object"||typeof a.id!="string"||!a.id||typeof a.name!="string"||!a.name||!Array.isArray(a.models))throw new TypeError("Harness returned an invalid model provider group");return{id:a.id,name:a.name,models:a.models.map(A=>{if(!A||typeof A!="object"||typeof A.id!="string"||!A.id||typeof A.name!="string"||!A.name||A.description!==void 0&&typeof A.description!="string")throw new TypeError("Harness returned an invalid model");return{id:A.id,name:A.name,...A.description===void 0?{}:{description:A.description},...A.reasoning===void 0?{}:{reasoning:Ole(A.reasoning)}}})}}),i=n.failures.map(a=>{if(!a||typeof a!="object"||typeof a.id!="string"||!a.id||typeof a.name!="string"||!a.name)throw new TypeError("Harness returned an invalid model provider failure");return{id:a.id,name:a.name}}),e=null;if(n.current!==void 0){if(!n.current||typeof n.current!="object"||typeof n.current.provider!="string"||!n.current.provider||typeof n.current.model!="string"||!n.current.model||n.current.reasoningEffort!==void 0&&(typeof n.current.reasoningEffort!="string"||!n.current.reasoningEffort))throw new TypeError("Harness returned an invalid current model");e={provider:n.current.provider,model:n.current.model,...n.current.reasoningEffort===void 0?{}:{reasoningEffort:n.current.reasoningEffort}}}else if(t)throw new TypeError("Harness returned no current model");return{groups:o,failures:i,current:e}}function qs(n,t){return`${n}/${t}`}function sx(n,t){return n?.provider===t?.provider&&n?.model===t?.model}function ble(n,t){return sx(n,t)&&n?.reasoningEffort===t?.reasoningEffort}function Mle(n,t){return sx(n,t)&&(t.reasoningEffort===void 0||n?.reasoningEffort===t.reasoningEffort)}function SM(n){if(!n?.provider||!n?.model)return"";let t=qs(n.provider,n.model);return n.reasoningEffort===void 0?t:`${t} \xB7 reasoningEffort=${Wr(n.reasoningEffort)}`}function zK(n,t,o){let i=new Error(`Harness ${o} did not confirm the selected model`);return i.code=ox,i.expected=n,i.actual=t,i.source=o,i}function gx(){let n=new Error("Conversation binding changed during model selection");return n.code=ix,n}function VK(n,t,o){if((typeof n?.sessionFor=="function"?n.sessionFor(t):null)!==o)throw gx()}function _le(n,t){for(let o of n.groups)for(let i of o.models)if(qs(o.id,i.id)===t)return{provider:o.id,model:i.id};return null}function Tle(n,t){let o=0;for(let i of n.groups)for(let e of i.models)if(o+=1,o===t)return{provider:i.id,model:e.id};return null}function WK(n){if(!/^\d+$/u.test(n))return null;let t=Number(n);return{index:Number.isSafeInteger(t)&&t>0?t:null}}function Kf(n,t){return t?n.groups.find(({id:i})=>i===t.provider)?.models.find(({id:i})=>i===t.model)??null:null}function Nle(n,t){return n?.reasoning?.efforts?.[t-1]??null}function DM(n,t){return n?.reasoning?.efforts?.find(({id:o})=>o===t)??null}function xf(n,t){return n?.reasoningEffort??t?.reasoning?.defaultEffort}function pE(n,t){if(t===void 0)return D("Default\uFF08\u7531\u6A21\u578B\u6216 Provider \u51B3\u5B9A\uFF09");let o=DM(n,t);if(!o)return Wr(t);let i=Wr(o.name),e=Wr(o.id);return i===e?e:`${i} (${e})`}function cx(n){let t=Kf(n,n.current);return pE(t,xf(n.current,t))}function Rle(n,t,o){return n===t&&n===o?D("\uFF08\u5F53\u524D\u3001\u9ED8\u8BA4\uFF09"):n===t?D("\uFF08\u5F53\u524D\uFF09"):n===o?D("\uFF08\u9ED8\u8BA4\uFF09"):""}function ZK(n){return[D("\u6A21\u578B\u5E8F\u53F7\u65E0\u6548\uFF1A{input}",{input:Wr(n)}),"",D("\u8BF7\u53D1\u9001 /models \u67E5\u770B\u5E76\u8F93\u5165\u6709\u6548\u7684\u6B63\u6574\u6570\u5E8F\u53F7\u3002")].join(` `)}function XK(n){return[D("\u63A8\u7406\u7B49\u7EA7\u5E8F\u53F7\u65E0\u6548\uFF1A{input}",{input:Wr(n)}),"",D("\u8BF7\u53D1\u9001 /reasoninglist \u67E5\u770B\u5E76\u8F93\u5165\u6709\u6548\u7684\u6B63\u6574\u6570\u5E8F\u53F7\u3002")].join(` `)}function QM(n,t,o){let i=[D("\u6A21\u578B\u4E0D\u652F\u6301\u63A8\u7406\u7B49\u7EA7\uFF1A{effort}",{effort:Wr(t)}),"",Wr(SM(n))],e=o?.reasoning?.efforts?.map(({id:a})=>Wr(a))??[];return e.length>0?i.push(D("\u53EF\u7528\u63A8\u7406\u7B49\u7EA7\uFF1A{efforts}",{efforts:e.join(", ")})):i.push(D("\u8BE5\u6A21\u578B\u4E0D\u63D0\u4F9B\u53EF\u5207\u6362\u7684\u63A8\u7406\u7B49\u7EA7\u3002")),i.join(` diff --git a/package-lock.json b/package-lock.json index 472d390..ff14e86 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@xmanrui/dsh-im", - "version": "4.0.0", + "version": "4.0.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@xmanrui/dsh-im", - "version": "4.0.0", + "version": "4.0.1", "license": "MIT", "dependencies": { "@tencent-connect/qqbot-connector": "1.2.0", diff --git a/package.json b/package.json index e269e4e..b29309d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@xmanrui/dsh-im", - "version": "4.0.0", + "version": "4.0.1", "description": "把九种 IM 机器人和公网 AI Office 接入本机 DeepSeek Harness。 Connect nine IM channels and a public AI Office to a local DeepSeek Harness.", "keywords": [ "deepseek-harness", diff --git a/plugin-src/client/index.js b/plugin-src/client/index.js index 48b3b6b..d371ee1 100644 --- a/plugin-src/client/index.js +++ b/plugin-src/client/index.js @@ -55,6 +55,16 @@ export const name = 'im-settings'; export const inject = ['slots', 'connection', 'locale', 'workspaces']; export const IM_PLUGIN_VERSION = manifest.version; +function callWorkspaceDirectoryApi(ctx, method, ...args) { + // Current DSH owns directory operations on uiWorkspace; legacy Hosts keep them on workspaces. + const uiWorkspace = typeof ctx.get === 'function' ? ctx.get('uiWorkspace') : undefined; + const service = typeof uiWorkspace?.[method] === 'function' ? uiWorkspace : ctx.workspaces; + if (typeof service?.[method] !== 'function') { + throw new Error('无法读取目录,请重试。'); + } + return service[method](...args); +} + const CHANNELS = Object.freeze([ { id: 'weixin', label: '微信' }, { id: 'feishu', label: '飞书' }, @@ -335,8 +345,9 @@ export function apply(ctx) { const updateRpcCall = (endpoint, payload, signal) => ctx.connection.rpc.call(UPDATE_RPC_CHANNEL, endpoint, payload, signal); const workspaceDirectoryPicker = Object.freeze({ - listDirectory: (path, signal) => ctx.workspaces.listDirectory(path, signal), - pickDirectory: () => ctx.workspaces.pickDirectory(), + listDirectory: (path, signal) => + callWorkspaceDirectoryApi(ctx, 'listDirectory', path, signal), + pickDirectory: () => callWorkspaceDirectoryApi(ctx, 'pickDirectory'), }); ctx.slots.inject('settings.section', () => ctx.slots.register({ diff --git a/test/client-ui.test.mjs b/test/client-ui.test.mjs index f166eb3..98ff388 100644 --- a/test/client-ui.test.mjs +++ b/test/client-ui.test.mjs @@ -792,6 +792,71 @@ test('client registers one top-level bilingual IM settings section with a direct } }); +test('client directory picker uses the current DSH uiWorkspace service', async () => { + const registrations = []; + const directoryCalls = []; + let uiWorkspace; + const ctx = { + effect() {}, + get(name) { + assert.equal(name, 'uiWorkspace'); + return uiWorkspace; + }, + locale: { + bind: () => (key) => key, + register: () => () => {}, + }, + connection: { rpc: { call: async () => ({ ok: true, value: {} }) } }, + workspaces: { + list: { + getSnapshot: () => ({ items: [] }), + subscribe: () => () => {}, + }, + }, + slots: { + inject(name, install) { + assert.equal(name, 'settings.section'); + install(); + }, + register(options, component) { + registrations.push({ options, component }); + return () => {}; + }, + }, + }; + + try { + applyClient(ctx); + uiWorkspace = { + async listDirectory(path, signal) { + directoryCalls.push({ operation: 'list', path, signal }); + return { path, entries: [] }; + }, + async pickDirectory() { + directoryCalls.push({ operation: 'pick' }); + return '/workspace/current-host'; + }, + }; + + const injected = registrations[0].options.inject(); + const signal = new AbortController().signal; + assert.deepEqual( + await injected.workspaceDirectoryPicker.listDirectory('/workspace/current', signal), + { path: '/workspace/current', entries: [] }, + ); + assert.equal( + await injected.workspaceDirectoryPicker.pickDirectory(), + '/workspace/current-host', + ); + assert.deepEqual(directoryCalls, [ + { operation: 'list', path: '/workspace/current', signal }, + { operation: 'pick' }, + ]); + } finally { + setImTranslator(null); + } +}); + test('all nine channel settings and connected cards render English copy', () => { const rpcCall = async () => ({ ok: true, value: {} }); const noop = () => {};