shennian 0.2.116 → 0.2.118

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/dist/publish-build-manifest.json +56 -56
  2. package/dist/src/agents/cursor.d.ts +9 -0
  3. package/dist/src/agents/cursor.js +1 -1
  4. package/dist/src/agents/custom.d.ts +9 -1
  5. package/dist/src/agents/custom.js +4 -4
  6. package/dist/src/agents/external-channel-instructions.js +5 -5
  7. package/dist/src/agents/gemini.d.ts +9 -0
  8. package/dist/src/agents/gemini.js +1 -1
  9. package/dist/src/agents/model-registry/discovery.js +2 -2
  10. package/dist/src/agents/openclaw.d.ts +9 -0
  11. package/dist/src/agents/openclaw.js +2 -2
  12. package/dist/src/agents/opencode.d.ts +9 -0
  13. package/dist/src/agents/opencode.js +1 -1
  14. package/dist/src/agents/pi-context.d.ts +1 -1
  15. package/dist/src/agents/pi-context.js +3 -3
  16. package/dist/src/agents/platform-instructions.d.ts +1 -0
  17. package/dist/src/agents/platform-instructions.js +11 -5
  18. package/dist/src/channels/registry.js +1 -1
  19. package/dist/src/channels/runtime.d.ts +1 -0
  20. package/dist/src/channels/runtime.js +3 -3
  21. package/dist/src/channels/secret-registry.js +1 -1
  22. package/dist/src/channels/wechat-channel/anchor.js +1 -1
  23. package/dist/src/channels/wechat-channel/automation-lane.js +2 -2
  24. package/dist/src/channels/wechat-channel/fingerprint.d.ts +3 -0
  25. package/dist/src/channels/wechat-channel/fingerprint.js +1 -1
  26. package/dist/src/channels/wechat-channel/helper-assets.d.ts +1 -1
  27. package/dist/src/channels/wechat-channel/helper-assets.js +1 -1
  28. package/dist/src/channels/wechat-channel/helper-client.js +3 -3
  29. package/dist/src/channels/wechat-channel/ledger.js +2 -1
  30. package/dist/src/channels/wechat-channel/media-resolver.d.ts +1 -1
  31. package/dist/src/channels/wechat-channel/media-resolver.js +1 -1
  32. package/dist/src/channels/wechat-channel/observer.js +6 -6
  33. package/dist/src/channels/wechat-channel/outbound-ledger.d.ts +1 -0
  34. package/dist/src/channels/wechat-channel/outbound-ledger.js +2 -2
  35. package/dist/src/channels/wechat-channel/outbound-sender.js +1 -1
  36. package/dist/src/channels/wechat-channel/runner.js +1 -1
  37. package/dist/src/channels/wechat-channel/scheduler.js +1 -1
  38. package/dist/src/channels/wechat-rpa.js +3 -3
  39. package/dist/src/commands/daemon.js +3 -3
  40. package/dist/src/commands/wechat/output.js +1 -1
  41. package/dist/src/commands/wechat/utils.js +1 -1
  42. package/dist/src/config/index.d.ts +1 -1
  43. package/dist/src/devtools/wechat-channel-action-smoke.d.ts +1 -0
  44. package/dist/src/devtools/wechat-channel-action-smoke.js +8 -8
  45. package/dist/src/index.js +2 -2
  46. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
1
  import type { AgentMessage } from '@mariozechner/pi-agent-core';
2
2
  import type { Model } from '@mariozechner/pi-ai';
3
- export declare const PI_DEFAULT_MODEL_ID = "qwen3.7-max";
3
+ export declare const PI_DEFAULT_MODEL_ID = "qwen3.6-plus";
4
4
  type ShellCommandSpec = {
5
5
  file: string;
6
6
  args: string[];
@@ -1,12 +1,12 @@
1
- import u from"node:fs";import l from"node:path";import{resolveShennianPath as h}from"../config/index.js";const S="qwen3.7-max";function w(e,t=process.platform){return t==="win32"?{file:"powershell.exe",args:["-NoLogo","-NoProfile","-NonInteractive","-ExecutionPolicy","Bypass","-Command",e],shell:"powershell"}:{file:"bash",args:["-c",e],shell:"bash"}}function d(e=S,t={}){return{id:e,name:e,api:"openai-completions",provider:t.provider??"shennian-proxy",baseUrl:t.baseUrl??"",reasoning:!1,input:["text","image"],cost:{input:0,output:0,cacheRead:0,cacheWrite:0},contextWindow:128e3,maxTokens:8192,...t.compat?{compat:t.compat}:{}}}const O=`\u4F60\u662F\u795E\u5FF5\u5185\u7F6E\u7F16\u7A0B\u52A9\u624B\uFF0C\u8FD0\u884C\u5728\u7528\u6237\u672C\u5730\u673A\u5668\u4E0A\u3002
1
+ import u from"node:fs";import l from"node:path";import{resolveShennianPath as x}from"../config/index.js";const S="qwen3.6-plus";function w(e,t=process.platform){return t==="win32"?{file:"powershell.exe",args:["-NoLogo","-NoProfile","-NonInteractive","-ExecutionPolicy","Bypass","-Command",e],shell:"powershell"}:{file:"bash",args:["-c",e],shell:"bash"}}function d(e=S,t={}){return{id:e,name:e,api:"openai-completions",provider:t.provider??"shennian-proxy",baseUrl:t.baseUrl??"",reasoning:!1,input:["text","image"],cost:{input:0,output:0,cacheRead:0,cacheWrite:0},contextWindow:128e3,maxTokens:8192,...t.compat?{compat:t.compat}:{}}}const O=`\u4F60\u662F\u795E\u5FF5\u5185\u7F6E\u7F16\u7A0B\u52A9\u624B\uFF0C\u8FD0\u884C\u5728\u7528\u6237\u672C\u5730\u673A\u5668\u4E0A\u3002
2
2
  \u4F60\u53EF\u4EE5\u8BFB\u5199\u6587\u4EF6\u3001\u6267\u884C shell \u547D\u4EE4\u3001\u5E2E\u52A9\u7528\u6237\u5B8C\u6210\u7F16\u7A0B\u548C\u7CFB\u7EDF\u7BA1\u7406\u4EFB\u52A1\u3002
3
3
  \u5DE5\u4F5C\u76EE\u5F55\u5DF2\u8BBE\u7F6E\uFF0C\u64CD\u4F5C\u6587\u4EF6\u65F6\u4F7F\u7528\u76F8\u5BF9\u8DEF\u5F84\u6216\u7EDD\u5BF9\u8DEF\u5F84\u5747\u53EF\u3002
4
4
  \u5F53\u524D shell \u4F1A\u968F\u64CD\u4F5C\u7CFB\u7EDF\u9009\u62E9\uFF1AWindows \u4F7F\u7528 PowerShell\uFF0CmacOS/Linux \u4F7F\u7528 bash\u3002Windows \u4E0B\u9700\u8981\u771F\u5B9E curl \u65F6\u4F7F\u7528 curl.exe\uFF0C\u907F\u514D PowerShell \u7684 curl \u522B\u540D\u3002
5
- \u4FDD\u6301\u56DE\u590D\u7B80\u6D01\u3001\u51C6\u786E\uFF0C\u4E2D\u6587\u56DE\u590D\u3002`,x=24e3;function E(e){let t=l.resolve(e||process.cwd());try{const n=u.existsSync(t)?u.statSync(t):null;n&&!n.isDirectory()&&(t=l.dirname(t))}catch{}for(;;){const n=l.join(t,"AGENTS.md");if(u.existsSync(n))return n;const o=l.dirname(t);if(o===t)return null;t=o}}function $(e){const t=E(e);if(!t)return null;try{const n=u.readFileSync(t,"utf-8").trim();if(!n)return null;const o=n.length>x?`${n.slice(0,x)}
5
+ \u4FDD\u6301\u56DE\u590D\u7B80\u6D01\u3001\u51C6\u786E\uFF0C\u4E2D\u6587\u56DE\u590D\u3002`,h=24e3;function E(e){let t=l.resolve(e||process.cwd());try{const n=u.existsSync(t)?u.statSync(t):null;n&&!n.isDirectory()&&(t=l.dirname(t))}catch{}for(;;){const n=l.join(t,"AGENTS.md");if(u.existsSync(n))return n;const o=l.dirname(t);if(o===t)return null;t=o}}function $(e){const t=E(e);if(!t)return null;try{const n=u.readFileSync(t,"utf-8").trim();if(!n)return null;const o=n.length>h?`${n.slice(0,h)}
6
6
 
7
7
  ...\uFF08AGENTS.md \u5DF2\u622A\u65AD\uFF09`:n;return`# AGENTS.md instructions for ${l.dirname(t)}
8
8
 
9
- ${o}`}catch{return null}}const P=9e4,b=6,N=h("sessions"),k="summary.json",C="snapshot.json",L="messages.jsonl",j="pi-context.json";function R(e){let t=0;for(const n of e)t+=JSON.stringify(n).length;return Math.ceil(t/3)}function D(e){const t=[];for(const n of e){const o=n,s=String(o.role??"unknown");if(Array.isArray(o.content)){for(const r of o.content)if(r.type==="text"&&typeof r.text=="string")t.push(`[${s}] ${r.text.slice(0,500)}`);else if(r.type==="toolCall"){const c=typeof r.name=="string"?r.name:typeof r.toolName=="string"?r.toolName:"tool",i=r.arguments??r.args??{};t.push(`[${s}] tool:${c}(${JSON.stringify(i).slice(0,200)})`)}}}return t.join(`
9
+ ${o}`}catch{return null}}const P=9e4,b=6,N=x("sessions"),k="summary.json",C="snapshot.json",L="messages.jsonl",j="pi-context.json";function R(e){let t=0;for(const n of e)t+=JSON.stringify(n).length;return Math.ceil(t/3)}function D(e){const t=[];for(const n of e){const o=n,s=String(o.role??"unknown");if(Array.isArray(o.content)){for(const r of o.content)if(r.type==="text"&&typeof r.text=="string")t.push(`[${s}] ${r.text.slice(0,500)}`);else if(r.type==="toolCall"){const c=typeof r.name=="string"?r.name:typeof r.toolName=="string"?r.toolName:"tool",i=r.arguments??r.args??{};t.push(`[${s}] tool:${c}(${JSON.stringify(i).slice(0,200)})`)}}}return t.join(`
10
10
  `)}function I(e){return JSON.parse(JSON.stringify(e))}function J(e){return l.join(N,e)}function A(e){if(!e||typeof e!="object")return null;const t=e;if(t.type==="text"&&typeof t.text=="string")return t.text;if(t.type==="thinking"&&typeof t.thinking=="string")return`[thinking] ${t.thinking}`;if(t.type==="toolCall"){const n=typeof t.name=="string"?t.name:typeof t.toolName=="string"?t.toolName:"tool",o=t.arguments??t.args??{};return`[toolCall] ${n}(${JSON.stringify(o).slice(0,300)})`}return t.type==="image"&&typeof t.mimeType=="string"?`[image] ${t.mimeType}`:null}function G(e,t){const n=[];for(const r of t){const c=String(r.role??"unknown"),i=Array.isArray(r.content)?r.content:[];for(const p of i){const a=A(p);a&&n.push(`[${c}] ${a}`)}}const s=n.slice(-8).join(`
11
11
  `).trim()||e?.trim()||null;return s?s.length>2e3?s.slice(s.length-2e3):s:null}function F(e,t){const n=Math.min(e.length,t.length);for(let o=0;o<n;o++)if(JSON.stringify(e[o])!==JSON.stringify(t[o]))return o;return n}async function U(e,t,n){const o=await fetch(`${e}/api/stream`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${t}`},body:JSON.stringify({model:d("qwen-plus"),context:{messages:[{role:"user",content:[{type:"text",text:n}]}]},options:{temperature:.3,maxTokens:500}})});if(!o.ok||!o.body)return null;const s=o.body.getReader(),r=new TextDecoder,c=[];let i="";for(;;){const{done:p,value:a}=await s.read();if(p)break;i+=r.decode(a,{stream:!0});const m=i.split(`
12
12
  `);i=m.pop()??"";for(const g of m){if(!g.startsWith("data: "))continue;const y=g.slice(6).trim();if(!y)continue;const f=JSON.parse(y);f.type==="text_delta"&&typeof f.delta=="string"&&c.push(f.delta)}}return c.join("").trim()||null}export{P as CONTEXT_TOKEN_THRESHOLD,b as KEEP_RECENT_MESSAGES,j as LEGACY_SUMMARY_FILENAME,L as MESSAGES_FILENAME,S as PI_DEFAULT_MODEL_ID,C as SNAPSHOT_FILENAME,k as SUMMARY_FILENAME,O as SYSTEM_PROMPT,G as buildRollingSummary,w as buildShellCommandSpec,I as cloneMessages,d as createPiModel,R as estimateTokens,E as findAgentsMd,J as getSessionDir,$ as loadAgentsMdInstructions,F as longestCommonPrefixLength,D as messagesToText,U as requestProxySummary};
@@ -1,4 +1,5 @@
1
1
  import type { ExternalChannelSessionStatus } from '@shennian/wire';
2
2
  export declare const PLATFORM_OUTPUT_INSTRUCTIONS = "## Shennian Output Instructions\n\nWhen you mention a local file that the user may want to open in Shennian, format it as a Markdown link using the exact local path:\n\n- Use [filename.ext](</absolute/path/to/filename.ext>) for absolute Unix, macOS, or Windows paths.\n- Use [filename.ext](<relative/path/to/filename.ext>) for paths relative to the current working directory.\n- Do not use file:// URLs for local files.\n- Do not put user-openable file paths only inside code blocks.\n- Keep normal http:// and https:// links unchanged.";
3
3
  export declare function buildPlatformInstructions(workDir: string, externalChannel?: ExternalChannelSessionStatus | null, sessionId?: string): string;
4
+ export declare function buildInlineChannelPreamble(workDir: string, externalChannel?: ExternalChannelSessionStatus | null, sessionId?: string): string;
4
5
  export declare function ensurePlatformInstructionsFile(workDir: string, externalChannel?: ExternalChannelSessionStatus | null, sessionId?: string): string;
@@ -1,4 +1,4 @@
1
- import s from"node:fs";import a from"node:path";import{createHash as c}from"node:crypto";import{resolveShennianPath as l}from"../config/index.js";import{buildExternalChannelInstructions as u}from"./external-channel-instructions.js";import{buildInstalledSkillInstructions as f}from"../skills/registry.js";const h=`## Shennian Output Instructions
1
+ import a from"node:fs";import i from"node:path";import{createHash as c}from"node:crypto";import{resolveShennianPath as u}from"../config/index.js";import{buildExternalChannelInstructions as l}from"./external-channel-instructions.js";import{buildInstalledSkillInstructions as f}from"../skills/registry.js";const h=`## Shennian Output Instructions
2
2
 
3
3
  When you mention a local file that the user may want to open in Shennian, format it as a Markdown link using the exact local path:
4
4
 
@@ -6,11 +6,17 @@ When you mention a local file that the user may want to open in Shennian, format
6
6
  - Use [filename.ext](<relative/path/to/filename.ext>) for paths relative to the current working directory.
7
7
  - Do not use file:// URLs for local files.
8
8
  - Do not put user-openable file paths only inside code blocks.
9
- - Keep normal http:// and https:// links unchanged.`;function m(n){const e=a.join(n,"AGENTS.md");try{return s.readFileSync(e,"utf8").trim()}catch{return""}}function p(n,e,o){const r=m(n),t=u(e,a.resolve(n),o),i=f();return`${["# Shennian Agent Instructions",r?`## Project Instructions
9
+ - Keep normal http:// and https:// links unchanged.`;function m(n){const t=i.join(n,"AGENTS.md");try{return a.readFileSync(t,"utf8").trim()}catch{return""}}function p(n,t,r){const o=m(n),e=l(t,i.resolve(n),r),s=f();return`${["# Shennian Agent Instructions",o?`## Project Instructions
10
10
 
11
- ${r}`:"",h,i,t?`## External Message Channel Instructions
11
+ ${o}`:"",h,s,e?`## External Message Channel Instructions
12
12
 
13
- ${t}`:""].filter(Boolean).join(`
13
+ ${e}`:""].filter(Boolean).join(`
14
14
 
15
15
  `)}
16
- `}function b(n,e,o){const r=c("sha256").update(`${a.resolve(n)}:${o??""}:${JSON.stringify(e??null)}`).digest("hex").slice(0,16),t=l("runtime","agent-instructions",`${r}.md`),i=p(n,e,o);s.mkdirSync(a.dirname(t),{recursive:!0});try{if(s.readFileSync(t,"utf8")===i)return t}catch{}return s.writeFileSync(t,i),t}export{h as PLATFORM_OUTPUT_INSTRUCTIONS,p as buildPlatformInstructions,b as ensurePlatformInstructionsFile};
16
+ `}function b(n,t,r){const o=l(t,i.resolve(n),r);return o?`# \u795E\u5FF5\u5916\u90E8\u6D88\u606F\u901A\u9053\u8BF4\u660E\uFF08\u7CFB\u7EDF\u6307\u4EE4\uFF0C\u975E\u7528\u6237\u8F93\u5165\uFF0C\u8BF7\u9075\u5FAA\u540E\u518D\u56DE\u5E94\u4E0B\u65B9\u7528\u6237\u6D88\u606F\uFF09
17
+
18
+ ${o}
19
+
20
+ ---
21
+
22
+ `:""}function $(n,t,r){const o=c("sha256").update(`${i.resolve(n)}:${r??""}:${JSON.stringify(t??null)}`).digest("hex").slice(0,16),e=u("runtime","agent-instructions",`${o}.md`),s=p(n,t,r);a.mkdirSync(i.dirname(e),{recursive:!0});try{if(a.readFileSync(e,"utf8")===s)return e}catch{}return a.writeFileSync(e,s),e}export{h as PLATFORM_OUTPUT_INSTRUCTIONS,b as buildInlineChannelPreamble,p as buildPlatformInstructions,$ as ensurePlatformInstructionsFile};
@@ -1 +1 @@
1
- import e from"node:fs";import t from"node:path";import{resolveShennianPath as l}from"../config/index.js";const i=l("channels.json");class d{list(){try{return JSON.parse(e.readFileSync(i,"utf-8")).channels??[]}catch{return[]}}get(r){return this.list().find(n=>n.id===r)}upsert(r){const n=this.list().filter(s=>s.id!==r.id);n.push(r),e.mkdirSync(t.dirname(i),{recursive:!0}),e.writeFileSync(i,JSON.stringify({channels:n},null,2))}replaceAll(r){e.mkdirSync(t.dirname(i),{recursive:!0}),e.writeFileSync(i,JSON.stringify({channels:r},null,2))}}export{d as ChannelConfigRegistry};
1
+ import e from"node:fs";import i from"node:path";import{resolveShennianPath as l}from"../config/index.js";function n(){return l("channels.json")}class o{list(){try{return JSON.parse(e.readFileSync(n(),"utf-8")).channels??[]}catch{return[]}}get(r){return this.list().find(t=>t.id===r)}upsert(r){const t=this.list().filter(s=>s.id!==r.id);t.push(r),e.mkdirSync(i.dirname(n()),{recursive:!0}),e.writeFileSync(n(),JSON.stringify({channels:t},null,2))}replaceAll(r){e.mkdirSync(i.dirname(n()),{recursive:!0}),e.writeFileSync(n(),JSON.stringify({channels:r},null,2))}}export{o as ChannelConfigRegistry};
@@ -123,6 +123,7 @@ export declare class ChannelRuntime {
123
123
  privacyConsentAccepted?: boolean;
124
124
  flowScriptPath?: string;
125
125
  }): Promise<ExternalChannelView>;
126
+ private findConflictingWeChatRpaBinding;
126
127
  }
127
128
  export type ExternalReplySendPlanItem = {
128
129
  text: string;
@@ -1,5 +1,5 @@
1
- import M from"node:crypto";import g from"node:fs";import u from"node:path";import{ChannelConfigRegistry as v}from"./registry.js";import{ChannelSecretRegistry as N}from"./secret-registry.js";import{WeComChannelAdapter as D}from"./wecom.js";import{WeChatRpaChannelAdapter as B}from"./wechat-rpa.js";import{ExternalWebSocketChannelAdapter as x}from"./websocket.js";import{splitExternalReplyText as F}from"./reply-split.js";class J{onExternalMessage;createReplyTarget;configs=new v;secrets=new N;adapters=new Map;completedReplyKeys=new Map;recentMessages=new Map;constructor(e,t,s={}){this.onExternalMessage=e,this.createReplyTarget=t;const o=new D(a=>this.ingest({...a,type:"external.message"}));this.adapters.set(o.type,o);const r=new x(a=>this.ingest({...a,type:"external.message"}));this.adapters.set(r.type,r);const i=new B(a=>this.ingest(a),{createProductRunner:s.createWeChatRpaProductRunner,automationLane:s.weChatAutomationLane});this.adapters.set(i.type,i)}async start(){for(const e of this.configs.list().filter(t=>t.enabled))await this.adapters.get(e.type)?.connect(e).catch(()=>{})}async stop(){for(const e of this.configs.list())await this.adapters.get(e.type)?.disconnect(e).catch(()=>{})}ingest(e){const t=this.findRecentMessageDuplicate(e);if(t)return t;const s=this.configs.get(e.channelId),o=e.managerSessionId??s?.sessionId??s?.managerSessionId;if(!o)throw new Error(`No session bound for channel ${e.channelId}`);const r=e.replyTarget||this.createReplyTarget({managerSessionId:o,channelId:e.channelId,conversationId:e.conversationId,messageId:e.messageId}),i={...e,replyTarget:r};return this.onExternalMessage(o,i),this.recordRecentMessage(i),i}async reply(e){const t=this.configs.get(e.channelId);if(!t)return{ok:!1,error:`Unknown channel: ${e.channelId}`};if((t.sessionId??t.managerSessionId)!==e.managerSessionId)return{ok:!1,error:"Channel is not bound to this session"};const s=this.adapters.get(t.type);if(!s)return{ok:!1,error:`Unsupported channel type: ${t.type}`};try{const o=W(t.type,e);if(!o.length)return{ok:!1,error:"Reply text or attachment is required"};let r=!1;for(const i of o){const a=i.idempotencyKey;if(a&&this.isReplyCompleted(t,e.conversationId,a))continue;const c=await s.send(t,{...e,...i});if(c?.status==="queued"){r=!0;continue}if(c?.status==="manual-review")return{ok:!1,error:c.reason||"Reply requires manual review before retry"};a&&this.markReplyCompleted(t,e.conversationId,a)}return r?{ok:!0,pending:!0}:{ok:!0}}catch(o){return{ok:!1,error:o instanceof Error?o.message:String(o)}}}isReplyCompleted(e,t,s){return this.loadReplyCompletionSet(e).has(A(e.id,t,s))}markReplyCompleted(e,t,s){const o=this.loadReplyCompletionSet(e);o.add(A(e.id,t,s));try{$(e.workDir,o)}catch{}}loadReplyCompletionSet(e){const t=u.resolve(e.workDir),s=this.completedReplyKeys.get(t);if(s)return s;const o=T(e.workDir);return this.completedReplyKeys.set(t,o),o}async getDefaultReplyTarget(e){const t=this.configs.list().find(r=>(r.sessionId??r.managerSessionId)===e&&r.enabled);if(!t)throw new Error("No enabled external channel is bound to this session");const s=this.adapters.get(t.type);if(!s?.defaultConversation)throw new Error(`External channel ${t.type} has no default conversation`);const o=await s.defaultConversation(t);return{channelId:t.id,conversationId:o.conversationId}}getManagerChannel(e,t,s={}){const o=this.configs.list().filter(c=>(c.sessionId??c.managerSessionId)===e&&c.type===t),r=o.find(c=>c.enabled)??o.at(-1);if(!r)return null;const i=this.secrets.get(r.secretRef),a=this.adapters.get(r.type)?.runtimeStatus?.(r)??{};return{id:r.id,type:r.type,name:r.name,sessionId:r.sessionId??r.managerSessionId,managerSessionId:r.managerSessionId,workDir:r.workDir,agentType:r.agentType,agentSessionId:r.agentSessionId,modelId:r.modelId,enabled:r.enabled,wsUrl:i?.wsUrl??"",token:s.includeSecret?i?.token??"":"",tokenConfigured:!!i?.token,canReply:!!i?.canReply,systemPrompt:typeof i?.systemPrompt=="string"?i.systemPrompt:"",...k(i),...a}}getChannelById(e,t={}){const s=this.configs.get(e);if(!s)return null;const o=this.secrets.get(s.secretRef),r=this.adapters.get(s.type)?.runtimeStatus?.(s)??{};return{id:s.id,type:s.type,name:s.name,sessionId:s.sessionId??s.managerSessionId,managerSessionId:s.managerSessionId,workDir:s.workDir,agentType:s.agentType,agentSessionId:s.agentSessionId,modelId:s.modelId,enabled:s.enabled,wsUrl:o?.wsUrl??"",token:t.includeSecret?o?.token??"":"",tokenConfigured:!!o?.token,canReply:!!o?.canReply,systemPrompt:typeof o?.systemPrompt=="string"?o.systemPrompt:"",...k(o),...r}}getChannelStatusById(e){const t=this.configs.get(e);if(!t)return null;const s=this.secrets.get(t.secretRef),o=this.adapters.get(t.type)?.runtimeStatus?.(t)??{};return{configured:!0,connected:y(t,s),type:t.type,channelId:t.id,name:t.name,canReply:!!s?.canReply,systemPrompt:typeof s?.systemPrompt=="string"?s.systemPrompt:"",...h(s),...o}}getManagerChannelStatus(e){const t=this.configs.list().find(r=>(r.sessionId??r.managerSessionId)===e&&r.enabled);if(!t)return null;const s=this.secrets.get(t.secretRef),o=this.adapters.get(t.type)?.runtimeStatus?.(t)??{};return{configured:!0,connected:y(t,s),type:t.type,channelId:t.id,name:t.name,canReply:!!s?.canReply,systemPrompt:typeof s?.systemPrompt=="string"?s.systemPrompt:"",...h(s),...o}}listManagerChannelStatuses(){return this.configs.list().filter(e=>e.enabled).map(e=>({managerSessionId:e.sessionId??e.managerSessionId,status:this.getManagerChannelStatus(e.sessionId??e.managerSessionId)})).filter(e=>!!e.status)}listManagerExternalChannels(e){return this.configs.list().filter(t=>t.enabled&&(t.sessionId??t.managerSessionId)===e).map(t=>{const s=this.secrets.get(t.secretRef),o=this.adapters.get(t.type)?.runtimeStatus?.(t)??{};return{configured:!0,connected:y(t,s),type:t.type,channelId:t.id,name:t.name,canReply:!!s?.canReply,systemPrompt:typeof s?.systemPrompt=="string"?s.systemPrompt:"",...h(s),...o}})}async syncManagerWeChatRpaChannel(e){const t=this.configs.list().find(a=>a.enabled&&a.type==="wechat-rpa"&&(a.sessionId??a.managerSessionId)===e);if(!t)throw new Error("No enabled WeChat RPA channel is bound to this session");const s=this.adapters.get(t.type);if(!s?.syncNow)throw new Error("WeChat RPA channel does not support manual sync");const o=Date.now()-120*1e3,r=await s.syncNow(t)??[],i=K(r,this.getRecentMessages(t.id,o));return{channel:this.getManagerChannel(e,"wechat-rpa",{includeSecret:!0}),messages:r,recentMessages:i}}async cancelManagerWeChatRpaOutbound(e){const t=this.configs.list().find(r=>r.enabled&&r.type==="wechat-rpa"&&(r.sessionId??r.managerSessionId)===e.managerSessionId);if(!t)throw new Error("No enabled WeChat RPA channel is bound to this session");const s=this.adapters.get(t.type);if(!s?.cancelOutbound)throw new Error("WeChat RPA channel does not support outbound cancellation");const o=await s.cancelOutbound(t,{idempotencyKey:e.idempotencyKey,replyId:e.replyId,reason:e.reason});return{cancelled:o.cancelled,status:o.status,channel:this.getManagerChannel(e.managerSessionId,"wechat-rpa",{includeSecret:!0})}}recordRecentMessage(e){const t=this.recentMessages.get(e.channelId)??[];this.findRecentMessageDuplicate(e)||(t.push(e),this.recentMessages.set(e.channelId,t.slice(-50)))}findRecentMessageDuplicate(e){const t=I(e);return t?(this.recentMessages.get(e.channelId)??[]).find(o=>I(o)===t)??null:null}getRecentMessages(e,t){return(this.recentMessages.get(e)??[]).filter(o=>{const r=Date.parse(o.receivedAt);return!Number.isFinite(r)||r>=t})}async upsertManagerChannel(e){const t=this.configs.get(e.id),s=this.configs.list(),o=e.sessionId||e.managerSessionId,r={id:e.id,type:e.type,name:e.name?.trim()||t?.name||"\u5916\u90E8\u6D88\u606F\u901A\u9053",sessionId:o,managerSessionId:o,workDir:e.workDir,agentType:e.agentType||t?.agentType,agentSessionId:e.agentSessionId??t?.agentSessionId??null,modelId:e.modelId??t?.modelId??null,enabled:e.enabled,secretRef:t?.secretRef||`channel:${e.id}`},i=this.secrets.get(r.secretRef),a=e.wsUrl?.trim()||i?.wsUrl||"",c=e.token?.trim()||i?.token||"",p=e.canReply??i?.canReply??!1,m=e.systemPrompt??(typeof i?.systemPrompt=="string"?i.systemPrompt:"");if(r.enabled&&(!a||!c))throw new Error("WebSocket \u5730\u5740\u548C Token \u5FC5\u586B");const f=s.filter(l=>l.id!==r.id).map(l=>(l.sessionId??l.managerSessionId)===o&&l.type===e.type?{...l,enabled:!1}:l);f.push(r),this.configs.replaceAll(f),(a||c)&&this.secrets.upsert(r.secretRef,{type:"websocket",wsUrl:a,token:c,canReply:p,systemPrompt:m});const d=this.adapters.get(r.type);for(const l of s)(l.sessionId??l.managerSessionId)===o&&l.type===e.type&&l.enabled&&await this.adapters.get(l.type)?.disconnect(l).catch(()=>{});return r.enabled&&d?.connect(r).catch(()=>{}),this.getManagerChannel(o,e.type,{includeSecret:!0})}async upsertManagerWeChatRpaChannel(e){const t=this.configs.get(e.id),s=this.configs.list(),o=e.sessionId||e.managerSessionId,r=w(e.groups);if(e.enabled&&!r.length)throw new Error("WeChat RPA \u81F3\u5C11\u9700\u8981\u914D\u7F6E\u4E00\u4E2A\u7FA4");if(e.enabled&&r.length>1)throw new Error("WeChat RPA \u6BCF\u4E2A\u5BF9\u8BDD\u53EA\u80FD\u7ED1\u5B9A\u4E00\u4E2A\u7FA4");const i={id:e.id,type:"wechat-rpa",name:e.name?.trim()||t?.name||"\u672C\u673A\u5FAE\u4FE1 RPA",sessionId:o,managerSessionId:o,workDir:e.workDir,agentType:e.agentType||t?.agentType,agentSessionId:e.agentSessionId??t?.agentSessionId??null,modelId:e.modelId??t?.modelId??null,enabled:e.enabled,secretRef:t?.secretRef||`channel:${e.id}`},a=this.secrets.get(i.secretRef),c=e.source||(a?.source==="wechat-channel"||a?.source==="macos-probe"||a?.source==="fixture-jsonl"||a?.source==="macos-flow"||a?.source==="windows-visual-flow"||a?.source==="wechat-rpa-lab"?a.source:E());if(e.enabled&&c==="windows-visual-flow")throw new Error("\u65E7 Windows visual flow \u5DF2\u5F52\u6863\uFF1BmacOS \u548C Windows \u6B63\u5F0F\u901A\u9053\u90FD\u4F7F\u7528 wechat-channel");const p=e.privacyConsentAccepted??!!a?.privacyConsentAccepted;if(e.enabled&&!p)throw new Error("\u542F\u7528\u524D\u9700\u8981\u786E\u8BA4\u5FAE\u4FE1\u901A\u9053\u6570\u636E\u4E0E\u9690\u79C1\u6388\u6743");const m=s.filter(d=>d.id!==i.id).map(d=>(d.sessionId??d.managerSessionId)===o&&d.type==="wechat-rpa"?{...d,enabled:!1}:d);m.push(i),this.configs.replaceAll(m),this.secrets.upsert(i.secretRef,{type:"wechat-rpa",source:c,groups:r,pollIntervalMs:R(e.pollIntervalMs,a?.pollIntervalMs),recentLimit:R(e.recentLimit,a?.recentLimit),idleSeconds:R(e.idleSeconds,a?.idleSeconds),forceForeground:e.forceForeground??(a?.forceForeground===void 0?!0:!!a.forceForeground),noRestore:e.noRestore??(a?.noRestore===void 0?!0:!!a.noRestore),downloadAttachments:e.downloadAttachments??(a?.downloadAttachments===void 0?!0:!!a.downloadAttachments),downloadAttachmentsDir:e.downloadAttachmentsDir?.trim()||b(a?.downloadAttachmentsDir),selfNickname:e.selfNickname?.trim()||b(a?.selfNickname),selfTriggerMarker:e.selfTriggerMarker?.trim()||void 0,deferInitialPoll:e.deferInitialPoll===!0?!0:void 0,privacyConsentAccepted:p,flowScriptPath:e.flowScriptPath?.trim()||b(a?.flowScriptPath),canReply:e.canReply??a?.canReply??!1,systemPrompt:e.systemPrompt??(typeof a?.systemPrompt=="string"?a.systemPrompt:"")});const f=this.adapters.get(i.type);for(const d of s)(d.sessionId??d.managerSessionId)===o&&d.type==="wechat-rpa"&&d.enabled&&await this.adapters.get(d.type)?.disconnect(d).catch(()=>{});return i.enabled&&f?.connect(i).catch(()=>{}),this.getManagerChannel(o,"wechat-rpa",{includeSecret:!0})}}function y(n,e){return!e||e.type!==n.type?!1:n.type==="wechat-rpa"?!0:n.type==="websocket"?!!(e.wsUrl&&e.token):n.type==="wecom"?!!(e.token||e.botId||e.secret):!!e.token}function k(n){return!n||n.type!=="wechat-rpa"?{}:{wechatRpaSource:typeof n.source=="string"?n.source:"",wechatRpaGroups:w(Array.isArray(n.groups)?n.groups:[]),pollIntervalMs:Number.isFinite(n.pollIntervalMs)?Number(n.pollIntervalMs):void 0,recentLimit:Number.isFinite(n.recentLimit)?Number(n.recentLimit):void 0,idleSeconds:Number.isFinite(n.idleSeconds)?Number(n.idleSeconds):void 0,forceForeground:n.forceForeground===void 0?!0:!!n.forceForeground,noRestore:n.noRestore===void 0?void 0:!!n.noRestore,downloadAttachments:n.downloadAttachments===void 0?!0:!!n.downloadAttachments,downloadAttachmentsDir:typeof n.downloadAttachmentsDir=="string"?n.downloadAttachmentsDir:"",selfNickname:typeof n.selfNickname=="string"?n.selfNickname:"",wechatRpaPrivacyConsentAccepted:!!n.privacyConsentAccepted,wechatRpaServerDecisionAvailable:!0,wechatRpaPreflightChecks:C(n)}}function h(n){return!n||n.type!=="wechat-rpa"?{}:{wechatRpaSource:typeof n.source=="string"?n.source:null,wechatRpaGroups:w(Array.isArray(n.groups)?n.groups:[]),pollIntervalMs:Number.isFinite(n.pollIntervalMs)?Number(n.pollIntervalMs):null,recentLimit:Number.isFinite(n.recentLimit)?Number(n.recentLimit):null,idleSeconds:Number.isFinite(n.idleSeconds)?Number(n.idleSeconds):null,forceForeground:n.forceForeground===void 0?!0:!!n.forceForeground,noRestore:n.noRestore===void 0?null:!!n.noRestore,downloadAttachments:n.downloadAttachments===void 0?!0:!!n.downloadAttachments,downloadAttachmentsDir:typeof n.downloadAttachmentsDir=="string"?n.downloadAttachmentsDir:null,selfNickname:typeof n.selfNickname=="string"?n.selfNickname:null,wechatRpaPrivacyConsentAccepted:!!n.privacyConsentAccepted,wechatRpaServerDecisionAvailable:!0,wechatRpaPreflightChecks:C(n)}}function w(n){const e=new Set,t=[];for(const s of n){const o=String(s?.name||"").replace(/\s+/g," ").trim();!o||e.has(o)||(e.add(o),t.push({name:o}))}return t}function E(){return"wechat-channel"}function C(n){const e=[];if(n.lastHelperPermissions&&typeof n.lastHelperPermissions=="object"){const t=n.lastHelperPermissions;e.push({code:"wechat_window_unavailable",ok:t.wechatWindowAvailable!==!1,severity:"blocking",message:t.wechatWindowAvailable===!1?"\u5FAE\u4FE1\u7A97\u53E3\u4E0D\u53EF\u7528\uFF0C\u8BF7\u6253\u5F00\u5E76\u767B\u5F55\u5FAE\u4FE1\u540E\u518D\u8BD5\u3002":"\u5FAE\u4FE1\u7A97\u53E3\u53EF\u7528\u3002"})}return e.push({code:"platform_unsupported",ok:n.source!=="windows-visual-flow",severity:"blocking",message:n.source==="windows-visual-flow"?"\u65E7 Windows visual flow \u5DF2\u5F52\u6863\uFF0C\u8BF7\u6539\u7528 wechat-channel\u3002":"\u5F53\u524D\u914D\u7F6E\u4F7F\u7528\u8DE8\u5E73\u53F0\u5FAE\u4FE1\u901A\u9053\u3002"}),e.push({code:"privacy_consent_required",ok:!!n.privacyConsentAccepted,severity:"blocking",message:n.privacyConsentAccepted?"\u5DF2\u786E\u8BA4\u5FAE\u4FE1\u901A\u9053\u6570\u636E\u4E0E\u9690\u79C1\u6388\u6743\u3002":"\u542F\u7528\u524D\u9700\u8981\u786E\u8BA4\u5FAE\u4FE1\u901A\u9053\u6570\u636E\u4E0E\u9690\u79C1\u6388\u6743\u3002"}),e.push({code:"server_decision_unavailable",ok:!0,severity:"blocking",message:"\u670D\u52A1\u7AEF\u5224\u65AD\u80FD\u529B\u53EF\u7528\u3002"}),e}function W(n,e){const t=F(e.text);if(!t.length&&!e.attachment)return[];if(n==="wechat-rpa"&&e.attachment&&t.length<=1)return[{text:t[0]??"",attachment:e.attachment,idempotencyKey:e.idempotencyKey}];const s=t.map((o,r)=>({text:o,attachment:void 0,idempotencyKey:t.length>1&&e.idempotencyKey?`${e.idempotencyKey}:${r+1}`:e.idempotencyKey}));return e.attachment&&s.push({text:"",attachment:e.attachment,idempotencyKey:t.length&&e.idempotencyKey?`${e.idempotencyKey}:attachment`:e.idempotencyKey}),s}function K(...n){const e=new Set,t=[];for(const s of n.flat()){const o=I(s);if(!o){t.push(s);continue}e.has(o)||(e.add(o),t.push(s))}return t}function I(n){const e=S(n.channelId),t=S(n.conversationId),s=S(n.messageId);return!e||!t||!s?null:`${e}
1
+ import M from"node:crypto";import u from"node:fs";import h from"node:path";import{ChannelConfigRegistry as v}from"./registry.js";import{ChannelSecretRegistry as N}from"./secret-registry.js";import{WeComChannelAdapter as D}from"./wecom.js";import{WeChatRpaChannelAdapter as B}from"./wechat-rpa.js";import{ExternalWebSocketChannelAdapter as x}from"./websocket.js";import{splitExternalReplyText as E}from"./reply-split.js";class J{onExternalMessage;createReplyTarget;configs=new v;secrets=new N;adapters=new Map;completedReplyKeys=new Map;recentMessages=new Map;constructor(e,t,n={}){this.onExternalMessage=e,this.createReplyTarget=t;const o=new D(a=>this.ingest({...a,type:"external.message"}));this.adapters.set(o.type,o);const r=new x(a=>this.ingest({...a,type:"external.message"}));this.adapters.set(r.type,r);const i=new B(a=>this.ingest(a),{createProductRunner:n.createWeChatRpaProductRunner,automationLane:n.weChatAutomationLane});this.adapters.set(i.type,i)}async start(){for(const e of this.configs.list().filter(t=>t.enabled))await this.adapters.get(e.type)?.connect(e).catch(()=>{})}async stop(){for(const e of this.configs.list())await this.adapters.get(e.type)?.disconnect(e).catch(()=>{})}ingest(e){const t=this.findRecentMessageDuplicate(e);if(t)return t;const n=this.configs.get(e.channelId),o=e.managerSessionId??n?.sessionId??n?.managerSessionId;if(!o)throw new Error(`No session bound for channel ${e.channelId}`);const r=e.replyTarget||this.createReplyTarget({managerSessionId:o,channelId:e.channelId,conversationId:e.conversationId,messageId:e.messageId}),i={...e,replyTarget:r};return this.onExternalMessage(o,i),this.recordRecentMessage(i),i}async reply(e){const t=this.configs.get(e.channelId);if(!t)return{ok:!1,error:`Unknown channel: ${e.channelId}`};if((t.sessionId??t.managerSessionId)!==e.managerSessionId)return{ok:!1,error:"Channel is not bound to this session"};const n=this.adapters.get(t.type);if(!n)return{ok:!1,error:`Unsupported channel type: ${t.type}`};try{const o=F(t.type,e);if(!o.length)return{ok:!1,error:"Reply text or attachment is required"};let r=!1;for(const i of o){const a=i.idempotencyKey;if(a&&this.isReplyCompleted(t,e.conversationId,a))continue;const d=await n.send(t,{...e,...i});if(d?.status==="queued"){r=!0;continue}if(d?.status==="manual-review")return{ok:!1,error:d.reason||"Reply requires manual review before retry"};a&&this.markReplyCompleted(t,e.conversationId,a)}return r?{ok:!0,pending:!0}:{ok:!0}}catch(o){return{ok:!1,error:o instanceof Error?o.message:String(o)}}}isReplyCompleted(e,t,n){return this.loadReplyCompletionSet(e).has(A(e.id,t,n))}markReplyCompleted(e,t,n){const o=this.loadReplyCompletionSet(e);o.add(A(e.id,t,n));try{$(e.workDir,o)}catch{}}loadReplyCompletionSet(e){const t=h.resolve(e.workDir),n=this.completedReplyKeys.get(t);if(n)return n;const o=T(e.workDir);return this.completedReplyKeys.set(t,o),o}async getDefaultReplyTarget(e){const t=this.configs.list().find(r=>(r.sessionId??r.managerSessionId)===e&&r.enabled);if(!t)throw new Error("No enabled external channel is bound to this session");const n=this.adapters.get(t.type);if(!n?.defaultConversation)throw new Error(`External channel ${t.type} has no default conversation`);const o=await n.defaultConversation(t);return{channelId:t.id,conversationId:o.conversationId}}getManagerChannel(e,t,n={}){const o=this.configs.list().filter(d=>(d.sessionId??d.managerSessionId)===e&&d.type===t),r=o.find(d=>d.enabled)??o.at(-1);if(!r)return null;const i=this.secrets.get(r.secretRef),a=this.adapters.get(r.type)?.runtimeStatus?.(r)??{};return{id:r.id,type:r.type,name:r.name,sessionId:r.sessionId??r.managerSessionId,managerSessionId:r.managerSessionId,workDir:r.workDir,agentType:r.agentType,agentSessionId:r.agentSessionId,modelId:r.modelId,enabled:r.enabled,wsUrl:i?.wsUrl??"",token:n.includeSecret?i?.token??"":"",tokenConfigured:!!i?.token,canReply:!!i?.canReply,systemPrompt:typeof i?.systemPrompt=="string"?i.systemPrompt:"",...C(i),...a}}getChannelById(e,t={}){const n=this.configs.get(e);if(!n)return null;const o=this.secrets.get(n.secretRef),r=this.adapters.get(n.type)?.runtimeStatus?.(n)??{};return{id:n.id,type:n.type,name:n.name,sessionId:n.sessionId??n.managerSessionId,managerSessionId:n.managerSessionId,workDir:n.workDir,agentType:n.agentType,agentSessionId:n.agentSessionId,modelId:n.modelId,enabled:n.enabled,wsUrl:o?.wsUrl??"",token:t.includeSecret?o?.token??"":"",tokenConfigured:!!o?.token,canReply:!!o?.canReply,systemPrompt:typeof o?.systemPrompt=="string"?o.systemPrompt:"",...C(o),...r}}getChannelStatusById(e){const t=this.configs.get(e);if(!t)return null;const n=this.secrets.get(t.secretRef),o=this.adapters.get(t.type)?.runtimeStatus?.(t)??{};return{configured:!0,connected:y(t,n),type:t.type,channelId:t.id,name:t.name,canReply:!!n?.canReply,systemPrompt:typeof n?.systemPrompt=="string"?n.systemPrompt:"",...w(n),...o}}getManagerChannelStatus(e){const t=this.configs.list().find(r=>(r.sessionId??r.managerSessionId)===e&&r.enabled);if(!t)return null;const n=this.secrets.get(t.secretRef),o=this.adapters.get(t.type)?.runtimeStatus?.(t)??{};return{configured:!0,connected:y(t,n),type:t.type,channelId:t.id,name:t.name,canReply:!!n?.canReply,systemPrompt:typeof n?.systemPrompt=="string"?n.systemPrompt:"",...w(n),...o}}listManagerChannelStatuses(){return this.configs.list().filter(e=>e.enabled).map(e=>({managerSessionId:e.sessionId??e.managerSessionId,status:this.getManagerChannelStatus(e.sessionId??e.managerSessionId)})).filter(e=>!!e.status)}listManagerExternalChannels(e){return this.configs.list().filter(t=>t.enabled&&(t.sessionId??t.managerSessionId)===e).map(t=>{const n=this.secrets.get(t.secretRef),o=this.adapters.get(t.type)?.runtimeStatus?.(t)??{};return{configured:!0,connected:y(t,n),type:t.type,channelId:t.id,name:t.name,canReply:!!n?.canReply,systemPrompt:typeof n?.systemPrompt=="string"?n.systemPrompt:"",...w(n),...o}})}async syncManagerWeChatRpaChannel(e){const t=this.configs.list().find(a=>a.enabled&&a.type==="wechat-rpa"&&(a.sessionId??a.managerSessionId)===e);if(!t)throw new Error("No enabled WeChat RPA channel is bound to this session");const n=this.adapters.get(t.type);if(!n?.syncNow)throw new Error("WeChat RPA channel does not support manual sync");const o=Date.now()-120*1e3,r=await n.syncNow(t)??[],i=K(r,this.getRecentMessages(t.id,o));return{channel:this.getManagerChannel(e,"wechat-rpa",{includeSecret:!0}),messages:r,recentMessages:i}}async cancelManagerWeChatRpaOutbound(e){const t=this.configs.list().find(r=>r.enabled&&r.type==="wechat-rpa"&&(r.sessionId??r.managerSessionId)===e.managerSessionId);if(!t)throw new Error("No enabled WeChat RPA channel is bound to this session");const n=this.adapters.get(t.type);if(!n?.cancelOutbound)throw new Error("WeChat RPA channel does not support outbound cancellation");const o=await n.cancelOutbound(t,{idempotencyKey:e.idempotencyKey,replyId:e.replyId,reason:e.reason});return{cancelled:o.cancelled,status:o.status,channel:this.getManagerChannel(e.managerSessionId,"wechat-rpa",{includeSecret:!0})}}recordRecentMessage(e){const t=this.recentMessages.get(e.channelId)??[];this.findRecentMessageDuplicate(e)||(t.push(e),this.recentMessages.set(e.channelId,t.slice(-50)))}findRecentMessageDuplicate(e){const t=I(e);return t?(this.recentMessages.get(e.channelId)??[]).find(o=>I(o)===t)??null:null}getRecentMessages(e,t){return(this.recentMessages.get(e)??[]).filter(o=>{const r=Date.parse(o.receivedAt);return!Number.isFinite(r)||r>=t})}async upsertManagerChannel(e){const t=this.configs.get(e.id),n=this.configs.list(),o=e.sessionId||e.managerSessionId,r={id:e.id,type:e.type,name:e.name?.trim()||t?.name||"\u5916\u90E8\u6D88\u606F\u901A\u9053",sessionId:o,managerSessionId:o,workDir:e.workDir,agentType:e.agentType||t?.agentType,agentSessionId:e.agentSessionId??t?.agentSessionId??null,modelId:e.modelId??t?.modelId??null,enabled:e.enabled,secretRef:t?.secretRef||`channel:${e.id}`},i=this.secrets.get(r.secretRef),a=e.wsUrl?.trim()||i?.wsUrl||"",d=e.token?.trim()||i?.token||"",p=e.canReply??i?.canReply??!1,m=e.systemPrompt??(typeof i?.systemPrompt=="string"?i.systemPrompt:"");if(r.enabled&&(!a||!d))throw new Error("WebSocket \u5730\u5740\u548C Token \u5FC5\u586B");const f=n.filter(l=>l.id!==r.id).map(l=>(l.sessionId??l.managerSessionId)===o&&l.type===e.type?{...l,enabled:!1}:l);f.push(r),this.configs.replaceAll(f),(a||d)&&this.secrets.upsert(r.secretRef,{type:"websocket",wsUrl:a,token:d,canReply:p,systemPrompt:m});const c=this.adapters.get(r.type);for(const l of n)(l.sessionId??l.managerSessionId)===o&&l.type===e.type&&l.enabled&&await this.adapters.get(l.type)?.disconnect(l).catch(()=>{});return r.enabled&&c?.connect(r).catch(()=>{}),this.getManagerChannel(o,e.type,{includeSecret:!0})}async upsertManagerWeChatRpaChannel(e){const t=this.configs.get(e.id),n=this.configs.list(),o=e.sessionId||e.managerSessionId,r=g(e.groups);if(e.enabled&&!r.length)throw new Error("WeChat RPA \u81F3\u5C11\u9700\u8981\u914D\u7F6E\u4E00\u4E2A\u7FA4");if(e.enabled&&r.length>1)throw new Error("WeChat RPA \u6BCF\u4E2A\u5BF9\u8BDD\u53EA\u80FD\u7ED1\u5B9A\u4E00\u4E2A\u7FA4");if(e.enabled){const c=this.findConflictingWeChatRpaBinding({excludeChannelId:e.id,boundSessionId:o,groupNames:r.map(l=>l.name)});if(c)throw new Error(`\u5FAE\u4FE1\u5BF9\u8BDD\u300C${c.groupName}\u300D\u5DF2\u88AB\u53E6\u4E00\u4E2A\u667A\u80FD\u4F53\u7ED1\u5B9A(\u901A\u9053 ${c.channelName}),\u540C\u4E00\u4E2A\u5BF9\u8BDD\u53EA\u80FD\u7ED1\u5B9A\u4E00\u6B21\u3002\u8BF7\u5148\u5728\u539F\u667A\u80FD\u4F53\u89E3\u7ED1,\u518D\u91CD\u65B0\u7ED1\u5B9A\u3002`)}const i={id:e.id,type:"wechat-rpa",name:e.name?.trim()||t?.name||"\u672C\u673A\u5FAE\u4FE1 RPA",sessionId:o,managerSessionId:o,workDir:e.workDir,agentType:e.agentType||t?.agentType,agentSessionId:e.agentSessionId??t?.agentSessionId??null,modelId:e.modelId??t?.modelId??null,enabled:e.enabled,secretRef:t?.secretRef||`channel:${e.id}`},a=this.secrets.get(i.secretRef),d=e.source||(a?.source==="wechat-channel"||a?.source==="macos-probe"||a?.source==="fixture-jsonl"||a?.source==="macos-flow"||a?.source==="windows-visual-flow"||a?.source==="wechat-rpa-lab"?a.source:W());if(e.enabled&&d==="windows-visual-flow")throw new Error("\u65E7 Windows visual flow \u5DF2\u5F52\u6863\uFF1BmacOS \u548C Windows \u6B63\u5F0F\u901A\u9053\u90FD\u4F7F\u7528 wechat-channel");const p=e.privacyConsentAccepted??!!a?.privacyConsentAccepted;if(e.enabled&&!p)throw new Error("\u542F\u7528\u524D\u9700\u8981\u786E\u8BA4\u5FAE\u4FE1\u901A\u9053\u6570\u636E\u4E0E\u9690\u79C1\u6388\u6743");const m=n.filter(c=>c.id!==i.id).map(c=>(c.sessionId??c.managerSessionId)===o&&c.type==="wechat-rpa"?{...c,enabled:!1}:c);m.push(i),this.configs.replaceAll(m),this.secrets.upsert(i.secretRef,{type:"wechat-rpa",source:d,groups:r,pollIntervalMs:R(e.pollIntervalMs,a?.pollIntervalMs),recentLimit:R(e.recentLimit,a?.recentLimit),idleSeconds:R(e.idleSeconds,a?.idleSeconds),forceForeground:e.forceForeground??(a?.forceForeground===void 0?!0:!!a.forceForeground),noRestore:e.noRestore??(a?.noRestore===void 0?!0:!!a.noRestore),downloadAttachments:e.downloadAttachments??(a?.downloadAttachments===void 0?!0:!!a.downloadAttachments),downloadAttachmentsDir:e.downloadAttachmentsDir?.trim()||b(a?.downloadAttachmentsDir),selfNickname:e.selfNickname?.trim()||b(a?.selfNickname),selfTriggerMarker:e.selfTriggerMarker?.trim()||void 0,deferInitialPoll:e.deferInitialPoll===!0?!0:void 0,privacyConsentAccepted:p,flowScriptPath:e.flowScriptPath?.trim()||b(a?.flowScriptPath),canReply:e.canReply??a?.canReply??!1,systemPrompt:e.systemPrompt??(typeof a?.systemPrompt=="string"?a.systemPrompt:"")});const f=this.adapters.get(i.type);for(const c of n)(c.sessionId??c.managerSessionId)===o&&c.type==="wechat-rpa"&&c.enabled&&await this.adapters.get(c.type)?.disconnect(c).catch(()=>{});return i.enabled&&f?.connect(i).catch(c=>{console.error(`[wechat-rpa] connect failed id=${i.id}: ${c instanceof Error?c.message:String(c)}`)}),this.getManagerChannel(o,"wechat-rpa",{includeSecret:!0})}findConflictingWeChatRpaBinding(e){const t=new Set(e.groupNames);if(!t.size)return null;for(const n of this.configs.list()){if(n.type!=="wechat-rpa"||!n.enabled||n.id===e.excludeChannelId||(n.sessionId??n.managerSessionId)===e.boundSessionId)continue;const o=this.secrets.get(n.secretRef),r=g(Array.isArray(o?.groups)?o.groups:[]);for(const i of r)if(t.has(i.name))return{channelId:n.id,channelName:n.name||n.id,groupName:i.name}}return null}}function y(s,e){return!e||e.type!==s.type?!1:s.type==="wechat-rpa"?!0:s.type==="websocket"?!!(e.wsUrl&&e.token):s.type==="wecom"?!!(e.token||e.botId||e.secret):!!e.token}function C(s){return!s||s.type!=="wechat-rpa"?{}:{wechatRpaSource:typeof s.source=="string"?s.source:"",wechatRpaGroups:g(Array.isArray(s.groups)?s.groups:[]),pollIntervalMs:Number.isFinite(s.pollIntervalMs)?Number(s.pollIntervalMs):void 0,recentLimit:Number.isFinite(s.recentLimit)?Number(s.recentLimit):void 0,idleSeconds:Number.isFinite(s.idleSeconds)?Number(s.idleSeconds):void 0,forceForeground:s.forceForeground===void 0?!0:!!s.forceForeground,noRestore:s.noRestore===void 0?void 0:!!s.noRestore,downloadAttachments:s.downloadAttachments===void 0?!0:!!s.downloadAttachments,downloadAttachmentsDir:typeof s.downloadAttachmentsDir=="string"?s.downloadAttachmentsDir:"",selfNickname:typeof s.selfNickname=="string"?s.selfNickname:"",wechatRpaPrivacyConsentAccepted:!!s.privacyConsentAccepted,wechatRpaServerDecisionAvailable:!0,wechatRpaPreflightChecks:k(s)}}function w(s){return!s||s.type!=="wechat-rpa"?{}:{wechatRpaSource:typeof s.source=="string"?s.source:null,wechatRpaGroups:g(Array.isArray(s.groups)?s.groups:[]),pollIntervalMs:Number.isFinite(s.pollIntervalMs)?Number(s.pollIntervalMs):null,recentLimit:Number.isFinite(s.recentLimit)?Number(s.recentLimit):null,idleSeconds:Number.isFinite(s.idleSeconds)?Number(s.idleSeconds):null,forceForeground:s.forceForeground===void 0?!0:!!s.forceForeground,noRestore:s.noRestore===void 0?null:!!s.noRestore,downloadAttachments:s.downloadAttachments===void 0?!0:!!s.downloadAttachments,downloadAttachmentsDir:typeof s.downloadAttachmentsDir=="string"?s.downloadAttachmentsDir:null,selfNickname:typeof s.selfNickname=="string"?s.selfNickname:null,wechatRpaPrivacyConsentAccepted:!!s.privacyConsentAccepted,wechatRpaServerDecisionAvailable:!0,wechatRpaPreflightChecks:k(s)}}function g(s){const e=new Set,t=[];for(const n of s){const o=String(n?.name||"").replace(/\s+/g," ").trim();!o||e.has(o)||(e.add(o),t.push({name:o}))}return t}function W(){return"wechat-channel"}function k(s){const e=[];if(s.lastHelperPermissions&&typeof s.lastHelperPermissions=="object"){const t=s.lastHelperPermissions;e.push({code:"wechat_window_unavailable",ok:t.wechatWindowAvailable!==!1,severity:"blocking",message:t.wechatWindowAvailable===!1?"\u5FAE\u4FE1\u7A97\u53E3\u4E0D\u53EF\u7528\uFF0C\u8BF7\u6253\u5F00\u5E76\u767B\u5F55\u5FAE\u4FE1\u540E\u518D\u8BD5\u3002":"\u5FAE\u4FE1\u7A97\u53E3\u53EF\u7528\u3002"})}return e.push({code:"platform_unsupported",ok:s.source!=="windows-visual-flow",severity:"blocking",message:s.source==="windows-visual-flow"?"\u65E7 Windows visual flow \u5DF2\u5F52\u6863\uFF0C\u8BF7\u6539\u7528 wechat-channel\u3002":"\u5F53\u524D\u914D\u7F6E\u4F7F\u7528\u8DE8\u5E73\u53F0\u5FAE\u4FE1\u901A\u9053\u3002"}),e.push({code:"privacy_consent_required",ok:!!s.privacyConsentAccepted,severity:"blocking",message:s.privacyConsentAccepted?"\u5DF2\u786E\u8BA4\u5FAE\u4FE1\u901A\u9053\u6570\u636E\u4E0E\u9690\u79C1\u6388\u6743\u3002":"\u542F\u7528\u524D\u9700\u8981\u786E\u8BA4\u5FAE\u4FE1\u901A\u9053\u6570\u636E\u4E0E\u9690\u79C1\u6388\u6743\u3002"}),e.push({code:"server_decision_unavailable",ok:!0,severity:"blocking",message:"\u670D\u52A1\u7AEF\u5224\u65AD\u80FD\u529B\u53EF\u7528\u3002"}),e}function F(s,e){const t=E(e.text);if(!t.length&&!e.attachment)return[];if(s==="wechat-rpa"&&e.attachment&&t.length<=1)return[{text:t[0]??"",attachment:e.attachment,idempotencyKey:e.idempotencyKey}];const n=t.map((o,r)=>({text:o,attachment:void 0,idempotencyKey:t.length>1&&e.idempotencyKey?`${e.idempotencyKey}:${r+1}`:e.idempotencyKey}));return e.attachment&&n.push({text:"",attachment:e.attachment,idempotencyKey:t.length&&e.idempotencyKey?`${e.idempotencyKey}:attachment`:e.idempotencyKey}),n}function K(...s){const e=new Set,t=[];for(const n of s.flat()){const o=I(n);if(!o){t.push(n);continue}e.has(o)||(e.add(o),t.push(n))}return t}function I(s){const e=S(s.channelId),t=S(s.conversationId),n=S(s.messageId);return!e||!t||!n?null:`${e}
2
2
  ${t}
3
- ${s}`}function S(n){return typeof n=="string"?n.trim():""}function A(n,e,t){return M.createHash("sha256").update(`${n}
3
+ ${n}`}function S(s){return typeof s=="string"?s.trim():""}function A(s,e,t){return M.createHash("sha256").update(`${s}
4
4
  ${e}
5
- ${t}`).digest("hex").slice(0,32)}function P(n){return u.join(n,".shennian","external-reply-idempotency.json")}function T(n){try{const e=JSON.parse(g.readFileSync(P(n),"utf8")),t=Array.isArray(e.completed)?e.completed:[];return new Set(t.map(s=>typeof s=="string"?s:s&&typeof s=="object"&&typeof s.key=="string"?s.key:"").filter(Boolean))}catch{return new Set}}function $(n,e){const t=P(n);g.mkdirSync(u.dirname(t),{recursive:!0});const s=Array.from(e).slice(-500);g.writeFileSync(t,JSON.stringify({updatedAt:new Date().toISOString(),completed:s.map(o=>({key:o}))},null,2)),e.clear();for(const o of s)e.add(o)}function R(n,e){const t=Number(n??e);return Number.isFinite(t)&&t>=0?t:void 0}function b(n){return typeof n=="string"&&n.trim()?n.trim():void 0}export{J as ChannelRuntime,W as planExternalReplySends};
5
+ ${t}`).digest("hex").slice(0,32)}function P(s){return h.join(s,".shennian","external-reply-idempotency.json")}function T(s){try{const e=JSON.parse(u.readFileSync(P(s),"utf8")),t=Array.isArray(e.completed)?e.completed:[];return new Set(t.map(n=>typeof n=="string"?n:n&&typeof n=="object"&&typeof n.key=="string"?n.key:"").filter(Boolean))}catch{return new Set}}function $(s,e){const t=P(s);u.mkdirSync(h.dirname(t),{recursive:!0});const n=Array.from(e).slice(-500);u.writeFileSync(t,JSON.stringify({updatedAt:new Date().toISOString(),completed:n.map(o=>({key:o}))},null,2)),e.clear();for(const o of n)e.add(o)}function R(s,e){const t=Number(s??e);return Number.isFinite(t)&&t>=0?t:void 0}function b(s){return typeof s=="string"&&s.trim()?s.trim():void 0}export{J as ChannelRuntime,F as planExternalReplySends};
@@ -1 +1 @@
1
- import r from"node:fs";import o from"node:path";import{resolveShennianPath as a}from"../config/index.js";const t=a("channels.secrets.json");function i(){return new Date().toISOString()}function u(){return{secrets:{}}}class m{load(){try{return{secrets:JSON.parse(r.readFileSync(t,"utf-8")).secrets??{}}}catch{return u()}}save(e){r.mkdirSync(o.dirname(t),{recursive:!0}),r.writeFileSync(t,JSON.stringify(e,null,2),{mode:384});try{r.chmodSync(t,384)}catch{}}get(e){return this.load().secrets[e]}upsert(e,c){const s=this.load(),n={...c,updatedAt:i()};return s.secrets[e]=n,this.save(s),n}}export{m as ChannelSecretRegistry};
1
+ import r from"node:fs";import o from"node:path";import{resolveShennianPath as a}from"../config/index.js";function t(){return a("channels.secrets.json")}function i(){return new Date().toISOString()}function u(){return{secrets:{}}}class p{load(){try{return{secrets:JSON.parse(r.readFileSync(t(),"utf-8")).secrets??{}}}catch{return u()}}save(e){r.mkdirSync(o.dirname(t()),{recursive:!0}),r.writeFileSync(t(),JSON.stringify(e,null,2),{mode:384});try{r.chmodSync(t(),384)}catch{}}get(e){return this.load().secrets[e]}upsert(e,c){const s=this.load(),n={...c,updatedAt:i()};return s.secrets[e]=n,this.save(s),n}}export{p as ChannelSecretRegistry};
@@ -1 +1 @@
1
- function i(e){return typeof e!="string"?"":e.normalize("NFKC").replace(/\s+/g," ").trim().toLowerCase()}function a(e){return i(e.anchorText||e.normalizedText||e.textExcerpt||"")}function l(e,t){const o=i(e),r=i(t);return!o||!r?0:o===r?1:1-f(o,r)/Math.max(o.length,r.length)}function h(e,t,o=.86){if(e.stableMessageKey&&e.stableMessageKey===t.stableMessageKey)return!0;if(e.senderRole!==t.senderRole||e.kind!==t.kind)return!1;const r=a(e),s=a(t);return!r||!s?!1:l(r,s)>=o}function u(e){const t=new Set,o=[];for(const r of e.current){const s=e.previous.findIndex((n,c)=>!t.has(c)&&h(n,r,e.threshold));if(s>=0){t.add(s);continue}o.push(r)}return o}function f(e,t){const o=Array.from({length:t.length+1},(s,n)=>n),r=Array.from({length:t.length+1},()=>0);for(let s=1;s<=e.length;s+=1){r[0]=s;for(let n=1;n<=t.length;n+=1)r[n]=Math.min(o[n]+1,r[n-1]+1,o[n-1]+(e[s-1]===t[n-1]?0:1));for(let n=0;n<=t.length;n+=1)o[n]=r[n]}return o[t.length]}export{u as filterNewWeChatMessagesByAnchor,h as isLikelySameWeChatMessage,i as normalizeWeChatAnchorText,a as weChatAnchorText,l as weChatTextSimilarity};
1
+ function i(e){return typeof e!="string"?"":e.normalize("NFKC").replace(/\s+/g," ").trim().toLowerCase()}function a(e){return i(e.anchorText||e.normalizedText||e.textExcerpt||"")}function l(e,t){const o=i(e),r=i(t);return!o||!r?0:o===r?1:1-f(o,r)/Math.max(o.length,r.length)}function h(e,t,o=.95){if(e.stableMessageKey&&e.stableMessageKey===t.stableMessageKey)return!0;if(e.senderRole!==t.senderRole||e.kind!==t.kind)return!1;const r=a(e),s=a(t);return!r||!s?!1:l(r,s)>=o}function u(e){const t=new Set,o=[];for(const r of e.current){const s=e.previous.findIndex((n,c)=>!t.has(c)&&h(n,r,e.threshold));if(s>=0){t.add(s);continue}o.push(r)}return o}function f(e,t){const o=Array.from({length:t.length+1},(s,n)=>n),r=Array.from({length:t.length+1},()=>0);for(let s=1;s<=e.length;s+=1){r[0]=s;for(let n=1;n<=t.length;n+=1)r[n]=Math.min(o[n]+1,r[n-1]+1,o[n-1]+(e[s-1]===t[n-1]?0:1));for(let n=0;n<=t.length;n+=1)o[n]=r[n]}return o[t.length]}export{u as filterNewWeChatMessagesByAnchor,h as isLikelySameWeChatMessage,i as normalizeWeChatAnchorText,a as weChatAnchorText,l as weChatTextSimilarity};
@@ -1,2 +1,2 @@
1
- import a from"node:fs";import f from"node:os";import l from"node:path";import{resolveShennianPath as m}from"../../config/index.js";const d=900*1e3,k=3e4,w=100,L=1e3;class M{tail=Promise.resolve();active=!1;queued=0;lastLabel=null;lockPath;staleMs;owner;defaultWaitMs;constructor(t={}){this.lockPath=t.lockPath,this.staleMs=t.staleMs??d,this.owner=t.owner??`${process.platform}:${process.pid}`,this.defaultWaitMs=t.defaultWaitMs??k}async run(t,i,r={}){const n=Math.max(0,r.waitMs??this.defaultWaitMs);this.queued+=1;const o=this.tail;let s;this.tail=new Promise(h=>{s=h}),await o.catch(()=>{}),this.queued-=1,this.active=!0,this.lastLabel=t;const c=Date.now()+n;let u=null;try{return u=await this.acquireLockWithWait(t,c),await i()}finally{u?.release(),this.active=!1,s()}}snapshot(){return{active:this.active,queued:this.queued,lastLabel:this.lastLabel,lockPath:this.lockPath}}async acquireLockWithWait(t,i){if(!this.lockPath)return null;a.mkdirSync(l.dirname(this.lockPath),{recursive:!0});let r=w,n=null;for(;;)try{return a.mkdirSync(this.lockPath),A(this.lockPath,{pid:process.pid,label:t,owner:this.owner,hostname:f.hostname(),startedAt:new Date().toISOString()}),{release:()=>{a.rmSync(this.lockPath,{recursive:!0,force:!0})}}}catch(o){if(!W(o))throw o;const s=P(this.lockPath);if(n=s,_(this.lockPath,s,this.staleMs)){a.rmSync(this.lockPath,{recursive:!0,force:!0});continue}const c=i-Date.now();if(c<=0)throw new Error(O(this.lockPath,n));await y(Math.min(r,c)),r=Math.min(r*2,L)}}}function C(e={}){return new M({lockPath:p(),...e})}function p(){return m("runtime","wechat-automation.lock")}function y(e){return new Promise(t=>setTimeout(t,e))}function A(e,t){a.writeFileSync(l.join(e,"owner.json"),`${JSON.stringify(t,null,2)}
2
- `,"utf8")}function P(e){try{const t=JSON.parse(a.readFileSync(l.join(e,"owner.json"),"utf8"));return t&&typeof t=="object"?t:null}catch{return null}}function _(e,t,i){if(t?.pid&&S(t.pid))return!1;try{return Date.now()-a.statSync(e).mtimeMs>i}catch{return!0}}function S(e){if(!Number.isInteger(e)||e<=0)return!1;try{return process.kill(e,0),!0}catch{return!1}}function O(e,t){return`wechat_automation_busy: another WeChat automation run is active (${[`lock=${e}`,t?.pid?`pid=${t.pid}`:"",t?.label?`label=${t.label}`:"",t?.startedAt?`startedAt=${t.startedAt}`:"",t?.owner?`owner=${t.owner}`:""].filter(Boolean).join(" ")})`}function W(e){return typeof e=="object"&&e!==null&&"code"in e&&e.code==="EEXIST"}export{M as SerialWeChatAutomationLane,C as createWeChatAutomationLane,p as defaultWeChatAutomationLockPath};
1
+ import{AsyncLocalStorage as L}from"node:async_hooks";import i from"node:fs";import m from"node:os";import u from"node:path";import{resolveShennianPath as P}from"../../config/index.js";const h=new L,w=900*1e3,p=3e4,y=100,A=1e3;class M{tail=Promise.resolve();active=!1;queued=0;lastLabel=null;lockPath;staleMs;owner;defaultWaitMs;constructor(t={}){this.lockPath=t.lockPath,this.staleMs=t.staleMs??w,this.owner=t.owner??`${process.platform}:${process.pid}`,this.defaultWaitMs=t.defaultWaitMs??p}async run(t,a,r={}){if(this.lockPath&&h.getStore()?.has(this.lockPath))return this.lastLabel=t,a();const n=Math.max(0,r.waitMs??this.defaultWaitMs);this.queued+=1;const o=this.tail;let s;this.tail=new Promise(l=>{s=l}),await o.catch(()=>{}),this.queued-=1,this.active=!0,this.lastLabel=t;const c=Date.now()+n;let f=null;try{if(f=await this.acquireLockWithWait(t,c),!this.lockPath)return await a();const l=h.getStore(),d=new Set(l??[]);return d.add(this.lockPath),await h.run(d,a)}finally{f?.release(),this.active=!1,s()}}snapshot(){return{active:this.active,queued:this.queued,lastLabel:this.lastLabel,lockPath:this.lockPath}}async acquireLockWithWait(t,a){if(!this.lockPath)return null;i.mkdirSync(u.dirname(this.lockPath),{recursive:!0});let r=y,n=null;for(;;)try{return i.mkdirSync(this.lockPath),O(this.lockPath,{pid:process.pid,label:t,owner:this.owner,hostname:m.hostname(),startedAt:new Date().toISOString()}),{release:()=>{i.rmSync(this.lockPath,{recursive:!0,force:!0})}}}catch(o){if(!b(o))throw o;const s=W(this.lockPath);if(n=s,v(this.lockPath,s,this.staleMs)){i.rmSync(this.lockPath,{recursive:!0,force:!0});continue}const c=a-Date.now();if(c<=0)throw new Error(T(this.lockPath,n));await _(Math.min(r,c)),r=Math.min(r*2,A)}}}function j(e={}){return new M({lockPath:S(),...e})}function S(){return P("runtime","wechat-automation.lock")}function _(e){return new Promise(t=>setTimeout(t,e))}function O(e,t){i.writeFileSync(u.join(e,"owner.json"),`${JSON.stringify(t,null,2)}
2
+ `,"utf8")}function W(e){try{const t=JSON.parse(i.readFileSync(u.join(e,"owner.json"),"utf8"));return t&&typeof t=="object"?t:null}catch{return null}}function v(e,t,a){if(t?.pid&&k(t.pid))return!1;if(t?.pid&&E(t)&&!k(t.pid))return!0;try{return Date.now()-i.statSync(e).mtimeMs>a}catch{return!0}}function E(e){return e?.hostname?e.hostname===m.hostname():!1}function k(e){if(!Number.isInteger(e)||e<=0)return!1;try{return process.kill(e,0),!0}catch{return!1}}function T(e,t){return`wechat_automation_busy: another WeChat automation run is active (${[`lock=${e}`,t?.pid?`pid=${t.pid}`:"",t?.label?`label=${t.label}`:"",t?.startedAt?`startedAt=${t.startedAt}`:"",t?.owner?`owner=${t.owner}`:""].filter(Boolean).join(" ")})`}function b(e){return typeof e=="object"&&e!==null&&"code"in e&&e.code==="EEXIST"}export{M as SerialWeChatAutomationLane,j as createWeChatAutomationLane,S as defaultWeChatAutomationLockPath};
@@ -6,6 +6,9 @@ export type WeChatConversationListFingerprint = {
6
6
  unreadText?: string | null;
7
7
  fingerprint?: string | null;
8
8
  bbox?: unknown;
9
+ titleBox?: unknown;
10
+ timeBox?: unknown;
11
+ previewBox?: unknown;
9
12
  };
10
13
  export type WeChatConversationFingerprintTarget = {
11
14
  bindingId: string;
@@ -1 +1 @@
1
- function l(n){return[n.title,n.preview,n.timeText,n.unreadText].map(e=>a(e)).filter(Boolean).join("|")}function h(n){const e=n.minScore??.72,t=n.visibleItems.map(o=>({...o,fingerprint:o.fingerprint||l(o)})),r=[];for(const o of n.targets){let i=null;for(const s of t){const c=u(o,s);(!i||c.score>i.score)&&(i=c)}if(i&&i.score>=e&&i.reason!=="none"){r.push(i);const s=t.findIndex(c=>c===i.item);s>=0&&t.splice(s,1)}}return r}function u(n,e){const t=e.fingerprint||l(e);if(n.lastConversationFingerprint&&t&&n.lastConversationFingerprint===t)return{bindingId:n.bindingId,item:e,score:1,reason:"fingerprint"};if(f(n.conversationDisplayName)&&d(e.title,n.conversationDisplayName))return{bindingId:n.bindingId,item:e,score:1,reason:"title"};if(f(n.conversationDisplayName)&&!d(e.title,n.conversationDisplayName))return{bindingId:n.bindingId,item:e,score:0,reason:"none"};const r=p(n.conversationDisplayName,e.title),o=n.lastPreview?p(n.lastPreview,e.preview):0;return r>=.8&&o>=.55?{bindingId:n.bindingId,item:e,score:r*.7+o*.3,reason:"title-preview"}:r>=.88?{bindingId:n.bindingId,item:e,score:r*.86,reason:"title"}:{bindingId:n.bindingId,item:e,score:r*.5+o*.2,reason:"none"}}function a(n){return typeof n=="string"?n.trim().replace(/\s+/g," ").toLowerCase():""}function f(n){const e=typeof n=="string"?n.trim():"";return/^[A-Z0-9_-]{1,6}$/.test(e)&&/[A-Z]/.test(e)}function d(n,e){const t=typeof n=="string"?n.trim().replace(/\s+/g," "):"",r=typeof e=="string"?e.trim().replace(/\s+/g," "):"";return!t||!r?!1:t===r?!0:g(t)===r}function g(n){return n.normalize("NFKC").replace(/\s*[((][0-9]+[))]\s*$/u,"").replace(/\s+/g," ").trim()}function p(n,e){const t=a(n),r=a(e);return!t||!r?0:t===r?1:1-v(t,r)/Math.max(t.length,r.length)}function v(n,e){const t=Array.from({length:e.length+1},(o,i)=>i),r=Array.from({length:e.length+1},()=>0);for(let o=1;o<=n.length;o+=1){r[0]=o;for(let i=1;i<=e.length;i+=1)r[i]=Math.min(t[i]+1,r[i-1]+1,t[i-1]+(n[o-1]===e[i-1]?0:1));for(let i=0;i<=e.length;i+=1)t[i]=r[i]}return t[e.length]}export{l as buildWeChatConversationFingerprint,h as matchWeChatConversationFingerprints,u as scoreConversationFingerprint};
1
+ const h=16;function l(n){return[s(n.title),s(n.timeText),d(n.preview)].filter(Boolean).join("|")}function d(n){const e=s(n);return e?Array.from(e).slice(0,16).join(""):""}function m(n){const e=n.minScore??.72,t=n.visibleItems.map(o=>({...o,fingerprint:o.fingerprint||l(o)})),i=[];for(const o of n.targets){let r=null;for(const c of t){const a=g(o,c);(!r||a.score>r.score)&&(r=a)}if(r&&r.score>=e&&r.reason!=="none"){i.push(r);const c=t.findIndex(a=>a===r.item);c>=0&&t.splice(c,1)}}return i}function g(n,e){const t=e.fingerprint||l(e);if(n.lastConversationFingerprint&&t&&n.lastConversationFingerprint===t)return{bindingId:n.bindingId,item:e,score:1,reason:"fingerprint"};if(f(n.conversationDisplayName)&&p(e.title,n.conversationDisplayName))return{bindingId:n.bindingId,item:e,score:1,reason:"title"};if(f(n.conversationDisplayName)&&!p(e.title,n.conversationDisplayName))return{bindingId:n.bindingId,item:e,score:0,reason:"none"};const i=u(n.conversationDisplayName,e.title),o=n.lastPreview?u(n.lastPreview,e.preview):0;return i>=.8&&o>=.55?{bindingId:n.bindingId,item:e,score:i*.7+o*.3,reason:"title-preview"}:i>=.88?{bindingId:n.bindingId,item:e,score:i*.86,reason:"title"}:{bindingId:n.bindingId,item:e,score:i*.5+o*.2,reason:"none"}}function s(n){return typeof n=="string"?n.trim().replace(/\s+/g," ").toLowerCase():""}function f(n){const e=typeof n=="string"?n.trim():"";return/^[A-Z0-9_-]{1,6}$/.test(e)&&/[A-Z]/.test(e)}function p(n,e){const t=typeof n=="string"?n.trim().replace(/\s+/g," "):"",i=typeof e=="string"?e.trim().replace(/\s+/g," "):"";return!t||!i?!1:t===i?!0:v(t)===i}function v(n){return n.normalize("NFKC").replace(/\s*[((][0-9]+[))]\s*$/u,"").replace(/\s+/g," ").trim()}function u(n,e){const t=s(n),i=s(e);return!t||!i?0:t===i?1:1-I(t,i)/Math.max(t.length,i.length)}function I(n,e){const t=Array.from({length:e.length+1},(o,r)=>r),i=Array.from({length:e.length+1},()=>0);for(let o=1;o<=n.length;o+=1){i[0]=o;for(let r=1;r<=e.length;r+=1)i[r]=Math.min(t[r]+1,i[r-1]+1,t[r-1]+(n[o-1]===e[r-1]?0:1));for(let r=0;r<=e.length;r+=1)t[r]=i[r]}return t[e.length]}export{l as buildWeChatConversationFingerprint,m as matchWeChatConversationFingerprints,g as scoreConversationFingerprint};
@@ -1,4 +1,4 @@
1
- export declare const WECHAT_CHANNEL_HELPER_VERSION = "0.1.10";
1
+ export declare const WECHAT_CHANNEL_HELPER_VERSION = "0.1.11";
2
2
  export declare const WECHAT_CHANNEL_HELPER_DIR_ENV = "SHENNIAN_WECHAT_CHANNEL_HELPER_DIR";
3
3
  export declare const SHENNIAN_HELPER_RUNTIME_DIR_ENV = "SHENNIAN_HELPER_RUNTIME_DIR";
4
4
  export declare const WECHAT_CHANNEL_HELPER_RUNTIME_REQUIRED_REASON = "helper_runtime_required";
@@ -1 +1 @@
1
- import c from"node:fs";import s from"node:path";import E from"node:crypto";const V="0.1.10",S="SHENNIAN_WECHAT_CHANNEL_HELPER_DIR",g="SHENNIAN_HELPER_RUNTIME_DIR",C="helper_runtime_required",u=["en","v"].join("");function $(n={}){const r=n.platform??process.platform;if(r!=="darwin"&&r!=="win32")return{ok:!1,reasonCode:"unsupported_platform",message:"WeChat channel helper is only available on macOS and Windows"};const e=n.baseDir?[s.resolve(n.baseDir)]:A({platform:r,env:n[u]??R(),homedir:n.homedir,includeInstalledDesktop:n.includeInstalledDesktop});let a=null;for(const t of e){const i=_({platform:r,baseDir:t,verifyIntegrity:n.verifyIntegrity});if(i.ok||(n.baseDir||c.existsSync(t))&&(a??=i,n.baseDir))return i}return a??{ok:!1,reasonCode:C,message:x(r,e)}}function _(n){const{platform:r,baseDir:e}=n,a=s.join(e,"manifest.json"),t=F(a);if(!t)return{ok:!1,reasonCode:"manifest_missing",message:`WeChat channel helper manifest is missing: ${a}`};const i=t.platforms[r],o=i?s.join(e,i.executable):"";if(!i||!o||!c.existsSync(o))return{ok:!1,reasonCode:"helper_missing",message:`WeChat channel helper executable is missing: ${o||e}`};if(N({platform:r,baseDir:e,verifyIntegrity:n.verifyIntegrity})&&i.sha256&&E.createHash("sha256").update(c.readFileSync(o)).digest("hex")!==i.sha256)return{ok:!1,reasonCode:"integrity_mismatch",message:`WeChat channel helper integrity mismatch: ${o}`};const f=H(r,o);return f.ok?{ok:!0,helperPath:o,helperDir:e,version:t.helperVersion,manifest:t,warning:i.signed&&i.notarized?void 0:"helper_not_signed_or_notarized"}:{ok:!1,reasonCode:"helper_not_executable",message:f.message}}function H(n,r){if(n!=="darwin")return{ok:!0};try{const e=c.statSync(r);return(e.mode&73)!==0?{ok:!0}:(c.chmodSync(r,e.mode|73),{ok:!0})}catch(e){return{ok:!1,message:`WeChat channel helper is not executable and could not be repaired: ${r}: ${e instanceof Error?e.message:String(e)}`}}}function A(n){const{platform:r,env:e}=n,a=r==="win32"?"windows":"macos",t=[],i=l=>{if(!l)return;const p=s.resolve(l);t.includes(p)||t.push(p)},o=e[S]?.trim();o&&i(j(o,a));const f=e[g]?.trim();if(f)for(const l of P(f,a))i(l);if(b({platform:r,platformDir:a,env:e,homedir:n.homedir,push:i}),n.includeInstalledDesktop===!1)return t;if(r==="darwin"){const l=n.homedir||e.HOME;l&&i(s.join(l,"Applications","Shennian.app","Contents","Resources","wechat-channel",a)),i(s.join("/Applications","Shennian.app","Contents","Resources","wechat-channel",a))}else if(r==="win32"){const l=n.homedir||e.USERPROFILE,p=e.LOCALAPPDATA||(l?s.join(l,"AppData","Local"):""),m=e.ProgramFiles||e.PROGRAMFILES,h=e["ProgramFiles(x86)"]||e["PROGRAMFILES(X86)"];for(const d of[p?s.join(p,"Programs","Shennian"):"",p?s.join(p,"Programs","shennian"):"",m?s.join(m,"Shennian"):"",h?s.join(h,"Shennian"):""])d&&i(s.join(d,"resources","wechat-channel",a))}return t}function b(n){const{platform:r,platformDir:e,env:a,push:t}=n;if(r==="darwin"){const i=n.homedir||a.HOME,o=k({platform:r,env:a,homedir:n.homedir});o&&(t(s.join(o,"Shennian Helper.app","Contents","Resources","wechat-channel",e)),t(s.join(o,"wechat-channel",e))),i&&t(s.join(i,"Applications","Shennian Helper.app","Contents","Resources","wechat-channel",e)),t(s.join("/Applications","Shennian Helper.app","Contents","Resources","wechat-channel",e)),t(s.join("/Library","Application Support","Shennian","Helper","wechat-channel",e))}else if(r==="win32"){const i=n.homedir||a.USERPROFILE,o=a.LOCALAPPDATA||(i?s.join(i,"AppData","Local"):""),f=a.ProgramFiles||a.PROGRAMFILES,l=a["ProgramFiles(x86)"]||a["PROGRAMFILES(X86)"];o&&(t(s.join(o,"Programs","Shennian Helper","resources","wechat-channel",e)),t(s.join(o,"Shennian","Helper","wechat-channel",e))),f&&t(s.join(f,"Shennian Helper","resources","wechat-channel",e)),l&&t(s.join(l,"Shennian Helper","resources","wechat-channel",e))}}function j(n,r){return c.existsSync(s.join(n,"manifest.json"))?n:s.join(n,r)}function P(n,r){if(c.existsSync(s.join(n,"manifest.json")))return[n];const e=s.join(n,"Shennian Helper.app","Contents","Resources","wechat-channel",r),a=s.join(n,"wechat-channel",r),t=s.join(n,r);return[e,a,t]}function k(n){const r=n[u],e=r[g]?.trim();if(e)return s.resolve(e);const a=n.homedir||(n.platform==="win32"?r.USERPROFILE:r.HOME);if(n.platform==="darwin")return a?s.join(a,"Library","Application Support","Shennian","Helper"):null;if(n.platform==="win32"){const t=r.LOCALAPPDATA||(a?s.join(a,"AppData","Local"):"");return t?s.join(t,"Shennian","Helper"):null}return null}function q(n={}){return k({platform:n.platform??process.platform,env:n.env??R(),homedir:n.homedir})}function D(n){const r=s.resolve(n);return O([s.join(r,"helper-runtime-package.json"),s.join(s.dirname(r),"..","helper-runtime-package.json")])}function U(n){const r=n.platform??process.platform;if(r!=="darwin"&&r!=="win32")return{ok:!1,reasonCode:"unsupported_platform",message:"WeChat channel helper runtime package is only available on macOS and Windows"};const e=n.manifestPath?[s.resolve(n.manifestPath)]:n.helperDir?D(n.helperDir):[],a=e.find(t=>c.existsSync(t));if(!a)return{ok:!1,reasonCode:"helper_runtime_package_manifest_missing",message:`Helper runtime package manifest is missing. Checked: ${e.join(", ")||"(none)"}`,manifestPath:e[0]};try{const t=JSON.parse(w(a)),i=I(t,r);return i.ok?{ok:!0,manifestPath:a,manifest:t}:{ok:!1,reasonCode:"helper_runtime_package_manifest_invalid",message:`${i.message}: ${a}`,manifestPath:a}}catch(t){return{ok:!1,reasonCode:"helper_runtime_package_manifest_invalid",message:`Helper runtime package manifest is invalid JSON: ${t instanceof Error?t.message:String(t)}`,manifestPath:a}}}function I(n,r){if(!n||typeof n!="object"||Array.isArray(n))return{ok:!1,message:"manifest must be an object"};const e=n;return e.schemaVersion!==1?{ok:!1,message:"schemaVersion must be 1"}:e.packageKind!=="shennian-helper-runtime"?{ok:!1,message:"packageKind must be shennian-helper-runtime"}:e.platform!==r?{ok:!1,message:`platform must be ${r}`}:e.helperVersion?!Number.isInteger(e.protocolVersion)||Number(e.protocolVersion)<=0?{ok:!1,message:"protocolVersion must be a positive integer"}:L(e.minCliVersion)?!e.sha256||typeof e.sha256.runtimeManifest!="string"?{ok:!1,message:"sha256.runtimeManifest is required"}:y(e.sha256.runtimeManifest)?e.sha256.entrypoint!==null&&typeof e.sha256.entrypoint!="string"?{ok:!1,message:"sha256.entrypoint must be a string or null"}:typeof e.sha256.entrypoint=="string"&&!y(e.sha256.entrypoint)?{ok:!1,message:"sha256.entrypoint must be a sha256 digest or null"}:!e.installTarget?.kind||!e.installTarget.defaultPath?{ok:!1,message:"installTarget.kind and installTarget.defaultPath are required"}:!e.payload?.kind||!e.payload.runtimeManifest||!e.payload.entrypoint?{ok:!1,message:"payload.kind, payload.runtimeManifest, and payload.entrypoint are required"}:r==="darwin"&&e.installTarget.kind!=="app-bundle"?{ok:!1,message:"darwin installTarget.kind must be app-bundle"}:r==="darwin"&&e.payload.kind!=="macos-helper-app"?{ok:!1,message:"darwin payload.kind must be macos-helper-app"}:r==="win32"&&e.installTarget.kind!=="directory"?{ok:!1,message:"win32 installTarget.kind must be directory"}:r==="win32"&&e.payload.kind!=="windows-helper-runtime"?{ok:!1,message:"win32 payload.kind must be windows-helper-runtime"}:!e.signature||typeof e.signature.requiredForRelease!="boolean"||typeof e.signature.signed!="boolean"||typeof e.signature.notarized!="boolean"?{ok:!1,message:"signature.requiredForRelease, signature.signed, and signature.notarized are required"}:{ok:!0}:{ok:!1,message:"sha256.runtimeManifest must be a sha256 digest"}:{ok:!1,message:"minCliVersion must be a semver string"}:{ok:!1,message:"helperVersion is required"}}function L(n){return typeof n=="string"&&/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/.test(n)}function y(n){return/^[a-f0-9]{64}$/i.test(n)}function N(n){return n.verifyIntegrity===!0?!0:n.verifyIntegrity===!1?!1:!v(n.platform,n.baseDir)}function v(n,r){const e=r.replace(/[\\/]+/g,"/");return n==="darwin"?/\/Shennian\.app\/Contents\/Resources\/wechat-channel\/macos$/i.test(e):n==="win32"?/\/resources\/wechat-channel\/windows$/i.test(e):!1}function x(n,r){const e=n==="win32"?"Windows":"macOS",a=r.length>0?` Checked helper directories: ${r.join(", ")}`:"";return`WeChat RPA requires Shennian Helper runtime on ${e}; open Shennian Desktop or use the \u4F7F\u7528\u5FAE\u4FE1 page to install Helper, then retry.${a}`}function R(){return globalThis.process?.[u]??{}}function F(n){try{const r=JSON.parse(w(n));return r.schemaVersion!==1||typeof r.helperVersion!="string"||!r.platforms?null:r}catch{return null}}function w(n){return c.readFileSync(n,"utf8").replace(/^\uFEFF/,"")}function O(n){const r=new Set,e=[];for(const a of n){const t=s.resolve(a);r.has(t)||(r.add(t),e.push(t))}return e}export{g as SHENNIAN_HELPER_RUNTIME_DIR_ENV,S as WECHAT_CHANNEL_HELPER_DIR_ENV,C as WECHAT_CHANNEL_HELPER_RUNTIME_REQUIRED_REASON,V as WECHAT_CHANNEL_HELPER_VERSION,q as getDefaultWeChatHelperRuntimeRoot,D as helperRuntimePackageManifestCandidates,U as readWeChatChannelHelperRuntimePackageManifest,$ as resolveWeChatChannelHelperAsset,I as validateWeChatChannelHelperRuntimePackageManifest};
1
+ import c from"node:fs";import s from"node:path";import E from"node:crypto";const V="0.1.11",S="SHENNIAN_WECHAT_CHANNEL_HELPER_DIR",g="SHENNIAN_HELPER_RUNTIME_DIR",C="helper_runtime_required",u=["en","v"].join("");function $(n={}){const r=n.platform??process.platform;if(r!=="darwin"&&r!=="win32")return{ok:!1,reasonCode:"unsupported_platform",message:"WeChat channel helper is only available on macOS and Windows"};const e=n.baseDir?[s.resolve(n.baseDir)]:A({platform:r,env:n[u]??R(),homedir:n.homedir,includeInstalledDesktop:n.includeInstalledDesktop});let a=null;for(const t of e){const i=_({platform:r,baseDir:t,verifyIntegrity:n.verifyIntegrity});if(i.ok||(n.baseDir||c.existsSync(t))&&(a??=i,n.baseDir))return i}return a??{ok:!1,reasonCode:C,message:x(r,e)}}function _(n){const{platform:r,baseDir:e}=n,a=s.join(e,"manifest.json"),t=F(a);if(!t)return{ok:!1,reasonCode:"manifest_missing",message:`WeChat channel helper manifest is missing: ${a}`};const i=t.platforms[r],o=i?s.join(e,i.executable):"";if(!i||!o||!c.existsSync(o))return{ok:!1,reasonCode:"helper_missing",message:`WeChat channel helper executable is missing: ${o||e}`};if(N({platform:r,baseDir:e,verifyIntegrity:n.verifyIntegrity})&&i.sha256&&E.createHash("sha256").update(c.readFileSync(o)).digest("hex")!==i.sha256)return{ok:!1,reasonCode:"integrity_mismatch",message:`WeChat channel helper integrity mismatch: ${o}`};const f=H(r,o);return f.ok?{ok:!0,helperPath:o,helperDir:e,version:t.helperVersion,manifest:t,warning:i.signed&&i.notarized?void 0:"helper_not_signed_or_notarized"}:{ok:!1,reasonCode:"helper_not_executable",message:f.message}}function H(n,r){if(n!=="darwin")return{ok:!0};try{const e=c.statSync(r);return(e.mode&73)!==0?{ok:!0}:(c.chmodSync(r,e.mode|73),{ok:!0})}catch(e){return{ok:!1,message:`WeChat channel helper is not executable and could not be repaired: ${r}: ${e instanceof Error?e.message:String(e)}`}}}function A(n){const{platform:r,env:e}=n,a=r==="win32"?"windows":"macos",t=[],i=l=>{if(!l)return;const p=s.resolve(l);t.includes(p)||t.push(p)},o=e[S]?.trim();o&&i(j(o,a));const f=e[g]?.trim();if(f)for(const l of P(f,a))i(l);if(b({platform:r,platformDir:a,env:e,homedir:n.homedir,push:i}),n.includeInstalledDesktop===!1)return t;if(r==="darwin"){const l=n.homedir||e.HOME;l&&i(s.join(l,"Applications","Shennian.app","Contents","Resources","wechat-channel",a)),i(s.join("/Applications","Shennian.app","Contents","Resources","wechat-channel",a))}else if(r==="win32"){const l=n.homedir||e.USERPROFILE,p=e.LOCALAPPDATA||(l?s.join(l,"AppData","Local"):""),m=e.ProgramFiles||e.PROGRAMFILES,h=e["ProgramFiles(x86)"]||e["PROGRAMFILES(X86)"];for(const d of[p?s.join(p,"Programs","Shennian"):"",p?s.join(p,"Programs","shennian"):"",m?s.join(m,"Shennian"):"",h?s.join(h,"Shennian"):""])d&&i(s.join(d,"resources","wechat-channel",a))}return t}function b(n){const{platform:r,platformDir:e,env:a,push:t}=n;if(r==="darwin"){const i=n.homedir||a.HOME,o=k({platform:r,env:a,homedir:n.homedir});o&&(t(s.join(o,"Shennian Helper.app","Contents","Resources","wechat-channel",e)),t(s.join(o,"wechat-channel",e))),i&&t(s.join(i,"Applications","Shennian Helper.app","Contents","Resources","wechat-channel",e)),t(s.join("/Applications","Shennian Helper.app","Contents","Resources","wechat-channel",e)),t(s.join("/Library","Application Support","Shennian","Helper","wechat-channel",e))}else if(r==="win32"){const i=n.homedir||a.USERPROFILE,o=a.LOCALAPPDATA||(i?s.join(i,"AppData","Local"):""),f=a.ProgramFiles||a.PROGRAMFILES,l=a["ProgramFiles(x86)"]||a["PROGRAMFILES(X86)"];o&&(t(s.join(o,"Programs","Shennian Helper","resources","wechat-channel",e)),t(s.join(o,"Shennian","Helper","wechat-channel",e))),f&&t(s.join(f,"Shennian Helper","resources","wechat-channel",e)),l&&t(s.join(l,"Shennian Helper","resources","wechat-channel",e))}}function j(n,r){return c.existsSync(s.join(n,"manifest.json"))?n:s.join(n,r)}function P(n,r){if(c.existsSync(s.join(n,"manifest.json")))return[n];const e=s.join(n,"Shennian Helper.app","Contents","Resources","wechat-channel",r),a=s.join(n,"wechat-channel",r),t=s.join(n,r);return[e,a,t]}function k(n){const r=n[u],e=r[g]?.trim();if(e)return s.resolve(e);const a=n.homedir||(n.platform==="win32"?r.USERPROFILE:r.HOME);if(n.platform==="darwin")return a?s.join(a,"Library","Application Support","Shennian","Helper"):null;if(n.platform==="win32"){const t=r.LOCALAPPDATA||(a?s.join(a,"AppData","Local"):"");return t?s.join(t,"Shennian","Helper"):null}return null}function q(n={}){return k({platform:n.platform??process.platform,env:n.env??R(),homedir:n.homedir})}function D(n){const r=s.resolve(n);return O([s.join(r,"helper-runtime-package.json"),s.join(s.dirname(r),"..","helper-runtime-package.json")])}function U(n){const r=n.platform??process.platform;if(r!=="darwin"&&r!=="win32")return{ok:!1,reasonCode:"unsupported_platform",message:"WeChat channel helper runtime package is only available on macOS and Windows"};const e=n.manifestPath?[s.resolve(n.manifestPath)]:n.helperDir?D(n.helperDir):[],a=e.find(t=>c.existsSync(t));if(!a)return{ok:!1,reasonCode:"helper_runtime_package_manifest_missing",message:`Helper runtime package manifest is missing. Checked: ${e.join(", ")||"(none)"}`,manifestPath:e[0]};try{const t=JSON.parse(w(a)),i=I(t,r);return i.ok?{ok:!0,manifestPath:a,manifest:t}:{ok:!1,reasonCode:"helper_runtime_package_manifest_invalid",message:`${i.message}: ${a}`,manifestPath:a}}catch(t){return{ok:!1,reasonCode:"helper_runtime_package_manifest_invalid",message:`Helper runtime package manifest is invalid JSON: ${t instanceof Error?t.message:String(t)}`,manifestPath:a}}}function I(n,r){if(!n||typeof n!="object"||Array.isArray(n))return{ok:!1,message:"manifest must be an object"};const e=n;return e.schemaVersion!==1?{ok:!1,message:"schemaVersion must be 1"}:e.packageKind!=="shennian-helper-runtime"?{ok:!1,message:"packageKind must be shennian-helper-runtime"}:e.platform!==r?{ok:!1,message:`platform must be ${r}`}:e.helperVersion?!Number.isInteger(e.protocolVersion)||Number(e.protocolVersion)<=0?{ok:!1,message:"protocolVersion must be a positive integer"}:L(e.minCliVersion)?!e.sha256||typeof e.sha256.runtimeManifest!="string"?{ok:!1,message:"sha256.runtimeManifest is required"}:y(e.sha256.runtimeManifest)?e.sha256.entrypoint!==null&&typeof e.sha256.entrypoint!="string"?{ok:!1,message:"sha256.entrypoint must be a string or null"}:typeof e.sha256.entrypoint=="string"&&!y(e.sha256.entrypoint)?{ok:!1,message:"sha256.entrypoint must be a sha256 digest or null"}:!e.installTarget?.kind||!e.installTarget.defaultPath?{ok:!1,message:"installTarget.kind and installTarget.defaultPath are required"}:!e.payload?.kind||!e.payload.runtimeManifest||!e.payload.entrypoint?{ok:!1,message:"payload.kind, payload.runtimeManifest, and payload.entrypoint are required"}:r==="darwin"&&e.installTarget.kind!=="app-bundle"?{ok:!1,message:"darwin installTarget.kind must be app-bundle"}:r==="darwin"&&e.payload.kind!=="macos-helper-app"?{ok:!1,message:"darwin payload.kind must be macos-helper-app"}:r==="win32"&&e.installTarget.kind!=="directory"?{ok:!1,message:"win32 installTarget.kind must be directory"}:r==="win32"&&e.payload.kind!=="windows-helper-runtime"?{ok:!1,message:"win32 payload.kind must be windows-helper-runtime"}:!e.signature||typeof e.signature.requiredForRelease!="boolean"||typeof e.signature.signed!="boolean"||typeof e.signature.notarized!="boolean"?{ok:!1,message:"signature.requiredForRelease, signature.signed, and signature.notarized are required"}:{ok:!0}:{ok:!1,message:"sha256.runtimeManifest must be a sha256 digest"}:{ok:!1,message:"minCliVersion must be a semver string"}:{ok:!1,message:"helperVersion is required"}}function L(n){return typeof n=="string"&&/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/.test(n)}function y(n){return/^[a-f0-9]{64}$/i.test(n)}function N(n){return n.verifyIntegrity===!0?!0:n.verifyIntegrity===!1?!1:!v(n.platform,n.baseDir)}function v(n,r){const e=r.replace(/[\\/]+/g,"/");return n==="darwin"?/\/Shennian\.app\/Contents\/Resources\/wechat-channel\/macos$/i.test(e):n==="win32"?/\/resources\/wechat-channel\/windows$/i.test(e):!1}function x(n,r){const e=n==="win32"?"Windows":"macOS",a=r.length>0?` Checked helper directories: ${r.join(", ")}`:"";return`WeChat RPA requires Shennian Helper runtime on ${e}; open Shennian Desktop or use the \u4F7F\u7528\u5FAE\u4FE1 page to install Helper, then retry.${a}`}function R(){return globalThis.process?.[u]??{}}function F(n){try{const r=JSON.parse(w(n));return r.schemaVersion!==1||typeof r.helperVersion!="string"||!r.platforms?null:r}catch{return null}}function w(n){return c.readFileSync(n,"utf8").replace(/^\uFEFF/,"")}function O(n){const r=new Set,e=[];for(const a of n){const t=s.resolve(a);r.has(t)||(r.add(t),e.push(t))}return e}export{g as SHENNIAN_HELPER_RUNTIME_DIR_ENV,S as WECHAT_CHANNEL_HELPER_DIR_ENV,C as WECHAT_CHANNEL_HELPER_RUNTIME_REQUIRED_REASON,V as WECHAT_CHANNEL_HELPER_VERSION,q as getDefaultWeChatHelperRuntimeRoot,D as helperRuntimePackageManifestCandidates,U as readWeChatChannelHelperRuntimePackageManifest,$ as resolveWeChatChannelHelperAsset,I as validateWeChatChannelHelperRuntimePackageManifest};
@@ -1,3 +1,3 @@
1
- import{spawn as w}from"node:child_process";import{randomUUID as W}from"node:crypto";import f from"node:fs";import v from"node:net";import M from"node:os";import d from"node:path";import{createInterface as g}from"node:readline";import{decideWeChatChannelActivityGate as A,normalizeWeChatChannelActivitySnapshot as x}from"./human-coordination.js";import{createWeChatChannelHelperHello as S,extractWeChatChannelHelperWarmupSnapshot as $,timeoutForWeChatChannelHelperCommand as q,validateWeChatChannelHelperReady as k}from"./helper-protocol.js";const H=["en","v"].join(""),m=1500,D=1500;class de{options;child=null;socket=null;lines=null;readyState=null;warmupState=null;stderrTail="";startPromise=null;requestQueueTail=Promise.resolve();lastGuardPassAtMs=0;pending=new Map;constructor(t){this.options=t}async start(){return this.hasTransport()&&this.readyState?this.readyState:this.startPromise?this.startPromise:(this.hasTransport()&&await this.stop(),this.startPromise=this.startFresh().catch(async t=>{throw await this.stop().catch(()=>{}),t}).finally(()=>{this.startPromise=null}),this.startPromise)}async startFresh(){const t=U(this.options.helperPath,this.options.args??[]);return t.transport==="macos-socket"?this.startDarwinAppSocketFresh(t):this.startStdioFresh(t)}async startStdioFresh(t){const r=w(t.command,t.args,{cwd:this.options.cwd,stdio:["pipe","pipe","pipe"],windowsHide:!0});this.child=r,this.stderrTail="",this.lines=g({input:r.stdout}),this.lines.on("line",i=>this.handleLine(i)),r.stderr.on("data",i=>{this.captureStderr(i)}),r.stdin.on("error",i=>{this.rejectAllPending(i instanceof Error?i:new Error(String(i)))}),r.once("error",i=>{this.child===r&&(this.child=null,this.readyState=null,this.lines?.close(),this.lines=null),this.rejectAllPending(i instanceof Error?i:new Error(String(i)))}),r.once("exit",(i,n)=>{this.child===r&&(this.child=null,this.readyState=null,this.lines?.close(),this.lines=null),this.rejectAllPending(new Error(this.formatHelperExitMessage(i,n)))});const s=new Promise((i,n)=>{const o=setTimeout(()=>n(new Error("WeChat channel helper handshake timed out")),1e4),l=u=>{clearTimeout(o),this.lines?.off("line",c),r.off("exit",a),n(new Error(`WeChat channel helper failed to start: ${u.message}`))},a=(u,h)=>{clearTimeout(o),r.off("error",l),this.lines?.off("line",c),n(new Error(this.formatHelperExitMessage(u,h)))},c=u=>{let h;try{h=JSON.parse(u)}catch{clearTimeout(o),r.off("error",l),this.lines?.off("line",c),n(new Error("WeChat channel helper sent invalid handshake JSON"));return}if(!R(h))return;clearTimeout(o),r.off("error",l),r.off("exit",a),this.lines?.off("line",c);const p=k(h,this.options.expectedHelperVersion,this.options.requiredCapabilities);p.ok?(this.readyState=h,this.captureWarmupSnapshot(h),i(h)):n(new Error(`${p.errorCode}: ${p.errorSummary}`))};r.once("error",l),r.once("exit",a),this.lines?.on("line",c)});try{r.stdin.write(`${JSON.stringify(S(this.options.expectedHelperVersion,this.options.requiredCapabilities))}
2
- `)}catch(i){throw await this.stop().catch(()=>{}),i}return s}async startDarwinAppSocketFresh(t){const r=await this.connectDarwinHelperSocket(t);this.socket=r,this.stderrTail="",this.lines=g({input:r}),this.lines.on("line",i=>this.handleLine(i)),r.on("error",i=>{this.socket===r&&(this.socket=null,this.readyState=null,this.lines?.close(),this.lines=null),this.rejectAllPending(i instanceof Error?i:new Error(String(i)))}),r.once("close",()=>{this.socket===r&&(this.socket=null,this.readyState=null,this.lines?.close(),this.lines=null),this.rejectAllPending(new Error("WeChat channel helper socket closed"))});const s=new Promise((i,n)=>{const o=setTimeout(()=>n(new Error("WeChat channel helper socket handshake timed out")),1e4),l=u=>{clearTimeout(o),this.lines?.off("line",c),r.off("close",a),n(new Error(`WeChat channel helper socket failed: ${u.message}`))},a=()=>{clearTimeout(o),r.off("error",l),this.lines?.off("line",c),n(new Error("WeChat channel helper socket closed before handshake"))},c=u=>{let h;try{h=JSON.parse(u)}catch{clearTimeout(o),r.off("error",l),r.off("close",a),this.lines?.off("line",c),n(new Error("WeChat channel helper sent invalid socket handshake JSON"));return}if(!R(h))return;clearTimeout(o),r.off("error",l),r.off("close",a),this.lines?.off("line",c);const p=k(h,this.options.expectedHelperVersion,this.options.requiredCapabilities);p.ok?(this.readyState=h,this.captureWarmupSnapshot(h),i(h)):n(new Error(`${p.errorCode}: ${p.errorSummary}`))};r.once("error",l),r.once("close",a),this.lines?.on("line",c)});try{this.writeFrame(S(this.options.expectedHelperVersion,this.options.requiredCapabilities))}catch(i){throw await this.stop().catch(()=>{}),i}return s}async request(t,r,s){const i=this.requestQueueTail.catch(()=>{}).then(()=>this.sendRequest(t,r,s));return this.requestQueueTail=i.then(()=>{},()=>{}),i}async sendRequest(t,r,s){await this.guardUnsafeWindowsCommand(t,s);try{return await this.sendRawRequest(t,r,s)}finally{await this.cleanupWindowsOverlaysAfterCommand(t,s).catch(()=>{})}}async sendRawRequest(t,r,s){if((!this.hasTransport()||!this.readyState)&&await this.start(),!this.hasTransport())throw new Error("WeChat channel helper is not started");const i=W(),n=q(t),o=Date.now();this.emitRequestTrace({phase:"request",at:new Date(o).toISOString(),id:i,command:t,traceId:s,params:r,timeoutMs:n});const l=new Promise((a,c)=>{const u=setTimeout(()=>{this.pending.get(i)&&(this.pending.delete(i),this.stopAfterCommandTimeout(t).finally(()=>{c(new Error(`helper_command_timeout: ${t}`))}))},n);this.pending.set(i,{resolve:a,reject:c,timer:u})});try{this.writeFrame({id:i,command:t,params:r,traceId:s})}catch(a){const c=this.pending.get(i);throw c&&clearTimeout(c.timer),this.pending.delete(i),this.emitRequestTrace({phase:"error",at:new Date().toISOString(),id:i,command:t,traceId:s,durationMs:Date.now()-o,errorSummary:a instanceof Error?a.message:String(a)}),a}try{const a=await l;return this.emitRequestTrace({phase:"response",at:new Date().toISOString(),id:i,command:t,traceId:s,durationMs:Date.now()-o,ok:a.ok,errorCode:a.errorCode,errorSummary:a.errorSummary,latencyMs:a.latencyMs,result:a.result}),this.captureWarmupSnapshot(a),this.captureWarmupSnapshot(a.result),a}catch(a){throw this.emitRequestTrace({phase:"error",at:new Date().toISOString(),id:i,command:t,traceId:s,durationMs:Date.now()-o,errorSummary:a instanceof Error?a.message:String(a)}),a}}async guardUnsafeWindowsCommand(t,r){if(!this.options.guardUnsafeWindowsCommands||!ee(t)||te(t)&&this.lastGuardPassAtMs>0&&Date.now()-this.lastGuardPassAtMs<D)return;let s=await this.sendRawRequest("permissions.check",{},r);if(!s.ok)throw new Error(`${s.errorCode??"permissions_check_failed"}: ${s.errorSummary??t}`);let i=s.result??{};if(i.wechatRunning!==!1&&O(i)&&await this.tryRecoverFromStuckShell(r)){if(s=await this.sendRawRequest("permissions.check",{},r),!s.ok)throw new Error(`${s.errorCode??"permissions_check_failed"}: ${s.errorSummary??t}`);i=s.result??{}}const n=await this.requestFirstMissingMacPermissionPrompt(i,r);if(n==="screen-recording")throw new Error("permission_screen_recording_missing");if(n==="accessibility")throw new Error("permission_accessibility_missing");if(n==="input-monitoring")throw new Error("permission_input_monitoring_missing");if(i.automation===!1)throw new Error("permission_automation_missing");if(i.wechatRunning===!1)throw new Error("wechat_not_running");if(N(i))throw new Error("wechat_login_required");if(L(i))throw new Error("windows_visible_desktop_unavailable");if(i.dpiMappingAvailable===!1||i.displayTopologySupported===!1)throw new Error("dpi_mapping_failed");if(i.wechatWindowAvailable===!1)throw new Error("wechat_window_unavailable");if(i.wechatMainWindowResponsive===!1)throw new Error("wechat_window_unresponsive");if(!this.options.skipUserActivityGuard&&ie(t)){const o=await this.sendRawRequest("activity.snapshot",{},r);if(!o.ok)throw new Error(`${o.errorCode??"user_activity_unknown"}: ${o.errorSummary??t}`);const l=A({snapshot:x(o.result),stage:"dangerous_action"});if(!l.ok)throw new Error(`user_active:${l.reasonCode}`)}await this.cleanupWindowsOverlays("before",t,r),this.lastGuardPassAtMs=Date.now()}async tryRecoverFromStuckShell(t){try{const r=await this.sendRawRequest("wechat.healthProbe",{},t);if(!r.ok||r.result?.stuckShell!==!0)return!1;const s=await this.sendRawRequest("windows.recoverWeChat",{allowRestart:!0,onlyIfUnresponsive:!1,waitAfterStartMs:4e3},t);return s.ok===!0&&s.result?.started===!0}catch{return!1}}async requestFirstMissingMacPermissionPrompt(t,r){const s=Y(t);if(!s||process.platform!=="darwin")return s;const i=X(s);try{await this.sendRawRequest(i,{},r)}catch{}return s}async cleanupWindowsOverlaysAfterCommand(t,r){!this.options.guardUnsafeWindowsCommands||!re(t)||await this.cleanupWindowsOverlays("after",t,r)}async cleanupWindowsOverlays(t,r,s){if(this.options.cleanupWindowsOverlays===!1)return;if(!this.readyState?.capabilities.includes("overlayCleanup"))throw new Error("helper_capability_missing: overlayCleanup");const i=await this.sendRawRequest("windows.cleanupOverlays",{stage:t,command:r,includeGeneratedMediaPreviews:!0,includeToolingTerminals:!1,waitMs:t==="after"?220:120},s);if(!i.ok)throw new Error(`${i.errorCode??"overlay_cleanup_failed"}: ${i.errorSummary??r}`)}async healthCheck(t){return this.request("health.check",{},t)}getReadyState(){return this.readyState?{...this.readyState,capabilities:[...this.readyState.capabilities]}:null}getWarmupState(){return this.warmupState?{warmState:this.warmupState.warmState,metrics:this.warmupState.metrics?{...this.warmupState.metrics}:void 0}:null}async stop(){const t=this.child,r=this.socket;this.child=null,this.socket=null,this.readyState=null,this.lines?.close(),this.lines=null,this.rejectAllPending(new Error("WeChat channel helper stopped")),r&&!r.destroyed&&r.destroy(),t&&await z(t)}hasTransport(){return!!this.child||!!this.socket}writeFrame(t){const r=`${JSON.stringify(t)}
3
- `;if(this.socket){this.socket.write(r);return}if(this.child){this.child.stdin.write(r);return}throw new Error("WeChat channel helper is not started")}handleLine(t){let r;try{r=JSON.parse(t)}catch{return}if(!Z(r))return;const s=this.pending.get(r.id);s&&(clearTimeout(s.timer),this.pending.delete(r.id),s.resolve(r))}captureWarmupSnapshot(t){const r=$(t);r&&(this.warmupState=r)}captureStderr(t){const r=Buffer.isBuffer(t)?t.toString("utf8"):String(t??"");r&&(this.stderrTail=(this.stderrTail+r).slice(-2e3))}formatHelperExitMessage(t,r){const s=[t===null?null:`code=${t}`,r?`signal=${r}`:null,this.stderrTail.trim()?`stderr=${this.stderrTail.trim()}`:null].filter(Boolean);return s.length?`WeChat channel helper process exited (${s.join(", ")})`:"WeChat channel helper process exited"}rejectAllPending(t){for(const[r,s]of this.pending)clearTimeout(s.timer),s.reject(t),this.pending.delete(r)}emitRequestTrace(t){try{this.options.requestLogger?.(t)}catch{}}async stopAfterCommandTimeout(t){const r=this.child?.pid;if(await this.stop().catch(()=>{}),process.platform==="win32"){const s=typeof r=="number"&&r>0?[r]:[];await F(s).catch(()=>{})}this.emitRequestTrace({phase:"error",at:new Date().toISOString(),id:`timeout-cleanup:${t}`,command:t,errorSummary:"helper process stopped after command timeout"})}async connectDarwinHelperSocket(t){try{return await _(t.runtimeFile)}catch{f.rmSync(t.runtimeFile,{force:!0})}await b(t,{},this.options.openHelperAppForTest);const r=await T(t.runtimeFile,this.options.macosRuntimeInitialReadyTimeoutMs??5e3);if(r.ok)return r.socket;f.rmSync(t.runtimeFile,{force:!0}),await b(t,{forceNewInstance:!0},this.options.openHelperAppForTest);const s=await T(t.runtimeFile,this.options.macosRuntimeRelaunchReadyTimeoutMs??1e4);if(s.ok)return s.socket;throw new Error(`WeChat channel Helper.app socket did not become ready: ${s.error.message}`)}}async function F(e=[]){if(process.platform!=="win32")return;const t=e.filter(r=>Number.isInteger(r)&&r>0);if(t.length!==0)for(const r of t)await E(["taskkill","/PID",String(r),"/T","/F"],[0,128])}function O(e){return e.wechatWindowAvailable===!1&&(e.captureCandidateCount??0)===0&&(e.restoreCandidateCount??0)===0&&(e.hiddenRestoreCandidateCount??0)===0}function N(e){return e.wechatWindowAvailable!==!1?!1:![...e.captureCandidates??[],...e.restoreCandidates??[],...e.hiddenRestoreCandidates??[]].some(I)}function L(e){return e.windowsVisibleDesktopAvailable===!1||e.rdpVisibleDesktopAvailable===!1||e.desktopSessionVisible===!1||e.screenLocked===!0||e.rdpDisconnected===!0}function G(e){const t=String(e.appName||"").toLowerCase();return t==="wechat"||t==="weixin"}function I(e){return!(!G(e)||e.visible===!1||e.minimized===!0)}function U(e,t,r=process.platform){if(r==="darwin"){const s=V(e);if(s&&t.length===0){const i=J();return{transport:"macos-socket",command:"/usr/bin/open",args:["-g",s,"--args","--socket-runtime",i],appPath:s,runtimeDir:i,runtimeFile:d.join(i,"runtime.json")}}}return{transport:"legacy-stdio",command:e,args:t}}function V(e){const t=d.resolve(e),r=`${d.sep}Contents${d.sep}MacOS${d.sep}`,s=t.lastIndexOf(r);if(s<0)return null;const i=t.slice(0,s);return i.endsWith(".app")?i:null}function J(){const e=j().SHENNIAN_HELPER_RUNTIME_DIR?.trim();return e?d.resolve(e):d.join(M.homedir(),"Library","Application Support","Shennian","Helper")}async function z(e){if(C(e))return;if(process.platform==="win32"&&e.pid){e.kill("SIGTERM"),await E(["taskkill","/PID",String(e.pid),"/T","/F"],[0,128]).catch(()=>{}),await y(e,m);return}e.kill("SIGTERM"),!await y(e,m)&&e.pid&&P(e.pid)&&(e.kill("SIGKILL"),await y(e,m))}function C(e){return e.exitCode!==null||e.signalCode!==null}function y(e,t){return C(e)?Promise.resolve(!0):new Promise(r=>{const s=setTimeout(()=>{e.off("exit",i),r(!1)},t),i=()=>{clearTimeout(s),r(!0)};e.once("exit",i)})}function E(e,t){return new Promise((r,s)=>{const[i,...n]=e,o=w(i,n,{stdio:"ignore",windowsHide:!0});o.once("error",s),o.once("exit",l=>{l!==null&&t.includes(l)?r():s(new Error(`${i} exited ${l}`))})})}function j(){return globalThis.process?.[H]??{}}function B(e){let t;try{t=JSON.parse(f.readFileSync(e,"utf8"))}catch(i){throw new Error(`runtime_file_unreadable: ${i instanceof Error?i.message:String(i)}`)}if(!t||typeof t!="object")throw new Error("runtime_file_invalid");const r=t.socketPath;if(typeof r!="string"||!r.trim())throw new Error("runtime_socket_missing");const s=t.pid;return{socketPath:r,...typeof s=="number"&&Number.isInteger(s)&&s>0?{pid:s}:{}}}async function _(e){const{socketPath:t,pid:r}=B(e);if(r&&!P(r))throw new Error(`runtime_process_stale: ${r}`);return new Promise((s,i)=>{const n=v.createConnection(t),o=setTimeout(()=>{n.destroy(),i(new Error(`runtime_socket_connect_timeout: ${t}`))},1e3);n.once("connect",()=>{clearTimeout(o),n.off("error",l),s(n)});const l=a=>{clearTimeout(o),n.destroy(),i(a)};n.once("error",l)})}async function T(e,t){const r=Date.now()+t;let s=null;for(;Date.now()<r;)try{return{ok:!0,socket:await _(e)}}catch(i){s=i instanceof Error?i:new Error(String(i));const n=r-Date.now();n>0&&await K(Math.min(150,n))}return{ok:!1,error:s??new Error("runtime file missing")}}async function b(e,t={},r=Q){f.mkdirSync(e.runtimeDir,{recursive:!0,mode:448});const s=t.forceNewInstance?["-n",...e.args]:e.args;try{await r(e.command,s)}catch(i){if(!(i instanceof Error?i.message:String(i)).includes("-1712"))throw i;await r(e.command,["-n",...e.args])}}function P(e){try{return process.kill(e,0),!0}catch{return!1}}async function Q(e,t){await new Promise((r,s)=>{const i=w(e,t,{stdio:["ignore","ignore","pipe"],windowsHide:!0});let n="";i.stderr.on("data",o=>{n=(n+String(o)).slice(-2e3)}),i.once("error",s),i.once("exit",(o,l)=>{o===0?r():s(new Error(`open Helper.app failed (${l?`signal=${l}`:`code=${o}`}${n.trim()?`, stderr=${n.trim()}`:""})`))})})}function K(e){return new Promise(t=>setTimeout(t,e))}function Y(e){return e.screenRecording===!1?"screen-recording":e.accessibility===!1?"accessibility":e.inputMonitoring===!1?"input-monitoring":null}function X(e){return e==="screen-recording"?"permissions.requestScreenRecording":e==="accessibility"?"permissions.requestAccessibility":"permissions.requestInputMonitoring"}function R(e){if(!e||typeof e!="object")return!1;const t=e;return t.type==="ready"&&typeof t.helperVersion=="string"&&typeof t.protocolVersion=="number"&&Array.isArray(t.capabilities)&&typeof t.pid=="number"}function Z(e){if(!e||typeof e!="object")return!1;const t=e;return typeof t.id=="string"&&typeof t.ok=="boolean"&&typeof t.latencyMs=="number"}function ee(e){return e==="windows.focus"||e==="windows.capture"||e==="screen.capture"||e==="ocr.recognize"||e==="image.cropHash"||e==="wechat.searchConversation"||e==="wechat.focusMessageInput"||e==="wechat.pasteAndSubmit"||e==="keyboard.primeTextPaste"||e.startsWith("mouse.")||e.startsWith("keyboard.")||e.startsWith("clipboard.")||e==="menu.pickItem"||e==="savePanel.saveToPath"}function te(e){return e==="windows.capture"||e==="screen.capture"||e==="ocr.recognize"||e==="image.cropHash"}function re(e){return e.startsWith("mouse.")||e.startsWith("keyboard.")||e.startsWith("clipboard.")||e==="menu.pickItem"||e==="savePanel.saveToPath"}function ie(e){return e==="windows.focus"||e==="wechat.searchConversation"||e==="wechat.focusMessageInput"||e==="wechat.pasteAndSubmit"||e==="keyboard.primeTextPaste"||e.startsWith("mouse.")||e.startsWith("keyboard.")||e.startsWith("clipboard.set")||e==="clipboard.restore"||e==="menu.pickItem"||e==="savePanel.saveToPath"}export{de as WeChatChannelHelperClient,U as helperLaunchSpec,F as stopWindowsWeChatChannelHelperProcesses};
1
+ import{spawn as w}from"node:child_process";import{randomUUID as x}from"node:crypto";import f from"node:fs";import D from"node:net";import H from"node:os";import d from"node:path";import{createInterface as g}from"node:readline";import{decideWeChatChannelActivityGate as S,normalizeWeChatChannelActivitySnapshot as k}from"./human-coordination.js";import{createWeChatChannelHelperHello as E,extractWeChatChannelHelperWarmupSnapshot as q,timeoutForWeChatChannelHelperCommand as O,validateWeChatChannelHelperReady as C}from"./helper-protocol.js";const F=["en","v"].join(""),m=1500,N=1500,_=8e3,G="SHENNIAN_WECHAT_DANGEROUS_GUARD_THROTTLE_MS";function L(){const t=process.env[G];if(t===void 0||t==="")return _;const e=Number.parseInt(t,10);return!Number.isFinite(e)||e<0?_:e}class me{options;child=null;socket=null;lines=null;readyState=null;warmupState=null;stderrTail="";startPromise=null;requestQueueTail=Promise.resolve();lastGuardPassAtMs=0;pending=new Map;constructor(e){this.options=e}async start(){return this.hasTransport()&&this.readyState?this.readyState:this.startPromise?this.startPromise:(this.hasTransport()&&await this.stop(),this.startPromise=this.startFresh().catch(async e=>{throw await this.stop().catch(()=>{}),e}).finally(()=>{this.startPromise=null}),this.startPromise)}async startFresh(){const e=B(this.options.helperPath,this.options.args??[]);return e.transport==="macos-socket"?this.startDarwinAppSocketFresh(e):this.startStdioFresh(e)}async startStdioFresh(e){const r=w(e.command,e.args,{cwd:this.options.cwd,stdio:["pipe","pipe","pipe"],windowsHide:!0});this.child=r,this.stderrTail="",this.lines=g({input:r.stdout}),this.lines.on("line",i=>this.handleLine(i)),r.stderr.on("data",i=>{this.captureStderr(i)}),r.stdin.on("error",i=>{this.rejectAllPending(i instanceof Error?i:new Error(String(i)))}),r.once("error",i=>{this.child===r&&(this.child=null,this.readyState=null,this.lines?.close(),this.lines=null),this.rejectAllPending(i instanceof Error?i:new Error(String(i)))}),r.once("exit",(i,n)=>{this.child===r&&(this.child=null,this.readyState=null,this.lines?.close(),this.lines=null),this.rejectAllPending(new Error(this.formatHelperExitMessage(i,n)))});const s=new Promise((i,n)=>{const a=setTimeout(()=>n(new Error("WeChat channel helper handshake timed out")),1e4),l=h=>{clearTimeout(a),this.lines?.off("line",c),r.off("exit",o),n(new Error(`WeChat channel helper failed to start: ${h.message}`))},o=(h,u)=>{clearTimeout(a),r.off("error",l),this.lines?.off("line",c),n(new Error(this.formatHelperExitMessage(h,u)))},c=h=>{let u;try{u=JSON.parse(h)}catch{clearTimeout(a),r.off("error",l),this.lines?.off("line",c),n(new Error("WeChat channel helper sent invalid handshake JSON"));return}if(!A(u))return;clearTimeout(a),r.off("error",l),r.off("exit",o),this.lines?.off("line",c);const p=C(u,this.options.expectedHelperVersion,this.options.requiredCapabilities);p.ok?(this.readyState=u,this.captureWarmupSnapshot(u),i(u)):n(new Error(`${p.errorCode}: ${p.errorSummary}`))};r.once("error",l),r.once("exit",o),this.lines?.on("line",c)});try{r.stdin.write(`${JSON.stringify(E(this.options.expectedHelperVersion,this.options.requiredCapabilities))}
2
+ `)}catch(i){throw await this.stop().catch(()=>{}),i}return s}async startDarwinAppSocketFresh(e){const r=await this.connectDarwinHelperSocket(e);this.socket=r,this.stderrTail="",this.lines=g({input:r}),this.lines.on("line",i=>this.handleLine(i)),r.on("error",i=>{this.socket===r&&(this.socket=null,this.readyState=null,this.lines?.close(),this.lines=null),this.rejectAllPending(i instanceof Error?i:new Error(String(i)))}),r.once("close",()=>{this.socket===r&&(this.socket=null,this.readyState=null,this.lines?.close(),this.lines=null),this.rejectAllPending(new Error("WeChat channel helper socket closed"))});const s=new Promise((i,n)=>{const a=setTimeout(()=>n(new Error("WeChat channel helper socket handshake timed out")),1e4),l=h=>{clearTimeout(a),this.lines?.off("line",c),r.off("close",o),n(new Error(`WeChat channel helper socket failed: ${h.message}`))},o=()=>{clearTimeout(a),r.off("error",l),this.lines?.off("line",c),n(new Error("WeChat channel helper socket closed before handshake"))},c=h=>{let u;try{u=JSON.parse(h)}catch{clearTimeout(a),r.off("error",l),r.off("close",o),this.lines?.off("line",c),n(new Error("WeChat channel helper sent invalid socket handshake JSON"));return}if(!A(u))return;clearTimeout(a),r.off("error",l),r.off("close",o),this.lines?.off("line",c);const p=C(u,this.options.expectedHelperVersion,this.options.requiredCapabilities);p.ok?(this.readyState=u,this.captureWarmupSnapshot(u),i(u)):n(new Error(`${p.errorCode}: ${p.errorSummary}`))};r.once("error",l),r.once("close",o),this.lines?.on("line",c)});try{this.writeFrame(E(this.options.expectedHelperVersion,this.options.requiredCapabilities))}catch(i){throw await this.stop().catch(()=>{}),i}return s}async request(e,r,s){const i=this.requestQueueTail.catch(()=>{}).then(()=>this.sendRequest(e,r,s));return this.requestQueueTail=i.then(()=>{},()=>{}),i}async sendRequest(e,r,s){await this.guardUnsafeWindowsCommand(e,s);try{return await this.sendRawRequest(e,r,s)}finally{await this.cleanupWindowsOverlaysAfterCommand(e,s).catch(()=>{})}}async sendRawRequest(e,r,s){if((!this.hasTransport()||!this.readyState)&&await this.start(),!this.hasTransport())throw new Error("WeChat channel helper is not started");const i=x(),n=O(e),a=Date.now();this.emitRequestTrace({phase:"request",at:new Date(a).toISOString(),id:i,command:e,traceId:s,params:r,timeoutMs:n});const l=new Promise((o,c)=>{const h=setTimeout(()=>{this.pending.get(i)&&(this.pending.delete(i),this.stopAfterCommandTimeout(e).finally(()=>{c(new Error(`helper_command_timeout: ${e}`))}))},n);this.pending.set(i,{resolve:o,reject:c,timer:h})});try{this.writeFrame({id:i,command:e,params:r,traceId:s})}catch(o){const c=this.pending.get(i);throw c&&clearTimeout(c.timer),this.pending.delete(i),this.emitRequestTrace({phase:"error",at:new Date().toISOString(),id:i,command:e,traceId:s,durationMs:Date.now()-a,errorSummary:o instanceof Error?o.message:String(o)}),o}try{const o=await l;return this.emitRequestTrace({phase:"response",at:new Date().toISOString(),id:i,command:e,traceId:s,durationMs:Date.now()-a,ok:o.ok,errorCode:o.errorCode,errorSummary:o.errorSummary,latencyMs:o.latencyMs,result:o.result}),this.captureWarmupSnapshot(o),this.captureWarmupSnapshot(o.result),o}catch(o){throw this.emitRequestTrace({phase:"error",at:new Date().toISOString(),id:i,command:e,traceId:s,durationMs:Date.now()-a,errorSummary:o instanceof Error?o.message:String(o)}),o}}async guardUnsafeWindowsCommand(e,r){if(!this.options.guardUnsafeWindowsCommands||!ne(e))return;const s=L(),i=M(e)?Math.max(N,s):s;if(i>0&&this.lastGuardPassAtMs>0&&Date.now()-this.lastGuardPassAtMs<i){if(!M(e)){if(!this.options.skipUserActivityGuard&&$(e)){const o=await this.sendRawRequest("activity.snapshot",{},r);if(!o.ok)throw new Error(`${o.errorCode??"user_activity_unknown"}: ${o.errorSummary??e}`);const c=S({snapshot:k(o.result),stage:"dangerous_action"});if(!c.ok)throw new Error(`user_active:${c.reasonCode}`)}await this.cleanupWindowsOverlays("before",e,r)}return}let n=await this.sendRawRequest("permissions.check",{},r);if(!n.ok)throw new Error(`${n.errorCode??"permissions_check_failed"}: ${n.errorSummary??e}`);let a=n.result??{};if(a.wechatRunning!==!1&&I(a)&&await this.tryRecoverFromStuckShell(r)){if(n=await this.sendRawRequest("permissions.check",{},r),!n.ok)throw new Error(`${n.errorCode??"permissions_check_failed"}: ${n.errorSummary??e}`);a=n.result??{}}const l=await this.requestFirstMissingMacPermissionPrompt(a,r);if(l==="screen-recording")throw new Error("permission_screen_recording_missing");if(l==="accessibility")throw new Error("permission_accessibility_missing");if(l==="input-monitoring")throw new Error("permission_input_monitoring_missing");if(a.automation===!1)throw new Error("permission_automation_missing");if(a.wechatRunning===!1)throw new Error("wechat_not_running");if(V(a))throw new Error("wechat_login_required");if(J(a))throw new Error("windows_visible_desktop_unavailable");if(a.dpiMappingAvailable===!1||a.displayTopologySupported===!1)throw new Error("dpi_mapping_failed");if(a.wechatWindowAvailable===!1)throw new Error("wechat_window_unavailable");if(a.wechatMainWindowResponsive===!1)throw new Error("wechat_window_unresponsive");if(!this.options.skipUserActivityGuard&&$(e)){const o=await this.sendRawRequest("activity.snapshot",{},r);if(!o.ok)throw new Error(`${o.errorCode??"user_activity_unknown"}: ${o.errorSummary??e}`);const c=S({snapshot:k(o.result),stage:"dangerous_action"});if(!c.ok)throw new Error(`user_active:${c.reasonCode}`)}await this.cleanupWindowsOverlays("before",e,r),this.lastGuardPassAtMs=Date.now()}async tryRecoverFromStuckShell(e){try{const r=await this.sendRawRequest("wechat.healthProbe",{},e);if(!r.ok||r.result?.stuckShell!==!0)return!1;const s=await this.sendRawRequest("windows.recoverWeChat",{allowRestart:!0,onlyIfUnresponsive:!1,waitAfterStartMs:4e3},e);return s.ok===!0&&s.result?.started===!0}catch{return!1}}async requestFirstMissingMacPermissionPrompt(e,r){const s=re(e);if(!s||process.platform!=="darwin")return s;const i=ie(s);try{await this.sendRawRequest(i,{},r)}catch{}return s}async cleanupWindowsOverlaysAfterCommand(e,r){!this.options.guardUnsafeWindowsCommands||!oe(e)||await this.cleanupWindowsOverlays("after",e,r)}async cleanupWindowsOverlays(e,r,s){if(this.options.cleanupWindowsOverlays===!1)return;if(!this.readyState?.capabilities.includes("overlayCleanup"))throw new Error("helper_capability_missing: overlayCleanup");const i=await this.sendRawRequest("windows.cleanupOverlays",{stage:e,command:r,includeGeneratedMediaPreviews:!0,includeToolingTerminals:!1,waitMs:e==="after"?220:120},s);if(!i.ok)throw new Error(`${i.errorCode??"overlay_cleanup_failed"}: ${i.errorSummary??r}`)}async healthCheck(e){return this.request("health.check",{},e)}getReadyState(){return this.readyState?{...this.readyState,capabilities:[...this.readyState.capabilities]}:null}getWarmupState(){return this.warmupState?{warmState:this.warmupState.warmState,metrics:this.warmupState.metrics?{...this.warmupState.metrics}:void 0}:null}async stop(){const e=this.child,r=this.socket;this.child=null,this.socket=null,this.readyState=null,this.lines?.close(),this.lines=null,this.rejectAllPending(new Error("WeChat channel helper stopped")),r&&!r.destroyed&&r.destroy(),e&&await Y(e)}hasTransport(){return!!this.child||!!this.socket}writeFrame(e){const r=`${JSON.stringify(e)}
3
+ `;if(this.socket){this.socket.write(r);return}if(this.child){this.child.stdin.write(r);return}throw new Error("WeChat channel helper is not started")}handleLine(e){let r;try{r=JSON.parse(e)}catch{return}if(!se(r))return;const s=this.pending.get(r.id);s&&(clearTimeout(s.timer),this.pending.delete(r.id),s.resolve(r))}captureWarmupSnapshot(e){const r=q(e);r&&(this.warmupState=r)}captureStderr(e){const r=Buffer.isBuffer(e)?e.toString("utf8"):String(e??"");r&&(this.stderrTail=(this.stderrTail+r).slice(-2e3))}formatHelperExitMessage(e,r){const s=[e===null?null:`code=${e}`,r?`signal=${r}`:null,this.stderrTail.trim()?`stderr=${this.stderrTail.trim()}`:null].filter(Boolean);return s.length?`WeChat channel helper process exited (${s.join(", ")})`:"WeChat channel helper process exited"}rejectAllPending(e){for(const[r,s]of this.pending)clearTimeout(s.timer),s.reject(e),this.pending.delete(r)}emitRequestTrace(e){try{this.options.requestLogger?.(e)}catch{}}async stopAfterCommandTimeout(e){const r=this.child?.pid;if(await this.stop().catch(()=>{}),process.platform==="win32"){const s=typeof r=="number"&&r>0?[r]:[];await U(s).catch(()=>{})}this.emitRequestTrace({phase:"error",at:new Date().toISOString(),id:`timeout-cleanup:${e}`,command:e,errorSummary:"helper process stopped after command timeout"})}async connectDarwinHelperSocket(e){try{return await b(e.runtimeFile)}catch{f.rmSync(e.runtimeFile,{force:!0})}await W(e,{},this.options.openHelperAppForTest);const r=await P(e.runtimeFile,this.options.macosRuntimeInitialReadyTimeoutMs??5e3);if(r.ok)return r.socket;f.rmSync(e.runtimeFile,{force:!0}),await W(e,{forceNewInstance:!0},this.options.openHelperAppForTest);const s=await P(e.runtimeFile,this.options.macosRuntimeRelaunchReadyTimeoutMs??1e4);if(s.ok)return s.socket;throw new Error(`WeChat channel Helper.app socket did not become ready: ${s.error.message}`)}}async function U(t=[]){if(process.platform!=="win32")return;const e=t.filter(r=>Number.isInteger(r)&&r>0);if(e.length!==0)for(const r of e)await R(["taskkill","/PID",String(r),"/T","/F"],[0,128])}function I(t){return t.wechatWindowAvailable===!1&&(t.captureCandidateCount??0)===0&&(t.restoreCandidateCount??0)===0&&(t.hiddenRestoreCandidateCount??0)===0}function V(t){return t.wechatWindowAvailable!==!1?!1:![...t.captureCandidates??[],...t.restoreCandidates??[],...t.hiddenRestoreCandidates??[]].some(j)}function J(t){return t.windowsVisibleDesktopAvailable===!1||t.rdpVisibleDesktopAvailable===!1||t.desktopSessionVisible===!1||t.screenLocked===!0||t.rdpDisconnected===!0}function z(t){const e=String(t.appName||"").toLowerCase();return e==="wechat"||e==="weixin"}function j(t){return!(!z(t)||t.visible===!1||t.minimized===!0)}function B(t,e,r=process.platform){if(r==="darwin"){const s=Q(t);if(s&&e.length===0){const i=K();return{transport:"macos-socket",command:"/usr/bin/open",args:["-g",s,"--args","--socket-runtime",i],appPath:s,runtimeDir:i,runtimeFile:d.join(i,"runtime.json")}}}return{transport:"legacy-stdio",command:t,args:e}}function Q(t){const e=d.resolve(t),r=`${d.sep}Contents${d.sep}MacOS${d.sep}`,s=e.lastIndexOf(r);if(s<0)return null;const i=e.slice(0,s);return i.endsWith(".app")?i:null}function K(){const t=X().SHENNIAN_HELPER_RUNTIME_DIR?.trim();return t?d.resolve(t):d.join(H.homedir(),"Library","Application Support","Shennian","Helper")}async function Y(t){if(T(t))return;if(process.platform==="win32"&&t.pid){t.kill("SIGTERM"),await R(["taskkill","/PID",String(t.pid),"/T","/F"],[0,128]).catch(()=>{}),await y(t,m);return}t.kill("SIGTERM"),!await y(t,m)&&t.pid&&v(t.pid)&&(t.kill("SIGKILL"),await y(t,m))}function T(t){return t.exitCode!==null||t.signalCode!==null}function y(t,e){return T(t)?Promise.resolve(!0):new Promise(r=>{const s=setTimeout(()=>{t.off("exit",i),r(!1)},e),i=()=>{clearTimeout(s),r(!0)};t.once("exit",i)})}function R(t,e){return new Promise((r,s)=>{const[i,...n]=t,a=w(i,n,{stdio:"ignore",windowsHide:!0});a.once("error",s),a.once("exit",l=>{l!==null&&e.includes(l)?r():s(new Error(`${i} exited ${l}`))})})}function X(){return globalThis.process?.[F]??{}}function Z(t){let e;try{e=JSON.parse(f.readFileSync(t,"utf8"))}catch(i){throw new Error(`runtime_file_unreadable: ${i instanceof Error?i.message:String(i)}`)}if(!e||typeof e!="object")throw new Error("runtime_file_invalid");const r=e.socketPath;if(typeof r!="string"||!r.trim())throw new Error("runtime_socket_missing");const s=e.pid;return{socketPath:r,...typeof s=="number"&&Number.isInteger(s)&&s>0?{pid:s}:{}}}async function b(t){const{socketPath:e,pid:r}=Z(t);if(r&&!v(r))throw new Error(`runtime_process_stale: ${r}`);return new Promise((s,i)=>{const n=D.createConnection(e),a=setTimeout(()=>{n.destroy(),i(new Error(`runtime_socket_connect_timeout: ${e}`))},1e3);n.once("connect",()=>{clearTimeout(a),n.off("error",l),s(n)});const l=o=>{clearTimeout(a),n.destroy(),i(o)};n.once("error",l)})}async function P(t,e){const r=Date.now()+e;let s=null;for(;Date.now()<r;)try{return{ok:!0,socket:await b(t)}}catch(i){s=i instanceof Error?i:new Error(String(i));const n=r-Date.now();n>0&&await te(Math.min(150,n))}return{ok:!1,error:s??new Error("runtime file missing")}}async function W(t,e={},r=ee){f.mkdirSync(t.runtimeDir,{recursive:!0,mode:448});const s=e.forceNewInstance?["-n",...t.args]:t.args;try{await r(t.command,s)}catch(i){if(!(i instanceof Error?i.message:String(i)).includes("-1712"))throw i;await r(t.command,["-n",...t.args])}}function v(t){try{return process.kill(t,0),!0}catch{return!1}}async function ee(t,e){await new Promise((r,s)=>{const i=w(t,e,{stdio:["ignore","ignore","pipe"],windowsHide:!0});let n="";i.stderr.on("data",a=>{n=(n+String(a)).slice(-2e3)}),i.once("error",s),i.once("exit",(a,l)=>{a===0?r():s(new Error(`open Helper.app failed (${l?`signal=${l}`:`code=${a}`}${n.trim()?`, stderr=${n.trim()}`:""})`))})})}function te(t){return new Promise(e=>setTimeout(e,t))}function re(t){return t.screenRecording===!1?"screen-recording":t.accessibility===!1?"accessibility":t.inputMonitoring===!1?"input-monitoring":null}function ie(t){return t==="screen-recording"?"permissions.requestScreenRecording":t==="accessibility"?"permissions.requestAccessibility":"permissions.requestInputMonitoring"}function A(t){if(!t||typeof t!="object")return!1;const e=t;return e.type==="ready"&&typeof e.helperVersion=="string"&&typeof e.protocolVersion=="number"&&Array.isArray(e.capabilities)&&typeof e.pid=="number"}function se(t){if(!t||typeof t!="object")return!1;const e=t;return typeof e.id=="string"&&typeof e.ok=="boolean"&&typeof e.latencyMs=="number"}function ne(t){return t==="windows.focus"||t==="windows.capture"||t==="screen.capture"||t==="ocr.recognize"||t==="image.cropHash"||t==="wechat.searchConversation"||t==="wechat.focusMessageInput"||t==="wechat.pasteAndSubmit"||t==="keyboard.primeTextPaste"||t.startsWith("mouse.")||t.startsWith("keyboard.")||t.startsWith("clipboard.")||t==="menu.pickItem"||t==="savePanel.saveToPath"}function M(t){return t==="windows.capture"||t==="screen.capture"||t==="ocr.recognize"||t==="image.cropHash"}function oe(t){return t.startsWith("mouse.")||t.startsWith("keyboard.")||t.startsWith("clipboard.")||t==="menu.pickItem"||t==="savePanel.saveToPath"}function $(t){return t==="windows.focus"||t==="wechat.searchConversation"||t==="wechat.focusMessageInput"||t==="wechat.pasteAndSubmit"||t==="keyboard.primeTextPaste"||t.startsWith("mouse.")||t.startsWith("keyboard.")||t.startsWith("clipboard.set")||t==="clipboard.restore"||t==="menu.pickItem"||t==="savePanel.saveToPath"}export{me as WeChatChannelHelperClient,B as helperLaunchSpec,U as stopWindowsWeChatChannelHelperProcesses};
@@ -1 +1,2 @@
1
- import b from"node:fs";import S from"node:path";import{WECHAT_CHANNEL_RECENT_MESSAGE_WINDOW as p}from"./runtime.js";import{filterNewWeChatMessagesByAnchor as A,isLikelySameWeChatMessage as g}from"./anchor.js";import{normalizeWeChatObservedWindowForLedger as C}from"./message-key.js";function F(e,n){try{const t=JSON.parse(b.readFileSync(e,"utf8"));if(t?.version===1&&t.runtimeId===n&&t.bindings&&typeof t.bindings=="object")return t}catch{}return{version:1,runtimeId:n,bindings:{}}}function _(e,n){b.mkdirSync(S.dirname(e),{recursive:!0}),b.writeFileSync(e,JSON.stringify(n,null,2))}function j(e){const n=e.ledger.bindings[e.bindingId],t=C(e.observedMessages).map(K),r=E(n?.recent??[],t).slice(-p),i=e.baselineOnly||!n?.baselineEstablished||!!n?.disabledSince?[]:R(n?.recent??[],r),s={bindingId:e.bindingId,baselineEstablished:!0,disabledSince:null,revision:(n?.revision??0)+(i.length>0?1:0),recent:r,pendingSendKeys:n?.pendingSendKeys??[],attachmentStates:I(r,n?.attachmentStates),vectorReferences:w(r,n?.vectorReferences,e.vectorReferences),cooldown:n?.cooldown};return e.ledger.bindings[e.bindingId]=s,{binding:s,newMessages:i}}function K(e){return Array.isArray(e.visualBlocks)?{...e,visualBlocks:e.visualBlocks.map(n=>{const{vectorBase64:t,...r}=n;return r})}:e}function z(e){const n=e.ledger.bindings[e.bindingId],t={bindingId:e.bindingId,baselineEstablished:!1,disabledSince:(e.disabledAt??new Date).toISOString(),revision:n?.revision??0,recent:n?.recent??[],pendingSendKeys:n?.pendingSendKeys??[],attachmentStates:n?.attachmentStates,vectorReferences:n?.vectorReferences,cooldown:n?.cooldown};return e.ledger.bindings[e.bindingId]=t,t}function w(e,n,t){const r=new Map;for(const i of t??[]){const s=o(i.stableMessageKey);if(!s)continue;const l=r.get(s)??[];l.push(i),r.set(s,l)}const a={};for(const i of e){const s=r.get(i.stableMessageKey)??B(i)??n?.[i.stableMessageKey];s?.length&&(a[i.stableMessageKey]=s)}return Object.keys(a).length?a:void 0}function B(e){const t=(Array.isArray(e.visualBlocks)?e.visualBlocks:[]).flatMap(r=>{const a=o(r.vectorStoreKey),i=o(r.blockId),s=o(r.blockKind)||"visual",l=o(r.modelVersion)||o(r.model)||"server-visual-embedding",f=Number(r.dims);return!a||!i||!Number.isFinite(f)||f<1?[]:[{stableMessageKey:e.stableMessageKey,blockId:i,blockKind:s,vectorStoreKey:a,model:l,...r.modelVersion!==void 0?{modelVersion:r.modelVersion}:{},dims:Math.round(f),...o(r.signature)?{signature:o(r.signature)}:{},...r.bbox!==void 0?{bbox:r.bbox}:{},observedAt:e.observedAt||new Date(0).toISOString()}]});return t.length?t:void 0}function R(e,n){const t=A({previous:e,current:n}),r=new Set(t.map(i=>i.stableMessageKey));return n.filter(i=>{if(r.has(i.stableMessageKey))return!0;const s=e.find(l=>g(l,i));return s?W(s,i):!1}).filter(i=>!(i.senderRole==="self"||i.isBaseline===!0||h(i.deliveryStatus)||y(i)&&!c(i)))}function I(e,n){const t={};for(const r of e){const a=N(r),i=n?.[r.stableMessageKey];if(!a&&i){t[r.stableMessageKey]=i;continue}a&&(t[r.stableMessageKey]=P(i,a))}return Object.keys(t).length?t:void 0}function N(e){const n=u(e.mediaMetadata);if(!n.length&&!y(e)&&!h(e.deliveryStatus))return null;const t=m(e.mediaMetadata)??n[0]??{},r=M(e.deliveryStatus),a=o(t.availability)||o(e.mediaMetadata?.availability)||(r==="needs-review"?"needs-review":"metadata-only");return{stableMessageKey:e.stableMessageKey,kind:e.kind||o(t.type)||"unknown",availability:a,...r?{deliveryStatus:r}:{},...o(t.localPath)?{localPath:o(t.localPath)}:{},...o(t.url)?{url:o(t.url)}:{},...o(t.name)||o(t.fileName)?{name:o(t.name)||o(t.fileName)}:{},...o(t.providerError)?{providerError:o(t.providerError)}:{},...o(t.reasonCode)||o(e.mediaMetadata?.edgeResolveReasonCode)?{reasonCode:o(t.reasonCode)||o(e.mediaMetadata?.edgeResolveReasonCode)}:{}}}function P(e,n){return!e||v(n)?n:v(e)?e:n}function v(e){return e.availability==="edge-local"&&!!e.localPath||e.availability==="server-url"&&!!e.url}function h(e){const n=M(e);return n==="suppressed"||n==="pending"||n==="needs-review"||n==="manual-review"}function E(e,n){return n.map(t=>{if(c(t))return t;const r=e.find(a=>g(a,t)&&c(a));return r?{...t,mediaMetadata:O(t.mediaMetadata,r.mediaMetadata)}:t})}function W(e,n){return y(e)&&!c(e)&&c(n)}function y(e){return u(e.mediaMetadata).length>0||/image|photo|video|file|document/i.test(e.kind||"")}function c(e){return u(e.mediaMetadata).some(n=>{const t=typeof n.localPath=="string"?n.localPath.trim():"",r=typeof n.url=="string"?n.url.trim():"",a=typeof n.availability=="string"?n.availability:"";return a==="edge-local"&&!!t||a==="server-url"&&!!r})}function O(e,n){const t=d(e)?{...e}:{},r=d(n)?n:{},a=m(r);return a?{...t,availability:a.availability,mediaStatus:"downloaded",attachment:{...d(t.attachment)?t.attachment:{},...a}}:e}function m(e){return u(e).find(n=>{const t=typeof n.availability=="string"?n.availability:"",r=typeof n.localPath=="string"?n.localPath.trim():"",a=typeof n.url=="string"?n.url.trim():"";return t==="edge-local"&&!!r||t==="server-url"&&!!a})??null}function u(e){return d(e)?Array.isArray(e.attachments)?e.attachments.filter(d):d(e.attachment)?[e.attachment]:e.localPath||e.url||e.name||e.fileName||e.availability?[e]:[]:[]}function d(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}function M(e){return String(e??"").trim().toLowerCase().replace(/_/g,"-")}function o(e){return typeof e=="string"?e.trim():""}export{R as filterDeliverableWeChatMessages,F as loadWeChatChannelLedger,z as markWeChatChannelBindingDisabled,_ as saveWeChatChannelLedger,j as updateWeChatChannelBindingLedger};
1
+ import y from"node:fs";import m from"node:path";import{WECHAT_CHANNEL_RECENT_MESSAGE_WINDOW as R}from"./runtime.js";import{filterNewWeChatMessagesByAnchor as C,isLikelySameWeChatMessage as p}from"./anchor.js";import{normalizeWeChatObservedWindowForLedger as B}from"./message-key.js";function J(e,t){try{const n=JSON.parse(y.readFileSync(e,"utf8"));if(n?.version===1&&n.runtimeId===t&&n.bindings&&typeof n.bindings=="object")return n}catch{}return{version:1,runtimeId:t,bindings:{}}}function $(e,t){y.mkdirSync(m.dirname(e),{recursive:!0}),y.writeFileSync(e,JSON.stringify(t,null,2))}function G(e){const t=e.ledger.bindings[e.bindingId],n=B(e.observedMessages).map(E),r=L(t?.recent??[],n).slice(-R),s=e.baselineOnly||!t?.baselineEstablished||!!t?.disabledSince?[]:x(t?.recent??[],r),l={bindingId:e.bindingId,baselineEstablished:!0,disabledSince:null,revision:(t?.revision??0)+(s.length>0?1:0),recent:r,pendingSendKeys:t?.pendingSendKeys??[],attachmentStates:W(r,t?.attachmentStates),vectorReferences:I(r,t?.vectorReferences,e.vectorReferences),cooldown:t?.cooldown};return e.ledger.bindings[e.bindingId]=l,{binding:l,newMessages:s}}function E(e){return Array.isArray(e.visualBlocks)?{...e,visualBlocks:e.visualBlocks.map(t=>{const{vectorBase64:n,...r}=t;return r})}:e}function U(e){const t=e.ledger.bindings[e.bindingId],n={bindingId:e.bindingId,baselineEstablished:!1,disabledSince:(e.disabledAt??new Date).toISOString(),revision:t?.revision??0,recent:t?.recent??[],pendingSendKeys:t?.pendingSendKeys??[],attachmentStates:t?.attachmentStates,vectorReferences:t?.vectorReferences,cooldown:t?.cooldown};return e.ledger.bindings[e.bindingId]=n,n}function I(e,t,n){const r=new Map;for(const s of n??[]){const l=o(s.stableMessageKey);if(!l)continue;const c=r.get(l)??[];c.push(s),r.set(l,c)}const i={};for(const s of e){const l=r.get(s.stableMessageKey)??N(s)??t?.[s.stableMessageKey];l?.length&&(i[s.stableMessageKey]=l)}return Object.keys(i).length?i:void 0}function N(e){const n=(Array.isArray(e.visualBlocks)?e.visualBlocks:[]).flatMap(r=>{const i=o(r.vectorStoreKey),s=o(r.blockId),l=o(r.blockKind)||"visual",c=o(r.modelVersion)||o(r.model)||"server-visual-embedding",u=Number(r.dims);return!i||!s||!Number.isFinite(u)||u<1?[]:[{stableMessageKey:e.stableMessageKey,blockId:s,blockKind:l,vectorStoreKey:i,model:c,...r.modelVersion!==void 0?{modelVersion:r.modelVersion}:{},dims:Math.round(u),...o(r.signature)?{signature:o(r.signature)}:{},...r.bbox!==void 0?{bbox:r.bbox}:{},observedAt:e.observedAt||new Date(0).toISOString()}]});return n.length?n:void 0}function x(e,t){const n=C({previous:e,current:t}),r=new Set(n.map(a=>a.stableMessageKey)),i=new Set(t.map(a=>a.stableMessageKey)),s=[],l=t.filter(a=>{if(r.has(a.stableMessageKey))return!0;const S=e.find(g=>g.stableMessageKey===a.stableMessageKey)??e.find(g=>!i.has(g.stableMessageKey)&&p(g,a));return S?T(S,a):!1}),c=new Set(l.map(a=>a.stableMessageKey)),u=l.filter(a=>!(a.senderRole==="self"||a.isBaseline===!0||v(a.deliveryStatus)||b(a)&&!d(a))),w=new Set(u.map(a=>a.stableMessageKey));for(const a of t)s.push({stableMessageKey:a.stableMessageKey,kind:a.kind||null,senderRole:a.senderRole||null,anchorText:(a.anchorText||a.normalizedText||a.textExcerpt||"").slice(0,40),isNewByAnchor:r.has(a.stableMessageKey),isCandidate:c.has(a.stableMessageKey),delivered:w.has(a.stableMessageKey),isBaseline:a.isBaseline===!0,deliveryStatus:a.deliveryStatus??null,hasAttachmentCandidate:b(a),hasAgentReadableAttachment:d(a),dropReason:O(a,c.has(a.stableMessageKey))});return P(s),u}function O(e,t){return t?e.senderRole==="self"?"sender-self":e.isBaseline===!0?"baseline":v(e.deliveryStatus)?`status:${M(e.deliveryStatus)}`:b(e)&&!d(e)?"attachment-not-readable":null:"not-new-and-no-media-upgrade"}function P(e){const t=process.env.SHENNIAN_WECHAT_DELIVERABILITY_TRACE;if(t)try{const n=JSON.stringify({at:new Date().toISOString(),messages:e})+`
2
+ `;t==="1"||t==="stderr"?process.stderr.write(`[wechat-deliverability] ${n}`):(y.mkdirSync(m.dirname(t),{recursive:!0}),y.appendFileSync(t,n))}catch{}}function W(e,t){const n={};for(const r of e){const i=k(r),s=t?.[r.stableMessageKey];if(!i&&s){n[r.stableMessageKey]=s;continue}i&&(n[r.stableMessageKey]=D(s,i))}return Object.keys(n).length?n:void 0}function k(e){const t=h(e.mediaMetadata);if(!t.length&&!b(e)&&!v(e.deliveryStatus))return null;const n=A(e.mediaMetadata)??t[0]??{},r=M(e.deliveryStatus),i=o(n.availability)||o(e.mediaMetadata?.availability)||(r==="needs-review"?"needs-review":"metadata-only");return{stableMessageKey:e.stableMessageKey,kind:e.kind||o(n.type)||"unknown",availability:i,...r?{deliveryStatus:r}:{},...o(n.localPath)?{localPath:o(n.localPath)}:{},...o(n.url)?{url:o(n.url)}:{},...o(n.name)||o(n.fileName)?{name:o(n.name)||o(n.fileName)}:{},...o(n.providerError)?{providerError:o(n.providerError)}:{},...o(n.reasonCode)||o(e.mediaMetadata?.edgeResolveReasonCode)?{reasonCode:o(n.reasonCode)||o(e.mediaMetadata?.edgeResolveReasonCode)}:{}}}function D(e,t){return!e||K(t)?t:K(e)?e:t}function K(e){return e.availability==="edge-local"&&!!e.localPath||e.availability==="server-url"&&!!e.url}function v(e){const t=M(e);return t==="suppressed"||t==="pending"||t==="needs-review"||t==="manual-review"}function L(e,t){const n=new Set(t.map(r=>r.stableMessageKey));return t.map(r=>{if(d(r))return r;const i=e.find(s=>s.stableMessageKey===r.stableMessageKey&&d(s))??e.find(s=>!n.has(s.stableMessageKey)&&p(s,r)&&d(s));return i?{...r,mediaMetadata:V(r.mediaMetadata,i.mediaMetadata)}:r})}function T(e,t){return b(e)&&!d(e)&&d(t)}function b(e){return h(e.mediaMetadata).length>0||/image|photo|video|file|document/i.test(e.kind||"")}function d(e){return h(e.mediaMetadata).some(t=>{const n=typeof t.localPath=="string"?t.localPath.trim():"",r=typeof t.url=="string"?t.url.trim():"",i=typeof t.availability=="string"?t.availability:"";return i==="edge-local"&&!!n||i==="server-url"&&!!r})}function V(e,t){const n=f(e)?{...e}:{},r=f(t)?t:{},i=A(r);return i?{...n,availability:i.availability,mediaStatus:"downloaded",attachment:{...f(n.attachment)?n.attachment:{},...i}}:e}function A(e){return h(e).find(t=>{const n=typeof t.availability=="string"?t.availability:"",r=typeof t.localPath=="string"?t.localPath.trim():"",i=typeof t.url=="string"?t.url.trim():"";return n==="edge-local"&&!!r||n==="server-url"&&!!i})??null}function h(e){return f(e)?Array.isArray(e.attachments)?e.attachments.filter(f):f(e.attachment)?[e.attachment]:e.localPath||e.url||e.name||e.fileName||e.availability?[e]:[]:[]}function f(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}function M(e){return String(e??"").trim().toLowerCase().replace(/_/g,"-")}function o(e){return typeof e=="string"?e.trim():""}export{x as filterDeliverableWeChatMessages,J as loadWeChatChannelLedger,U as markWeChatChannelBindingDisabled,$ as saveWeChatChannelLedger,G as updateWeChatChannelBindingLedger};
@@ -28,7 +28,7 @@ export type WeChatChannelVisibleMediaCandidate = {
28
28
  fileName?: string | null;
29
29
  mimeType?: string | null;
30
30
  size?: number | null;
31
- mediaStatus?: 'available' | 'not_downloaded' | 'metadata_only' | 'unsupported' | string | null;
31
+ mediaStatus?: 'available' | 'not_downloaded' | 'loading' | 'metadata_only' | 'unsupported' | string | null;
32
32
  observedAt?: string | null;
33
33
  contextText?: string | null;
34
34
  };