From c44340accb02ddd7ee9f9d44a1b40fd5e7ab0c4e Mon Sep 17 00:00:00 2001 From: xmanrui <841206367@qq.com> Date: Tue, 18 Aug 2026 00:03:33 +0800 Subject: [PATCH] release: prepare dsh-im 0.5.0 --- README.md | 30 ++++++++++++++++++++++-- lib/index.js | 4 ++-- package-lock.json | 4 ++-- package.json | 2 +- src/channels/discord/discord-api.mjs | 2 +- src/channels/weixin/weixin-api.mjs | 2 +- test/channels/weixin/weixin-api.test.mjs | 2 +- 7 files changed, 36 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index a411d70..8ce1612 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,20 @@ QQ 扫码接入使用腾讯 QQBot v2 官方流程。默认腾讯授权页会把 飞书扫码绑定会把扫码者作为允许使用者;手动凭据同样无法识别扫码人,因此使用飞书应用的可见范围作为入站访问范围。请在飞书开放平台中只向信任的租户、群或成员开放应用。 -每个机器人维护独立的 Harness 工作区。新接入机器人会把 Harness Host 进程当时的工作目录(`process.cwd()`)记录为默认值;该路径会持久化,不会因为以后从其他目录重启 Host 而改变。设置页的机器人卡片会显示当前路径,并可直接修改。任何已在对应平台可见范围内、能够正常向机器人发消息的用户都可以发送 `/workspace /工作区绝对路径` 切换工作区,不再区分管理员和普通用户。插件只接受存在的绝对目录;切换成功后会清除该机器人的旧 Harness 会话映射,其他机器人不受影响。切换只对后续消息生效;已经开始生成的回复会继续完成。 +每个机器人维护独立的 Harness 工作区。新接入机器人会把 Harness Host 进程当时的工作目录(`process.cwd()`)记录为默认值;该路径会持久化,不会因为以后从其他目录重启 Host 而改变。设置页的机器人卡片会显示当前路径,并可直接修改。 + +## 机器人命令 + +| 命令 | 作用 | +| --- | --- | +| `/workspace <工作区绝对路径>` | 切换当前机器人的 Harness 工作区。 | + +示例:`/workspace /Users/alice/projects/my-app` + +- 只接受已经存在的绝对目录;路径无效时机器人会返回具体提示和正确用法。 +- 任何已在对应平台可见范围内、能够正常向机器人发消息的用户都可以执行,不区分管理员和普通用户。 +- 切换成功后只清除当前机器人的旧 Harness 会话映射,不影响其他机器人。 +- 新工作区对后续消息生效;已经开始生成的回复会继续完成。 ## 设计 @@ -152,7 +165,20 @@ QQ QR binding uses Tencent's official QQBot v2 flow. Tencent's default authoriza Feishu QR binding records the scanner as an allowed user. Manual credentials cannot identify a scanner, so the Feishu application's visibility becomes its inbound access scope. Restrict the application to trusted tenants, groups, or members. -Each bot maintains an independent Harness workspace. A newly connected bot records the Harness Host process's current working directory (`process.cwd()`) as its default; the path is persisted and does not change when the Host is later restarted from another directory. Every bot card shows the current path and lets it be edited. Any user who is already within that platform bot's visibility scope and can normally message the bot can send `/workspace /absolute/path` to switch it, with no additional administrator/ordinary-user distinction. Only existing absolute directories are accepted. A successful switch clears that bot's old Harness session mappings without affecting other bots. The switch applies to subsequent messages; a reply that has already started generating is allowed to finish. +Each bot maintains an independent Harness workspace. A newly connected bot records the Harness Host process's current working directory (`process.cwd()`) as its default; the path is persisted and does not change when the Host is later restarted from another directory. Every bot card shows the current path and lets it be edited. + +## Bot commands + +| Command | Description | +| --- | --- | +| `/workspace ` | Switch the current bot's Harness workspace. | + +Example: `/workspace /Users/alice/projects/my-app` + +- The path must be an existing absolute directory. The bot returns an actionable error and the correct usage when validation fails. +- Any user who is already within the platform bot's visibility scope and can normally message it can run the command; there is no additional administrator/ordinary-user distinction. +- A successful switch clears only the current bot's old Harness session mappings and does not affect other bots. +- The new workspace applies to subsequent messages; a reply that has already started generating is allowed to finish. ## Design diff --git a/lib/index.js b/lib/index.js index df6dd90..1530db9 100644 --- a/lib/index.js +++ b/lib/index.js @@ -66,7 +66,7 @@ ${T}`),f.pop(),`{${x}}`}case"number":return isFinite(Q)?String(Q):a?a(Q):"null"; `).trim();c&&c!==this.#o&&(this.#o=c,o={type:"text",text:c});continue}if(e.type==="assistant/message"){let A=y$(e);A&&A!==this.#o&&(this.#o=A,o={type:"text",text:A});continue}e.type==="tool/call"?o={type:"tool",name:e.data?.name??"\u5DE5\u5177"}:e.type==="tool/result"&&(o={type:"status",text:"\u6B63\u5728\u6574\u7406\u7ED3\u679C\u2026"})}}}}return o}},vh=class extends Error{constructor(a,o){super(`${a}: ${o?.message??"unknown Harness RPC error"}`),this.name="HarnessRpcError",this.method=a,this.code=o?.code??"internal",this.details=o?.details??{}}},Ph=class{#A;#e;#t;#a;#s;#o;#I=null;constructor({baseUrl:a,workspace:o,agentPreset:i="standard",autostart:e=!1,dshBin:r="dsh",fetchImpl:A=fetch}){this.#A=new URL(a),this.#e=o,this.#t=i,this.#a=e,this.#s=r,this.#o=A}async rpc(a,o={},i=3e4,e={}){let r=e.rpcId??`dingtalk-${Nv()}`,A=AbortSignal.timeout(i),t=e.signal?AbortSignal.any([e.signal,A]):A,s=await this.#o(new URL(`/api/${a}`,this.#A),{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({type:"client-request",rpcId:r,method:a,payload:o}),signal:t});if(!s.ok)throw new Error(`Harness transport ${a} failed: HTTP ${s.status}`);let g=await s.json();if(g?.type!=="server-response"||g?.rpcId!==r)throw new Error(`Harness returned an invalid response for ${a}`);if(!g.result?.ok)throw new vh(a,g.result?.error);return g.result.value}async health(a={}){return await this.rpc("host.describe",{},5e3,a),!0}async ensureRunning(a={}){try{return await this.health(a)}catch(e){if(!this.#a)throw e}if(!this.#I||this.#I.exitCode!==null){let e=this.#A.port||(this.#A.protocol==="https:"?"443":"80");this.#I=f$(this.#s,["web","--host",this.#A.hostname,"--port",e],{cwd:this.#e,env:process.env,stdio:["ignore","inherit","inherit"]}),this.#I.on("error",r=>{console.error("[dsh-dingtalk] failed to start Harness:",r.message)})}let o=Date.now()+6e4,i;for(;Date.now()t.path===o);return r?r.workspaceId:(await this.rpc("workspace.create",{path:o},3e4,i)).workspace.workspaceId}async createSession(a={}){await this.ensureRunning(a);let o=await this.workspaceId(a);return(await this.rpc("session.create",{workspaceId:o,agentPreset:this.#t},3e4,a)).sessionId}async sessionExists(a,o={}){try{return await this.rpc("session.history",{sessionId:a,maxMessages:1},3e4,o),!0}catch(i){if(i instanceof vh&&i.code==="session-not-found")return!1;throw i}}async ask(a,o,i={}){typeof i=="number"&&(i={timeoutMs:i});let e=i.timeoutMs??6e5,r=i.signal,A=typeof i.onUpdate=="function"?i.onUpdate:null;await this.ensureRunning({signal:r});let t=await this.rpc("session.history",{sessionId:a,maxMessages:1},3e4,{signal:r}),s=Math.max(-1,...(t.events??[]).map(({event:C})=>C.seq??-1)),g=`dingtalk-${Nv()}`,c=new oS({promptRpcId:g,afterSeq:s});await this.rpc("session.prompt",{sessionId:a,mode:"queue",content:[{type:"text",text:o}],clientTimeZone:Intl.DateTimeFormat().resolvedOptions().timeZone},3e4,{rpcId:g,signal:r});let l=Date.now()+e;for(;Date.now()[e?.requestId,e]):Object.entries(n.pendingSenders&&typeof n.pendingSenders=="object"?n.pendingSenders:{});for(let[e,r]of i){let A=_$(r,e);if(!A)continue;let t=Object.values(o).find(s=>s.staffId===A.staffId);(!t||t.lastSeenAtnew Date().toISOString()}={}){if(!_t(a))throw new TypeError("state path is required");if(typeof o!="function"||typeof i!="function")throw new TypeError("idFactory and now must be functions");this.#A=a,this.#a=o,this.#s=i}async load(){try{this.#e=N$(JSON.parse(await D$(this.#A,"utf8")))}catch(a){if(a?.code!=="ENOENT")throw a;this.#e=structuredClone(Gh),await this.#o()}return this}sessionFor(a){return this.#e.sessions[a]??null}async setSession(a,o){let i=_t(a),e=_t(o);if(!i||!e)throw new TypeError("key and sessionId are required");this.#e.sessions[i]=e,await this.#o()}async clearSession(a){let o=_t(a);!o||!(o in this.#e.sessions)||(delete this.#e.sessions[o],await this.#o())}async clearSessions(){this.#e.sessions={},await this.#o()}hasSeen(a){let o=_t(a);return!!(o&&this.#e.seenMessageIds.includes(o))}async markSeen(a){let o=_t(a);if(!o)throw new TypeError("messageId is required");this.hasSeen(o)||(this.#e.seenMessageIds.push(o),this.#e.seenMessageIds.length>1e3&&this.#e.seenMessageIds.splice(0,this.#e.seenMessageIds.length-1e3),await this.#o())}pendingSenders(){return Object.values(this.#e.pendingSenders).sort((a,o)=>a.requestedAt.localeCompare(o.requestedAt)).map(a=>structuredClone(a))}pendingSender(a){let o=_t(a),i=o?this.#e.pendingSenders[o]:null;return i?structuredClone(i):null}async recordPendingSender(a,o,i){let e=a&&typeof a=="object"?a:{staffId:a,displayName:o,lastSeenAt:i},r=_t(e.staffId);if(!r)throw new TypeError("staffId is required");let A=_t(e.lastSeenAt)??_t(e.requestedAt)??this.#s(),t=Object.values(this.#e.pendingSenders).find(g=>g.staffId===r),s={requestId:t?.requestId??`ding_sender_${this.#a()}`,staffId:r,displayName:Rv(e.displayName??e.nick??o),requestedAt:t?.requestedAt??A,lastSeenAt:A};return this.#e.pendingSenders[s.requestId]=s,await this.#o(),structuredClone(s)}async removePendingSender(a){let o=_t(a);return!o||!this.#e.pendingSenders[o]?!1:(delete this.#e.pendingSenders[o],await this.#o(),!0)}async removePendingSenderByStaffId(a){let o=_t(a),i=o?Object.values(this.#e.pendingSenders).find(e=>e.staffId===o):null;return i?this.removePendingSender(i.requestId):!1}snapshot(){return structuredClone(this.#e)}async remove(){await this.#t;try{await O$(this.#A)}catch(a){if(a?.code!=="ENOENT")throw a}this.#e=structuredClone(Gh)}async#o(){let a=`${JSON.stringify(this.#e,null,2)} `,o=this.#t.then(async()=>{await S$(b$(this.#A),{recursive:!0,mode:448});let i=`${this.#A}.tmp`;await M$(i,a,{encoding:"utf8",mode:384}),await w$(i,this.#A)});this.#t=o.then(()=>{},()=>{}),await o}};import{mkdir as T$,readFile as R$,rename as k$,stat as v$,unlink as P$,writeFile as G$}from"node:fs/promises";import{dirname as F$,isAbsolute as vv,resolve as Lh}from"node:path";var HPe=Object.freeze({version:1,workspaces:Object.freeze({})});function Ci(n){if(typeof n!="string"||!/^[A-Za-z0-9_-]{1,128}$/.test(n))throw new TypeError("Invalid bot id");return n}function L$(n){if(!n||n.version!==1||!n.workspaces||typeof n.workspaces!="object"||Array.isArray(n.workspaces))return null;let a={};for(let[o,i]of Object.entries(n.workspaces)){if(!/^[A-Za-z0-9_-]{1,128}$/.test(o)||typeof i!="string"||!vv(i))return null;a[o]=Lh(i)}return{version:1,workspaces:a}}async function U$(n){if(typeof n!="string"||!n.trim()||!vv(n.trim())){let i=new Error("\u5DE5\u4F5C\u533A\u5FC5\u987B\u662F\u7EDD\u5BF9\u8DEF\u5F84\u3002");throw i.code="workspace-not-absolute",i}let a=Lh(n.trim()),o;try{o=await v$(a)}catch(i){let e=new Error("\u5DE5\u4F5C\u533A\u8DEF\u5F84\u4E0D\u5B58\u5728\u3002",{cause:i});throw e.code="workspace-not-found",e}if(!o.isDirectory()){let i=new Error("\u5DE5\u4F5C\u533A\u8DEF\u5F84\u5FC5\u987B\u6307\u5411\u4E00\u4E2A\u76EE\u5F55\u3002");throw i.code="workspace-not-directory",i}return a}var ga=class{#A;#e;#t={};#a=new Map;#s=1;#o=new Map;#I=1;#g=new Map;#n=new WeakMap;#i=new Set;#r=Promise.resolve();#c=new Map;constructor(a,{defaultWorkspace:o=process.cwd()}={}){if(typeof a!="string"||!a)throw new TypeError("workspace store path is required");this.#A=a,this.#e=Lh(o)}async load(){try{let a=L$(JSON.parse(await R$(this.#A,"utf8")));if(!a)throw new Error("dsh-im workspace config is invalid");this.#t=a.workspaces}catch(a){if(a?.code!=="ENOENT")throw a;this.#t={}}this.#a.clear(),this.#s=1,this.#o.clear(),this.#I=1,this.#g.clear(),this.#i.clear();for(let a of Object.keys(this.#t))this.#a.set(a,this.#l()),this.#o.set(a,this.#E());return this}has(a){let o=Ci(a);return Object.hasOwn(this.#t,o)&&!this.#g.has(o)}incarnationFor(a){return this.#o.get(Ci(a))??null}workspaceFor(a){return this.#t[Ci(a)]??this.#e}generationFor(a){return this.#a.get(Ci(a))??null}async whenIdle(){await this.#r}async whenBotIdle(a){let o=Ci(a);for(;;){let i=this.#c.get(o);if(!i||(await i,this.#c.get(o)===i))return}}async ensure(a,{workspace:o=this.#e}={}){let i=Ci(a),e=Lh(o);return this.#h(i,async()=>{if(this.#t[i])this.#a.has(i)||this.#a.set(i,this.#l());else{this.#t[i]=e,this.#a.set(i,this.#l()),this.#o.set(i,this.#E());try{await this.#d()}catch(r){throw delete this.#t[i],this.#a.delete(i),this.#o.delete(i),r}}return this.#t[i]})}async setWorkspace(a,o,{clearSessions:i,incarnation:e}={}){let r=Ci(a);if(!this.has(r)||e!==void 0&&e!==this.incarnationFor(r)){let t=new Error("\u627E\u4E0D\u5230\u8981\u4FEE\u6539\u7684\u673A\u5668\u4EBA\u3002");throw t.code="workspace-bot-not-found",t}let A=await U$(o);return this.#h(r,async()=>{if(!this.has(r)||e!==void 0&&e!==this.incarnationFor(r)){let s=new Error("\u627E\u4E0D\u5230\u8981\u4FEE\u6539\u7684\u673A\u5668\u4EBA\u3002");throw s.code="workspace-bot-not-found",s}if(A===this.workspaceFor(r))return A;let t=this.#t[r];this.#a.set(r,this.#l()),await i?.(),this.#t[r]=A;try{await this.#d()}catch(s){throw this.#t[r]=t,s}return A})}async invalidateSessions(a,{clearSessions:o}={}){let i=Ci(a);return this.#h(i,async()=>{this.#a.set(i,this.#l()),await o?.()})}async beginRemoval(a,{clearSessions:o}={}){let i=Ci(a);return this.#h(i,async()=>{let e=this.#g.get(i);if(e)return e;let r=Object.freeze({});this.#g.set(i,r),this.#n.set(r,{botId:i,incarnation:this.incarnationFor(i)}),this.#a.set(i,this.#l());try{await o?.()}catch(A){throw this.#g.get(i)===r&&this.#g.delete(i),A}return r})}async abortRemoval(a){let{botId:o}=this.#C(a);return this.#h(o,async()=>this.#g.get(o)!==a?!1:(this.#g.delete(o),Object.hasOwn(this.#t,o)&&(this.#a.set(o,this.#l()),this.#o.has(o)||this.#o.set(o,this.#E())),!0))}async finishRemoval(a){let{botId:o,incarnation:i}=this.#C(a);return this.#h(o,async()=>this.#g.get(o)!==a?{removed:!1,persisted:!0,error:null,stale:!0}:this.incarnationFor(o)!==i?(this.#g.delete(o),{removed:!1,persisted:!0,error:null,stale:!0}):(this.#g.delete(o),this.#p(o)))}async retireAfterConfigCommit(a){let o=Ci(a);return this.#h(o,async()=>(this.#g.delete(o),this.#p(o)))}async remove(a){let o=await this.retireAfterConfigCommit(a);if(o.error)throw o.error;return o.removed}async reconcile(a){let o=new Set([...a].map(Ci)),i=new Set([...Object.keys(this.#t),...this.#i]);for(let e of i)o.has(e)||await this.remove(e)}decorateStatus(a){return!a||typeof a!="object"||!Array.isArray(a.bots)?a:{...a,bots:a.bots.map(o=>o?.botId?{...o,workspace:this.workspaceFor(o.botId)}:o)}}#l(){let a=this.#s;return this.#s+=1,a}#E(){let a=this.#I;return this.#I+=1,a}#C(a){if(!a||typeof a!="object")throw new TypeError("Invalid workspace removal transaction");let o=this.#n.get(a);if(!o)throw new TypeError("Invalid workspace removal transaction");return o}async#p(a){let o=Object.hasOwn(this.#t,a),i=o||this.#i.has(a);if(delete this.#t[a],this.#a.delete(a),this.#o.delete(a),!i)return{removed:!1,persisted:!0,error:null,stale:!1};try{return await this.#u(),{removed:o,persisted:!0,error:null,stale:!1}}catch(e){return this.#i.add(a),{removed:o,persisted:!1,error:e,stale:!1}}}async#h(a,o){let i=this.#r.then(o,o),e=i.then(()=>{},()=>{});return this.#r=e,this.#c.set(a,e),e.finally(()=>{this.#c.get(a)===e&&this.#c.delete(a)}),i}async#d(){let a={version:1,workspaces:this.#t};await T$(F$(this.#A),{recursive:!0,mode:448});let o=`${this.#A}.tmp`;await G$(o,`${JSON.stringify(a,null,2)} `,{encoding:"utf8",mode:384}),await k$(o,this.#A),this.#i.clear()}async#u(){if(Object.keys(this.#t).length>0){await this.#d();return}try{await P$(this.#A),this.#i.clear()}catch(a){if(a?.code!=="ENOENT")throw a;this.#i.clear()}}};function kv(n,a){return a&&typeof a.then=="function"?a.then(o=>n.decorateStatus(o)):n.decorateStatus(a)}function H$(n){return Promise.resolve(n.status())}function Ga(n,{workspaces:a,method:o="remove",botIdFromRemoved:i=e=>e?.botId}){if(!n||!a||typeof n[o]!="function")throw new TypeError("configStore removal observer dependencies are required");return new Proxy(n,{get(e,r){let A=Reflect.get(e,r,e);return r===o?async(...t)=>{let s=await A.apply(e,t),g=s?i(s,t):null;return g&&await a.retireAfterConfigCommit(g),s}:typeof A=="function"?A.bind(e):A}})}function Fa(n,{botId:a,workspaces:o,state:i}){if(!n||!o||!i)throw new TypeError("harness, workspaces, and state are required");let e=o.incarnationFor(a),r=()=>o.has(a)&&o.incarnationFor(a)===e,A=new Map,t=new Proxy(n,{get(g,c){if(c==="currentWorkspace")return()=>o.workspaceFor(a);if(c==="switchWorkspace")return C=>{if(!r()){let u=new Error("\u627E\u4E0D\u5230\u8981\u4FEE\u6539\u7684\u673A\u5668\u4EBA\u3002");return u.code="workspace-bot-not-found",Promise.reject(u)}return o.setWorkspace(a,C,{clearSessions:()=>i.clearSessions(),incarnation:e})};if(c==="createSession")return async(C={})=>{if(await o.whenBotIdle(a),!r()){let f=new Error("\u627E\u4E0D\u5230\u8981\u4FEE\u6539\u7684\u673A\u5668\u4EBA\u3002");throw f.code="workspace-bot-not-found",f}let u=o.generationFor(a),Q=await g.createSession({...C,workspace:o.workspaceFor(a)});return A.set(Q,u),Q};if(c==="sessionExists")return(C,...u)=>{if(!r())return!1;let Q=A.get(C);return Q!==void 0&&Q!==o.generationFor(a)?(A.delete(C),!1):g.sessionExists(C,...u)};if(c==="ask")return(C,...u)=>{let Q=A.get(C);if(A.delete(C),!r()||Q!==void 0&&Q!==o.generationFor(a)){let f=new Error("The bot workspace changed before this prompt started.");throw f.code=rS,f}return g.ask(C,...u)};let l=Reflect.get(g,c,g);return typeof l=="function"?l.bind(g):l}}),s=new Proxy(i,{get(g,c){if(c==="sessionFor")return(C,...u)=>{if(!r())return null;let Q=g.sessionFor(C,...u);return Q&&!A.has(Q)&&A.set(Q,o.generationFor(a)),Q};if(c==="setSession")return(C,u,...Q)=>{let f=A.get(u);return!r()||f!==void 0&&f!==o.generationFor(a)?(A.delete(u),!1):g.setSession(C,u,...Q)};let l=Reflect.get(g,c,g);return typeof l=="function"?l.bind(g):l}});return Object.freeze({harness:t,state:s})}function La(n,{workspaces:a,stateFor:o}){if(!n||!a||typeof o!="function")throw new TypeError("controller, workspaces, and stateFor are required");let i=new Map,e=(t,s)=>{let c=(i.get(t)??Promise.resolve()).catch(()=>{}).then(s);return i.set(t,c),c.finally(()=>{i.get(t)===c&&i.delete(t)})},r=(t,s)=>{let g=a.incarnationFor(t);return e(t,async()=>{if(!(await n.status())?.bots?.some(C=>C?.botId===t)){let C=new Error("\u627E\u4E0D\u5230\u8981\u4FEE\u6539\u7684\u673A\u5668\u4EBA\u3002");throw C.code="workspace-bot-not-found",C}let l=await o(t);return await a.setWorkspace(t,s,{clearSessions:()=>l.clearSessions(),incarnation:g}),a.decorateStatus(await n.status())})},A=(t,s)=>e(t,async()=>{let g=await a.beginRemoval(t,{clearSessions:async()=>{try{let c=await o(t);if(!c||typeof c.clearSessions!="function")throw new TypeError("bot state does not support session cleanup");await c.clearSessions()}catch(c){console.warn(`[dsh-im] ignored session cleanup failure while deleting bot ${t}:`,c?.message??c)}}});try{let c=await s();return await a.finishRemoval(g),a.decorateStatus(c)}catch(c){let l=await H$(n).catch(()=>null);throw Array.isArray(l?.bots)&&!l.bots.some(u=>u?.botId===t)?await a.finishRemoval(g):await a.abortRemoval(g),c}});return new Proxy(n,{get(t,s){if(s==="updateWorkspace")return r;let g=Reflect.get(t,s,t);return typeof g!="function"?g:s==="deleteBot"?(c,...l)=>A(c,()=>g.call(t,c,...l)):s==="disconnect"?async(...c)=>{let C=(await t.status())?.bots?.[0]?.botId;return C?A(C,()=>g.apply(t,c)):kv(a,g.apply(t,c))}:(...c)=>kv(a,g.apply(t,c))}})}var Pv=Object.freeze([250,1e3,3e3,5e3,1e4,3e4]);function K$(n){if(!Array.isArray(n)||n.length===0)return[...Pv];let a=n.filter(o=>Number.isFinite(o)&&o>=0);return a.length>0?a:[...Pv]}var Uh=class{#A;#e;#t;#a;#s;#o;#I;#g=null;#n=null;#i=0;#r=!1;#c=!1;#l;#E;constructor({controller:a,harness:o,logger:i=console,retryDelaysMs:e,healthyIntervalMs:r=15e3,setTimeoutImpl:A=setTimeout,clearTimeoutImpl:t=clearTimeout}){if(!a||typeof a.initialize!="function"||typeof a.status!="function")throw new TypeError("ConnectionSupervisor requires a controller");if(!o||typeof o.ensureRunning!="function")throw new TypeError("ConnectionSupervisor requires a Harness client");this.#A=a,this.#e=o,this.#t=i,this.#a=K$(e),this.#s=Number.isFinite(r)&&r>=0?r:15e3,this.#o=A,this.#I=t,this.#l=new Promise(s=>{this.#E=s})}get ready(){return this.#l}start(){return this.#c||this.#r?this:(this.#c=!0,this.#C(0),this)}async close(){this.#r||(this.#r=!0,this.#g!==null&&this.#I(this.#g),this.#g=null,await this.#n?.catch(()=>{}),this.#E?.(null),this.#E=null)}#C(a){this.#r||(this.#g=this.#o(()=>{this.#g=null,this.#p()},a),this.#g?.unref?.())}async#p(){if(this.#r||this.#n)return;let a=this.#h();this.#n=a;try{await a}finally{this.#n===a&&(this.#n=null)}}async#h(){try{if(await this.#e.ensureRunning(),this.#r)return;let a=await this.#A.initialize();if(this.#r)return;this.#E?.(a),this.#E=null;let{configured:o,connected:i}=a.totals;if(i65535)throw new Error("dsh-dingtalk requires an initialized DSH webServer port");return new URL(`http://127.0.0.1:${o}`)}function J$(n){let a=Ls(n.dshHome??process.env.DSH_HOME??Zl(x$(),".dsh")),o=Ls(n.dataDir??Zl(a,"integrations","dsh-dingtalk"));return{root:o,config:Ls(n.configPath??Zl(o,"config.json")),bots:Ls(n.botsDir??Zl(o,"bots")),workspaces:Ls(n.workspacesPath??Zl(o,"workspaces.json"))}}async function sS(n,a={},o={}){if(!n?.credentials)throw new TypeError("dsh-dingtalk requires ctx.credentials");if(!n?.webServer)throw new TypeError("dsh-dingtalk requires ctx.webServer");let i=o.ConfigStore??Mh,e=o.DeviceAuth??_h,r=o.StateStore??Fh,A=o.HarnessClient??Ph,t=o.Controller??Jl,s=o.Runtime??Wl,g=o.createConnectionSupervisor??nS,c=typeof n.logger=="function"?n.logger("dsh-dingtalk"):n.logger??console,l=J$(a),C=await new i(l.config).load(),u=Ls(a.workspace??process.cwd()),Q=o.WorkspaceStore??ga,f=o.workspaces??await new Q(l.workspaces,{defaultWorkspace:u}).load(),y=typeof C.list=="function",m=y?C.list():[];y&&await f.reconcile(m.map(k=>k.botId)),await Promise.all(m.map(k=>f.ensure(k.botId)));let T=typeof C.remove=="function"?Ga(C,{workspaces:f}):C,R=o.deviceAuth??new e({baseUrl:a.registrationBaseUrl}),G=new Map,j=k=>Ls(l.bots,k,"state.json"),x=async k=>{let $=G.get(k);return $||($=await new r(j(k)).load(),G.set(k,$)),$},H=new A({baseUrl:j$(n.webServer,a.harnessBaseUrl),workspace:u,agentPreset:a.agentPreset??"standard",autostart:!1,dshBin:a.dshBin??"dsh"}),J=new t({deviceAuth:R,credentials:n.credentials,configStore:T,logger:c,createRuntime:async({botId:k,config:$,clientSecret:U})=>{let b=await x(k);await f.ensure(k);let L=Fa(H,{botId:k,workspaces:f,state:b});return new s({config:$,clientSecret:U,harness:L.harness,state:L.state,replyTimeoutMs:a.replyTimeoutMs??6e5,maxMessageChars:a.maxMessageChars??4e3,connectTimeoutMs:a.connectTimeoutMs??15e3,logger:{error:(...ee)=>c.error?.(`[${k}]`,...ee),warn:(...ee)=>c.warn?.(`[${k}]`,...ee),info:(...ee)=>c.info?.(`[${k}]`,...ee),debug:(...ee)=>c.debug?.(`[${k}]`,...ee)}})},deleteState:async({botId:k})=>{let $=G.get(k);if(G.delete(k),$&&typeof $.remove=="function")await $.remove();else try{await Y$(j(k))}catch(U){if(U?.code!=="ENOENT")throw U}}}),K=La(J,{workspaces:f,stateFor:x}),F=g({controller:K,harness:H,logger:c,retryDelaysMs:a.retryDelaysMs,healthyIntervalMs:a.healthyIntervalMs}).start();return{controller:K,ready:F.ready,async close(){await F.close(),await K.close(),H.stopManagedProcess()}}}import z$ from"qrcode";var q$=new Set(["loopback","trusted-host"]);function Ua(n){if(n===void 0)return"loopback";if(q$.has(n))return n;throw new TypeError('dsh-im rpcAuthority must be "loopback" or "trusted-host"')}import{isAbsolute as V$}from"node:path";var ir="bot.workspace.set";function Ha(n){return n!==null&&typeof n=="object"&&!Array.isArray(n)&&Object.keys(n).every(a=>["botId","workspace"].includes(a))&&typeof n.botId=="string"&&/^[A-Za-z0-9_-]{1,128}$/.test(n.botId)&&typeof n.workspace=="string"&&n.workspace.length<=4096&&V$(n.workspace.trim())}function Ka(n){return["workspace-not-absolute","workspace-not-found","workspace-not-directory","workspace-bot-not-found"].includes(n?.code)?{code:n.code,message:n.message}:null}var Fv="/dingtalk",Ft=Object.freeze({status:"connection.status",beginProvisioning:"provision.begin",pollProvisioning:"provision.poll",cancelProvisioning:"provision.cancel",bindCredentials:"bot.bind-credentials",reconnectBot:"bot.reconnect",deleteBot:"bot.delete",setWorkspace:ir,approveSender:"bot.sender.approve",revokeSender:"bot.sender.revoke"}),Lv=Object.freeze(Object.values(Ft)),W$=new Set(["clientSecret","client_secret","deviceCode","device_code","secretRef","staffId","senderStaffId","verificationUrl","verificationUri","userCode"]);function cS(n){return n!==null&&typeof n=="object"&&!Array.isArray(n)}function bn(n,a){return cS(n)&&Object.keys(n).every(o=>a.includes(o))}function Us(n){return typeof n=="string"&&/^[A-Za-z0-9_-]{1,128}$/.test(n)}function Gv(n,a){return typeof n=="string"&&n.trim().length>0&&n.length<=a}function Z$(n,a){return cS(a)?n===Ft.status?bn(a,[])?null:"connection.status does not accept fields.":n===Ft.beginProvisioning?bn(a,["locale"])&&(a.locale===void 0||a.locale==="zh-CN")?null:"provision.begin received unsupported fields.":[Ft.pollProvisioning,Ft.cancelProvisioning].includes(n)?bn(a,["attemptId"])&&Us(a.attemptId)?null:`${n} requires an attemptId.`:n===Ft.bindCredentials?bn(a,["clientId","clientSecret"])&&Gv(a.clientId,256)&&Gv(a.clientSecret,1024)?null:"bot.bind-credentials requires Client ID and Client Secret.":n===Ft.reconnectBot?bn(a,["botId"])&&Us(a.botId)?null:"bot.reconnect requires a botId.":n===Ft.deleteBot?bn(a,["botId","confirm"])&&Us(a.botId)&&a.confirm===!0?null:"bot.delete requires a botId and confirm=true.":n===Ft.setWorkspace?Ha(a)?null:"\u8BF7\u8F93\u5165\u5DE5\u4F5C\u533A\u7EDD\u5BF9\u8DEF\u5F84\u3002":n===Ft.approveSender?bn(a,["botId","requestId","confirm"])&&Us(a.botId)&&Us(a.requestId)&&a.confirm===!0?null:"bot.sender.approve requires botId, requestId, and confirm=true.":n===Ft.revokeSender?bn(a,["botId","senderKey","confirm"])&&Us(a.botId)&&Us(a.senderKey)&&a.confirm===!0?null:"bot.sender.revoke requires botId, senderKey, and confirm=true.":"Unknown DingTalk endpoint.":"Payload must be an object."}function Hh(n){return{ok:!1,error:{code:"bad-request",message:n}}}function Kh(){return{ok:!1,error:{code:"cancelled",message:"The request was cancelled."}}}function X$(){return{ok:!1,error:{code:"dingtalk-operation-failed",message:"\u9489\u9489\u64CD\u4F5C\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002"}}}function dc(n){if(Array.isArray(n))return n.map(dc);if(!cS(n))return n;let a={};for(let[o,i]of Object.entries(n))W$.has(o)||(a[o]=dc(i));return a}async function $$(n){return z$.toDataURL(n,{type:"image/png",errorCorrectionLevel:"M",margin:2,width:320})}async function gS(n,a){return!n||typeof n.verificationUrl!="string"?dc(n):dc({...n,qrCodeDataUrl:await a(n.verificationUrl)})}async function Hs(n,a){let o=structuredClone(n);return o?.provisioning&&(o.provisioning=await gS(o.provisioning,a)),dc(o)}function eee(n){for(let a of["status","startProvisioning","registrationStatus","cancelProvisioning","bindCredentials","reconnectBot","deleteBot","approveSender","revokeSender"])if(typeof n?.[a]!="function")throw new TypeError(`A complete DingTalk controller is required (${a})`)}function Uv(n,{encodeQr:a=$$}={}){eee(n);let o=new Map,i=e=>{let r=o.get(e);return r||(o.size>=16&&o.delete(o.keys().next().value),r=Promise.resolve().then(()=>a(e)),o.set(e,r)),r};return async(e,r,A)=>{if(A?.aborted)return Kh();if(!Lv.includes(e))return Hh("Unknown DingTalk endpoint.");let t=Z$(e,r);if(t)return Hh(t);try{let s;if(e===Ft.status)s=await Hs(await n.status(),i);else if(e===Ft.beginProvisioning){let g=await n.startProvisioning({signal:A});if(A?.aborted)return await n.cancelProvisioning(g.attemptId),Kh();s=await gS(g,i)}else if(e===Ft.pollProvisioning){let g=await n.registrationStatus(r.attemptId);if(!g)return Hh("The provisioning attempt no longer exists.");s=await gS(g,i)}else if(e===Ft.cancelProvisioning){if(s=await n.cancelProvisioning(r.attemptId),!s)return Hh("The provisioning attempt no longer exists.");s=dc(s)}else if(e===Ft.bindCredentials)s=await Hs(await n.bindCredentials(r),i);else if(e===Ft.reconnectBot)s=await Hs(await n.reconnectBot(r.botId),i);else if(e===Ft.deleteBot)s=await Hs(await n.deleteBot(r.botId),i);else if(e===Ft.setWorkspace){if(typeof n.updateWorkspace!="function")throw new Error("Workspace update is unavailable");s=await Hs(await n.updateWorkspace(r.botId,r.workspace),i)}else e===Ft.approveSender?s=await Hs(await n.approveSender(r.botId,r.requestId),i):s=await Hs(await n.revokeSender(r.botId,r.senderKey),i);return A?.aborted?Kh():{ok:!0,value:s}}catch(s){let g=Ka(s);return A?.aborted?Kh():g?{ok:!1,error:g}:X$()}}}function Yh(n,a,o,i){if(!n?.connection?.rpc||typeof n.connection.rpc.handle!="function")throw new TypeError("DSH Host Connection RPC is required");return n.connection.rpc.handle(Fv,Uv(a,o),{authority:Ua(i)})}async function Hv(n,a={}){if(a?.controller)return Yh(n,a.controller,a.rpcOptions,a.rpcAuthority);let o=await sS(n,a,a.internals),i=Yh(n,o.controller,a.rpcOptions,a.rpcAuthority);return n.effect(()=>async()=>{await o.close()},"dsh-dingtalk: close bot connections"),i}import{createHash as Aee}from"node:crypto";import{mkdir as tee,readFile as aee,rename as ree,unlink as iee,writeFile as oee}from"node:fs/promises";import{dirname as nee}from"node:path";var IS=Object.freeze({version:1,bots:Object.freeze([])});function Fo(n){return typeof n=="string"&&n.trim()?n.trim():null}function Kv(n){return n.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function Xl(n,{botPrefix:a,tokenRefPrefix:o}){let i=Fo(n);if(!i)throw new TypeError("platformId is required");let e=Aee("sha256").update(i).digest("hex").slice(0,24);return{botId:`${a}_${e}`,tokenRef:`${o}_${e.toUpperCase()}`}}function xh(n,a){let o=Fo(n)??"";return o.length<=10?o?`${o.slice(0,3)}\u2022\u2022\u2022`:a:`${o.slice(0,6)}\u2022\u2022\u2022\u2022${o.slice(-4)}`}var Bc=class{#A;#e;#t;#a;#s;#o;#I=IS;#g=Promise.resolve();constructor(a,{channel:o,botPrefix:i,tokenRefPrefix:e}){this.#A=a,this.#e=o,this.#t=i,this.#a=e,this.#s=new RegExp(`^${Kv(i)}_[a-f0-9]{24}$`),this.#o=new RegExp(`^${Kv(e)}_[A-F0-9]{24}$`)}async load(){try{let a=this.#i(JSON.parse(await aee(this.#A,"utf8")));if(!a)throw new Error(`dsh-im ${this.#e} config contains invalid bot data`);this.#I=a}catch(a){if(a?.code!=="ENOENT")throw a;this.#I=IS}return this}list(){return structuredClone(this.#I.bots)}get(a){let o=this.#I.bots.find(i=>i.botId===a);return o?structuredClone(o):null}getByPlatformId(a){let o=this.#I.bots.find(i=>i.platformId===a);return o?structuredClone(o):null}async save(a){let o=this.#n(a);if(!o)throw new Error(`Refusing to persist incomplete ${this.#e} bot data`);return this.#r(i=>{let e=i.find(t=>t.platformId===o.platformId&&t.botId!==o.botId),r=i.find(t=>t.tokenRef===o.tokenRef&&t.botId!==o.botId);if(e||r)throw new Error(`Duplicate ${this.#e} bot identity`);let A=i.findIndex(t=>t.botId===o.botId);return A===-1?i.push(o):i[A]=o,structuredClone(o)})}async remove(a){if(!this.#s.test(a))throw new TypeError(`Invalid ${this.#e} bot id`);return this.#r(o=>{let i=o.findIndex(r=>r.botId===a);if(i===-1)return null;let[e]=o.splice(i,1);return structuredClone(e)})}async clear(){let a=this.#g.then(async()=>{try{await iee(this.#A)}catch(o){if(o?.code!=="ENOENT")throw o}this.#I=IS});this.#g=a.then(()=>{},()=>{}),await a}#n(a){if(!a||typeof a!="object")return null;let o=Fo(a.platformId),i=Fo(a.botId),e=Fo(a.tokenRef),r=Fo(a.name);if(!o||!i||!e||!r||!this.#s.test(i)||!this.#o.test(e))return null;let A=Xl(o,{botPrefix:this.#t,tokenRefPrefix:this.#a});return A.botId!==i||A.tokenRef!==e?null:Object.freeze({botId:i,platformId:o,tokenRef:e,name:r,username:Fo(a.username),createdAt:Fo(a.createdAt)??new Date().toISOString(),connectedAt:Fo(a.connectedAt)})}#i(a){if(!a||a.version!==1||!Array.isArray(a.bots))return null;let o=a.bots.map(A=>this.#n(A));if(o.some(A=>A===null))return null;let i=new Set,e=new Set,r=new Set;for(let A of o){if(i.has(A.botId)||e.has(A.platformId)||r.has(A.tokenRef))return null;i.add(A.botId),e.add(A.platformId),r.add(A.tokenRef)}return Object.freeze({version:1,bots:Object.freeze(o)})}async#r(a){let o,i=this.#g.then(async()=>{let e=[...this.#I.bots];o=a(e);let r=Object.freeze({version:1,bots:Object.freeze(e)});await this.#c(r),this.#I=r});return this.#g=i.then(()=>{},()=>{}),await i,o}async#c(a){await tee(nee(this.#A),{recursive:!0,mode:448});let o=`${this.#A}.tmp`;await oee(o,`${JSON.stringify(a,null,2)} -`,{encoding:"utf8",mode:384}),await ree(o,this.#A)}};var Yv=Object.freeze({botPrefix:"discord",tokenRefPrefix:"DSH_DISCORD_BOT_TOKEN"});function xv(n){return Xl(n,Yv)}function jv(n){return xh(n,"Discord\u673A\u5668\u4EBA")}var jh=class extends Bc{constructor(a){super(a,{channel:"Discord",...Yv})}};function $l(n){return typeof n=="string"&&n.trim()?n.trim():null}function Jh(n,a){return Object.freeze({code:n,message:a})}var uc=class{#A;#e;#t;#a;#s;#o;#I;#g;#n;#i=new Map;#r=new Map;#c=new Map;#l=0;#E=!1;constructor({descriptor:a,credentials:o,configStore:i,inspectToken:e,deriveIdentity:r,maskPlatformId:A,createRuntime:t,deleteState:s=async()=>{},logger:g=console}){if(!a?.key||!a?.label||!a?.connectionLabel)throw new TypeError("TokenBotController requires a channel descriptor");if(!o||typeof o.resolve!="function"||typeof o.set!="function"||typeof o.unset!="function")throw new TypeError(`${a.label} requires the DSH credential provider`);if(!i||typeof i.list!="function"||typeof i.save!="function"||typeof i.remove!="function")throw new TypeError(`${a.label} requires a config store`);if(typeof e!="function"||typeof r!="function"||typeof A!="function"||typeof t!="function")throw new TypeError(`${a.label} controller dependencies are incomplete`);this.#A=a,this.#e=o,this.#t=i,this.#a=e,this.#s=r,this.#o=A,this.#I=t,this.#g=s,this.#n=g}async initialize(){if(this.#E)return this.status();for(let a of this.#t.list())await this.#u(a.botId,async()=>{if(this.#E||this.#i.get(a.botId)?.status?.ready)return;let o=await this.#h(a.tokenRef);if(!o){this.#r.set(a.botId,Jh("missing-token",`${this.#A.label}\u673A\u5668\u4EBA\u51ED\u636E\u7F3A\u5931\uFF0C\u8BF7\u79FB\u9664\u540E\u91CD\u65B0\u63A5\u5165\u3002`));return}try{await this.#C(a,o),this.#r.delete(a.botId)}catch(i){this.#r.set(a.botId,Jh("connection-failed",`${this.#A.label}\u8FDE\u63A5\u672A\u5C31\u7EEA\uFF0C\u63D2\u4EF6\u4F1A\u81EA\u52A8\u91CD\u8BD5\u3002`)),this.#n.warn?.(`[dsh-im:${this.#A.key}] bot ${a.botId} failed to initialize:`,i)}finally{this.#B()}});return this.status()}async bindCredentials({token:a}={}){if(this.#E)throw new Error(`${this.#A.label} controller is closed`);let o=$l(a);if(!o)throw new TypeError(`${this.#A.label} Bot Token is required`);let i=await this.#a(o),e=$l(i?.platformId),r=$l(i?.name);if(!e||!r)throw new Error(`${this.#A.label} returned an invalid bot identity`);let A=this.#s(e);return await this.#u(A.botId,async()=>{if(this.#E)throw new Error(`${this.#A.label} controller is closed`);let t=this.#t.getByPlatformId(e),s=await this.#e.resolve(A.tokenRef).catch(()=>{}),g={botId:A.botId,platformId:e,tokenRef:A.tokenRef,name:r,username:$l(i.username),createdAt:t?.createdAt??new Date().toISOString(),connectedAt:new Date().toISOString()};await this.#e.set(A.tokenRef,o);try{await this.#t.save(g)}catch(c){throw await this.#d(A.tokenRef,s),c}try{await this.#C(g,o),this.#r.delete(A.botId)}catch(c){this.#r.set(A.botId,Jh("connection-failed",`${this.#A.label}\u673A\u5668\u4EBA\u5DF2\u63A5\u5165\uFF0C\u6D88\u606F\u8FDE\u63A5\u6682\u672A\u5C31\u7EEA\u3002`)),this.#n.warn?.(`[dsh-im:${this.#A.key}] bot ${A.botId} credential connection failed:`,c)}this.#B()}),this.status()}async reconnectBot(a){let o=this.#t.get(a);if(!o)throw new Error(`Unknown ${this.#A.label} bot`);return await this.#u(a,async()=>{let i=await this.#h(o.tokenRef);if(!i)throw new Error(`${this.#A.label} bot token is missing`);try{await this.#C(o,i),this.#r.delete(a)}catch(e){throw this.#r.set(a,Jh("connection-failed",`${this.#A.label}\u8FDE\u63A5\u4ECD\u672A\u5C31\u7EEA\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002`)),e}finally{this.#B()}}),this.status()}async deleteBot(a){let o=this.#t.get(a);if(!o)throw new Error(`Unknown ${this.#A.label} bot`);return await this.#u(a,async()=>{let i=await this.#e.resolve(o.tokenRef).catch(()=>{});await this.#p(a);try{await this.#e.unset(o.tokenRef),await this.#t.remove(a)}catch(e){throw i?.value&&(await this.#e.set(o.tokenRef,i.value).catch(()=>{}),await this.#C(o,i.value).catch(()=>{})),new Error(`Unable to remove the ${this.#A.label} bot safely.`,{cause:e})}await this.#g({botId:a,config:o}).catch(e=>{this.#n.warn?.(`[dsh-im:${this.#A.key}] bot ${a} state cleanup failed:`,e)}),this.#r.delete(a),this.#B()}),this.status()}status(){let a=this.#t.list().map(i=>{let e=this.#i.get(i.botId)?.status??null,r=e?.ready===!0&&e.connectionState==="connected"&&e.harnessReachable===!0,A=r?"connected":e?.connectionState==="connecting"?"connecting":this.#r.has(i.botId)||e?.connectionState==="failed"?"error":"offline";return{botId:i.botId,state:A,connected:r,configured:!0,bot:{name:i.name,username:i.username,idMasked:this.#o(i.platformId)},health:{status:r?"healthy":A==="error"?"error":"offline",summary:r?`${this.#A.label}${this.#A.connectionLabel}\u8FD0\u884C\u6B63\u5E38`:A==="error"?`${this.#A.label}\u8FDE\u63A5\u672A\u5C31\u7EEA\uFF0C\u63D2\u4EF6\u4F1A\u81EA\u52A8\u91CD\u8BD5`:`${this.#A.label}\u8FDE\u63A5\u5F53\u524D\u79BB\u7EBF`,lastCheckedAt:e?.lastCheckedAt??null,lastConnectedAt:e?.lastConnectedAt??null},stats:{messagesReceived:e?.messagesReceived??0,messagesReplied:e?.messagesReplied??0},error:structuredClone(this.#r.get(i.botId)??null)}}),o=a.filter(i=>i.connected).length;return{schemaVersion:1,revision:this.#l,state:a.length===0?"disconnected":o===a.length?"connected":o>0?"degraded":"offline",bots:a,totals:{configured:a.length,connected:o}}}async close(){this.#E||(this.#E=!0,await Promise.allSettled([...this.#c.values()]),await Promise.allSettled([...this.#i.keys()].map(a=>this.#p(a))))}async#C(a,o){if(this.#E)throw new Error(`${this.#A.label} controller is closed`);if(await this.#p(a.botId),this.#E)throw new Error(`${this.#A.label} controller is closed`);let i=await this.#I({botId:a.botId,config:a,token:o});if(!i||typeof i.start!="function"||typeof i.stop!="function")throw new TypeError(`createRuntime returned an invalid ${this.#A.label} runtime`);this.#i.set(a.botId,i);try{await i.start()}catch(e){throw await i.stop().catch(()=>{}),this.#i.delete(a.botId),e}}async#p(a){let o=this.#i.get(a);this.#i.delete(a),await o?.stop().catch(i=>{this.#n.warn?.(`[dsh-im:${this.#A.key}] bot ${a} failed to stop cleanly:`,i)})}async#h(a){let o=await this.#e.resolve(a).catch(()=>{});return $l(o?.value)}async#d(a,o){o?.value?await this.#e.set(a,o.value).catch(()=>{}):await this.#e.unset(a).catch(()=>{})}#u(a,o){let r=(this.#c.get(a)??Promise.resolve()).catch(()=>{}).then(o).finally(()=>{this.#c.get(a)===r&&this.#c.delete(a)});return this.#c.set(a,r),r}#B(){this.#l+=1}};var see="https://discord.com/api/v10/";function eE(n){return typeof n=="string"&&n.trim()?n.trim():null}function gee(n,a){let o=AbortSignal.timeout(a);return n?AbortSignal.any([n,o]):o}function cee(n,a){return new Promise((o,i)=>{if(a?.aborted){i(a.reason??new DOMException("Aborted","AbortError"));return}let e=setTimeout(o,n);e?.unref?.(),a?.addEventListener("abort",()=>{clearTimeout(e),i(a.reason??new DOMException("Aborted","AbortError"))},{once:!0})})}function Qc(n,a){let o=eE(n);if(!o||!/^\d{5,30}$/.test(o))throw new TypeError(`Invalid Discord ${a}`);return o}function Iee(n){return typeof n=="string"&&/^[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{4,}\.[A-Za-z0-9_-]{20,}$/.test(n.trim())}var AE=class{#A;#e;#t;constructor({token:a,fetchImpl:o=fetch,baseUrl:i=see}){if(!Iee(a))throw new TypeError("Discord Bot Token is invalid");if(typeof o!="function")throw new TypeError("DiscordApi requires fetch");this.#A=a.trim(),this.#e=o,this.#t=new URL(i)}getCurrentUser(a={}){return this.#a("users/@me",{...a,method:"GET"})}getGatewayBot(a={}){return this.#a("gateway/bot",{...a,method:"GET"})}createMessage({channelId:a,content:o,replyToMessageId:i,signal:e}){return this.#a(`channels/${Qc(a,"channel id")}/messages`,{method:"POST",signal:e,body:{content:o,allowed_mentions:{parse:[],replied_user:!1},...i?{message_reference:{message_id:Qc(i,"message id"),channel_id:Qc(a,"channel id"),fail_if_not_exists:!1}}:{}}})}editMessage({channelId:a,messageId:o,content:i,signal:e}){return this.#a(`channels/${Qc(a,"channel id")}/messages/${Qc(o,"message id")}`,{method:"PATCH",signal:e,body:{content:i,allowed_mentions:{parse:[],replied_user:!1}}})}sendTyping({channelId:a,signal:o}){return this.#a(`channels/${Qc(a,"channel id")}/typing`,{method:"POST",signal:o,expectBody:!1})}async#a(a,{method:o,body:i,signal:e,timeoutMs:r=15e3,expectBody:A=!0,retry:t=!0}){let s;try{s=await this.#e(new URL(a,this.#t),{method:o,headers:{authorization:`Bot ${this.#A}`,"content-type":"application/json","user-agent":"DeepSeek-Harness-dsh-im (https://github.com/xmanrui/dsh-im, 0.4.0)"},...i===void 0?{}:{body:JSON.stringify(i)},signal:gee(e,r),redirect:"error"})}catch(c){throw c?.name==="AbortError"||c?.name==="TimeoutError"?c:new Error(`Discord ${o} transport failed`)}let g=null;if(A||s.status===429||!s.ok)try{g=await s.json()}catch{if(A)throw new Error(`Discord ${o} returned invalid JSON`)}if(s.status===429&&t){let c=Math.min(1e4,Math.max(50,Number(g?.retry_after)*1e3||1e3));return await cee(c,e),this.#a(a,{method:o,body:i,signal:e,timeoutMs:r,expectBody:A,retry:!1})}if(!s.ok){let c=new Error(eE(g?.message)??`Discord API failed with HTTP ${s.status}`);throw c.code=`discord-${s.status}`,c}return A?g:null}};async function Jv(n,a={}){let i=await new AE({token:n,...a}).getCurrentUser();if(!i?.id||i?.bot!==!0)throw new Error("Discord token does not belong to a bot");return{platformId:String(i.id),name:eE(i.global_name)??eE(i.username)??"Discord\u673A\u5668\u4EBA",username:eE(i.username)}}function qh(n){return typeof n=="string"?n.trim():""}function yr(){return{messagesReceived:0,messagesReplied:0,messagesRejected:0,lastMessageAt:null,lastReplyAt:null,lastRejectedAt:null,lastError:null}}var ji=class{#A;#e;#t;#a;#s;#o;#I;#g=new Map;constructor({descriptor:a,bot:o,harness:i,state:e,status:r=yr(),logger:A=console,replyTimeoutMs:t=6e5}){if(!a?.key||!a?.label)throw new TypeError("A channel descriptor is required");if(!o||typeof o.sendText!="function")throw new TypeError("A bot client is required");if(!i||!e)throw new TypeError("Harness client and state store are required");this.#A=a,this.#e=o,this.#t=i,this.#a=e,this.#s=r,this.#o=A,this.#I=t}get status(){return structuredClone(this.#s)}accept(a){let o=qh(a?.conversationId),i=a?.kind==="group"?"group":"direct",e=`${i}:${o}`,A=(this.#g.get(e)??Promise.resolve()).catch(()=>{}).then(()=>this.#n({...a,kind:i,conversationId:o})).finally(()=>{this.#g.get(e)===A&&this.#g.delete(e)});return this.#g.set(e,A),A}async waitForIdle(){await Promise.allSettled([...this.#g.values()])}async#n(a){let o=qh(a.messageId),i=qh(a.senderId);if(!o||!i||!a.conversationId||a.senderIsBot===!0||this.#a.hasSeen(o))return;if(this.#s.messagesReceived+=1,this.#s.lastMessageAt=new Date().toISOString(),a.kind==="group"&&a.addressed!==!0){this.#s.messagesRejected+=1,this.#s.lastRejectedAt=new Date().toISOString();return}let e=a.replyTarget,r=qh(a.content);try{if(!r){await this.#e.sendText(e,"\u76EE\u524D\u4EC5\u652F\u6301\u6587\u5B57\u6D88\u606F\u3002"),await this.#a.markSeen(o);return}let A=r.toLowerCase();if(A==="/help"){await this.#e.sendText(e,[`${this.#A.label}\u673A\u5668\u4EBA\u5DF2\u8FDE\u63A5 DeepSeek Harness\u3002`,"","\u76F4\u63A5\u53D1\u9001\u6587\u5B57\u5373\u53EF\u7EE7\u7EED\u5F53\u524D\u4F1A\u8BDD\u3002","/new \u5F00\u542F\u4E00\u4E2A\u5168\u65B0\u4F1A\u8BDD","/workspace \u5DE5\u4F5C\u533A\u7EDD\u5BF9\u8DEF\u5F84 \u5207\u6362\u5DE5\u4F5C\u533A","/status \u68C0\u67E5\u8FDE\u63A5\u72B6\u6001","/help \u663E\u793A\u672C\u5E2E\u52A9"].join(` +`,{encoding:"utf8",mode:384}),await ree(o,this.#A)}};var Yv=Object.freeze({botPrefix:"discord",tokenRefPrefix:"DSH_DISCORD_BOT_TOKEN"});function xv(n){return Xl(n,Yv)}function jv(n){return xh(n,"Discord\u673A\u5668\u4EBA")}var jh=class extends Bc{constructor(a){super(a,{channel:"Discord",...Yv})}};function $l(n){return typeof n=="string"&&n.trim()?n.trim():null}function Jh(n,a){return Object.freeze({code:n,message:a})}var uc=class{#A;#e;#t;#a;#s;#o;#I;#g;#n;#i=new Map;#r=new Map;#c=new Map;#l=0;#E=!1;constructor({descriptor:a,credentials:o,configStore:i,inspectToken:e,deriveIdentity:r,maskPlatformId:A,createRuntime:t,deleteState:s=async()=>{},logger:g=console}){if(!a?.key||!a?.label||!a?.connectionLabel)throw new TypeError("TokenBotController requires a channel descriptor");if(!o||typeof o.resolve!="function"||typeof o.set!="function"||typeof o.unset!="function")throw new TypeError(`${a.label} requires the DSH credential provider`);if(!i||typeof i.list!="function"||typeof i.save!="function"||typeof i.remove!="function")throw new TypeError(`${a.label} requires a config store`);if(typeof e!="function"||typeof r!="function"||typeof A!="function"||typeof t!="function")throw new TypeError(`${a.label} controller dependencies are incomplete`);this.#A=a,this.#e=o,this.#t=i,this.#a=e,this.#s=r,this.#o=A,this.#I=t,this.#g=s,this.#n=g}async initialize(){if(this.#E)return this.status();for(let a of this.#t.list())await this.#u(a.botId,async()=>{if(this.#E||this.#i.get(a.botId)?.status?.ready)return;let o=await this.#h(a.tokenRef);if(!o){this.#r.set(a.botId,Jh("missing-token",`${this.#A.label}\u673A\u5668\u4EBA\u51ED\u636E\u7F3A\u5931\uFF0C\u8BF7\u79FB\u9664\u540E\u91CD\u65B0\u63A5\u5165\u3002`));return}try{await this.#C(a,o),this.#r.delete(a.botId)}catch(i){this.#r.set(a.botId,Jh("connection-failed",`${this.#A.label}\u8FDE\u63A5\u672A\u5C31\u7EEA\uFF0C\u63D2\u4EF6\u4F1A\u81EA\u52A8\u91CD\u8BD5\u3002`)),this.#n.warn?.(`[dsh-im:${this.#A.key}] bot ${a.botId} failed to initialize:`,i)}finally{this.#B()}});return this.status()}async bindCredentials({token:a}={}){if(this.#E)throw new Error(`${this.#A.label} controller is closed`);let o=$l(a);if(!o)throw new TypeError(`${this.#A.label} Bot Token is required`);let i=await this.#a(o),e=$l(i?.platformId),r=$l(i?.name);if(!e||!r)throw new Error(`${this.#A.label} returned an invalid bot identity`);let A=this.#s(e);return await this.#u(A.botId,async()=>{if(this.#E)throw new Error(`${this.#A.label} controller is closed`);let t=this.#t.getByPlatformId(e),s=await this.#e.resolve(A.tokenRef).catch(()=>{}),g={botId:A.botId,platformId:e,tokenRef:A.tokenRef,name:r,username:$l(i.username),createdAt:t?.createdAt??new Date().toISOString(),connectedAt:new Date().toISOString()};await this.#e.set(A.tokenRef,o);try{await this.#t.save(g)}catch(c){throw await this.#d(A.tokenRef,s),c}try{await this.#C(g,o),this.#r.delete(A.botId)}catch(c){this.#r.set(A.botId,Jh("connection-failed",`${this.#A.label}\u673A\u5668\u4EBA\u5DF2\u63A5\u5165\uFF0C\u6D88\u606F\u8FDE\u63A5\u6682\u672A\u5C31\u7EEA\u3002`)),this.#n.warn?.(`[dsh-im:${this.#A.key}] bot ${A.botId} credential connection failed:`,c)}this.#B()}),this.status()}async reconnectBot(a){let o=this.#t.get(a);if(!o)throw new Error(`Unknown ${this.#A.label} bot`);return await this.#u(a,async()=>{let i=await this.#h(o.tokenRef);if(!i)throw new Error(`${this.#A.label} bot token is missing`);try{await this.#C(o,i),this.#r.delete(a)}catch(e){throw this.#r.set(a,Jh("connection-failed",`${this.#A.label}\u8FDE\u63A5\u4ECD\u672A\u5C31\u7EEA\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002`)),e}finally{this.#B()}}),this.status()}async deleteBot(a){let o=this.#t.get(a);if(!o)throw new Error(`Unknown ${this.#A.label} bot`);return await this.#u(a,async()=>{let i=await this.#e.resolve(o.tokenRef).catch(()=>{});await this.#p(a);try{await this.#e.unset(o.tokenRef),await this.#t.remove(a)}catch(e){throw i?.value&&(await this.#e.set(o.tokenRef,i.value).catch(()=>{}),await this.#C(o,i.value).catch(()=>{})),new Error(`Unable to remove the ${this.#A.label} bot safely.`,{cause:e})}await this.#g({botId:a,config:o}).catch(e=>{this.#n.warn?.(`[dsh-im:${this.#A.key}] bot ${a} state cleanup failed:`,e)}),this.#r.delete(a),this.#B()}),this.status()}status(){let a=this.#t.list().map(i=>{let e=this.#i.get(i.botId)?.status??null,r=e?.ready===!0&&e.connectionState==="connected"&&e.harnessReachable===!0,A=r?"connected":e?.connectionState==="connecting"?"connecting":this.#r.has(i.botId)||e?.connectionState==="failed"?"error":"offline";return{botId:i.botId,state:A,connected:r,configured:!0,bot:{name:i.name,username:i.username,idMasked:this.#o(i.platformId)},health:{status:r?"healthy":A==="error"?"error":"offline",summary:r?`${this.#A.label}${this.#A.connectionLabel}\u8FD0\u884C\u6B63\u5E38`:A==="error"?`${this.#A.label}\u8FDE\u63A5\u672A\u5C31\u7EEA\uFF0C\u63D2\u4EF6\u4F1A\u81EA\u52A8\u91CD\u8BD5`:`${this.#A.label}\u8FDE\u63A5\u5F53\u524D\u79BB\u7EBF`,lastCheckedAt:e?.lastCheckedAt??null,lastConnectedAt:e?.lastConnectedAt??null},stats:{messagesReceived:e?.messagesReceived??0,messagesReplied:e?.messagesReplied??0},error:structuredClone(this.#r.get(i.botId)??null)}}),o=a.filter(i=>i.connected).length;return{schemaVersion:1,revision:this.#l,state:a.length===0?"disconnected":o===a.length?"connected":o>0?"degraded":"offline",bots:a,totals:{configured:a.length,connected:o}}}async close(){this.#E||(this.#E=!0,await Promise.allSettled([...this.#c.values()]),await Promise.allSettled([...this.#i.keys()].map(a=>this.#p(a))))}async#C(a,o){if(this.#E)throw new Error(`${this.#A.label} controller is closed`);if(await this.#p(a.botId),this.#E)throw new Error(`${this.#A.label} controller is closed`);let i=await this.#I({botId:a.botId,config:a,token:o});if(!i||typeof i.start!="function"||typeof i.stop!="function")throw new TypeError(`createRuntime returned an invalid ${this.#A.label} runtime`);this.#i.set(a.botId,i);try{await i.start()}catch(e){throw await i.stop().catch(()=>{}),this.#i.delete(a.botId),e}}async#p(a){let o=this.#i.get(a);this.#i.delete(a),await o?.stop().catch(i=>{this.#n.warn?.(`[dsh-im:${this.#A.key}] bot ${a} failed to stop cleanly:`,i)})}async#h(a){let o=await this.#e.resolve(a).catch(()=>{});return $l(o?.value)}async#d(a,o){o?.value?await this.#e.set(a,o.value).catch(()=>{}):await this.#e.unset(a).catch(()=>{})}#u(a,o){let r=(this.#c.get(a)??Promise.resolve()).catch(()=>{}).then(o).finally(()=>{this.#c.get(a)===r&&this.#c.delete(a)});return this.#c.set(a,r),r}#B(){this.#l+=1}};var see="https://discord.com/api/v10/";function eE(n){return typeof n=="string"&&n.trim()?n.trim():null}function gee(n,a){let o=AbortSignal.timeout(a);return n?AbortSignal.any([n,o]):o}function cee(n,a){return new Promise((o,i)=>{if(a?.aborted){i(a.reason??new DOMException("Aborted","AbortError"));return}let e=setTimeout(o,n);e?.unref?.(),a?.addEventListener("abort",()=>{clearTimeout(e),i(a.reason??new DOMException("Aborted","AbortError"))},{once:!0})})}function Qc(n,a){let o=eE(n);if(!o||!/^\d{5,30}$/.test(o))throw new TypeError(`Invalid Discord ${a}`);return o}function Iee(n){return typeof n=="string"&&/^[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{4,}\.[A-Za-z0-9_-]{20,}$/.test(n.trim())}var AE=class{#A;#e;#t;constructor({token:a,fetchImpl:o=fetch,baseUrl:i=see}){if(!Iee(a))throw new TypeError("Discord Bot Token is invalid");if(typeof o!="function")throw new TypeError("DiscordApi requires fetch");this.#A=a.trim(),this.#e=o,this.#t=new URL(i)}getCurrentUser(a={}){return this.#a("users/@me",{...a,method:"GET"})}getGatewayBot(a={}){return this.#a("gateway/bot",{...a,method:"GET"})}createMessage({channelId:a,content:o,replyToMessageId:i,signal:e}){return this.#a(`channels/${Qc(a,"channel id")}/messages`,{method:"POST",signal:e,body:{content:o,allowed_mentions:{parse:[],replied_user:!1},...i?{message_reference:{message_id:Qc(i,"message id"),channel_id:Qc(a,"channel id"),fail_if_not_exists:!1}}:{}}})}editMessage({channelId:a,messageId:o,content:i,signal:e}){return this.#a(`channels/${Qc(a,"channel id")}/messages/${Qc(o,"message id")}`,{method:"PATCH",signal:e,body:{content:i,allowed_mentions:{parse:[],replied_user:!1}}})}sendTyping({channelId:a,signal:o}){return this.#a(`channels/${Qc(a,"channel id")}/typing`,{method:"POST",signal:o,expectBody:!1})}async#a(a,{method:o,body:i,signal:e,timeoutMs:r=15e3,expectBody:A=!0,retry:t=!0}){let s;try{s=await this.#e(new URL(a,this.#t),{method:o,headers:{authorization:`Bot ${this.#A}`,"content-type":"application/json","user-agent":"DeepSeek-Harness-dsh-im (https://github.com/xmanrui/dsh-im, 0.5.0)"},...i===void 0?{}:{body:JSON.stringify(i)},signal:gee(e,r),redirect:"error"})}catch(c){throw c?.name==="AbortError"||c?.name==="TimeoutError"?c:new Error(`Discord ${o} transport failed`)}let g=null;if(A||s.status===429||!s.ok)try{g=await s.json()}catch{if(A)throw new Error(`Discord ${o} returned invalid JSON`)}if(s.status===429&&t){let c=Math.min(1e4,Math.max(50,Number(g?.retry_after)*1e3||1e3));return await cee(c,e),this.#a(a,{method:o,body:i,signal:e,timeoutMs:r,expectBody:A,retry:!1})}if(!s.ok){let c=new Error(eE(g?.message)??`Discord API failed with HTTP ${s.status}`);throw c.code=`discord-${s.status}`,c}return A?g:null}};async function Jv(n,a={}){let i=await new AE({token:n,...a}).getCurrentUser();if(!i?.id||i?.bot!==!0)throw new Error("Discord token does not belong to a bot");return{platformId:String(i.id),name:eE(i.global_name)??eE(i.username)??"Discord\u673A\u5668\u4EBA",username:eE(i.username)}}function qh(n){return typeof n=="string"?n.trim():""}function yr(){return{messagesReceived:0,messagesReplied:0,messagesRejected:0,lastMessageAt:null,lastReplyAt:null,lastRejectedAt:null,lastError:null}}var ji=class{#A;#e;#t;#a;#s;#o;#I;#g=new Map;constructor({descriptor:a,bot:o,harness:i,state:e,status:r=yr(),logger:A=console,replyTimeoutMs:t=6e5}){if(!a?.key||!a?.label)throw new TypeError("A channel descriptor is required");if(!o||typeof o.sendText!="function")throw new TypeError("A bot client is required");if(!i||!e)throw new TypeError("Harness client and state store are required");this.#A=a,this.#e=o,this.#t=i,this.#a=e,this.#s=r,this.#o=A,this.#I=t}get status(){return structuredClone(this.#s)}accept(a){let o=qh(a?.conversationId),i=a?.kind==="group"?"group":"direct",e=`${i}:${o}`,A=(this.#g.get(e)??Promise.resolve()).catch(()=>{}).then(()=>this.#n({...a,kind:i,conversationId:o})).finally(()=>{this.#g.get(e)===A&&this.#g.delete(e)});return this.#g.set(e,A),A}async waitForIdle(){await Promise.allSettled([...this.#g.values()])}async#n(a){let o=qh(a.messageId),i=qh(a.senderId);if(!o||!i||!a.conversationId||a.senderIsBot===!0||this.#a.hasSeen(o))return;if(this.#s.messagesReceived+=1,this.#s.lastMessageAt=new Date().toISOString(),a.kind==="group"&&a.addressed!==!0){this.#s.messagesRejected+=1,this.#s.lastRejectedAt=new Date().toISOString();return}let e=a.replyTarget,r=qh(a.content);try{if(!r){await this.#e.sendText(e,"\u76EE\u524D\u4EC5\u652F\u6301\u6587\u5B57\u6D88\u606F\u3002"),await this.#a.markSeen(o);return}let A=r.toLowerCase();if(A==="/help"){await this.#e.sendText(e,[`${this.#A.label}\u673A\u5668\u4EBA\u5DF2\u8FDE\u63A5 DeepSeek Harness\u3002`,"","\u76F4\u63A5\u53D1\u9001\u6587\u5B57\u5373\u53EF\u7EE7\u7EED\u5F53\u524D\u4F1A\u8BDD\u3002","/new \u5F00\u542F\u4E00\u4E2A\u5168\u65B0\u4F1A\u8BDD","/workspace \u5DE5\u4F5C\u533A\u7EDD\u5BF9\u8DEF\u5F84 \u5207\u6362\u5DE5\u4F5C\u533A","/status \u68C0\u67E5\u8FDE\u63A5\u72B6\u6001","/help \u663E\u793A\u672C\u5E2E\u52A9"].join(` `)),await this.#a.markSeen(o);return}if(A==="/status"){await this.#t.ensureRunning(),await this.#e.sendText(e,`${this.#A.label}\u673A\u5668\u4EBA\u4E0E DeepSeek Harness \u8FDE\u63A5\u6B63\u5E38\u3002`),await this.#a.markSeen(o);return}let t=await Pr(r,this.#t);if(t){await this.#e.sendText(e,t.message),await this.#a.markSeen(o);return}let s=`${a.kind}:${a.conversationId}`;if(A==="/new"){await this.#a.clearSession(s),await this.#e.sendText(e,"\u5DF2\u5F00\u542F\u65B0\u4F1A\u8BDD\u3002\u8BF7\u53D1\u9001\u4F60\u7684\u95EE\u9898\u3002"),await this.#a.markSeen(o);return}await this.#e.sendTyping?.(e).catch(C=>{this.#o.warn?.(`[dsh-im:${this.#A.key}] typing indicator failed:`,C)});let g=null,c=!1;if(typeof this.#e.openStream=="function")try{g=await this.#e.openStream(e)}catch(C){this.#o.warn?.(`[dsh-im:${this.#A.key}] unable to start a streamed reply; using text:`,C)}let{answer:l}=await rr({harness:this.#t,state:this.#a,key:s,text:r,askOptions:{timeoutMs:this.#I,onUpdate:g?async C=>{let u=C.type==="text"?C.text:C.type==="tool"?`\u6B63\u5728\u4F7F\u7528${C.name}\u2026`:C.text;u&&await g.update(u)}:void 0}});if(g)try{await g.finish(l),c=!0}catch(C){g.cancel?.(),this.#o.warn?.(`[dsh-im:${this.#A.key}] streamed reply finalization failed; using text:`,C)}c||await this.#e.sendText(e,l),await this.#a.markSeen(o),this.#s.messagesReplied+=1,this.#s.lastReplyAt=new Date().toISOString(),this.#s.lastError=null}catch(A){this.#s.lastError=A?.message??String(A),this.#o.error?.(`[dsh-im:${this.#A.key}] failed to process a message:`,A);try{await this.#e.sendText(e,"\u6D88\u606F\u5904\u7406\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002"),await this.#a.markSeen(o)}catch(t){this.#o.error?.(`[dsh-im:${this.#A.key}] failed to send the safe error reply:`,t)}}}};var lS=Object.freeze({key:"discord",label:"Discord",connectionLabel:" Gateway \u957F\u8FDE\u63A5"}),Vh=class extends ji{constructor(a){super({descriptor:lS,...a})}};var tE=class extends uc{constructor(a){super({...a,descriptor:lS,inspectToken:a.inspectToken??Jv,deriveIdentity:xv,maskPlatformId:jv})}};import{spawn as lee}from"node:child_process";import{randomUUID as qv}from"node:crypto";var Vv=n=>new Promise(a=>setTimeout(a,n));function Eee(n){return(n?.data?.message?.content??[]).filter(a=>a.type==="text"&&typeof a.text=="string").map(a=>a.text).join(` `).trim()}var ES=class{#A;#e;#t=null;#a=null;#s=new Map;#o="";#I=!1;#g=null;constructor({promptRpcId:a,afterSeq:o=-1}){this.#A=a,this.#e=o}get finished(){return this.#I}get answer(){return this.#o.trim()}get reason(){return this.#g}consume(a){let o=null,i=[...a].map(e=>e?.event??e).filter(Boolean).sort((e,r)=>(e.seq??-1)-(r.seq??-1));for(let e of i){let r=e.seq??-1;if(!(r<=this.#e)){if(this.#e=r,e.type==="turn/start"&&(this.#t=e.data?.turn??null),e.type==="user/message"&&e.data?.source?.rpcId===this.#A){this.#a=this.#t;continue}if(this.#a!==null){if(e.type==="turn/end"){if(e.data?.turn!==this.#a)continue;this.#I=!0,this.#g=e.data?.reason??null,this.#t=null;continue}if(e.data?.turn===this.#a){if(e.type==="assistant/chunk"&&e.data?.chunk?.type==="text-delta"){let A=e.data?.step??0,t=e.data.chunk.index??0,s=`${A}:${t}`;this.#s.set(s,(this.#s.get(s)??"")+e.data.chunk.text);let g=`${A}:`,c=[...this.#s.entries()].filter(([l])=>l.startsWith(g)).sort(([l],[C])=>Number(l.split(":")[1])-Number(C.split(":")[1])).map(([,l])=>l).join(` `).trim();c&&c!==this.#o&&(this.#o=c,o={type:"text",text:c});continue}if(e.type==="assistant/message"){let A=Eee(e);A&&A!==this.#o&&(this.#o=A,o={type:"text",text:A});continue}e.type==="tool/call"?o={type:"tool",name:e.data?.name??"\u5DE5\u5177"}:e.type==="tool/result"&&(o={type:"status",text:"\u6B63\u5728\u6574\u7406\u7ED3\u679C\u2026"})}}}}return o}},zh=class extends Error{constructor(a,o){super(`${a}: ${o?.message??"unknown Harness RPC error"}`),this.name="HarnessRpcError",this.method=a,this.code=o?.code??"internal",this.details=o?.details??{}}},Ya=class{#A;#e;#t;#a;#s;#o=null;constructor({baseUrl:a,workspace:o,agentPreset:i="standard",autostart:e=!1,dshBin:r="dsh"}){this.#A=new URL(a),this.#e=o,this.#t=i,this.#a=e,this.#s=r}async rpc(a,o={},i=3e4,e={}){let r=e.rpcId??`weixin-${qv()}`,A=await fetch(new URL(`/api/${a}`,this.#A),{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({type:"client-request",rpcId:r,method:a,payload:o}),signal:AbortSignal.timeout(i)});if(!A.ok)throw new Error(`Harness transport ${a} failed: HTTP ${A.status}`);let t=await A.json();if(t?.type!=="server-response"||t?.rpcId!==r)throw new Error(`Harness returned an invalid response for ${a}`);if(!t.result?.ok)throw new zh(a,t.result?.error);return t.result.value}async health(){return await this.rpc("host.describe",{},5e3),!0}async ensureRunning(){try{return await this.health()}catch(i){if(!this.#a)throw i}if(!this.#o||this.#o.exitCode!==null){let i=this.#A.port||(this.#A.protocol==="https:"?"443":"80");this.#o=lee(this.#s,["web","--host",this.#A.hostname,"--port",i],{cwd:this.#e,env:process.env,stdio:["ignore","inherit","inherit"]}),this.#o.on("error",e=>{console.error("[dsh-weixin] failed to start Harness:",e.message)})}let a=Date.now()+6e4,o;for(;Date.now()A.path===o);return e?e.workspaceId:(await this.rpc("workspace.create",{path:o})).workspace.workspaceId}async createSession(a={}){await this.ensureRunning();let o=await this.workspaceId(a);return(await this.rpc("session.create",{workspaceId:o,agentPreset:this.#t})).sessionId}async sessionExists(a){try{return await this.rpc("session.history",{sessionId:a,maxMessages:1}),!0}catch(o){if(o instanceof zh&&o.code==="session-not-found")return!1;throw o}}async ask(a,o,i={}){typeof i=="number"&&(i={timeoutMs:i});let e=i.timeoutMs??6e5,r=typeof i.onUpdate=="function"?i.onUpdate:null;await this.ensureRunning();let A=await this.rpc("session.history",{sessionId:a,maxMessages:1}),t=Math.max(-1,...(A.events??[]).map(({event:l})=>l.seq??-1)),s=`weixin-${qv()}`,g=new ES({promptRpcId:s,afterSeq:t});await this.rpc("session.prompt",{sessionId:a,mode:"queue",content:[{type:"text",text:o}],clientTimeZone:Intl.DateTimeFormat().resolvedOptions().timeZone},3e4,{rpcId:s});let c=Date.now()+e;for(;Date.now()a;){let r=e.lastIndexOf(` @@ -170,7 +170,7 @@ ${r.join(` `,{encoding:"utf8",mode:384}),await Pde(o,this.#A)}};var vu=class extends Ya{};var Kde="https://work.weixin.qq.com/ai/qc/generate",Yde="https://work.weixin.qq.com/ai/qc/query_result";function e1(){return process.platform==="win32"?2:process.platform==="linux"?3:1}function EI(n){return typeof n=="string"&&n.trim()?n.trim():null}function xde(n){let a=EI(n);if(!a)return null;try{let o=new URL(a);return o.protocol==="https:"&&o.hostname==="work.weixin.qq.com"&&(!o.port||o.port==="443")?o.href:null}catch{return null}}function jde(n,a){return n?AbortSignal.any([n,AbortSignal.timeout(a)]):AbortSignal.timeout(a)}async function A1(n,a,o){let i=await n(a,{method:"GET",redirect:"error",signal:jde(o,1e4),headers:{accept:"application/json"}});if(!i.ok)throw new Error(`Enterprise WeChat QR service returned HTTP ${i.status}`);return i.json()}var Pu=class{#A;#e;#t;#a;constructor({fetch:a=globalThis.fetch,clock:o=()=>Date.now(),source:i="deepseek-harness",platform:e=e1()}={}){if(typeof a!="function")throw new TypeError("A fetch implementation is required");this.#A=a,this.#e=o,this.#t=i,this.#a=[1,2,3].includes(e)?e:e1()}async start({signal:a}={}){let o=new URL(Kde);o.searchParams.set("source",this.#t),o.searchParams.set("plat",String(this.#a));let i=await A1(this.#A,o,a),e=EI(i?.data?.scode),r=xde(i?.data?.auth_url);if(!e||!r)throw new Error("Enterprise WeChat QR service returned invalid data");return{scode:e,verificationUrl:r,expiresAt:this.#e()+3e5,pollIntervalMs:3e3}}async poll({scode:a,signal:o}={}){let i=EI(a);if(!i)throw new TypeError("Enterprise WeChat QR poll code is required");let e=new URL(Yde);e.searchParams.set("scode",i);let r=await A1(this.#A,e,o),A=EI(r?.data?.status)?.toLowerCase();if(A==="success"){let t=EI(r?.data?.bot_info?.botid),s=EI(r?.data?.bot_info?.secret);if(!t||!s)throw new Error("Enterprise WeChat QR result omitted bot credentials");return{status:"success",remoteBotId:t,secret:s}}return["expired","timeout"].includes(A)?{status:"expired"}:["fail","failed","error"].includes(A)?{status:"failed"}:{status:"waiting"}}};import{mkdir as Jde,readFile as qde,rename as Vde,unlink as zde,writeFile as Wde}from"node:fs/promises";import{dirname as Zde}from"node:path";var Gu=Object.freeze({version:1,sessions:{},seenMessageIds:[]});function Xde(n){if(!n||typeof n!="object")return structuredClone(Gu);let a={};if(n.sessions&&typeof n.sessions=="object"&&!Array.isArray(n.sessions))for(let[o,i]of Object.entries(n.sessions))typeof o=="string"&&typeof i=="string"&&i&&(a[o]=i);return{version:1,sessions:a,seenMessageIds:Array.isArray(n.seenMessageIds)?n.seenMessageIds.filter(o=>typeof o=="string").slice(-1e3):[]}}var Fu=class{#A;#e=structuredClone(Gu);#t=Promise.resolve();constructor(a){this.#A=a}async load(){try{this.#e=Xde(JSON.parse(await qde(this.#A,"utf8")))}catch(a){if(a?.code!=="ENOENT")throw a;this.#e=structuredClone(Gu),await this.#a()}return this}sessionFor(a){return this.#e.sessions[a]??null}async setSession(a,o){this.#e.sessions[a]=o,await this.#a()}async clearSession(a){delete this.#e.sessions[a],await this.#a()}async clearSessions(){this.#e.sessions={},await this.#a()}hasSeen(a){return this.#e.seenMessageIds.includes(a)}async markSeen(a){this.hasSeen(a)||(this.#e.seenMessageIds.push(a),this.#e.seenMessageIds.length>1e3&&this.#e.seenMessageIds.splice(0,this.#e.seenMessageIds.length-1e3),await this.#a())}async remove(){try{await zde(this.#A)}catch(a){if(a?.code!=="ENOENT")throw a}this.#e=structuredClone(Gu)}async#a(){let a=`${JSON.stringify(this.#e,null,2)} `,o=this.#t.then(async()=>{await Jde(Zde(this.#A),{recursive:!0,mode:448});let i=`${this.#A}.tmp`;await Wde(i,a,{encoding:"utf8",mode:384}),await Vde(i,this.#A)});this.#t=o.then(()=>{},()=>{}),await o}};import{randomUUID as $de}from"node:crypto";var t1=new Set(["pending","connecting"]),kC=new Set(["connected","failed","cancelled","expired"]);function Dg(n){return typeof n=="string"&&n.trim()?n.trim():null}function Er(n,a){return Object.freeze({code:n,message:a})}function wg(n){return n?{attemptId:n.id,status:n.state,pollIntervalMs:n.pollIntervalMs,qrRevision:n.qrRevision,...n.verificationUrl?{verificationUrl:n.verificationUrl}:{},...n.expiresAt?{expiresAt:n.expiresAt}:{},...n.botId?{botId:n.botId}:{},...n.error?{error:structuredClone(n.error)}:{}}:null}var vC=class{#A;#e;#t;#a;#s;#o;#I=new Map;#g=new Map;#n=new Map;#i=null;#r=new Map;#c=0;#l=!1;constructor({qrAuth:a,credentials:o,configStore:i,createRuntime:e,deleteState:r=async()=>{},logger:A=console}){if(!a||typeof a.start!="function"||typeof a.poll!="function")throw new TypeError("Enterprise WeChat QR auth is required");if(!o||typeof o.resolve!="function"||typeof o.set!="function"||typeof o.unset!="function")throw new TypeError("WecomController requires the DSH credential provider");if(!i||typeof i.list!="function"||typeof i.save!="function"||typeof i.remove!="function")throw new TypeError("WecomController requires a config store");if(typeof e!="function")throw new TypeError("createRuntime is required");this.#A=a,this.#e=o,this.#t=i,this.#a=e,this.#s=r,this.#o=A}async initialize(){if(this.#l)return this.status();for(let a of this.#t.list())await this.#Q(a.botId,async()=>{let o=this.#I.get(a.botId)?.status;if(this.#l||o?.ready||o?.wecomConnectionState==="connecting")return;let i=await this.#u(a.secretRef);if(!i){this.#g.set(a.botId,Er("missing-secret","\u4F01\u4E1A\u5FAE\u4FE1\u673A\u5668\u4EBA\u51ED\u636E\u7F3A\u5931\uFF0C\u8BF7\u79FB\u9664\u540E\u91CD\u65B0\u626B\u7801\u3002"));return}try{await this.#h(a,i),this.#g.delete(a.botId)}catch{this.#g.set(a.botId,Er("connection-failed","\u4F01\u4E1A\u5FAE\u4FE1\u8FDE\u63A5\u672A\u5C31\u7EEA\uFF0C\u63D2\u4EF6\u4F1A\u81EA\u52A8\u91CD\u8BD5\u3002")),this.#o.warn?.(`[dsh-im:wecom] bot ${a.botId} failed to initialize`)}finally{this.#f()}});return this.status()}async startProvisioning(){if(this.#l)throw new Error("Enterprise WeChat controller is closed");this.#i&&await this.cancelProvisioning(this.#i);let a={id:$de(),state:"pending",createdAt:Date.now(),expiresAt:null,pollIntervalMs:3e3,qrRevision:1,verificationUrl:null,scode:null,botId:null,error:null,controller:new AbortController,polling:null,transition:null};this.#n.set(a.id,a),this.#i=a.id,this.#f();try{let o=await this.#A.start({signal:a.controller.signal});if(a.scode=Dg(o.scode),a.verificationUrl=Dg(o.verificationUrl),a.expiresAt=Number(o.expiresAt),a.pollIntervalMs=Math.min(1e4,Math.max(500,Number(o.pollIntervalMs)||3e3)),!a.scode||!a.verificationUrl||!Number.isFinite(a.expiresAt))throw new Error("Enterprise WeChat QR auth returned incomplete data");return this.#f(),wg(a)}catch(o){throw a.state=a.controller.signal.aborted?"cancelled":"failed",a.error=a.controller.signal.aborted?Er("cancelled","\u626B\u7801\u7ED1\u5B9A\u5DF2\u53D6\u6D88\u3002"):Er("qr-start-failed","\u65E0\u6CD5\u751F\u6210\u4F01\u4E1A\u5FAE\u4FE1\u4E8C\u7EF4\u7801\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002"),this.#y(a),o}}async registrationStatus(a){let o=this.#n.get(a);if(!o||kC.has(o.state))return wg(o);if(o.state==="connecting")return await o.transition?.catch(()=>{}),wg(o);if(Date.now()>=o.expiresAt)return o.state="expired",o.error=Er("expired","\u4F01\u4E1A\u5FAE\u4FE1\u4E8C\u7EF4\u7801\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u91CD\u65B0\u751F\u6210\u3002"),o.controller.abort(),this.#y(o),wg(o);if(!o.polling){let i=this.#E(o).finally(()=>{o.polling===i&&(o.polling=null)});o.polling=i}return await o.polling.catch(()=>{}),wg(o)}async bindCredentials({botId:a,secret:o}={}){if(this.#l)throw new Error("Enterprise WeChat controller is closed");let i=Dg(a),e=Dg(o);if(!i||!e)throw new TypeError("Enterprise WeChat Bot ID and Secret are required");if(this.#i&&await this.cancelProvisioning(this.#i),this.#l)throw new Error("Enterprise WeChat controller is closed");let r=ku(i);return await this.#Q(r.botId,async()=>{if(this.#l)throw new Error("Enterprise WeChat controller is closed");let A=this.#t.getByRemoteBotId(i),t=await this.#e.resolve(r.secretRef).catch(()=>{});if(this.#l)throw new Error("Enterprise WeChat controller is closed");let s={botId:r.botId,remoteBotId:i,secretRef:r.secretRef,createdAt:A?.createdAt??new Date().toISOString(),connectedAt:new Date().toISOString()};await this.#e.set(r.secretRef,e);try{await this.#t.save(s)}catch(g){throw await this.#B(r.secretRef,t),g}try{await this.#h(s,e),this.#g.delete(r.botId)}catch{this.#g.set(r.botId,Er("connection-failed","\u4F01\u4E1A\u5FAE\u4FE1\u673A\u5668\u4EBA\u5DF2\u7ED1\u5B9A\uFF0C\u6D88\u606F\u8FDE\u63A5\u6682\u672A\u5C31\u7EEA\u3002")),this.#o.warn?.(`[dsh-im:wecom] bot ${r.botId} credential connection failed`)}this.#f()}),this.status()}async cancelProvisioning(a){let o=this.#n.get(a);return o?(kC.has(o.state)||(o.controller.abort(),await Promise.allSettled([o.polling,o.transition].filter(Boolean)),kC.has(o.state)||(o.state="cancelled"),o.error??=Er("cancelled","\u626B\u7801\u7ED1\u5B9A\u5DF2\u53D6\u6D88\u3002"),this.#y(o)),wg(o)):null}async reconnectBot(a){let o=this.#t.get(a);if(!o)throw new Error("Unknown Enterprise WeChat bot");return await this.#Q(a,async()=>{let i=await this.#u(o.secretRef);if(!i)throw new Error("Enterprise WeChat bot secret is missing");try{await this.#h(o,i),this.#g.delete(a)}catch(e){throw this.#g.set(a,Er("connection-failed","\u4F01\u4E1A\u5FAE\u4FE1\u8FDE\u63A5\u4ECD\u672A\u5C31\u7EEA\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002")),e}finally{this.#f()}}),this.status()}async deleteBot(a){let o=this.#t.get(a);if(!o)throw new Error("Unknown Enterprise WeChat bot");return await this.#Q(a,async()=>{let i=await this.#e.resolve(o.secretRef).catch(()=>{});await this.#d(a);try{await this.#e.unset(o.secretRef),await this.#t.remove(a)}catch(e){throw i?.value&&(await this.#e.set(o.secretRef,i.value).catch(()=>{}),await this.#h(o,i.value).catch(()=>{})),new Error("Unable to remove the Enterprise WeChat bot safely.",{cause:e})}await this.#s({botId:a,config:o}).catch(e=>{this.#o.warn?.(`[dsh-im:wecom] bot ${a} state cleanup failed:`,e)}),this.#g.delete(a),this.#f()}),this.status()}status(){let a=this.#t.list().map(e=>{let r=this.#I.get(e.botId)?.status??null,A=r?.ready===!0&&r.wecomConnectionState==="connected"&&r.harnessReachable===!0,t=A?"connected":r?.wecomConnectionState==="connecting"?"connecting":this.#g.has(e.botId)||r?.wecomConnectionState==="failed"?"error":"offline";return{botId:e.botId,state:t,connected:A,configured:!0,bot:{name:"\u4F01\u4E1A\u5FAE\u4FE1\u673A\u5668\u4EBA",appIdMasked:Xx(e.remoteBotId)},health:{status:A?"healthy":t==="error"?"error":"offline",summary:A?"\u4F01\u4E1A\u5FAE\u4FE1 WebSocket \u957F\u8FDE\u63A5\u8FD0\u884C\u6B63\u5E38":t==="error"?"\u4F01\u4E1A\u5FAE\u4FE1\u8FDE\u63A5\u672A\u5C31\u7EEA\uFF0C\u63D2\u4EF6\u4F1A\u81EA\u52A8\u91CD\u8BD5":"\u4F01\u4E1A\u5FAE\u4FE1\u8FDE\u63A5\u5F53\u524D\u79BB\u7EBF",lastCheckedAt:r?.lastCheckedAt??null,lastConnectedAt:r?.lastConnectedAt??null},stats:{messagesReceived:r?.messagesReceived??0,messagesReplied:r?.messagesReplied??0},error:structuredClone(this.#g.get(e.botId)??null)}}),o=a.filter(e=>e.connected).length,i=this.#i?this.#n.get(this.#i):null;return{schemaVersion:1,revision:this.#c,state:i&&t1.has(i.state)?"provisioning":a.length===0?"disconnected":o===a.length?"connected":o>0?"degraded":"offline",bots:a,totals:{configured:a.length,connected:o},...i&&t1.has(i.state)?{provisioning:wg(i)}:{}}}async close(){this.#l||(this.#l=!0,this.#i&&await this.cancelProvisioning(this.#i),await Promise.allSettled([...this.#r.values()]),await Promise.allSettled([...this.#I.keys()].map(a=>this.#d(a))))}async#E(a){try{let o=await this.#A.poll({scode:a.scode,signal:a.controller.signal});if(a.controller.signal.aborted||kC.has(a.state)||o.status==="waiting")return;if(o.status==="expired"){a.state="expired",a.error=Er("expired","\u4F01\u4E1A\u5FAE\u4FE1\u4E8C\u7EF4\u7801\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u91CD\u65B0\u751F\u6210\u3002"),this.#y(a);return}if(o.status!=="success"){a.state="failed",a.error=Er("qr-connect-failed","\u4F01\u4E1A\u5FAE\u4FE1\u626B\u7801\u6CA1\u6709\u5B8C\u6210\uFF0C\u8BF7\u91CD\u65B0\u751F\u6210\u4E8C\u7EF4\u7801\u3002"),this.#y(a);return}a.state="connecting",a.verificationUrl=null,a.expiresAt=null,a.scode=null,this.#f();let i=this.#C(a,o);a.transition=i,await i}catch{if(a.controller.signal.aborted)return;a.state="failed",a.error=Er("qr-connect-failed","\u4F01\u4E1A\u5FAE\u4FE1\u626B\u7801\u670D\u52A1\u6682\u65F6\u4E0D\u53EF\u7528\uFF0C\u8BF7\u91CD\u65B0\u751F\u6210\u4E8C\u7EF4\u7801\u3002"),this.#o.warn?.("[dsh-im:wecom] QR polling failed"),this.#y(a)}}async#C(a,o){try{let i=Dg(o.remoteBotId),e=Dg(o.secret);if(!i||!e)throw new Error("Enterprise WeChat authorization returned incomplete credentials");a.botId=await this.#p(a,{remoteBotId:i,secret:e}),a.state="connected",a.error=null}catch{a.controller.signal.aborted?(a.state="cancelled",a.error=Er("cancelled","\u626B\u7801\u7ED1\u5B9A\u5DF2\u53D6\u6D88\u3002")):(a.state="failed",a.error=Er("activation-failed","\u4F01\u4E1A\u5FAE\u4FE1\u5DF2\u6388\u6743\uFF0C\u4F46\u65E0\u6CD5\u5B89\u5168\u4FDD\u5B58\u63A5\u5165\u914D\u7F6E\u3002"),this.#o.error?.("[dsh-im:wecom] provisioning failed"))}finally{this.#y(a)}}async#p(a,{remoteBotId:o,secret:i}){let e=ku(o),r=this.#t.getByRemoteBotId(o),A=await this.#e.resolve(e.secretRef).catch(()=>{}),t={botId:e.botId,remoteBotId:o,secretRef:e.secretRef,createdAt:r?.createdAt??new Date().toISOString(),connectedAt:new Date().toISOString()};return this.#Q(e.botId,async()=>{await this.#e.set(e.secretRef,i);try{if(a.controller.signal.aborted)throw new DOMException("Cancelled","AbortError");await this.#t.save(t)}catch(s){throw await this.#B(e.secretRef,A),s}try{if(a.controller.signal.aborted)throw new DOMException("Cancelled","AbortError");await this.#h(t,i),this.#g.delete(e.botId)}catch(s){if(a.controller.signal.aborted)throw await this.#d(e.botId),r?await this.#t.save(r).catch(()=>{}):await this.#t.remove(e.botId).catch(()=>null)&&await this.#s({botId:e.botId,config:t}).catch(c=>{this.#o.warn?.("[dsh-im:wecom] cancelled bot state cleanup failed:",c)}),await this.#B(e.secretRef,A),s;this.#g.set(e.botId,Er("connection-failed","\u4F01\u4E1A\u5FAE\u4FE1\u673A\u5668\u4EBA\u5DF2\u7ED1\u5B9A\uFF0C\u6D88\u606F\u8FDE\u63A5\u6682\u672A\u5C31\u7EEA\u3002")),this.#o.warn?.(`[dsh-im:wecom] bot ${e.botId} activation connection failed`)}return this.#f(),e.botId})}async#h(a,o){if(this.#l)throw new Error("Enterprise WeChat controller is closed");if(await this.#d(a.botId),this.#l)throw new Error("Enterprise WeChat controller is closed");let i=await this.#a({botId:a.botId,config:a,secret:o});if(!i||typeof i.start!="function"||typeof i.stop!="function")throw new TypeError("createRuntime returned an invalid Enterprise WeChat runtime");this.#I.set(a.botId,i);try{await i.start()}catch(e){throw await i.stop().catch(()=>{}),this.#I.delete(a.botId),e}}async#d(a){let o=this.#I.get(a);this.#I.delete(a),await o?.stop().catch(i=>{this.#o.warn?.(`[dsh-im:wecom] bot ${a} failed to stop cleanly:`,i)})}async#u(a){let o=await this.#e.resolve(a).catch(()=>{});return Dg(o?.value)}async#B(a,o){o?.value?await this.#e.set(a,o.value).catch(()=>{}):await this.#e.unset(a).catch(()=>{})}#Q(a,o){let r=(this.#r.get(a)??Promise.resolve()).catch(()=>{}).then(o).finally(()=>{this.#r.get(a)===r&&this.#r.delete(a)});return this.#r.set(a,r),r}#y(a){a.scode=null,a.verificationUrl=null,a.expiresAt=null,this.#i===a.id&&(this.#i=null),this.#f();let o=[...this.#n.values()].filter(i=>kC.has(i.state));for(;o.length>16;)this.#n.delete(o.shift().id)}#f(){this.#c+=1}};import{WSAuthFailureError as iBe,WSClient as oBe,WSReconnectExhaustedError as nBe}from"@wecom/aibot-node-sdk";import{generateReqId as eBe}from"@wecom/aibot-node-sdk";var ABe=["\u4F01\u4E1A\u5FAE\u4FE1\u673A\u5668\u4EBA\u5DF2\u8FDE\u63A5 DeepSeek Harness\u3002","","\u76F4\u63A5\u53D1\u9001\u6587\u5B57\u5373\u53EF\u7EE7\u7EED\u5F53\u524D\u4F1A\u8BDD\u3002","/new \u5F00\u542F\u4E00\u4E2A\u5168\u65B0\u4F1A\u8BDD","/workspace \u5DE5\u4F5C\u533A\u7EDD\u5BF9\u8DEF\u5F84 \u5207\u6362\u5DE5\u4F5C\u533A","/status \u68C0\u67E5\u8FDE\u63A5\u72B6\u6001","/help \u663E\u793A\u672C\u5E2E\u52A9"].join(` `),tBe=18e3;function X_(n){return n?.body&&typeof n.body=="object"?n.body:{}}function a1(n){let a=X_(n);return a.chattype==="group"?`group:${a.chatid}`:`direct:${a.from?.userid}`}function aBe(n){let a=X_(n);return a.msgtype==="text"?typeof a.text?.content=="string"?a.text.content.trim():"":a.msgtype==="voice"?typeof a.voice?.content=="string"?a.voice.content.trim():"":a.msgtype==="mixed"&&Array.isArray(a.mixed?.msg_item)?a.mixed.msg_item.filter(o=>o?.msgtype==="text"&&typeof o.text?.content=="string").map(o=>o.text.content).join(` -`).trim():""}function Lu(n,a=tBe){let o=String(n??"").trim();if(!o)return[];let i=[],e="",r=0;for(let A of o){let t=Buffer.byteLength(A);e&&r+t>a?(i.push(e),e=A,r=t):(e+=A,r+=t)}return e&&i.push(e),i}function rBe(n){return n?.type==="text"?n.text:n?.type==="tool"?`\u6B63\u5728\u4F7F\u7528${n.name}\u2026`:n?.text}function $_(){return{messagesReceived:0,messagesReplied:0,messagesRejected:0,lastMessageAt:null,lastReplyAt:null,lastRejectedAt:null,lastError:null}}var Uu=class{#A;#e;#t;#a;#s;#o;#I;#g=new Map;constructor({client:a,harness:o,state:i,status:e=$_(),logger:r=console,replyTimeoutMs:A=6e5,generateStreamId:t=eBe}){if(!a||typeof a.replyStream!="function"||typeof a.sendMessage!="function")throw new TypeError("Enterprise WeChat client is required");if(!o||!i)throw new TypeError("Harness client and state store are required");this.#A=a,this.#e=o,this.#t=i,this.#a=e,this.#s=r,this.#o=A,this.#I=t}get status(){return structuredClone(this.#a)}accept(a){let o=a1(a),e=(this.#g.get(o)??Promise.resolve()).catch(()=>{}).then(()=>this.#r(a)).finally(()=>{this.#g.get(o)===e&&this.#g.delete(o)});return this.#g.set(o,e),e}async waitForIdle(){await Promise.allSettled([...this.#g.values()])}async#n(a,o){for(let i of Lu(o))await this.#A.sendMessage(a,{msgtype:"markdown",markdown:{content:i}})}async#i(a,o,i){let e=Lu(i);if(e.length!==0)try{await this.#A.replyStream(a,this.#I("stream"),e[0],!0);for(let r of e.slice(1))await this.#A.sendMessage(o,{msgtype:"markdown",markdown:{content:r}})}catch{await this.#n(o,i)}}async#r(a){let o=X_(a),i=typeof o.msgid=="string"?o.msgid:"",e=typeof o.from?.userid=="string"?o.from.userid:"",r=o.chattype==="group"?o.chatid:e;if(!i||!e||!r||!["single","group"].includes(o.chattype)||this.#t.hasSeen(i))return;this.#a.messagesReceived+=1,this.#a.lastMessageAt=new Date().toISOString();let A=aBe(a),t=null,s=!1;try{if(!A){await this.#i(a,r,"\u76EE\u524D\u652F\u6301\u6587\u5B57\u3001\u8BED\u97F3\u8F6C\u5199\u548C\u56FE\u6587\u6DF7\u6392\u4E2D\u7684\u6587\u5B57\u6D88\u606F\u3002"),await this.#t.markSeen(i);return}let g=A.toLowerCase();if(g==="/help"){await this.#i(a,r,ABe),await this.#t.markSeen(i);return}if(g==="/status"){await this.#e.ensureRunning(),await this.#i(a,r,"\u4F01\u4E1A\u5FAE\u4FE1\u673A\u5668\u4EBA\u4E0E DeepSeek Harness \u8FDE\u63A5\u6B63\u5E38\u3002"),await this.#t.markSeen(i);return}let c=a1(a);if(g==="/new"){await this.#t.clearSession(c),await this.#i(a,r,"\u5DF2\u5F00\u542F\u65B0\u4F1A\u8BDD\u3002\u8BF7\u53D1\u9001\u4F60\u7684\u95EE\u9898\u3002"),await this.#t.markSeen(i);return}let l=await Pr(A,this.#e);if(l){await this.#i(a,r,l.message),await this.#t.markSeen(i);return}t=this.#I("stream");try{await this.#A.replyStream(a,t,"\u6B63\u5728\u601D\u8003\u4E2D\u2026",!1),s=!0}catch(f){this.#s.warn?.("[dsh-im:wecom] unable to start a stream; using an active reply:",f)}let{answer:C}=await rr({harness:this.#e,state:this.#t,key:c,text:A,askOptions:{timeoutMs:this.#o,onUpdate:s&&typeof this.#A.replyStreamNonBlocking=="function"?async f=>{let y=Lu(rBe(f))[0];y&&await this.#A.replyStreamNonBlocking(a,t,y,!1)}:void 0}}),u=Lu(C||"\u4EFB\u52A1\u5DF2\u5B8C\u6210\uFF0C\u4F46\u6CA1\u6709\u751F\u6210\u53EF\u663E\u793A\u7684\u6587\u672C\u3002"),Q=!1;if(s&&u.length>0)try{await this.#A.replyStream(a,t,u[0],!0);for(let f of u.slice(1))await this.#A.sendMessage(r,{msgtype:"markdown",markdown:{content:f}});Q=!0}catch(f){this.#s.warn?.("[dsh-im:wecom] stream finalization failed; using an active reply:",f)}Q||await this.#n(r,C),await this.#t.markSeen(i),this.#a.messagesReplied+=1,this.#a.lastReplyAt=new Date().toISOString(),this.#a.lastError=null}catch(g){this.#a.lastError=g?.message??String(g),this.#s.error?.("[dsh-im:wecom] failed to process an inbound message");try{s&&t?await this.#A.replyStream(a,t,"\u6D88\u606F\u5904\u7406\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002",!0):await this.#i(a,r,"\u6D88\u606F\u5904\u7406\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002"),await this.#t.markSeen(i)}catch{this.#s.error?.("[dsh-im:wecom] failed to send the safe error reply")}}}};function sBe(){let n=new Error("Enterprise WeChat WebSocket authentication timed out");return n.code="connect-timeout",n}function gBe(){return{startedAt:null,ready:!1,wecomConnectionState:"idle",harnessReachable:!1,lastCheckedAt:null,lastConnectedAt:null,lastError:null,...$_()}}var PC=class{#A;#e;#t;#a;#s;#o;#I;#g;#n;#i=gBe();#r=null;#c=null;#l=null;#E=null;constructor({config:a,secret:o,harness:i,state:e,logger:r=console,replyTimeoutMs:A=6e5,connectTimeoutMs:t=2e4,maxReconnectAttempts:s=10,createClient:g=c=>new oBe(c)}){if(!a||!o||!i||!e)throw new TypeError("WecomRuntime requires config, secret, Harness, and state");this.#A=a,this.#e=o,this.#t=i,this.#a=e,this.#s=r,this.#o=A,this.#I=t,this.#g=s,this.#n=g}get status(){return structuredClone(this.#i)}async start(){if(this.#i.ready&&this.#r)return this.status;if(this.#l)return this.#l;let a=new AbortController;return this.#E=a,this.#l=this.#C(a.signal).finally(()=>{this.#E===a&&(this.#E=null),this.#l=null}),this.#l}async#C(a){await this.#p(),a.throwIfAborted(),this.#i.startedAt=new Date().toISOString(),this.#i.wecomConnectionState="connecting",this.#i.lastError=null,await this.#t.ensureRunning(),this.#i.harnessReachable=!0;let o={debug(){},info(){},warn(){},error(){}},i=this.#n({botId:this.#A.remoteBotId,secret:this.#e,logger:o,maxReconnectAttempts:this.#g});if(!i||typeof i.connect!="function"||typeof i.disconnect!="function")throw new TypeError("Enterprise WeChat client factory returned an invalid client");this.#r=i,this.#c=new Uu({client:i,harness:this.#t,state:this.#a,status:this.#i,logger:this.#s,replyTimeoutMs:this.#o});let e,r,A=!1,t=new Promise((Q,f)=>{e=Q,r=f}),s=()=>{if(this.#r!==i||a.aborted)return;A=!0;let Q=Date.now();this.#i.ready=!0,this.#i.wecomConnectionState="connected",this.#i.lastCheckedAt=Q,this.#i.lastConnectedAt=Q,this.#i.lastError=null,e()},g=()=>{this.#r===i&&(this.#i.ready=!1,this.#i.wecomConnectionState="connecting",this.#i.lastCheckedAt=Date.now())},c=()=>{this.#r===i&&(this.#i.ready=!1,this.#i.wecomConnectionState="connecting",this.#i.lastCheckedAt=Date.now())},l=Q=>{if(this.#r!==i)return;let f=Q instanceof iBe||Q instanceof nBe;!A&&f&&r(Q),f&&(this.#i.ready=!1,this.#i.wecomConnectionState="failed"),this.#i.lastError=f?Q.name:"connection-error",this.#s.warn?.(`[dsh-im:wecom] bot ${this.#A.botId} connection error`)},C=Q=>this.#c?.accept(Q);i.on("authenticated",s),i.on("disconnected",g),i.on("reconnecting",c),i.on("error",l),i.on("message",C);let u;try{return i.connect(),await Promise.race([t,new Promise((Q,f)=>{a.addEventListener("abort",()=>f(a.reason),{once:!0})}),new Promise((Q,f)=>{u=setTimeout(()=>f(sBe()),this.#I)})]),this.status}catch(Q){throw a.aborted?(await this.#p(),Q):(this.#i.ready=!1,this.#i.wecomConnectionState="failed",this.#i.lastError=Q?.message??String(Q),await this.#p(),Q)}finally{clearTimeout(u)}}async stop(){let a=this.#l;return this.#E?.abort(new DOMException("Enterprise WeChat runtime stopped","AbortError")),await this.#p(),await a?.catch(()=>{}),this.status}async#p(){let a=this.#r,o=this.#c;this.#r=null,this.#c=null;try{a?.disconnect(),a?.removeAllListeners?.()}catch{this.#s.warn?.(`[dsh-im:wecom] bot ${this.#A.botId} failed to stop cleanly`)}await o?.waitForIdle(),this.#i.ready=!1,this.#i.wecomConnectionState="idle"}};var r1=Object.freeze([250,1e3,3e3,5e3,1e4,3e4]);function cBe(n){if(!Array.isArray(n)||n.length===0)return[...r1];let a=n.filter(o=>Number.isFinite(o)&&o>=0);return a.length>0?a:[...r1]}var Hu=class{#A;#e;#t;#a;#s;#o;#I;#g=null;#n=null;#i=0;#r=!1;#c=!1;#l;#E;constructor({controller:a,harness:o,logger:i=console,retryDelaysMs:e,healthyIntervalMs:r=15e3,setTimeoutImpl:A=setTimeout,clearTimeoutImpl:t=clearTimeout}){if(!a||typeof a.initialize!="function"||typeof a.status!="function")throw new TypeError("ConnectionSupervisor requires a controller");if(!o||typeof o.ensureRunning!="function")throw new TypeError("ConnectionSupervisor requires a Harness client");this.#A=a,this.#e=o,this.#t=i,this.#a=cBe(e),this.#s=Number.isFinite(r)&&r>=0?r:15e3,this.#o=A,this.#I=t,this.#l=new Promise(s=>{this.#E=s})}get ready(){return this.#l}start(){return this.#c||this.#r?this:(this.#c=!0,this.#C(0),this)}async close(){this.#r||(this.#r=!0,this.#g!==null&&this.#I(this.#g),this.#g=null,await this.#n?.catch(()=>{}),this.#E?.(null),this.#E=null)}#C(a){this.#r||(this.#g=this.#o(()=>{this.#g=null,this.#p()},a),this.#g?.unref?.())}async#p(){if(this.#r||this.#n)return;let a=this.#h();this.#n=a;try{await a}finally{this.#n===a&&(this.#n=null)}}async#h(){try{if(await this.#e.ensureRunning(),this.#r)return;let a=await this.#A.initialize();if(this.#r)return;this.#E?.(a),this.#E=null;let{configured:o,connected:i}=a.totals;if(i65535)throw new Error("dsh-im Enterprise WeChat requires an initialized DSH webServer port");return new URL(`http://127.0.0.1:${o}`)}function CBe(n){let a=Og(n.dshHome??process.env.DSH_HOME??GC(lBe(),".dsh")),o=Og(n.dataDir??GC(a,"integrations","dsh-wecom"));return{config:Og(n.configPath??GC(o,"config.json")),bots:Og(n.botsDir??GC(o,"bots")),workspaces:Og(n.workspacesPath??GC(o,"workspaces.json"))}}async function AN(n,a={},o={}){if(!n?.credentials)throw new TypeError("dsh-im Enterprise WeChat requires ctx.credentials");if(!n?.webServer)throw new TypeError("dsh-im Enterprise WeChat requires ctx.webServer");let i=o.ConfigStore??Ru,e=o.StateStore??Fu,r=o.HarnessClient??vu,A=o.Controller??vC,t=o.Runtime??PC,s=o.QrAuth??Pu,g=o.createConnectionSupervisor??eN,c=typeof n.logger=="function"?n.logger("dsh-im:wecom"):n.logger??console,l=CBe(a),C=await new i(l.config).load(),u=Og(a.workspace??process.cwd()),Q=o.WorkspaceStore??ga,f=o.workspaces??await new Q(l.workspaces,{defaultWorkspace:u}).load(),y=C.list();await f.reconcile(y.map(F=>F.botId)),await Promise.all(y.map(F=>f.ensure(F.botId)));let m=typeof C.remove=="function"?Ga(C,{workspaces:f}):C,T=o.qrAuth??new s({source:a.qrSource??"deepseek-harness",platform:a.qrPlatform}),R=new Map,G=F=>Og(l.bots,F,"state.json"),j=async F=>{let k=R.get(F);return k||(k=await new e(G(F)).load(),R.set(F,k)),k},x=new r({baseUrl:EBe(n.webServer,a.harnessBaseUrl),workspace:u,agentPreset:a.agentPreset??"standard",autostart:!1,dshBin:a.dshBin??"dsh"}),H=new A({qrAuth:T,credentials:n.credentials,configStore:m,logger:c,createRuntime:async({botId:F,config:k,secret:$})=>{let U=await j(F);await f.ensure(F);let b=Fa(x,{botId:F,workspaces:f,state:U});return new t({config:k,secret:$,harness:b.harness,state:b.state,replyTimeoutMs:a.replyTimeoutMs??6e5,connectTimeoutMs:a.connectTimeoutMs??2e4,maxReconnectAttempts:a.maxReconnectAttempts??10,logger:{error:(...L)=>c.error?.(`[${F}]`,...L),warn:(...L)=>c.warn?.(`[${F}]`,...L),info:(...L)=>c.info?.(`[${F}]`,...L),debug:(...L)=>c.debug?.(`[${F}]`,...L)}})},deleteState:async({botId:F})=>{let k=R.get(F);if(R.delete(F),k&&typeof k.remove=="function")await k.remove();else try{await IBe(G(F))}catch($){if($?.code!=="ENOENT")throw $}}}),J=La(H,{workspaces:f,stateFor:j}),K=g({controller:J,harness:x,logger:c,retryDelaysMs:a.retryDelaysMs,healthyIntervalMs:a.healthyIntervalMs}).start();return{controller:J,ready:K.ready,async close(){await K.close(),await J.close(),x.stopManagedProcess()}}}import pBe from"qrcode";var o1="/wecom",Sa=Object.freeze({status:"connection.status",beginProvisioning:"provision.begin",pollProvisioning:"provision.poll",cancelProvisioning:"provision.cancel",bindCredentials:"bot.bind-credentials",reconnectBot:"bot.reconnect",deleteBot:"bot.delete",setWorkspace:ir}),n1=Object.freeze(Object.values(Sa)),hBe=new Set(["secret","secretRef","scode","remoteBotId","verificationUrl","bot_info","botid"]);function rN(n){return n!==null&&typeof n=="object"&&!Array.isArray(n)}function CI(n,a){return rN(n)&&Object.keys(n).every(o=>a.includes(o))}function tN(n){return typeof n=="string"&&/^[A-Za-z0-9_-]{1,128}$/.test(n)}function i1(n,a){return typeof n=="string"&&n.trim().length>0&&n.length<=a}function dBe(n,a){return rN(a)?n===Sa.status?CI(a,[])?null:"connection.status does not accept fields.":n===Sa.beginProvisioning?CI(a,["locale"])&&(a.locale===void 0||a.locale==="zh-CN")?null:"provision.begin received unsupported fields.":[Sa.pollProvisioning,Sa.cancelProvisioning].includes(n)?CI(a,["attemptId"])&&tN(a.attemptId)?null:`${n} requires an attemptId.`:n===Sa.bindCredentials?CI(a,["botId","secret"])&&i1(a.botId,512)&&i1(a.secret,1024)?null:"bot.bind-credentials requires Bot ID and Secret.":n===Sa.reconnectBot?CI(a,["botId"])&&tN(a.botId)?null:"bot.reconnect requires a botId.":n===Sa.deleteBot?CI(a,["botId","confirm"])&&tN(a.botId)&&a.confirm===!0?null:"bot.delete requires a botId and confirm=true.":n===Sa.setWorkspace?Ha(a)?null:"\u8BF7\u8F93\u5165\u5DE5\u4F5C\u533A\u7EDD\u5BF9\u8DEF\u5F84\u3002":"Unknown Enterprise WeChat endpoint.":"Payload must be an object."}function pI(n){if(Array.isArray(n))return n.map(pI);if(!rN(n))return n;let a={};for(let[o,i]of Object.entries(n))hBe.has(o)||(a[o]=pI(i));return a}async function BBe(n){return pBe.toDataURL(n,{type:"image/png",errorCorrectionLevel:"M",margin:2,width:320})}async function aN(n,a){return!n||typeof n.verificationUrl!="string"?pI(n):pI({...n,qrCodeDataUrl:await a(n.verificationUrl)})}async function FC(n,a){let o=structuredClone(n);return o?.provisioning&&(o.provisioning=await aN(o.provisioning,a)),pI(o)}function s1(n,{encodeQr:a=BBe}={}){for(let e of["status","startProvisioning","registrationStatus","cancelProvisioning","bindCredentials","reconnectBot","deleteBot"])if(typeof n?.[e]!="function")throw new TypeError(`A complete Enterprise WeChat controller is required (${e})`);let o=new Map,i=e=>{let r=o.get(e);return r||(o.size>=16&&o.delete(o.keys().next().value),r=Promise.resolve().then(()=>a(e)),o.set(e,r)),r};return async(e,r,A)=>{if(A?.aborted)return{ok:!1,error:{code:"cancelled",message:"The request was cancelled."}};if(!n1.includes(e))return{ok:!1,error:{code:"bad-request",message:"Unknown Enterprise WeChat endpoint."}};let t=dBe(e,r);if(t)return{ok:!1,error:{code:"bad-request",message:t}};try{let s;if(e===Sa.status)s=await FC(await n.status(),i);else if(e===Sa.beginProvisioning)s=await aN(await n.startProvisioning(),i);else if(e===Sa.pollProvisioning){let g=await n.registrationStatus(r.attemptId);if(!g)return{ok:!1,error:{code:"bad-request",message:"The provisioning attempt no longer exists."}};s=await aN(g,i)}else if(e===Sa.cancelProvisioning)s=pI(await n.cancelProvisioning(r.attemptId));else if(e===Sa.bindCredentials)s=await FC(await n.bindCredentials(r),i);else if(e===Sa.reconnectBot)s=await FC(await n.reconnectBot(r.botId),i);else if(e===Sa.setWorkspace){if(typeof n.updateWorkspace!="function")throw new Error("Workspace update is unavailable");s=await FC(await n.updateWorkspace(r.botId,r.workspace),i)}else s=await FC(await n.deleteBot(r.botId),i);return A?.aborted?{ok:!1,error:{code:"cancelled",message:"The request was cancelled."}}:{ok:!0,value:s}}catch(s){let g=Ka(s);return A?.aborted?{ok:!1,error:{code:"cancelled",message:"The request was cancelled."}}:{ok:!1,error:g??{code:"wecom-operation-failed",message:"\u4F01\u4E1A\u5FAE\u4FE1\u64CD\u4F5C\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002"}}}}}function Ku(n,a,o,i){if(!n?.connection?.rpc||typeof n.connection.rpc.handle!="function")throw new TypeError("DSH Host Connection RPC is required");return n.connection.rpc.handle(o1,s1(a,o),{authority:Ua(i)})}async function g1(n,a={}){if(a?.controller)return Ku(n,a.controller,a.rpcOptions,a.rpcAuthority);let o=await AN(n,a,a.internals),i=Ku(n,o.controller,a.rpcOptions,a.rpcAuthority);return n.effect(()=>async()=>o.close(),"dsh-im: close Enterprise WeChat bot connections"),i}import{unlink as XBe}from"node:fs/promises";import{homedir as $Be}from"node:os";import{join as KC,resolve as Mg}from"node:path";import{createHash as _Be}from"node:crypto";import{mkdir as NBe,readFile as TBe,rename as RBe,unlink as kBe,writeFile as vBe}from"node:fs/promises";import{dirname as PBe}from"node:path";import{randomBytes as uBe,randomUUID as QBe}from"node:crypto";var xu="https://ilinkai.weixin.qq.com/",fBe="2.4.6",yBe="3",mBe="bot",SBe=2<<16|1024|6,DBe=15e3,c1=35e3,wBe=new Set(["wait","scaned","confirmed","expired","scaned_but_redirect","need_verifycode","verify_code_blocked","binded_redirect"]),ct=class extends Error{constructor(a,o,i={}){super(o,i),this.name="WeixinApiError",this.code=a,this.status=i.status}};function Di(n){return typeof n=="string"&&n.trim()?n.trim():null}function iN(n){let a=n.toLowerCase().replace(/\.$/,"");return a==="weixin.qq.com"||a.endsWith(".weixin.qq.com")}function dI(n){let a;try{a=new URL(n)}catch{throw new ct("invalid-base-url","\u5FAE\u4FE1\u670D\u52A1\u8FD4\u56DE\u4E86\u65E0\u6548\u7684\u8FDE\u63A5\u5730\u5740\u3002")}if(a.protocol!=="https:"||!iN(a.hostname)||a.port!==""&&a.port!=="443")throw new ct("untrusted-base-url","\u5FAE\u4FE1\u670D\u52A1\u8FD4\u56DE\u4E86\u4E0D\u53D7\u4FE1\u4EFB\u7684\u8FDE\u63A5\u5730\u5740\u3002");return a.username="",a.password="",a.search="",a.hash="",a.pathname.endsWith("/")||(a.pathname+="/"),a.toString()}function OBe(n){let a=Di(n);if(!a)throw new ct("invalid-qr","\u5FAE\u4FE1\u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u626B\u7801\u5730\u5740\u3002");let o;try{o=new URL(a)}catch{throw new ct("invalid-qr","\u5FAE\u4FE1\u670D\u52A1\u8FD4\u56DE\u4E86\u65E0\u6548\u7684\u626B\u7801\u5730\u5740\u3002")}if(o.protocol!=="https:"||!iN(o.hostname))throw new ct("untrusted-qr","\u5FAE\u4FE1\u670D\u52A1\u8FD4\u56DE\u4E86\u4E0D\u53D7\u4FE1\u4EFB\u7684\u626B\u7801\u5730\u5740\u3002");return o.toString()}function l1(){return{"iLink-App-Id":mBe,"iLink-App-ClientVersion":String(SBe)}}function MBe(n){let a={...l1(),"content-type":"application/json",AuthorizationType:"ilink_bot_token","X-WECHAT-UIN":Buffer.from(String(uBe(4).readUInt32BE(0)),"utf8").toString("base64")};return Di(n)&&(a.Authorization=`Bearer ${n.trim()}`),a}function Yu(){return{channel_version:fBe,bot_agent:"DeepSeekHarness/0.4.0"}}function I1(n){return n?.reason instanceof Error?n.reason:new DOMException("The operation was aborted","AbortError")}async function hI(n,{method:a,baseUrl:o,endpoint:i,body:e,token:r,timeoutMs:A=DBe,signal:t,authenticated:s=!0}){let g=dI(o),c=new URL(i,g);if(!iN(c.hostname))throw new ct("untrusted-endpoint","\u62D2\u7EDD\u8BBF\u95EE\u4E0D\u53D7\u4FE1\u4EFB\u7684\u5FAE\u4FE1\u670D\u52A1\u5730\u5740\u3002");let l=new AbortController,C=!1,u=()=>l.abort(t?.reason);if(t?.aborted)throw I1(t);t?.addEventListener("abort",u,{once:!0});let Q=A>0?setTimeout(()=>{C=!0,l.abort()},A):null;try{let f=await n(c,{method:a,headers:s?MBe(r):l1(),...e===void 0?{}:{body:JSON.stringify(e)},signal:l.signal});if(!f.ok)throw new ct("http-error",`\u5FAE\u4FE1\u670D\u52A1\u8BF7\u6C42\u5931\u8D25\uFF08HTTP ${f.status}\uFF09\u3002`,{status:f.status});try{return await f.json()}catch(y){throw new ct("invalid-response","\u5FAE\u4FE1\u670D\u52A1\u8FD4\u56DE\u4E86\u65E0\u6CD5\u89E3\u6790\u7684\u54CD\u5E94\u3002",{cause:y})}}catch(f){throw t?.aborted?I1(t):C?new ct("timeout","\u5FAE\u4FE1\u670D\u52A1\u8BF7\u6C42\u8D85\u65F6\u3002",{cause:f}):f instanceof ct?f:new ct("network-error","\u6682\u65F6\u65E0\u6CD5\u8BBF\u95EE\u5FAE\u4FE1\u670D\u52A1\u3002",{cause:f})}finally{Q&&clearTimeout(Q),t?.removeEventListener("abort",u)}}function bBe(n){if(!n||typeof n!="object"||!wBe.has(n.status))throw new ct("invalid-login-status","\u5FAE\u4FE1\u670D\u52A1\u8FD4\u56DE\u4E86\u65E0\u6CD5\u8BC6\u522B\u7684\u626B\u7801\u72B6\u6001\u3002");return n}function E1({fetchImpl:n=fetch}={}){if(typeof n!="function")throw new TypeError("fetchImpl must be a function");return Object.freeze({async beginLogin({localTokens:a=[],botType:o=yBe,signal:i}={}){let e=[...new Set(a.map(Di).filter(Boolean))].slice(-10),r=await hI(n,{method:"POST",baseUrl:xu,endpoint:`ilink/bot/get_bot_qrcode?bot_type=${encodeURIComponent(o)}`,body:{local_token_list:e},timeoutMs:1e4,signal:i}),A=Di(r?.qrcode);if(!A)throw new ct("invalid-qr","\u5FAE\u4FE1\u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u4E8C\u7EF4\u7801\u4EE4\u724C\u3002");return{qrcode:A,qrcodeUrl:OBe(r.qrcode_img_content)}},async pollLogin({qrcode:a,baseUrl:o=xu,verifyCode:i,signal:e}){let r=Di(a);if(!r)throw new TypeError("qrcode is required");let A=`ilink/bot/get_qrcode_status?qrcode=${encodeURIComponent(r)}`;Di(i)&&(A+=`&verify_code=${encodeURIComponent(i.trim())}`);let t=await hI(n,{method:"GET",baseUrl:o,endpoint:A,timeoutMs:c1,signal:e,authenticated:!1});return bBe(t)},async getUpdates({baseUrl:a,token:o,getUpdatesBuf:i="",timeoutMs:e,signal:r}){try{return await hI(n,{method:"POST",baseUrl:a,endpoint:"ilink/bot/getupdates",body:{get_updates_buf:i,base_info:Yu()},token:o,timeoutMs:e??c1,signal:r})}catch(A){if(A instanceof ct&&A.code==="timeout")return{ret:0,msgs:[],get_updates_buf:i};throw A}},async sendText({baseUrl:a,token:o,toUserId:i,text:e,contextToken:r,runId:A,signal:t}){let s=Di(i),g=Di(e);if(!s||!g)throw new TypeError("toUserId and text are required");let c=await hI(n,{method:"POST",baseUrl:a,endpoint:"ilink/bot/sendmessage",token:o,signal:t,body:{msg:{from_user_id:"",to_user_id:s,client_id:`dsh-weixin-${QBe()}`,message_type:2,message_state:2,item_list:[{type:1,text_item:{text:g}}],...Di(r)?{context_token:r.trim()}:{},...Di(A)?{run_id:A.trim()}:{}},base_info:Yu()}});if(c?.ret!==void 0&&c.ret!==0)throw new ct("send-rejected","\u5FAE\u4FE1\u670D\u52A1\u62D2\u7EDD\u4E86\u56DE\u590D\u6D88\u606F\u3002");return!0},async notifyStart({baseUrl:a,token:o,signal:i}){let e=await hI(n,{method:"POST",baseUrl:a,endpoint:"ilink/bot/msg/notifystart",token:o,signal:i,timeoutMs:1e4,body:{base_info:Yu()}});if(e?.ret!==void 0&&e.ret!==0)throw new ct("start-rejected","\u5FAE\u4FE1\u8D26\u53F7\u8FDE\u63A5\u542F\u52A8\u5931\u8D25\u3002");return e},async notifyStop({baseUrl:a,token:o,signal:i}){return hI(n,{method:"POST",baseUrl:a,endpoint:"ilink/bot/msg/notifystop",token:o,signal:i,timeoutMs:1e4,body:{base_info:Yu()}})}})}function C1(n){for(let a of n?.item_list??[]){if(a?.type===1&&typeof a.text_item?.text=="string"){let o=a.text_item.text.trim();if(o)return o}if(a?.type===3&&typeof a.voice_item?.text=="string"){let o=a.voice_item.text.trim();if(o)return o}}return null}function p1(n){return n?.message_id!==void 0&&n.message_id!==null?String(n.message_id):Di(n?.client_id)}function h1(n,a=4e3){if(n.length<=a)return[n];let o=[],i=n;for(;i.length>a;){let e=i.lastIndexOf(` +`).trim():""}function Lu(n,a=tBe){let o=String(n??"").trim();if(!o)return[];let i=[],e="",r=0;for(let A of o){let t=Buffer.byteLength(A);e&&r+t>a?(i.push(e),e=A,r=t):(e+=A,r+=t)}return e&&i.push(e),i}function rBe(n){return n?.type==="text"?n.text:n?.type==="tool"?`\u6B63\u5728\u4F7F\u7528${n.name}\u2026`:n?.text}function $_(){return{messagesReceived:0,messagesReplied:0,messagesRejected:0,lastMessageAt:null,lastReplyAt:null,lastRejectedAt:null,lastError:null}}var Uu=class{#A;#e;#t;#a;#s;#o;#I;#g=new Map;constructor({client:a,harness:o,state:i,status:e=$_(),logger:r=console,replyTimeoutMs:A=6e5,generateStreamId:t=eBe}){if(!a||typeof a.replyStream!="function"||typeof a.sendMessage!="function")throw new TypeError("Enterprise WeChat client is required");if(!o||!i)throw new TypeError("Harness client and state store are required");this.#A=a,this.#e=o,this.#t=i,this.#a=e,this.#s=r,this.#o=A,this.#I=t}get status(){return structuredClone(this.#a)}accept(a){let o=a1(a),e=(this.#g.get(o)??Promise.resolve()).catch(()=>{}).then(()=>this.#r(a)).finally(()=>{this.#g.get(o)===e&&this.#g.delete(o)});return this.#g.set(o,e),e}async waitForIdle(){await Promise.allSettled([...this.#g.values()])}async#n(a,o){for(let i of Lu(o))await this.#A.sendMessage(a,{msgtype:"markdown",markdown:{content:i}})}async#i(a,o,i){let e=Lu(i);if(e.length!==0)try{await this.#A.replyStream(a,this.#I("stream"),e[0],!0);for(let r of e.slice(1))await this.#A.sendMessage(o,{msgtype:"markdown",markdown:{content:r}})}catch{await this.#n(o,i)}}async#r(a){let o=X_(a),i=typeof o.msgid=="string"?o.msgid:"",e=typeof o.from?.userid=="string"?o.from.userid:"",r=o.chattype==="group"?o.chatid:e;if(!i||!e||!r||!["single","group"].includes(o.chattype)||this.#t.hasSeen(i))return;this.#a.messagesReceived+=1,this.#a.lastMessageAt=new Date().toISOString();let A=aBe(a),t=null,s=!1;try{if(!A){await this.#i(a,r,"\u76EE\u524D\u652F\u6301\u6587\u5B57\u3001\u8BED\u97F3\u8F6C\u5199\u548C\u56FE\u6587\u6DF7\u6392\u4E2D\u7684\u6587\u5B57\u6D88\u606F\u3002"),await this.#t.markSeen(i);return}let g=A.toLowerCase();if(g==="/help"){await this.#i(a,r,ABe),await this.#t.markSeen(i);return}if(g==="/status"){await this.#e.ensureRunning(),await this.#i(a,r,"\u4F01\u4E1A\u5FAE\u4FE1\u673A\u5668\u4EBA\u4E0E DeepSeek Harness \u8FDE\u63A5\u6B63\u5E38\u3002"),await this.#t.markSeen(i);return}let c=a1(a);if(g==="/new"){await this.#t.clearSession(c),await this.#i(a,r,"\u5DF2\u5F00\u542F\u65B0\u4F1A\u8BDD\u3002\u8BF7\u53D1\u9001\u4F60\u7684\u95EE\u9898\u3002"),await this.#t.markSeen(i);return}let l=await Pr(A,this.#e);if(l){await this.#i(a,r,l.message),await this.#t.markSeen(i);return}t=this.#I("stream");try{await this.#A.replyStream(a,t,"\u6B63\u5728\u601D\u8003\u4E2D\u2026",!1),s=!0}catch(f){this.#s.warn?.("[dsh-im:wecom] unable to start a stream; using an active reply:",f)}let{answer:C}=await rr({harness:this.#e,state:this.#t,key:c,text:A,askOptions:{timeoutMs:this.#o,onUpdate:s&&typeof this.#A.replyStreamNonBlocking=="function"?async f=>{let y=Lu(rBe(f))[0];y&&await this.#A.replyStreamNonBlocking(a,t,y,!1)}:void 0}}),u=Lu(C||"\u4EFB\u52A1\u5DF2\u5B8C\u6210\uFF0C\u4F46\u6CA1\u6709\u751F\u6210\u53EF\u663E\u793A\u7684\u6587\u672C\u3002"),Q=!1;if(s&&u.length>0)try{await this.#A.replyStream(a,t,u[0],!0);for(let f of u.slice(1))await this.#A.sendMessage(r,{msgtype:"markdown",markdown:{content:f}});Q=!0}catch(f){this.#s.warn?.("[dsh-im:wecom] stream finalization failed; using an active reply:",f)}Q||await this.#n(r,C),await this.#t.markSeen(i),this.#a.messagesReplied+=1,this.#a.lastReplyAt=new Date().toISOString(),this.#a.lastError=null}catch(g){this.#a.lastError=g?.message??String(g),this.#s.error?.("[dsh-im:wecom] failed to process an inbound message");try{s&&t?await this.#A.replyStream(a,t,"\u6D88\u606F\u5904\u7406\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002",!0):await this.#i(a,r,"\u6D88\u606F\u5904\u7406\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002"),await this.#t.markSeen(i)}catch{this.#s.error?.("[dsh-im:wecom] failed to send the safe error reply")}}}};function sBe(){let n=new Error("Enterprise WeChat WebSocket authentication timed out");return n.code="connect-timeout",n}function gBe(){return{startedAt:null,ready:!1,wecomConnectionState:"idle",harnessReachable:!1,lastCheckedAt:null,lastConnectedAt:null,lastError:null,...$_()}}var PC=class{#A;#e;#t;#a;#s;#o;#I;#g;#n;#i=gBe();#r=null;#c=null;#l=null;#E=null;constructor({config:a,secret:o,harness:i,state:e,logger:r=console,replyTimeoutMs:A=6e5,connectTimeoutMs:t=2e4,maxReconnectAttempts:s=10,createClient:g=c=>new oBe(c)}){if(!a||!o||!i||!e)throw new TypeError("WecomRuntime requires config, secret, Harness, and state");this.#A=a,this.#e=o,this.#t=i,this.#a=e,this.#s=r,this.#o=A,this.#I=t,this.#g=s,this.#n=g}get status(){return structuredClone(this.#i)}async start(){if(this.#i.ready&&this.#r)return this.status;if(this.#l)return this.#l;let a=new AbortController;return this.#E=a,this.#l=this.#C(a.signal).finally(()=>{this.#E===a&&(this.#E=null),this.#l=null}),this.#l}async#C(a){await this.#p(),a.throwIfAborted(),this.#i.startedAt=new Date().toISOString(),this.#i.wecomConnectionState="connecting",this.#i.lastError=null,await this.#t.ensureRunning(),this.#i.harnessReachable=!0;let o={debug(){},info(){},warn(){},error(){}},i=this.#n({botId:this.#A.remoteBotId,secret:this.#e,logger:o,maxReconnectAttempts:this.#g});if(!i||typeof i.connect!="function"||typeof i.disconnect!="function")throw new TypeError("Enterprise WeChat client factory returned an invalid client");this.#r=i,this.#c=new Uu({client:i,harness:this.#t,state:this.#a,status:this.#i,logger:this.#s,replyTimeoutMs:this.#o});let e,r,A=!1,t=new Promise((Q,f)=>{e=Q,r=f}),s=()=>{if(this.#r!==i||a.aborted)return;A=!0;let Q=Date.now();this.#i.ready=!0,this.#i.wecomConnectionState="connected",this.#i.lastCheckedAt=Q,this.#i.lastConnectedAt=Q,this.#i.lastError=null,e()},g=()=>{this.#r===i&&(this.#i.ready=!1,this.#i.wecomConnectionState="connecting",this.#i.lastCheckedAt=Date.now())},c=()=>{this.#r===i&&(this.#i.ready=!1,this.#i.wecomConnectionState="connecting",this.#i.lastCheckedAt=Date.now())},l=Q=>{if(this.#r!==i)return;let f=Q instanceof iBe||Q instanceof nBe;!A&&f&&r(Q),f&&(this.#i.ready=!1,this.#i.wecomConnectionState="failed"),this.#i.lastError=f?Q.name:"connection-error",this.#s.warn?.(`[dsh-im:wecom] bot ${this.#A.botId} connection error`)},C=Q=>this.#c?.accept(Q);i.on("authenticated",s),i.on("disconnected",g),i.on("reconnecting",c),i.on("error",l),i.on("message",C);let u;try{return i.connect(),await Promise.race([t,new Promise((Q,f)=>{a.addEventListener("abort",()=>f(a.reason),{once:!0})}),new Promise((Q,f)=>{u=setTimeout(()=>f(sBe()),this.#I)})]),this.status}catch(Q){throw a.aborted?(await this.#p(),Q):(this.#i.ready=!1,this.#i.wecomConnectionState="failed",this.#i.lastError=Q?.message??String(Q),await this.#p(),Q)}finally{clearTimeout(u)}}async stop(){let a=this.#l;return this.#E?.abort(new DOMException("Enterprise WeChat runtime stopped","AbortError")),await this.#p(),await a?.catch(()=>{}),this.status}async#p(){let a=this.#r,o=this.#c;this.#r=null,this.#c=null;try{a?.disconnect(),a?.removeAllListeners?.()}catch{this.#s.warn?.(`[dsh-im:wecom] bot ${this.#A.botId} failed to stop cleanly`)}await o?.waitForIdle(),this.#i.ready=!1,this.#i.wecomConnectionState="idle"}};var r1=Object.freeze([250,1e3,3e3,5e3,1e4,3e4]);function cBe(n){if(!Array.isArray(n)||n.length===0)return[...r1];let a=n.filter(o=>Number.isFinite(o)&&o>=0);return a.length>0?a:[...r1]}var Hu=class{#A;#e;#t;#a;#s;#o;#I;#g=null;#n=null;#i=0;#r=!1;#c=!1;#l;#E;constructor({controller:a,harness:o,logger:i=console,retryDelaysMs:e,healthyIntervalMs:r=15e3,setTimeoutImpl:A=setTimeout,clearTimeoutImpl:t=clearTimeout}){if(!a||typeof a.initialize!="function"||typeof a.status!="function")throw new TypeError("ConnectionSupervisor requires a controller");if(!o||typeof o.ensureRunning!="function")throw new TypeError("ConnectionSupervisor requires a Harness client");this.#A=a,this.#e=o,this.#t=i,this.#a=cBe(e),this.#s=Number.isFinite(r)&&r>=0?r:15e3,this.#o=A,this.#I=t,this.#l=new Promise(s=>{this.#E=s})}get ready(){return this.#l}start(){return this.#c||this.#r?this:(this.#c=!0,this.#C(0),this)}async close(){this.#r||(this.#r=!0,this.#g!==null&&this.#I(this.#g),this.#g=null,await this.#n?.catch(()=>{}),this.#E?.(null),this.#E=null)}#C(a){this.#r||(this.#g=this.#o(()=>{this.#g=null,this.#p()},a),this.#g?.unref?.())}async#p(){if(this.#r||this.#n)return;let a=this.#h();this.#n=a;try{await a}finally{this.#n===a&&(this.#n=null)}}async#h(){try{if(await this.#e.ensureRunning(),this.#r)return;let a=await this.#A.initialize();if(this.#r)return;this.#E?.(a),this.#E=null;let{configured:o,connected:i}=a.totals;if(i65535)throw new Error("dsh-im Enterprise WeChat requires an initialized DSH webServer port");return new URL(`http://127.0.0.1:${o}`)}function CBe(n){let a=Og(n.dshHome??process.env.DSH_HOME??GC(lBe(),".dsh")),o=Og(n.dataDir??GC(a,"integrations","dsh-wecom"));return{config:Og(n.configPath??GC(o,"config.json")),bots:Og(n.botsDir??GC(o,"bots")),workspaces:Og(n.workspacesPath??GC(o,"workspaces.json"))}}async function AN(n,a={},o={}){if(!n?.credentials)throw new TypeError("dsh-im Enterprise WeChat requires ctx.credentials");if(!n?.webServer)throw new TypeError("dsh-im Enterprise WeChat requires ctx.webServer");let i=o.ConfigStore??Ru,e=o.StateStore??Fu,r=o.HarnessClient??vu,A=o.Controller??vC,t=o.Runtime??PC,s=o.QrAuth??Pu,g=o.createConnectionSupervisor??eN,c=typeof n.logger=="function"?n.logger("dsh-im:wecom"):n.logger??console,l=CBe(a),C=await new i(l.config).load(),u=Og(a.workspace??process.cwd()),Q=o.WorkspaceStore??ga,f=o.workspaces??await new Q(l.workspaces,{defaultWorkspace:u}).load(),y=C.list();await f.reconcile(y.map(F=>F.botId)),await Promise.all(y.map(F=>f.ensure(F.botId)));let m=typeof C.remove=="function"?Ga(C,{workspaces:f}):C,T=o.qrAuth??new s({source:a.qrSource??"deepseek-harness",platform:a.qrPlatform}),R=new Map,G=F=>Og(l.bots,F,"state.json"),j=async F=>{let k=R.get(F);return k||(k=await new e(G(F)).load(),R.set(F,k)),k},x=new r({baseUrl:EBe(n.webServer,a.harnessBaseUrl),workspace:u,agentPreset:a.agentPreset??"standard",autostart:!1,dshBin:a.dshBin??"dsh"}),H=new A({qrAuth:T,credentials:n.credentials,configStore:m,logger:c,createRuntime:async({botId:F,config:k,secret:$})=>{let U=await j(F);await f.ensure(F);let b=Fa(x,{botId:F,workspaces:f,state:U});return new t({config:k,secret:$,harness:b.harness,state:b.state,replyTimeoutMs:a.replyTimeoutMs??6e5,connectTimeoutMs:a.connectTimeoutMs??2e4,maxReconnectAttempts:a.maxReconnectAttempts??10,logger:{error:(...L)=>c.error?.(`[${F}]`,...L),warn:(...L)=>c.warn?.(`[${F}]`,...L),info:(...L)=>c.info?.(`[${F}]`,...L),debug:(...L)=>c.debug?.(`[${F}]`,...L)}})},deleteState:async({botId:F})=>{let k=R.get(F);if(R.delete(F),k&&typeof k.remove=="function")await k.remove();else try{await IBe(G(F))}catch($){if($?.code!=="ENOENT")throw $}}}),J=La(H,{workspaces:f,stateFor:j}),K=g({controller:J,harness:x,logger:c,retryDelaysMs:a.retryDelaysMs,healthyIntervalMs:a.healthyIntervalMs}).start();return{controller:J,ready:K.ready,async close(){await K.close(),await J.close(),x.stopManagedProcess()}}}import pBe from"qrcode";var o1="/wecom",Sa=Object.freeze({status:"connection.status",beginProvisioning:"provision.begin",pollProvisioning:"provision.poll",cancelProvisioning:"provision.cancel",bindCredentials:"bot.bind-credentials",reconnectBot:"bot.reconnect",deleteBot:"bot.delete",setWorkspace:ir}),n1=Object.freeze(Object.values(Sa)),hBe=new Set(["secret","secretRef","scode","remoteBotId","verificationUrl","bot_info","botid"]);function rN(n){return n!==null&&typeof n=="object"&&!Array.isArray(n)}function CI(n,a){return rN(n)&&Object.keys(n).every(o=>a.includes(o))}function tN(n){return typeof n=="string"&&/^[A-Za-z0-9_-]{1,128}$/.test(n)}function i1(n,a){return typeof n=="string"&&n.trim().length>0&&n.length<=a}function dBe(n,a){return rN(a)?n===Sa.status?CI(a,[])?null:"connection.status does not accept fields.":n===Sa.beginProvisioning?CI(a,["locale"])&&(a.locale===void 0||a.locale==="zh-CN")?null:"provision.begin received unsupported fields.":[Sa.pollProvisioning,Sa.cancelProvisioning].includes(n)?CI(a,["attemptId"])&&tN(a.attemptId)?null:`${n} requires an attemptId.`:n===Sa.bindCredentials?CI(a,["botId","secret"])&&i1(a.botId,512)&&i1(a.secret,1024)?null:"bot.bind-credentials requires Bot ID and Secret.":n===Sa.reconnectBot?CI(a,["botId"])&&tN(a.botId)?null:"bot.reconnect requires a botId.":n===Sa.deleteBot?CI(a,["botId","confirm"])&&tN(a.botId)&&a.confirm===!0?null:"bot.delete requires a botId and confirm=true.":n===Sa.setWorkspace?Ha(a)?null:"\u8BF7\u8F93\u5165\u5DE5\u4F5C\u533A\u7EDD\u5BF9\u8DEF\u5F84\u3002":"Unknown Enterprise WeChat endpoint.":"Payload must be an object."}function pI(n){if(Array.isArray(n))return n.map(pI);if(!rN(n))return n;let a={};for(let[o,i]of Object.entries(n))hBe.has(o)||(a[o]=pI(i));return a}async function BBe(n){return pBe.toDataURL(n,{type:"image/png",errorCorrectionLevel:"M",margin:2,width:320})}async function aN(n,a){return!n||typeof n.verificationUrl!="string"?pI(n):pI({...n,qrCodeDataUrl:await a(n.verificationUrl)})}async function FC(n,a){let o=structuredClone(n);return o?.provisioning&&(o.provisioning=await aN(o.provisioning,a)),pI(o)}function s1(n,{encodeQr:a=BBe}={}){for(let e of["status","startProvisioning","registrationStatus","cancelProvisioning","bindCredentials","reconnectBot","deleteBot"])if(typeof n?.[e]!="function")throw new TypeError(`A complete Enterprise WeChat controller is required (${e})`);let o=new Map,i=e=>{let r=o.get(e);return r||(o.size>=16&&o.delete(o.keys().next().value),r=Promise.resolve().then(()=>a(e)),o.set(e,r)),r};return async(e,r,A)=>{if(A?.aborted)return{ok:!1,error:{code:"cancelled",message:"The request was cancelled."}};if(!n1.includes(e))return{ok:!1,error:{code:"bad-request",message:"Unknown Enterprise WeChat endpoint."}};let t=dBe(e,r);if(t)return{ok:!1,error:{code:"bad-request",message:t}};try{let s;if(e===Sa.status)s=await FC(await n.status(),i);else if(e===Sa.beginProvisioning)s=await aN(await n.startProvisioning(),i);else if(e===Sa.pollProvisioning){let g=await n.registrationStatus(r.attemptId);if(!g)return{ok:!1,error:{code:"bad-request",message:"The provisioning attempt no longer exists."}};s=await aN(g,i)}else if(e===Sa.cancelProvisioning)s=pI(await n.cancelProvisioning(r.attemptId));else if(e===Sa.bindCredentials)s=await FC(await n.bindCredentials(r),i);else if(e===Sa.reconnectBot)s=await FC(await n.reconnectBot(r.botId),i);else if(e===Sa.setWorkspace){if(typeof n.updateWorkspace!="function")throw new Error("Workspace update is unavailable");s=await FC(await n.updateWorkspace(r.botId,r.workspace),i)}else s=await FC(await n.deleteBot(r.botId),i);return A?.aborted?{ok:!1,error:{code:"cancelled",message:"The request was cancelled."}}:{ok:!0,value:s}}catch(s){let g=Ka(s);return A?.aborted?{ok:!1,error:{code:"cancelled",message:"The request was cancelled."}}:{ok:!1,error:g??{code:"wecom-operation-failed",message:"\u4F01\u4E1A\u5FAE\u4FE1\u64CD\u4F5C\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002"}}}}}function Ku(n,a,o,i){if(!n?.connection?.rpc||typeof n.connection.rpc.handle!="function")throw new TypeError("DSH Host Connection RPC is required");return n.connection.rpc.handle(o1,s1(a,o),{authority:Ua(i)})}async function g1(n,a={}){if(a?.controller)return Ku(n,a.controller,a.rpcOptions,a.rpcAuthority);let o=await AN(n,a,a.internals),i=Ku(n,o.controller,a.rpcOptions,a.rpcAuthority);return n.effect(()=>async()=>o.close(),"dsh-im: close Enterprise WeChat bot connections"),i}import{unlink as XBe}from"node:fs/promises";import{homedir as $Be}from"node:os";import{join as KC,resolve as Mg}from"node:path";import{createHash as _Be}from"node:crypto";import{mkdir as NBe,readFile as TBe,rename as RBe,unlink as kBe,writeFile as vBe}from"node:fs/promises";import{dirname as PBe}from"node:path";import{randomBytes as uBe,randomUUID as QBe}from"node:crypto";var xu="https://ilinkai.weixin.qq.com/",fBe="2.4.6",yBe="3",mBe="bot",SBe=2<<16|1024|6,DBe=15e3,c1=35e3,wBe=new Set(["wait","scaned","confirmed","expired","scaned_but_redirect","need_verifycode","verify_code_blocked","binded_redirect"]),ct=class extends Error{constructor(a,o,i={}){super(o,i),this.name="WeixinApiError",this.code=a,this.status=i.status}};function Di(n){return typeof n=="string"&&n.trim()?n.trim():null}function iN(n){let a=n.toLowerCase().replace(/\.$/,"");return a==="weixin.qq.com"||a.endsWith(".weixin.qq.com")}function dI(n){let a;try{a=new URL(n)}catch{throw new ct("invalid-base-url","\u5FAE\u4FE1\u670D\u52A1\u8FD4\u56DE\u4E86\u65E0\u6548\u7684\u8FDE\u63A5\u5730\u5740\u3002")}if(a.protocol!=="https:"||!iN(a.hostname)||a.port!==""&&a.port!=="443")throw new ct("untrusted-base-url","\u5FAE\u4FE1\u670D\u52A1\u8FD4\u56DE\u4E86\u4E0D\u53D7\u4FE1\u4EFB\u7684\u8FDE\u63A5\u5730\u5740\u3002");return a.username="",a.password="",a.search="",a.hash="",a.pathname.endsWith("/")||(a.pathname+="/"),a.toString()}function OBe(n){let a=Di(n);if(!a)throw new ct("invalid-qr","\u5FAE\u4FE1\u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u626B\u7801\u5730\u5740\u3002");let o;try{o=new URL(a)}catch{throw new ct("invalid-qr","\u5FAE\u4FE1\u670D\u52A1\u8FD4\u56DE\u4E86\u65E0\u6548\u7684\u626B\u7801\u5730\u5740\u3002")}if(o.protocol!=="https:"||!iN(o.hostname))throw new ct("untrusted-qr","\u5FAE\u4FE1\u670D\u52A1\u8FD4\u56DE\u4E86\u4E0D\u53D7\u4FE1\u4EFB\u7684\u626B\u7801\u5730\u5740\u3002");return o.toString()}function l1(){return{"iLink-App-Id":mBe,"iLink-App-ClientVersion":String(SBe)}}function MBe(n){let a={...l1(),"content-type":"application/json",AuthorizationType:"ilink_bot_token","X-WECHAT-UIN":Buffer.from(String(uBe(4).readUInt32BE(0)),"utf8").toString("base64")};return Di(n)&&(a.Authorization=`Bearer ${n.trim()}`),a}function Yu(){return{channel_version:fBe,bot_agent:"DeepSeekHarness/0.5.0"}}function I1(n){return n?.reason instanceof Error?n.reason:new DOMException("The operation was aborted","AbortError")}async function hI(n,{method:a,baseUrl:o,endpoint:i,body:e,token:r,timeoutMs:A=DBe,signal:t,authenticated:s=!0}){let g=dI(o),c=new URL(i,g);if(!iN(c.hostname))throw new ct("untrusted-endpoint","\u62D2\u7EDD\u8BBF\u95EE\u4E0D\u53D7\u4FE1\u4EFB\u7684\u5FAE\u4FE1\u670D\u52A1\u5730\u5740\u3002");let l=new AbortController,C=!1,u=()=>l.abort(t?.reason);if(t?.aborted)throw I1(t);t?.addEventListener("abort",u,{once:!0});let Q=A>0?setTimeout(()=>{C=!0,l.abort()},A):null;try{let f=await n(c,{method:a,headers:s?MBe(r):l1(),...e===void 0?{}:{body:JSON.stringify(e)},signal:l.signal});if(!f.ok)throw new ct("http-error",`\u5FAE\u4FE1\u670D\u52A1\u8BF7\u6C42\u5931\u8D25\uFF08HTTP ${f.status}\uFF09\u3002`,{status:f.status});try{return await f.json()}catch(y){throw new ct("invalid-response","\u5FAE\u4FE1\u670D\u52A1\u8FD4\u56DE\u4E86\u65E0\u6CD5\u89E3\u6790\u7684\u54CD\u5E94\u3002",{cause:y})}}catch(f){throw t?.aborted?I1(t):C?new ct("timeout","\u5FAE\u4FE1\u670D\u52A1\u8BF7\u6C42\u8D85\u65F6\u3002",{cause:f}):f instanceof ct?f:new ct("network-error","\u6682\u65F6\u65E0\u6CD5\u8BBF\u95EE\u5FAE\u4FE1\u670D\u52A1\u3002",{cause:f})}finally{Q&&clearTimeout(Q),t?.removeEventListener("abort",u)}}function bBe(n){if(!n||typeof n!="object"||!wBe.has(n.status))throw new ct("invalid-login-status","\u5FAE\u4FE1\u670D\u52A1\u8FD4\u56DE\u4E86\u65E0\u6CD5\u8BC6\u522B\u7684\u626B\u7801\u72B6\u6001\u3002");return n}function E1({fetchImpl:n=fetch}={}){if(typeof n!="function")throw new TypeError("fetchImpl must be a function");return Object.freeze({async beginLogin({localTokens:a=[],botType:o=yBe,signal:i}={}){let e=[...new Set(a.map(Di).filter(Boolean))].slice(-10),r=await hI(n,{method:"POST",baseUrl:xu,endpoint:`ilink/bot/get_bot_qrcode?bot_type=${encodeURIComponent(o)}`,body:{local_token_list:e},timeoutMs:1e4,signal:i}),A=Di(r?.qrcode);if(!A)throw new ct("invalid-qr","\u5FAE\u4FE1\u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u4E8C\u7EF4\u7801\u4EE4\u724C\u3002");return{qrcode:A,qrcodeUrl:OBe(r.qrcode_img_content)}},async pollLogin({qrcode:a,baseUrl:o=xu,verifyCode:i,signal:e}){let r=Di(a);if(!r)throw new TypeError("qrcode is required");let A=`ilink/bot/get_qrcode_status?qrcode=${encodeURIComponent(r)}`;Di(i)&&(A+=`&verify_code=${encodeURIComponent(i.trim())}`);let t=await hI(n,{method:"GET",baseUrl:o,endpoint:A,timeoutMs:c1,signal:e,authenticated:!1});return bBe(t)},async getUpdates({baseUrl:a,token:o,getUpdatesBuf:i="",timeoutMs:e,signal:r}){try{return await hI(n,{method:"POST",baseUrl:a,endpoint:"ilink/bot/getupdates",body:{get_updates_buf:i,base_info:Yu()},token:o,timeoutMs:e??c1,signal:r})}catch(A){if(A instanceof ct&&A.code==="timeout")return{ret:0,msgs:[],get_updates_buf:i};throw A}},async sendText({baseUrl:a,token:o,toUserId:i,text:e,contextToken:r,runId:A,signal:t}){let s=Di(i),g=Di(e);if(!s||!g)throw new TypeError("toUserId and text are required");let c=await hI(n,{method:"POST",baseUrl:a,endpoint:"ilink/bot/sendmessage",token:o,signal:t,body:{msg:{from_user_id:"",to_user_id:s,client_id:`dsh-weixin-${QBe()}`,message_type:2,message_state:2,item_list:[{type:1,text_item:{text:g}}],...Di(r)?{context_token:r.trim()}:{},...Di(A)?{run_id:A.trim()}:{}},base_info:Yu()}});if(c?.ret!==void 0&&c.ret!==0)throw new ct("send-rejected","\u5FAE\u4FE1\u670D\u52A1\u62D2\u7EDD\u4E86\u56DE\u590D\u6D88\u606F\u3002");return!0},async notifyStart({baseUrl:a,token:o,signal:i}){let e=await hI(n,{method:"POST",baseUrl:a,endpoint:"ilink/bot/msg/notifystart",token:o,signal:i,timeoutMs:1e4,body:{base_info:Yu()}});if(e?.ret!==void 0&&e.ret!==0)throw new ct("start-rejected","\u5FAE\u4FE1\u8D26\u53F7\u8FDE\u63A5\u542F\u52A8\u5931\u8D25\u3002");return e},async notifyStop({baseUrl:a,token:o,signal:i}){return hI(n,{method:"POST",baseUrl:a,endpoint:"ilink/bot/msg/notifystop",token:o,signal:i,timeoutMs:1e4,body:{base_info:Yu()}})}})}function C1(n){for(let a of n?.item_list??[]){if(a?.type===1&&typeof a.text_item?.text=="string"){let o=a.text_item.text.trim();if(o)return o}if(a?.type===3&&typeof a.voice_item?.text=="string"){let o=a.voice_item.text.trim();if(o)return o}}return null}function p1(n){return n?.message_id!==void 0&&n.message_id!==null?String(n.message_id):Di(n?.client_id)}function h1(n,a=4e3){if(n.length<=a)return[n];let o=[],i=n;for(;i.length>a;){let e=i.lastIndexOf(` `,a);er===null))return null;let o=new Set,i=new Set,e=new Set;for(let r of a){if(o.has(r.botId)||i.has(r.accountId)||e.has(r.tokenRef))return null;o.add(r.botId),i.add(r.accountId),e.add(r.tokenRef)}return Object.freeze({version:1,accounts:Object.freeze(a)})}var ju=class{#A;#e=oN;#t=Promise.resolve();constructor(a){this.#A=a}async load(){try{let a=FBe(JSON.parse(await TBe(this.#A,"utf8")));if(!a)throw new Error("dsh-weixin config contains invalid account data");this.#e=a}catch(a){if(a?.code!=="ENOENT")throw a;this.#e=oN}return this}list(){return structuredClone(this.#e.accounts)}get(a){let o=this.#e.accounts.find(i=>i.botId===a);return o?structuredClone(o):null}getByAccountId(a){let o=this.#e.accounts.find(i=>i.accountId===a);return o?structuredClone(o):null}async save(a){let o=u1(a);if(!o)throw new Error("Refusing to persist incomplete dsh-weixin account data");return this.#a(i=>{let e=i.find(t=>t.accountId===o.accountId&&t.botId!==o.botId),r=i.find(t=>t.tokenRef===o.tokenRef&&t.botId!==o.botId);if(e||r)throw new Error("Duplicate Weixin account identity");let A=i.findIndex(t=>t.botId===o.botId);return A===-1?i.push(o):i[A]=o,structuredClone(o)})}async remove(a){if(!d1(a))throw new TypeError("Invalid Weixin bot id");return this.#a(o=>{let i=o.findIndex(r=>r.botId===a);if(i===-1)return null;let[e]=o.splice(i,1);return structuredClone(e)})}async clear(){let a=this.#t.then(async()=>{try{await kBe(this.#A)}catch(o){if(o?.code!=="ENOENT")throw o}this.#e=oN});this.#t=a.then(()=>{},()=>{}),await a}async#a(a){let o,i=this.#t.then(async()=>{let e=[...this.#e.accounts];o=a(e);let r=Object.freeze({version:1,accounts:Object.freeze(e)});await this.#s(r),this.#e=r});return this.#t=i.then(()=>{},()=>{}),await i,o}async#s(a){await NBe(PBe(this.#A),{recursive:!0,mode:448});let o=`${this.#A}.tmp`;await vBe(o,`${JSON.stringify(a,null,2)} `,{encoding:"utf8",mode:384}),await RBe(o,this.#A)}};import{mkdir as LBe,readFile as UBe,rename as HBe,unlink as KBe,writeFile as YBe}from"node:fs/promises";import{dirname as xBe}from"node:path";var Ju=Object.freeze({version:1,sessions:{},seenMessageIds:[],getUpdatesBuf:""});function jBe(n){if(!n||typeof n!="object")return structuredClone(Ju);let a={};if(n.sessions&&typeof n.sessions=="object"&&!Array.isArray(n.sessions))for(let[o,i]of Object.entries(n.sessions))typeof o=="string"&&typeof i=="string"&&i&&(a[o]=i);return{version:1,sessions:a,seenMessageIds:Array.isArray(n.seenMessageIds)?n.seenMessageIds.filter(o=>typeof o=="string").slice(-1e3):[],getUpdatesBuf:typeof n.getUpdatesBuf=="string"?n.getUpdatesBuf:""}}var qu=class{#A;#e=structuredClone(Ju);#t=Promise.resolve();constructor(a){this.#A=a}async load(){try{this.#e=jBe(JSON.parse(await UBe(this.#A,"utf8")))}catch(a){if(a?.code!=="ENOENT")throw a;this.#e=structuredClone(Ju),await this.#a()}return this}sessionFor(a){return this.#e.sessions[a]??null}async setSession(a,o){this.#e.sessions[a]=o,await this.#a()}async clearSession(a){delete this.#e.sessions[a],await this.#a()}async clearSessions(){this.#e.sessions={},await this.#a()}hasSeen(a){return this.#e.seenMessageIds.includes(a)}async markSeen(a){this.hasSeen(a)||(this.#e.seenMessageIds.push(a),this.#e.seenMessageIds.length>1e3&&this.#e.seenMessageIds.splice(0,this.#e.seenMessageIds.length-1e3),await this.#a())}getUpdatesBuf(){return this.#e.getUpdatesBuf}async setGetUpdatesBuf(a){typeof a!="string"||a===this.#e.getUpdatesBuf||(this.#e.getUpdatesBuf=a,await this.#a())}snapshot(){return structuredClone(this.#e)}async remove(){try{await KBe(this.#A)}catch(a){if(a?.code!=="ENOENT")throw a}this.#e=structuredClone(Ju)}async#a(){let a=`${JSON.stringify(this.#e,null,2)} `,o=this.#t.then(async()=>{await LBe(xBe(this.#A),{recursive:!0,mode:448});let i=`${this.#A}.tmp`;await YBe(i,a,{encoding:"utf8",mode:384}),await HBe(i,this.#A)});this.#t=o.then(()=>{},()=>{}),await o}};import{randomUUID as JBe}from"node:crypto";var Q1=new Set(["starting","pending","scanned","needs_verification","connecting"]),Vu=new Set(["connected","expired","failed","cancelled"]),f1=5*6e4;function BI(n){return typeof n=="string"&&n.trim()?n.trim():null}function qBe(){return new DOMException("Provisioning was cancelled","AbortError")}function y1(n,a){let o=BI(n);return o?dI(o.includes("://")?o:`https://${o}`):dI(a)}function LC(n){return n?{attemptId:n.id,status:n.state,...n.verificationUrl?{verificationUrl:n.verificationUrl}:{},...n.expiresAt?{expiresAt:n.expiresAt}:{},pollIntervalMs:1e3,...n.state==="needs_verification"?{verificationRequired:!0}:{},...n.botId?{botId:n.botId}:{},...n.alreadyConnected?{alreadyConnected:!0}:{},...n.error?{error:structuredClone(n.error)}:{}}:null}function Mr(n,a){return Object.freeze({code:n,message:a})}var UC=class{#A;#e;#t;#a;#s;#o;#I=new Map;#g=new Map;#n=new Map;#i=null;#r=new Map;#c=0;#l=!1;constructor({api:a,credentials:o,configStore:i,createRuntime:e,deleteState:r=async()=>{},logger:A=console}){if(!a||typeof a.beginLogin!="function"||typeof a.pollLogin!="function")throw new TypeError("WeixinController requires a Weixin API client");if(!o||typeof o.resolve!="function"||typeof o.set!="function"||typeof o.unset!="function")throw new TypeError("WeixinController requires the DSH credential provider");if(!i||typeof i.list!="function"||typeof i.save!="function"||typeof i.remove!="function")throw new TypeError("WeixinController requires a config store");if(typeof e!="function")throw new TypeError("createRuntime is required");this.#A=a,this.#e=o,this.#t=i,this.#a=e,this.#s=r,this.#o=A}async initialize(){if(this.#l)return this.status();for(let a of this.#t.list())this.#I.get(a.botId)?.status?.ready!==!0&&await this.#Q(a.botId,async()=>{let i=this.#t.get(a.botId);if(!(!i||this.#l))try{let e=await this.#d(i.tokenRef);if(!e){this.#g.set(i.botId,Mr("missing-token","\u767B\u5F55\u51ED\u636E\u7F3A\u5931\uFF0C\u8BF7\u79FB\u9664\u8D26\u53F7\u540E\u91CD\u65B0\u626B\u7801\u3002"));return}await this.#p(i,e),this.#g.delete(i.botId)}catch(e){this.#g.set(i.botId,Mr("connection-failed","\u5FAE\u4FE1\u8FDE\u63A5\u672A\u5C31\u7EEA\uFF0C\u63D2\u4EF6\u4F1A\u81EA\u52A8\u91CD\u8BD5\u3002")),this.#o.warn?.(`[dsh-weixin] account ${i.botId} failed to initialize:`,e)}finally{this.#f()}});return this.status()}async startProvisioning(){if(this.#l)throw new Error("dsh-weixin controller is closed");this.#i&&await this.cancelProvisioning(this.#i);let a={id:JBe(),state:"starting",createdAt:Date.now(),expiresAt:Date.now()+f1,controller:new AbortController,pendingVerifyCode:null,verifyResolve:null,currentBaseUrl:xu,error:null,botId:null,task:null};this.#n.set(a.id,a),this.#i=a.id,this.#f();try{let o=(await Promise.all(this.#t.list().slice(-10).map(async e=>this.#d(e.tokenRef)))).filter(Boolean),i=await this.#A.beginLogin({localTokens:o,signal:a.controller.signal});return this.#B(a),a.qrcode=i.qrcode,a.verificationUrl=i.qrcodeUrl,a.state="pending",a.expiresAt=Date.now()+f1,this.#f(),a.task=this.#E(a),LC(a)}catch(o){throw a.controller.signal.aborted?(a.state="cancelled",a.error=Mr("cancelled","\u626B\u7801\u7ED1\u5B9A\u5DF2\u53D6\u6D88\u3002")):(a.state="failed",a.error=Mr(o instanceof ct?o.code:"qr-start-failed",o instanceof ct?o.message:"\u65E0\u6CD5\u751F\u6210\u5FAE\u4FE1\u4E8C\u7EF4\u7801\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002")),this.#i===a.id&&(this.#i=null),this.#f(),o}}registrationStatus(a){return LC(this.#n.get(a))}async submitVerification(a,o){let i=this.#n.get(a);if(!i||i.state!=="needs_verification")throw new Error("The provisioning attempt is not waiting for a verification code");let e=BI(o);if(!e||!/^\d{4,8}$/.test(e))throw new TypeError("Verification code must contain 4 to 8 digits");return i.pendingVerifyCode=e,i.state="scanned",i.verifyResolve?.(),i.verifyResolve=null,this.#f(),LC(i)}async cancelProvisioning(a){let o=this.#n.get(a);return o?(Vu.has(o.state)||(o.controller.abort(),o.verifyResolve?.(),o.verifyResolve=null,await o.task?.catch(()=>{}),Vu.has(o.state)||(o.state="cancelled"),o.error??=Mr("cancelled","\u626B\u7801\u7ED1\u5B9A\u5DF2\u53D6\u6D88\u3002")),this.#i===o.id&&(this.#i=null),this.#f(),LC(o)):null}async reconnectBot(a){let o=this.#t.get(a);if(!o)throw new Error("Unknown Weixin account");return await this.#Q(a,async()=>{let i=await this.#d(o.tokenRef);if(!i)throw new Error("The Weixin token is missing");try{await this.#p(o,i),this.#g.delete(a)}catch(e){throw this.#g.set(a,Mr("connection-failed","\u5FAE\u4FE1\u8FDE\u63A5\u4ECD\u672A\u5C31\u7EEA\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002")),e}finally{this.#f()}}),this.status()}async deleteBot(a){let o=this.#t.get(a);if(!o)throw new Error("Unknown Weixin account");return await this.#Q(a,async()=>{let i=await this.#e.resolve(o.tokenRef).catch(()=>{});await this.#h(a);try{await this.#e.unset(o.tokenRef),await this.#t.remove(a)}catch(e){throw i?.value&&(await this.#e.set(o.tokenRef,i.value).catch(()=>{}),await this.#p(o,i.value).catch(()=>{})),new Error("Unable to remove the Weixin account safely.",{cause:e})}try{await this.#s({botId:a,config:o})}catch(e){this.#o.warn?.(`[dsh-weixin] account ${a} state cleanup failed:`,e)}this.#g.delete(a),this.#f()}),this.status()}status(){let a=this.#t.list().map(e=>{let r=this.#I.get(e.botId)?.status??null,A=r?.ready===!0&&r.weixinConnectionState==="connected"&&r.harnessReachable===!0,t=A?"connected":r?.weixinConnectionState==="connecting"?"connecting":this.#g.has(e.botId)||r?.weixinConnectionState==="failed"?"error":"offline",s=this.#g.get(e.botId)??(t==="error"?Mr("connection-failed","\u5FAE\u4FE1\u8FDE\u63A5\u672A\u5C31\u7EEA\uFF0C\u63D2\u4EF6\u4F1A\u81EA\u52A8\u91CD\u8BD5\u3002"):null);return{botId:e.botId,state:t,connected:A,configured:!0,bot:{name:"\u5FAE\u4FE1\u673A\u5668\u4EBA",accountIdMasked:B1(e.accountId)},health:{status:A?"healthy":t==="error"?"error":"offline",summary:A?"\u5FAE\u4FE1\u6D88\u606F\u957F\u8F6E\u8BE2\u8FD0\u884C\u6B63\u5E38":t==="error"?"\u5FAE\u4FE1\u8FDE\u63A5\u672A\u5C31\u7EEA\uFF0C\u63D2\u4EF6\u4F1A\u81EA\u52A8\u91CD\u8BD5":"\u5FAE\u4FE1\u8FDE\u63A5\u5F53\u524D\u79BB\u7EBF",lastCheckedAt:r?.lastCheckedAt??null},stats:{messagesReceived:r?.messagesReceived??0,messagesReplied:r?.messagesReplied??0},error:s?structuredClone(s):null}}),o=a.filter(e=>e.connected).length,i=this.#i?this.#n.get(this.#i):null;return{schemaVersion:1,revision:this.#c,state:i&&Q1.has(i.state)?"provisioning":a.length===0?"disconnected":o===a.length?"connected":o>0?"degraded":"offline",bots:a,totals:{configured:a.length,connected:o},...i&&Q1.has(i.state)?{provisioning:LC(i)}:{}}}async close(){this.#l||(this.#l=!0,this.#i&&await this.cancelProvisioning(this.#i),await Promise.allSettled([...this.#I.keys()].map(a=>this.#h(a))))}async#E(a){try{for(;!a.controller.signal.aborted&&Date.now(){a.verifyResolve=i,a.controller.signal.aborted&&i()}),a.verifyResolve=null,this.#B(a));let o=await this.#A.pollLogin({qrcode:a.qrcode,baseUrl:a.currentBaseUrl,verifyCode:a.pendingVerifyCode,signal:a.controller.signal});if(this.#B(a),o.status==="wait")a.state="pending";else if(o.status==="scaned")a.pendingVerifyCode=null,a.state="scanned";else if(o.status==="need_verifycode")a.pendingVerifyCode=null,a.state="needs_verification";else if(o.status==="verify_code_blocked"){a.state="failed",a.error=Mr("verification-blocked","\u914D\u5BF9\u7801\u591A\u6B21\u9519\u8BEF\uFF0C\u8BF7\u91CD\u65B0\u751F\u6210\u4E8C\u7EF4\u7801\u3002");break}else if(o.status==="expired"){a.state="expired",a.error=Mr("expired","\u4E8C\u7EF4\u7801\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u91CD\u65B0\u751F\u6210\u3002");break}else if(o.status==="scaned_but_redirect")a.currentBaseUrl=y1(o.redirect_host,a.currentBaseUrl),a.state="scanned";else if(o.status==="binded_redirect"){let i=this.#t.list().find(e=>this.#I.get(e.botId)?.status?.ready===!0)??this.#t.list()[0];i?(a.state="connected",a.botId=i.botId,a.alreadyConnected=!0):(a.state="failed",a.error=Mr("already-bound","\u8BE5\u5FAE\u4FE1\u8D26\u53F7\u5DF2\u7ED1\u5B9A\uFF0C\u4F46\u672C\u673A\u6CA1\u6709\u53EF\u6062\u590D\u7684\u51ED\u636E\u3002"));break}else if(o.status==="confirmed"){let i=BI(o.bot_token),e=BI(o.ilink_bot_id),r=BI(o.ilink_user_id);if(!i||!e||!r)throw new ct("incomplete-login","\u5FAE\u4FE1\u6388\u6743\u6210\u529F\uFF0C\u4F46\u8FD4\u56DE\u7684\u8D26\u53F7\u51ED\u636E\u4E0D\u5B8C\u6574\u3002");a.state="connecting",this.#f();let A=y1(o.baseurl,a.currentBaseUrl);a.botId=await this.#C(a,{token:i,accountId:e,ownerUserId:r,baseUrl:A}),a.state="connected",a.error=null;break}this.#f()}!a.controller.signal.aborted&&Date.now()>=a.expiresAt&&!Vu.has(a.state)&&(a.state="expired",a.error=Mr("expired","\u4E8C\u7EF4\u7801\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u91CD\u65B0\u751F\u6210\u3002"))}catch(o){a.controller.signal.aborted||o?.name==="AbortError"?(a.state="cancelled",a.error=Mr("cancelled","\u626B\u7801\u7ED1\u5B9A\u5DF2\u53D6\u6D88\u3002")):(a.state="failed",a.error=Mr(o instanceof ct?o.code:"activation-failed",o instanceof ct?o.message:"\u5FAE\u4FE1\u5DF2\u6388\u6743\uFF0C\u4F46\u65E0\u6CD5\u4FDD\u5B58\u51ED\u636E\u6216\u542F\u52A8\u6D88\u606F\u8FDE\u63A5\u3002"),this.#o.error?.("[dsh-weixin] provisioning failed:",o))}finally{a.pendingVerifyCode=null,a.verifyResolve?.(),a.verifyResolve=null,this.#i===a.id&&(this.#i=null),this.#f(),this.#y()}}async#C(a,{token:o,accountId:i,ownerUserId:e,baseUrl:r}){let A=nN(i),t=this.#t.getByAccountId(i),s={botId:A.botId,accountId:i,tokenRef:A.tokenRef,ownerUserId:e,baseUrl:r,createdAt:t?.createdAt??new Date().toISOString(),connectedAt:new Date().toISOString()},g=await this.#e.resolve(A.tokenRef).catch(()=>{});return this.#Q(A.botId,async()=>{await this.#e.set(A.tokenRef,o);try{return this.#B(a),await this.#t.save(s),this.#B(a),await this.#p(s,o),this.#B(a),this.#g.delete(A.botId),this.#f(),A.botId}catch(c){throw await this.#h(A.botId),t?await this.#t.save(t).catch(()=>{}):this.#t.get(A.botId)&&await this.#t.remove(A.botId).catch(()=>null)&&await this.#s({botId:A.botId,config:s}).catch(C=>{this.#o.warn?.("[dsh-weixin] failed to clean up cancelled bot state:",C)}),await this.#u(A.tokenRef,g),t&&g?.value&&await this.#p(t,g.value).catch(()=>{}),c}})}async#p(a,o){await this.#h(a.botId);let i=await this.#a({botId:a.botId,config:a,token:o});if(!i||typeof i.start!="function"||typeof i.stop!="function")throw new TypeError("createRuntime returned an invalid Weixin runtime");try{await i.start(),this.#I.set(a.botId,i)}catch(e){throw await i.stop().catch(()=>{}),e}}async#h(a){let o=this.#I.get(a);this.#I.delete(a),await o?.stop().catch(i=>{this.#o.warn?.(`[dsh-weixin] account ${a} failed to stop cleanly:`,i)})}async#d(a){let o=await this.#e.resolve(a).catch(()=>{});return BI(o?.value)}async#u(a,o){try{o?.value?await this.#e.set(a,o.value):await this.#e.unset(a)}catch(i){this.#o.error?.(`[dsh-weixin] failed to restore credential ${a}:`,i)}}#B(a){if(a.controller.signal.aborted||this.#i!==a.id)throw qBe()}#Q(a,o){let r=(this.#r.get(a)??Promise.resolve()).catch(()=>{}).then(o).finally(()=>{this.#r.get(a)===r&&this.#r.delete(a)});return this.#r.set(a,r),r}#y(){for(let[a,o]of this.#n)a!==this.#i&&Vu.has(o.state)&&this.#n.size>16&&this.#n.delete(a)}#f(){this.#c+=1}};var VBe=["\u5FAE\u4FE1\u5DF2\u8FDE\u63A5 DeepSeek Harness\u3002","","\u76F4\u63A5\u53D1\u9001\u6587\u5B57\u6216\u5E26\u6587\u5B57\u8BC6\u522B\u7ED3\u679C\u7684\u8BED\u97F3\u5373\u53EF\u7EE7\u7EED\u5F53\u524D\u4F1A\u8BDD\u3002","/new \u5F00\u542F\u4E00\u4E2A\u5168\u65B0\u4F1A\u8BDD","/workspace \u5DE5\u4F5C\u533A\u7EDD\u5BF9\u8DEF\u5F84 \u5207\u6362\u5DE5\u4F5C\u533A","/status \u68C0\u67E5\u8FDE\u63A5\u72B6\u6001","/help \u663E\u793A\u672C\u5E2E\u52A9"].join(` diff --git a/package-lock.json b/package-lock.json index 00fe1ac..85e4b83 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@xmanrui/dsh-im", - "version": "0.4.0", + "version": "0.5.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@xmanrui/dsh-im", - "version": "0.4.0", + "version": "0.5.0", "license": "MIT", "dependencies": { "@tencent-connect/qqbot-connector": "1.2.0", diff --git a/package.json b/package.json index deae152..3080ab2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@xmanrui/dsh-im", - "version": "0.4.0", + "version": "0.5.0", "description": "通过扫码、App Manifest或机器人凭据把IM机器人接入DeepSeek Harness(支持飞书、微信、钉钉、企业微信、QQ、Slack、Telegram、Discord和WhatsApp)。", "license": "MIT", "type": "module", diff --git a/src/channels/discord/discord-api.mjs b/src/channels/discord/discord-api.mjs index 97bd83f..76e57a0 100644 --- a/src/channels/discord/discord-api.mjs +++ b/src/channels/discord/discord-api.mjs @@ -108,7 +108,7 @@ export class DiscordApi { headers: { authorization: `Bot ${this.#token}`, 'content-type': 'application/json', - 'user-agent': 'DeepSeek-Harness-dsh-im (https://github.com/xmanrui/dsh-im, 0.4.0)', + 'user-agent': 'DeepSeek-Harness-dsh-im (https://github.com/xmanrui/dsh-im, 0.5.0)', }, ...(body === undefined ? {} : { body: JSON.stringify(body) }), signal: requestSignal(signal, timeoutMs), diff --git a/src/channels/weixin/weixin-api.mjs b/src/channels/weixin/weixin-api.mjs index 1a31225..da23c1a 100644 --- a/src/channels/weixin/weixin-api.mjs +++ b/src/channels/weixin/weixin-api.mjs @@ -92,7 +92,7 @@ function authenticatedHeaders(token) { function baseInfo() { return { channel_version: WEIXIN_PROTOCOL_VERSION, - bot_agent: 'DeepSeekHarness/0.4.0', + bot_agent: 'DeepSeekHarness/0.5.0', }; } diff --git a/test/channels/weixin/weixin-api.test.mjs b/test/channels/weixin/weixin-api.test.mjs index ba2d150..2cc36ee 100644 --- a/test/channels/weixin/weixin-api.test.mjs +++ b/test/channels/weixin/weixin-api.test.mjs @@ -93,7 +93,7 @@ test('sendText emits the iLink message envelope without reflecting the token in assert.equal(body.msg.context_token, 'message-context'); assert.equal(body.msg.item_list[0].text_item.text, 'Harness reply'); assert.equal(body.base_info.channel_version, '2.4.6'); - assert.equal(body.base_info.bot_agent, 'DeepSeekHarness/0.4.0'); + assert.equal(body.base_info.bot_agent, 'DeepSeekHarness/0.5.0'); assert.doesNotMatch(calls[0].init.body, /host-only-token/); });