shennian 0.2.134 → 0.2.136

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.
@@ -2,7 +2,7 @@
2
2
  "schemaVersion": 1,
3
3
  "jsMinified": true,
4
4
  "minifier": "esbuild",
5
- "fileCount": 136,
5
+ "fileCount": 137,
6
6
  "files": [
7
7
  {
8
8
  "file": "bin/shennian.js",
@@ -86,13 +86,13 @@
86
86
  },
87
87
  {
88
88
  "file": "src/agents/model-registry/discovery.js",
89
- "beforeBytes": 6872,
90
- "afterBytes": 3206
89
+ "beforeBytes": 6946,
90
+ "afterBytes": 3266
91
91
  },
92
92
  {
93
93
  "file": "src/agents/model-registry/parsers.js",
94
- "beforeBytes": 16473,
95
- "afterBytes": 8011
94
+ "beforeBytes": 18044,
95
+ "afterBytes": 8783
96
96
  },
97
97
  {
98
98
  "file": "src/agents/model-registry/runner.js",
@@ -111,8 +111,8 @@
111
111
  },
112
112
  {
113
113
  "file": "src/agents/model-registry.js",
114
- "beforeBytes": 918,
115
- "afterBytes": 829
114
+ "beforeBytes": 964,
115
+ "afterBytes": 876
116
116
  },
117
117
  {
118
118
  "file": "src/agents/openclaw.js",
@@ -216,8 +216,8 @@
216
216
  },
217
217
  {
218
218
  "file": "src/channels/wechat-channel/helper-assets.js",
219
- "beforeBytes": 17193,
220
- "afterBytes": 9150
219
+ "beforeBytes": 17157,
220
+ "afterBytes": 9152
221
221
  },
222
222
  {
223
223
  "file": "src/channels/wechat-channel/helper-client.js",
@@ -321,8 +321,8 @@
321
321
  },
322
322
  {
323
323
  "file": "src/channels/wechat-rpa/product-channel.js",
324
- "beforeBytes": 20794,
325
- "afterBytes": 10908
324
+ "beforeBytes": 20920,
325
+ "afterBytes": 10980
326
326
  },
327
327
  {
328
328
  "file": "src/channels/wechat-rpa-session-sync.js",
@@ -331,8 +331,8 @@
331
331
  },
332
332
  {
333
333
  "file": "src/channels/wechat-rpa.js",
334
- "beforeBytes": 58837,
335
- "afterBytes": 30003
334
+ "beforeBytes": 58039,
335
+ "afterBytes": 29680
336
336
  },
337
337
  {
338
338
  "file": "src/channels/wecom.js",
@@ -349,6 +349,11 @@
349
349
  "beforeBytes": 3288,
350
350
  "afterBytes": 1780
351
351
  },
352
+ {
353
+ "file": "src/commands/daemon-start-wait.js",
354
+ "beforeBytes": 1013,
355
+ "afterBytes": 422
356
+ },
352
357
  {
353
358
  "file": "src/commands/daemon-windows.js",
354
359
  "beforeBytes": 3570,
@@ -356,8 +361,8 @@
356
361
  },
357
362
  {
358
363
  "file": "src/commands/daemon.js",
359
- "beforeBytes": 37193,
360
- "afterBytes": 17873
364
+ "beforeBytes": 37422,
365
+ "afterBytes": 18015
361
366
  },
362
367
  {
363
368
  "file": "src/commands/external-attachments.js",
@@ -386,8 +391,8 @@
386
391
  },
387
392
  {
388
393
  "file": "src/commands/pair.js",
389
- "beforeBytes": 11239,
390
- "afterBytes": 6479
394
+ "beforeBytes": 11420,
395
+ "afterBytes": 6596
391
396
  },
392
397
  {
393
398
  "file": "src/commands/runtime.js",
@@ -626,8 +631,8 @@
626
631
  },
627
632
  {
628
633
  "file": "src/session/handlers/wechat-helper-runtime.js",
629
- "beforeBytes": 7107,
630
- "afterBytes": 3846
634
+ "beforeBytes": 7243,
635
+ "afterBytes": 3930
631
636
  },
632
637
  {
633
638
  "file": "src/session/manager.js",
@@ -1,2 +1,2 @@
1
- import{createInterface as m}from"node:readline";import{resolveBuiltinCommand as c,spawnResolvedCommand as v}from"../command-spec.js";import{fallbackClaudeAliasModels as M,discoverClaudeAliasModelsFromEnv as w,fallbackGeminiModels as C,parseCodexAppServerModels as y,parseCursorModels as h,parseOpenCodeModels as g}from"./parsers.js";import{runResolvedCommand as f}from"./runner.js";import{DISCOVERY_WORKDIR as E}from"./types.js";import{buildAgentProcessEnv as O,readLatestUserEnv as R}from"../../agent-env.js";import{CODEX_APP_SERVER_CLIENT_INFO as S}from"../codex-utils.js";function l(e,r,o,i,a,n){return e.stdin?new Promise((t,s)=>{const u=setTimeout(()=>{r.delete(o),s(new Error(`codex app-server request timed out: ${i}`))},n);r.set(o,{resolve:t,reject:s,timer:u}),e.stdin.write(`${JSON.stringify({id:o,method:i,params:a})}
2
- `,p=>{p&&(clearTimeout(u),r.delete(o),s(p))})}):Promise.reject(new Error("codex app-server stdin unavailable"))}async function x(e){const r=v(e,["app-server","--listen","stdio://"],{cwd:E,stdio:["pipe","pipe","pipe"],env:O({NO_COLOR:"1"})}),o=new Map;let i=1;m({input:r.stdout}).on("line",n=>{if(!n.trim())return;let t;try{t=JSON.parse(n)}catch{return}if(t.id==null)return;const s=o.get(t.id);s&&(clearTimeout(s.timer),o.delete(t.id),t.error?s.reject(new Error(t.error.message||`codex app-server error ${t.error.code??""}`.trim())):s.resolve(t.result))});try{await l(r,o,i++,"initialize",{clientInfo:S,capabilities:{experimentalApi:!0}},1e4);const[n,t]=await Promise.all([l(r,o,i++,"model/list",{},15e3),l(r,o,i++,"config/read",{},15e3).catch(()=>null)]),s=typeof t=="object"&&t!==null&&"config"in t?t.config:void 0;return y({...typeof n=="object"&&n!==null?n:{},config:s})}finally{for(const[n,t]of o)clearTimeout(t.timer),t.reject(new Error("codex app-server stopped")),o.delete(n);r.kill("SIGTERM"),setTimeout(()=>r.kill("SIGKILL"),1e3).unref()}}async function A(){const e=c("cursor");if(!e)return[];const r=await f(e,["--list-models"]);return h(r.stdout)}async function I(){const e=c("opencode");if(!e)return[];const r=await f(e,["models"]);return g(r.stdout)}async function P(){if(!c("claude"))return[];const e=w(R());return e.length>0?e:M()}async function T(){const e=c("codex");return e?await x(e).catch(()=>[]):[]}async function _(){return c("gemini")?C():[]}const d=[{id:"qwen3.6-plus",name:"Qwen 3.6 Plus",provider:"dashscope",isDefault:!0}],b=[{id:"codex",name:"Codex",provider:"shennian",isDefault:!0},{id:"claude",name:"Claude Code",provider:"shennian"}];async function k(e){if(!e.serverUrl||!e.authToken)return d;try{const r=await fetch(`${e.serverUrl.replace(/\/$/,"")}/api/models`,{headers:{Authorization:`Bearer ${e.authToken}`}});if(!r.ok)return d;const o=await r.json(),i=(o.models??[]).map(a=>({id:a.id,name:a.name?.trim()||a.id,provider:a.provider,isDefault:a.id===o.default}));return i.length>0?i:d}catch{return d}}async function D(e,r){switch(e){case"claude":return P();case"codex":return T();case"gemini":return _();case"cursor":return A();case"openclaw":return[];case"opencode":return I();case"pi":return k(r);case"manager":return b}}async function F(e,r){return e.type.startsWith("custom:")?e.models??[]:D(e.type,r)}export{F as discoverModelsForAgent};
1
+ import{createInterface as m}from"node:readline";import{resolveBuiltinCommand as c,spawnResolvedCommand as v}from"../command-spec.js";import{fallbackClaudeAliasModels as M,discoverClaudeAliasModelsFromEnv as C,fallbackCodexModels as w,fallbackGeminiModels as y,parseCodexAppServerModels as h,parseCursorModels as g,parseOpenCodeModels as E}from"./parsers.js";import{runResolvedCommand as f}from"./runner.js";import{DISCOVERY_WORKDIR as x}from"./types.js";import{buildAgentProcessEnv as O,readLatestUserEnv as R}from"../../agent-env.js";import{CODEX_APP_SERVER_CLIENT_INFO as A}from"../codex-utils.js";function d(e,r,o,i,a,n){return e.stdin?new Promise((t,s)=>{const u=setTimeout(()=>{r.delete(o),s(new Error(`codex app-server request timed out: ${i}`))},n);r.set(o,{resolve:t,reject:s,timer:u}),e.stdin.write(`${JSON.stringify({id:o,method:i,params:a})}
2
+ `,p=>{p&&(clearTimeout(u),r.delete(o),s(p))})}):Promise.reject(new Error("codex app-server stdin unavailable"))}async function S(e){const r=v(e,["app-server","--listen","stdio://"],{cwd:x,stdio:["pipe","pipe","pipe"],env:O({NO_COLOR:"1"})}),o=new Map;let i=1;m({input:r.stdout}).on("line",n=>{if(!n.trim())return;let t;try{t=JSON.parse(n)}catch{return}if(t.id==null)return;const s=o.get(t.id);s&&(clearTimeout(s.timer),o.delete(t.id),t.error?s.reject(new Error(t.error.message||`codex app-server error ${t.error.code??""}`.trim())):s.resolve(t.result))});try{await d(r,o,i++,"initialize",{clientInfo:A,capabilities:{experimentalApi:!0}},1e4);const[n,t]=await Promise.all([d(r,o,i++,"model/list",{},15e3),d(r,o,i++,"config/read",{},15e3).catch(()=>null)]),s=typeof t=="object"&&t!==null&&"config"in t?t.config:void 0;return h({...typeof n=="object"&&n!==null?n:{},config:s})}finally{for(const[n,t]of o)clearTimeout(t.timer),t.reject(new Error("codex app-server stopped")),o.delete(n);r.kill("SIGTERM"),setTimeout(()=>r.kill("SIGKILL"),1e3).unref()}}async function b(){const e=c("cursor");if(!e)return[];const r=await f(e,["--list-models"]);return g(r.stdout)}async function k(){const e=c("opencode");if(!e)return[];const r=await f(e,["models"]);return E(r.stdout)}async function I(){if(!c("claude"))return[];const e=C(R());return e.length>0?e:M()}async function P(){const e=c("codex");if(!e)return[];const r=await S(e).catch(()=>[]);return r.length>0?r:w()}async function T(){return c("gemini")?y():[]}const l=[{id:"qwen3.6-plus",name:"Qwen 3.6 Plus",provider:"dashscope",isDefault:!0}],_=[{id:"codex",name:"Codex",provider:"shennian",isDefault:!0},{id:"claude",name:"Claude Code",provider:"shennian"}];async function D(e){if(!e.serverUrl||!e.authToken)return l;try{const r=await fetch(`${e.serverUrl.replace(/\/$/,"")}/api/models`,{headers:{Authorization:`Bearer ${e.authToken}`}});if(!r.ok)return l;const o=await r.json(),i=(o.models??[]).map(a=>({id:a.id,name:a.name?.trim()||a.id,provider:a.provider,isDefault:a.id===o.default}));return i.length>0?i:l}catch{return l}}async function L(e,r){switch(e){case"claude":return I();case"codex":return P();case"gemini":return T();case"cursor":return b();case"openclaw":return[];case"opencode":return k();case"pi":return D(r);case"manager":return _}}async function U(e,r){return e.type.startsWith("custom:")?e.models??[]:L(e.type,r)}export{U as discoverModelsForAgent};
@@ -1,5 +1,6 @@
1
1
  import type { ModelInfo } from '@shennian/wire';
2
2
  export declare function stripAnsi(text: string): string;
3
+ export declare function fallbackCodexModels(): ModelInfo[];
3
4
  export declare function parseCursorModels(raw: string): ModelInfo[];
4
5
  export declare function parseOpenClawModels(raw: string): ModelInfo[];
5
6
  export declare function parseOpenCodeModels(raw: string): ModelInfo[];
@@ -1,4 +1,4 @@
1
- function m(n){return n.replace(/\u001b\[[0-9;?]*[a-zA-Z]/g,"").replace(/\u001b\][^\u0007]*\u0007/g,"").replace(/[\u0000-\u0008\u000b-\u001f\u007f]/g,"")}function c(n){const e=new Set,o=[];for(const t of n)!t.id||e.has(t.id)||(e.add(t.id),o.push(t));return o}function h(n){return n&&n[0].toUpperCase()+n.slice(1)}function A(n){return n.split("-").map(e=>e==="gpt"?"GPT":e==="codex"?"Codex":/^\d/.test(e)?e:h(e)).join("-")}function M(n){switch(n){case"low":return"Low";case"medium":return"Medium";case"high":return"High";case"xhigh":return"Extra High";default:return h(n)}}function O(n){return n.split("-").map(e=>/^\d/.test(e)?e:h(e)).join(" ")}function S(n,e,o=!1){const t=e.toLowerCase().replace(/\s+/g,"-");return`gemini-${n}-${t}${o?"-preview":""}`}function H(n){const e=[],o=m(n);for(const t of o.split(`
2
- `)){const i=t.trim().match(/^(\S+)\s+-\s+(.+?)(?:\s+\(([^)]+)\))?$/);if(!i)continue;const[,s,r,a]=i,u=a?.split(",").map(d=>d.trim().toLowerCase())??[];e.push({id:s,name:r.trim()||O(s),description:a?.trim(),isDefault:u.includes("default")||u.includes("current")})}return c(e)}function T(n){try{const o=(JSON.parse(n).models??[]).filter(t=>t.available).map(t=>({id:t.key,name:t.name?.trim()||t.key,provider:t.key.split("/")[0],description:t.tags?.join(", "),isDefault:t.tags?.includes("default")??!1}));return c(o)}catch{return[]}}function F(n){const e=m(n).split(`
3
- `).map(o=>o.trim()).filter(Boolean).filter(o=>!o.includes(" ")).map(o=>({id:o,name:o,provider:o.includes("/")?o.split("/")[0]:void 0}));return c(e)}function G(n){const e=m(n).replace(/\s+/g," "),o=[[/\d+\.Default\(recommended\).*?(?:Sonnet|currently\s*Sonnet)\s*([\d.]+)/i,"default","Default (recommended)"],[/\d+\.Sonnet.*?Sonnet\s*([\d.]+)/i,"sonnet","Sonnet"],[/\d+\.Opus.*?Opus\s*([\d.]+)/i,"opus","Opus"],[/\d+\.Haiku.*?Haiku\s*([\d.]+)/i,"haiku","Haiku"]],t=[];for(const[a,u,d]of o){const l=e.match(a);l&&t.push(g({id:u,name:d,description:`v${l[1]}`,provider:"anthropic",isDefault:u==="default"}))}if(t.length>0)return c(t);const i=[],s=e.match(/currently\s*(Sonnet|Opus|Haiku)\s*([34](?:\.\d)?)/i),r={sonnet:"sonnet",opus:"opus",haiku:"haiku"};for(const a of e.matchAll(/\b(Sonnet|Opus|Haiku)\s*([34](?:\.\d)?)\b/gi)){const u=(a[1]??"").toLowerCase(),d=r[u];if(!d)continue;const l=a[2]??"",v=s&&s[1]?.toLowerCase()===u&&s[2]===l;i.push(g({id:d,name:`${h(u)} ${l}`,provider:"anthropic",isDefault:!!v}))}return c(i)}function _(n,e){const o=Math.max(n.length,e.length);for(let t=0;t<o;t++){const i=n[t]??-1,s=e[t]??-1;if(i!==s)return i-s}return 0}function R(n,e){const o=n.match(new RegExp(`^claude-${e}-([0-9]+(?:-[0-9]+)*)$`,"i"));if(!o?.[1])return null;const t=o[1].split("-").map(i=>Number.parseInt(i,10)).filter(i=>Number.isFinite(i));return t.length===0||t.length>2?null:t}function k(n){if(n?.length)return`v${n.join(".")}`}const C={default:"Default (recommended)",sonnet:"Sonnet",opus:"Opus",haiku:"Haiku"},y={default:"ANTHROPIC_MODEL",sonnet:"ANTHROPIC_DEFAULT_SONNET_MODEL",opus:"ANTHROPIC_DEFAULT_OPUS_MODEL",haiku:"ANTHROPIC_DEFAULT_HAIKU_MODEL"},w=[{id:"max",name:"Max"}],$=[{id:"low",name:"Low"},{id:"medium",name:"Medium"},{id:"high",name:"High"},{id:"xhigh",name:"Extra High"},{id:"max",name:"Max"}];function g(n){return{...n,supportedReasoningEfforts:$,defaultReasoningEffort:"medium"}}function E(n,e){return n[e]?.trim()||null}function D(n,e){const o=E(e,"ANTHROPIC_BASE_URL")?.toLowerCase()??"";return o.includes("deepseek.com")||n.toLowerCase().startsWith("deepseek-")?"deepseek":o?"custom":"anthropic"}function x(n){const e=new Set;for(const t of Object.values(y)){const i=E(n,t);i&&e.add(i)}const o=Array.from(e).map((t,i)=>{const s=D(t,n),r={id:t,name:t,provider:s,isDefault:i===0};return s==="deepseek"?{...r,supportedReasoningEfforts:w,defaultReasoningEffort:E(n,"CLAUDE_CODE_EFFORT_LEVEL")??"max"}:g(r)});return c(o)}function I(n=process.env){return x(n)}function N(n,e=process.env){return n.map(o=>{if(!["default","sonnet","opus","haiku"].includes(o.id))return o;const t=o.id,i=E(e,y[t]);if(!i)return o;const r=`Claude Code alias: ${C[t]}`;return{...o,name:i,description:o.description?`${r} \xB7 ${o.description}`:r,provider:D(i,e)}})}function U(n){const e=m(n),o=new Map;for(const i of["sonnet","opus","haiku"])for(const s of e.matchAll(new RegExp(`\\bclaude-${i}-[0-9.-]+\\b`,"gi"))){const r=s[0].trim().toLowerCase().replace(/\./g,"-").replace(/-+$/g,""),a=R(r,i);if(!a)continue;const u=o.get(i);(!u||_(u,a)<0)&&o.set(i,a)}const t=[];for(const i of["default","sonnet","opus","haiku"]){if(!new RegExp(`\\b${i}\\b`,"i").test(e))continue;const s=(i==="default"?o.get("sonnet"):o.get(i))??null;t.push(g({id:i,name:C[i],description:k(s),provider:"anthropic",isDefault:i==="default"}))}return c(t)}function P(n=process.env){const e=x(n);return e.length>0?e:N([{id:"default",name:"Default (recommended)",provider:"anthropic",isDefault:!0},{id:"sonnet",name:"Sonnet",provider:"anthropic"},{id:"opus",name:"Opus",provider:"anthropic"},{id:"haiku",name:"Haiku",provider:"anthropic"}],n).map(g)}function j(n){const e=m(n),o=[];for(const t of e.split(`
4
- `)){const i=t.match(/\b(gpt-[a-z0-9][a-z0-9.-]*)\b/i);if(!i)continue;const s=i[1].trim(),r=t.replace(s,"").replace(/^[^\w]+/,"").trim()||void 0;o.push({id:s,name:A(s),description:r,provider:"openai",isDefault:/\bcurrent\b/i.test(t)||/\bselected\b/i.test(t)})}return c(o)}function V(n){const e=typeof n=="object"&&n!==null?n:{},o=typeof e.config?.model=="string"?e.config.model:void 0,t=typeof e.config?.model_reasoning_effort=="string"?e.config.model_reasoning_effort:void 0,i=[];for(const s of e.data??[]){if(typeof s!="object"||s===null)continue;const r=s,a=typeof r.id=="string"?r.id:typeof r.model=="string"?r.model:"";if(!a)continue;const u=typeof r.displayName=="string"?r.displayName:typeof r.name=="string"?r.name:void 0,d=typeof r.description=="string"?r.description:void 0,l=typeof r.defaultReasoningEffort=="string"?r.defaultReasoningEffort:void 0,v=Array.isArray(r.supportedReasoningEfforts)?r.supportedReasoningEfforts.map(f=>{if(typeof f!="object"||f===null)return null;const p=f,b=typeof p.reasoningEffort=="string"?p.reasoningEffort:typeof p.id=="string"?p.id:"";return b?{id:b,name:M(b),description:typeof p.description=="string"?p.description:void 0}:null}).filter(f=>f!=null):void 0,L=Array.isArray(r.additionalSpeedTiers)?r.additionalSpeedTiers.filter(f=>typeof f=="string"):void 0;i.push({id:a,name:u||A(a),description:d,provider:"openai",isDefault:o?o===a:r.isDefault===!0,supportedReasoningEfforts:v,defaultReasoningEffort:l,additionalSpeedTiers:L})}return o&&!i.some(s=>s.id===o)&&i.unshift({id:o,name:A(o),provider:"openai",isDefault:!0,defaultReasoningEffort:t}),c(i)}function z(n){const e=m(n),o=[];for(const t of e.matchAll(/\b(?:auto-)?gemini-[a-z0-9.-]+\b/gi)){const i=t[0].trim();o.push({id:i,name:i,provider:"google",isDefault:/\bcurrent\b/i.test(e)&&e.includes(i)})}for(const t of e.matchAll(/\bGemini\s*(\d(?:\.\d)?)\s*(Pro|Flash(?:[- ]Lite)?)\s*(Preview)?\b/gi)){const i=t[1]??"",s=(t[2]??"").replace(/\s+/g,"-"),r=!!t[3],a=S(i,s,r);o.push({id:a,name:`Gemini ${i} ${s.replace(/-/g," ")}`.trim(),provider:"google",isDefault:/\bcurrent\b/i.test(e)&&e.includes(t[0]??"")})}for(const t of e.matchAll(/\bAuto\s*Gemini\s*(\d(?:\.\d)?)\b/gi)){const i=t[1]??"",s=`auto-gemini-${i}`;o.push({id:s,name:`Auto Gemini ${i}`,provider:"google",isDefault:/\bcurrent\b/i.test(e)&&e.includes(t[0]??"")})}return c(o)}function B(){return[{id:"gemini-2.5-pro",name:"Gemini 2.5 Pro",provider:"google",isDefault:!0},{id:"gemini-2.5-flash",name:"Gemini 2.5 Flash",provider:"google"},{id:"auto-gemini-2.5",name:"Auto Gemini 2.5",provider:"google"}]}export{N as applyClaudeModelEnvOverrides,I as discoverClaudeAliasModelsFromEnv,P as fallbackClaudeAliasModels,B as fallbackGeminiModels,D as inferClaudeOverrideProvider,U as parseClaudeBinaryModels,G as parseClaudeModels,V as parseCodexAppServerModels,j as parseCodexModels,H as parseCursorModels,z as parseGeminiModels,T as parseOpenClawModels,F as parseOpenCodeModels,E as readEnvValue,m as stripAnsi};
1
+ function m(e){return e.replace(/\u001b\[[0-9;?]*[a-zA-Z]/g,"").replace(/\u001b\][^\u0007]*\u0007/g,"").replace(/[\u0000-\u0008\u000b-\u001f\u007f]/g,"")}function d(e){const n=new Set,t=[];for(const o of e)!o.id||n.has(o.id)||(n.add(o.id),t.push(o));return t}function h(e){return e&&e[0].toUpperCase()+e.slice(1)}function b(e){return e.split("-").map(n=>n==="gpt"?"GPT":n==="codex"?"Codex":/^\d/.test(n)?n:h(n)).join("-")}function O(e){switch(e){case"low":return"Low";case"medium":return"Medium";case"high":return"High";case"xhigh":return"Extra High";default:return h(e)}}const D=[{id:"gpt-5.6-sol",name:"GPT-5.6 Sol",description:"Latest GPT-5.6 Codex model.",provider:"openai"},{id:"gpt-5.6-terra",name:"GPT-5.6 Terra",description:"Latest GPT-5.6 Codex model.",provider:"openai"},{id:"gpt-5.6-luna",name:"GPT-5.6 Luna",description:"Latest GPT-5.6 Codex model.",provider:"openai"}];function A(e){const n={...e};return e.supportedReasoningEfforts&&(n.supportedReasoningEfforts=e.supportedReasoningEfforts.map(t=>({...t}))),e.additionalSpeedTiers&&(n.additionalSpeedTiers=[...e.additionalSpeedTiers]),n}function R(e,n){const t=n??e.find(i=>i.isDefault)?.id,o=d([...e.map(A),...D.map(i=>A(i))]);return t?o.map(i=>({...i,isDefault:i.id===t})):o}function H(){return D.map((e,n)=>({...A(e),...n===0?{isDefault:!0}:{}}))}function _(e){return e.split("-").map(n=>/^\d/.test(n)?n:h(n)).join(" ")}function k(e,n,t=!1){const o=n.toLowerCase().replace(/\s+/g,"-");return`gemini-${e}-${o}${t?"-preview":""}`}function F(e){const n=[],t=m(e);for(const o of t.split(`
2
+ `)){const i=o.trim().match(/^(\S+)\s+-\s+(.+?)(?:\s+\(([^)]+)\))?$/);if(!i)continue;const[,s,r,a]=i,u=a?.split(",").map(c=>c.trim().toLowerCase())??[];n.push({id:s,name:r.trim()||_(s),description:a?.trim(),isDefault:u.includes("default")||u.includes("current")})}return d(n)}function I(e){try{const t=(JSON.parse(e).models??[]).filter(o=>o.available).map(o=>({id:o.key,name:o.name?.trim()||o.key,provider:o.key.split("/")[0],description:o.tags?.join(", "),isDefault:o.tags?.includes("default")??!1}));return d(t)}catch{return[]}}function U(e){const n=m(e).split(`
3
+ `).map(t=>t.trim()).filter(Boolean).filter(t=>!t.includes(" ")).map(t=>({id:t,name:t,provider:t.includes("/")?t.split("/")[0]:void 0}));return d(n)}function j(e){const n=m(e).replace(/\s+/g," "),t=[[/\d+\.Default\(recommended\).*?(?:Sonnet|currently\s*Sonnet)\s*([\d.]+)/i,"default","Default (recommended)"],[/\d+\.Sonnet.*?Sonnet\s*([\d.]+)/i,"sonnet","Sonnet"],[/\d+\.Opus.*?Opus\s*([\d.]+)/i,"opus","Opus"],[/\d+\.Haiku.*?Haiku\s*([\d.]+)/i,"haiku","Haiku"]],o=[];for(const[a,u,c]of t){const l=n.match(a);l&&o.push(g({id:u,name:c,description:`v${l[1]}`,provider:"anthropic",isDefault:u==="default"}))}if(o.length>0)return d(o);const i=[],s=n.match(/currently\s*(Sonnet|Opus|Haiku)\s*([34](?:\.\d)?)/i),r={sonnet:"sonnet",opus:"opus",haiku:"haiku"};for(const a of n.matchAll(/\b(Sonnet|Opus|Haiku)\s*([34](?:\.\d)?)\b/gi)){const u=(a[1]??"").toLowerCase(),c=r[u];if(!c)continue;const l=a[2]??"",C=s&&s[1]?.toLowerCase()===u&&s[2]===l;i.push(g({id:c,name:`${h(u)} ${l}`,provider:"anthropic",isDefault:!!C}))}return d(i)}function T(e,n){const t=Math.max(e.length,n.length);for(let o=0;o<t;o++){const i=e[o]??-1,s=n[o]??-1;if(i!==s)return i-s}return 0}function w(e,n){const t=e.match(new RegExp(`^claude-${n}-([0-9]+(?:-[0-9]+)*)$`,"i"));if(!t?.[1])return null;const o=t[1].split("-").map(i=>Number.parseInt(i,10)).filter(i=>Number.isFinite(i));return o.length===0||o.length>2?null:o}function $(e){if(e?.length)return`v${e.join(".")}`}const x={default:"Default (recommended)",sonnet:"Sonnet",opus:"Opus",haiku:"Haiku"},y={default:"ANTHROPIC_MODEL",sonnet:"ANTHROPIC_DEFAULT_SONNET_MODEL",opus:"ANTHROPIC_DEFAULT_OPUS_MODEL",haiku:"ANTHROPIC_DEFAULT_HAIKU_MODEL"},G=[{id:"max",name:"Max"}],N=[{id:"low",name:"Low"},{id:"medium",name:"Medium"},{id:"high",name:"High"},{id:"xhigh",name:"Extra High"},{id:"max",name:"Max"}];function g(e){return{...e,supportedReasoningEfforts:N,defaultReasoningEffort:"medium"}}function E(e,n){return e[n]?.trim()||null}function L(e,n){const t=E(n,"ANTHROPIC_BASE_URL")?.toLowerCase()??"";return t.includes("deepseek.com")||e.toLowerCase().startsWith("deepseek-")?"deepseek":t?"custom":"anthropic"}function M(e){const n=new Set;for(const o of Object.values(y)){const i=E(e,o);i&&n.add(i)}const t=Array.from(n).map((o,i)=>{const s=L(o,e),r={id:o,name:o,provider:s,isDefault:i===0};return s==="deepseek"?{...r,supportedReasoningEfforts:G,defaultReasoningEffort:E(e,"CLAUDE_CODE_EFFORT_LEVEL")??"max"}:g(r)});return d(t)}function V(e=process.env){return M(e)}function P(e,n=process.env){return e.map(t=>{if(!["default","sonnet","opus","haiku"].includes(t.id))return t;const o=t.id,i=E(n,y[o]);if(!i)return t;const r=`Claude Code alias: ${x[o]}`;return{...t,name:i,description:t.description?`${r} \xB7 ${t.description}`:r,provider:L(i,n)}})}function z(e){const n=m(e),t=new Map;for(const i of["sonnet","opus","haiku"])for(const s of n.matchAll(new RegExp(`\\bclaude-${i}-[0-9.-]+\\b`,"gi"))){const r=s[0].trim().toLowerCase().replace(/\./g,"-").replace(/-+$/g,""),a=w(r,i);if(!a)continue;const u=t.get(i);(!u||T(u,a)<0)&&t.set(i,a)}const o=[];for(const i of["default","sonnet","opus","haiku"]){if(!new RegExp(`\\b${i}\\b`,"i").test(n))continue;const s=(i==="default"?t.get("sonnet"):t.get(i))??null;o.push(g({id:i,name:x[i],description:$(s),provider:"anthropic",isDefault:i==="default"}))}return d(o)}function B(e=process.env){const n=M(e);return n.length>0?n:P([{id:"default",name:"Default (recommended)",provider:"anthropic",isDefault:!0},{id:"sonnet",name:"Sonnet",provider:"anthropic"},{id:"opus",name:"Opus",provider:"anthropic"},{id:"haiku",name:"Haiku",provider:"anthropic"}],e).map(g)}function q(e){const n=m(e),t=[];for(const o of n.split(`
4
+ `)){const i=o.match(/\b(gpt-[a-z0-9][a-z0-9.-]*)\b/i);if(!i)continue;const s=i[1].trim(),r=o.replace(s,"").replace(/^[^\w]+/,"").trim()||void 0;t.push({id:s,name:b(s),description:r,provider:"openai",isDefault:/\bcurrent\b/i.test(o)||/\bselected\b/i.test(o)})}return d(t)}function K(e){const n=typeof e=="object"&&e!==null?e:{},t=typeof n.config?.model=="string"?n.config.model:void 0,o=typeof n.config?.model_reasoning_effort=="string"?n.config.model_reasoning_effort:void 0,i=[];for(const s of n.data??[]){if(typeof s!="object"||s===null)continue;const r=s,a=typeof r.id=="string"?r.id:typeof r.model=="string"?r.model:"";if(!a)continue;const u=typeof r.displayName=="string"?r.displayName:typeof r.name=="string"?r.name:void 0,c=typeof r.description=="string"?r.description:void 0,l=typeof r.defaultReasoningEffort=="string"?r.defaultReasoningEffort:void 0,C=Array.isArray(r.supportedReasoningEfforts)?r.supportedReasoningEfforts.map(f=>{if(typeof f!="object"||f===null)return null;const p=f,v=typeof p.reasoningEffort=="string"?p.reasoningEffort:typeof p.id=="string"?p.id:"";return v?{id:v,name:O(v),description:typeof p.description=="string"?p.description:void 0}:null}).filter(f=>f!=null):void 0,S=Array.isArray(r.additionalSpeedTiers)?r.additionalSpeedTiers.filter(f=>typeof f=="string"):void 0;i.push({id:a,name:u||b(a),description:c,provider:"openai",isDefault:t?t===a:r.isDefault===!0,supportedReasoningEfforts:C,defaultReasoningEffort:l,additionalSpeedTiers:S})}return t&&!i.some(s=>s.id===t)&&i.unshift({id:t,name:b(t),provider:"openai",isDefault:!0,defaultReasoningEffort:o}),R(d(i),t)}function J(e){const n=m(e),t=[];for(const o of n.matchAll(/\b(?:auto-)?gemini-[a-z0-9.-]+\b/gi)){const i=o[0].trim();t.push({id:i,name:i,provider:"google",isDefault:/\bcurrent\b/i.test(n)&&n.includes(i)})}for(const o of n.matchAll(/\bGemini\s*(\d(?:\.\d)?)\s*(Pro|Flash(?:[- ]Lite)?)\s*(Preview)?\b/gi)){const i=o[1]??"",s=(o[2]??"").replace(/\s+/g,"-"),r=!!o[3],a=k(i,s,r);t.push({id:a,name:`Gemini ${i} ${s.replace(/-/g," ")}`.trim(),provider:"google",isDefault:/\bcurrent\b/i.test(n)&&n.includes(o[0]??"")})}for(const o of n.matchAll(/\bAuto\s*Gemini\s*(\d(?:\.\d)?)\b/gi)){const i=o[1]??"",s=`auto-gemini-${i}`;t.push({id:s,name:`Auto Gemini ${i}`,provider:"google",isDefault:/\bcurrent\b/i.test(n)&&n.includes(o[0]??"")})}return d(t)}function W(){return[{id:"gemini-2.5-pro",name:"Gemini 2.5 Pro",provider:"google",isDefault:!0},{id:"gemini-2.5-flash",name:"Gemini 2.5 Flash",provider:"google"},{id:"auto-gemini-2.5",name:"Auto Gemini 2.5",provider:"google"}]}export{P as applyClaudeModelEnvOverrides,V as discoverClaudeAliasModelsFromEnv,B as fallbackClaudeAliasModels,H as fallbackCodexModels,W as fallbackGeminiModels,L as inferClaudeOverrideProvider,z as parseClaudeBinaryModels,j as parseClaudeModels,K as parseCodexAppServerModels,q as parseCodexModels,F as parseCursorModels,J as parseGeminiModels,I as parseOpenClawModels,U as parseOpenCodeModels,E as readEnvValue,m as stripAnsi};
@@ -1,9 +1,10 @@
1
1
  export { getCachedAgentInfos, refreshAgentInfos, resolveAgentInfos, } from './model-registry/service.js';
2
- import { applyClaudeModelEnvOverrides, discoverClaudeAliasModelsFromEnv, fallbackClaudeAliasModels, parseClaudeBinaryModels, parseClaudeModels, parseCodexModels, parseCodexAppServerModels, parseCursorModels, parseGeminiModels, parseOpenClawModels, stripAnsi } from './model-registry/parsers.js';
2
+ import { applyClaudeModelEnvOverrides, discoverClaudeAliasModelsFromEnv, fallbackCodexModels, fallbackClaudeAliasModels, parseClaudeBinaryModels, parseClaudeModels, parseCodexModels, parseCodexAppServerModels, parseCursorModels, parseGeminiModels, parseOpenClawModels, stripAnsi } from './model-registry/parsers.js';
3
3
  /** Used by Vitest / `model-switching-e2e.ts` — not a stable public API. */
4
4
  export declare const modelSwitchingTestExports: {
5
5
  readonly applyClaudeModelEnvOverrides: typeof applyClaudeModelEnvOverrides;
6
6
  readonly discoverClaudeAliasModelsFromEnv: typeof discoverClaudeAliasModelsFromEnv;
7
+ readonly fallbackCodexModels: typeof fallbackCodexModels;
7
8
  readonly fallbackClaudeAliasModels: typeof fallbackClaudeAliasModels;
8
9
  readonly parseClaudeBinaryModels: typeof parseClaudeBinaryModels;
9
10
  readonly parseClaudeModels: typeof parseClaudeModels;
@@ -1 +1 @@
1
- import{getCachedAgentInfos as m,refreshAgentInfos as u,resolveAgentInfos as v}from"./model-registry/service.js";import{applyClaudeModelEnvOverrides as e,discoverClaudeAliasModelsFromEnv as s,fallbackClaudeAliasModels as o,parseClaudeBinaryModels as r,parseClaudeModels as l,parseCodexModels as d,parseCodexAppServerModels as a,parseCursorModels as p,parseGeminiModels as n,parseOpenClawModels as i,stripAnsi as t}from"./model-registry/parsers.js";const M={applyClaudeModelEnvOverrides:e,discoverClaudeAliasModelsFromEnv:s,fallbackClaudeAliasModels:o,parseClaudeBinaryModels:r,parseClaudeModels:l,parseCodexModels:d,parseCodexAppServerModels:a,parseCursorModels:p,parseOpenClawModels:i,parseGeminiModels:n,stripAnsi:t};export{m as getCachedAgentInfos,M as modelSwitchingTestExports,u as refreshAgentInfos,v as resolveAgentInfos};
1
+ import{getCachedAgentInfos as m,refreshAgentInfos as u,resolveAgentInfos as v}from"./model-registry/service.js";import{applyClaudeModelEnvOverrides as e,discoverClaudeAliasModelsFromEnv as s,fallbackCodexModels as o,fallbackClaudeAliasModels as l,parseClaudeBinaryModels as r,parseClaudeModels as d,parseCodexModels as a,parseCodexAppServerModels as p,parseCursorModels as n,parseGeminiModels as i,parseOpenClawModels as t,stripAnsi as C}from"./model-registry/parsers.js";const f={applyClaudeModelEnvOverrides:e,discoverClaudeAliasModelsFromEnv:s,fallbackCodexModels:o,fallbackClaudeAliasModels:l,parseClaudeBinaryModels:r,parseClaudeModels:d,parseCodexModels:a,parseCodexAppServerModels:p,parseCursorModels:n,parseOpenClawModels:t,parseGeminiModels:i,stripAnsi:C};export{m as getCachedAgentInfos,f as modelSwitchingTestExports,u as refreshAgentInfos,v as resolveAgentInfos};
@@ -1 +1 @@
1
- import c from"node:fs";import s from"node:path";import E from"node:crypto";const V="0.1.12",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 w from"node:crypto";const V="0.1.12",E="SHENNIAN_WECHAT_CHANNEL_HELPER_DIR",d="SHENNIAN_HELPER_RUNTIME_DIR",S="helper_runtime_required",C=["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.env??y(),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:S,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(L({platform:r,baseDir:e,verifyIntegrity:n.verifyIntegrity})&&i.sha256&&w.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[E]?.trim();o&&i(j(o,a));const f=e[d]?.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"):""),u=e.ProgramFiles||e.PROGRAMFILES,m=e["ProgramFiles(x86)"]||e["PROGRAMFILES(X86)"];for(const h of[p?s.join(p,"Programs","Shennian"):"",p?s.join(p,"Programs","shennian"):"",u?s.join(u,"Shennian"):"",m?s.join(m,"Shennian"):""])h&&i(s.join(h,"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=g({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 g(n){const r=n.env,e=r[d]?.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 g({platform:n.platform??process.platform,env:n.env??y(),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(R(a)),i=v(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 v(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"}:I(e.minCliVersion)?!e.sha256||typeof e.sha256.runtimeManifest!="string"?{ok:!1,message:"sha256.runtimeManifest is required"}:k(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"&&!k(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 I(n){return typeof n=="string"&&/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/.test(n)}function k(n){return/^[a-f0-9]{64}$/i.test(n)}function L(n){return n.verifyIntegrity===!0?!0:n.verifyIntegrity===!1?!1:!N(n.platform,n.baseDir)}function N(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 y(){return globalThis.process?.[C]??{}}function F(n){try{const r=JSON.parse(R(n));return r.schemaVersion!==1||typeof r.helperVersion!="string"||!r.platforms?null:r}catch{return null}}function R(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{d as SHENNIAN_HELPER_RUNTIME_DIR_ENV,E as WECHAT_CHANNEL_HELPER_DIR_ENV,S as WECHAT_CHANNEL_HELPER_RUNTIME_REQUIRED_REASON,V as WECHAT_CHANNEL_HELPER_VERSION,q as getDefaultWeChatHelperRuntimeRoot,D as helperRuntimePackageManifestCandidates,U as readWeChatChannelHelperRuntimePackageManifest,$ as resolveWeChatChannelHelperAsset,v as validateWeChatChannelHelperRuntimePackageManifest};
@@ -1,16 +1,16 @@
1
- import C from"node:crypto";import p from"node:fs";import y from"node:path";import{loadConfig as W}from"../../config/index.js";import{createWeChatChannelProductRunner as $}from"../wechat-channel/runner.js";import{createWeChatChannelApiClient as O}from"../wechat-channel/client.js";import{createWeChatAutomationLane as k}from"../wechat-channel/automation-lane.js";import{createWeChatChannelRuntime as D}from"../wechat-channel/runtime.js";import{cancelWeChatOutboundRecord as T,enqueueWeChatOutboundReply as K,loadWeChatChannelOutboundLedger as b,saveWeChatChannelOutboundLedger as S}from"../wechat-channel/outbound-ledger.js";import{loadWeChatChannelLedger as P}from"../wechat-channel/ledger.js";const j="wechat-channel";function fe(e){const t=D({runtimeId:e.config.id,machineId:ee(),pollIntervalMs:e.secret.pollIntervalMs,foregroundPolicy:E(e.secret.forceForeground),bindings:q(e.config,e.secret)}),n=x(e.config.workDir,t.runtimeId),o=f(e.config.workDir,t.runtimeId),r=F(e.config.workDir,t.runtimeId),a=[];p.mkdirSync(y.dirname(r),{recursive:!0}),p.writeFileSync(r,"");const c=e.createRunner??$,l=z(),u=c({runtime:t,workDir:e.config.workDir,api:l,ledgerPath:n,outboundLedgerPath:o,lane:k(),helperRequestLogger:B(r),onInboundMessages:(h,s)=>{const m=t.bindings.find(N=>N.bindingId===h.bindingId)??h,g=v(e.config,m,s);if(!g)return;const R=e.onMessage?.({...g,managerSessionId:e.config.managerSessionId})??g;a.push(R)}});return{runtime:t,runner:u,emitted:a,ledgerPath:n,outboundLedgerPath:o,helperTracePath:r}}function z(){let e=null;const t=()=>(e??=O(),e);return{observe:(...n)=>t().observe(...n),structureWindow:(...n)=>t().structureWindow(...n),embedVisual:(...n)=>t().embedVisual(...n),classifyWindow:(...n)=>t().classifyWindow(...n),upsertRuntime:async(...n)=>(await Promise.resolve().then(()=>t().upsertRuntime(...n)).catch(()=>null),{ok:!0}),ingest:async(...n)=>(await Promise.resolve().then(()=>t().ingest(...n)).catch(()=>null),{ok:!0}),reportRunStatus:async(...n)=>(await Promise.resolve().then(()=>t().reportRunStatus(...n)).catch(()=>null),{ok:!0}),reportOutboundStatus:async(...n)=>(await Promise.resolve().then(()=>t().reportOutboundStatus(...n)).catch(()=>null),{ok:!0})}}function E(e){return e===!1?"polite":"work"}function he(e){const t=e.config.id,n=A(e.config,e.conversationName),o=P(x(e.config.workDir,t),t),r=f(e.config.workDir,t),a=b(r,t),c=e.reply.attachment?[X(e.reply.attachment)]:[],l=K({ledger:a,replyId:e.reply.messageId||e.reply.idempotencyKey||d("wechat-channel-reply",`${Date.now()}:${e.reply.text}`),idempotencyKey:e.reply.idempotencyKey||d("wechat-channel-send",`${e.config.id}
1
+ import C from"node:crypto";import p from"node:fs";import y from"node:path";import{loadConfig as W}from"../../config/index.js";import{createWeChatChannelProductRunner as $}from"../wechat-channel/runner.js";import{createWeChatChannelApiClient as O}from"../wechat-channel/client.js";import{createWeChatAutomationLane as k}from"../wechat-channel/automation-lane.js";import{createWeChatChannelRuntime as D}from"../wechat-channel/runtime.js";import{cancelWeChatOutboundRecord as T,enqueueWeChatOutboundReply as K,loadWeChatChannelOutboundLedger as b,saveWeChatChannelOutboundLedger as S}from"../wechat-channel/outbound-ledger.js";import{loadWeChatChannelLedger as P}from"../wechat-channel/ledger.js";const j="wechat-channel";function fe(e){const t=D({runtimeId:e.config.id,machineId:ee(),pollIntervalMs:e.secret.pollIntervalMs,foregroundPolicy:E(e.secret.forceForeground),bindings:q(e.config,e.secret)}),n=x(e.config.workDir,t.runtimeId),o=f(e.config.workDir,t.runtimeId),r=F(e.config.workDir,t.runtimeId),a=[];p.mkdirSync(y.dirname(r),{recursive:!0}),p.writeFileSync(r,"");const s=e.createRunner??$,l=z(),u=s({runtime:t,workDir:e.config.workDir,api:l,ledgerPath:n,outboundLedgerPath:o,lane:k(),helperRequestLogger:B(r),onInboundMessages:(h,c)=>{const m=t.bindings.find(N=>N.bindingId===h.bindingId)??h,g=v(e.config,m,c);if(!g)return;const M=e.onMessage?.({...g,managerSessionId:e.config.managerSessionId})??g;a.push(M)}});return{runtime:t,runner:u,emitted:a,ledgerPath:n,outboundLedgerPath:o,helperTracePath:r}}function z(){let e=null;const t=()=>(e??=O(),e);return{observe:(...n)=>t().observe(...n),structureWindow:(...n)=>t().structureWindow(...n),embedVisual:(...n)=>t().embedVisual(...n),classifyWindow:(...n)=>t().classifyWindow(...n),upsertRuntime:async(...n)=>(await Promise.resolve().then(()=>t().upsertRuntime(...n)).catch(()=>null),{ok:!0}),ingest:async(...n)=>(await Promise.resolve().then(()=>t().ingest(...n)).catch(()=>null),{ok:!0}),reportRunStatus:async(...n)=>(await Promise.resolve().then(()=>t().reportRunStatus(...n)).catch(()=>null),{ok:!0}),reportOutboundStatus:async(...n)=>(await Promise.resolve().then(()=>t().reportOutboundStatus(...n)).catch(()=>null),{ok:!0})}}function E(e){return e===!1?"polite":"work"}function he(e){const t=e.config.id,n=R(e.config,e.conversationName),o=P(x(e.config.workDir,t),t),r=f(e.config.workDir,t),a=b(r,t),s=e.reply.attachment?[X(e.reply.attachment)]:[],l=K({ledger:a,replyId:e.reply.messageId||e.reply.idempotencyKey||d("wechat-channel-reply",`${Date.now()}:${e.reply.text}`),idempotencyKey:e.reply.idempotencyKey||d("wechat-channel-send",`${e.config.id}
2
2
  ${e.reply.conversationId}
3
3
  ${e.reply.text}
4
- ${c.join(`
5
- `)}`),bindingId:n,runtimeId:t,sessionId:e.config.sessionId||e.config.managerSessionId,conversationName:e.conversationName,replyBaseRevision:o.bindings[n]?.revision??0,text:e.reply.text,attachmentLocalRefs:c});return S(r,a),l}function ge(e){return b(f(e.config.workDir,e.config.id),e.config.id).records.filter(n=>["queued","sending","sent_unconfirmed"].includes(n.sendStatus)).length}function pe(e){return b(f(e.config.workDir,e.config.id),e.config.id).records.filter(n=>["queued","sending","sent_unconfirmed"].includes(n.sendStatus)).slice(0,100).map(n=>({replyId:n.replyId,idempotencyKey:n.idempotencyKey,status:n.sendStatus,conversationName:n.conversationName,reasonCode:n.deferReason||n.failureCode||null,nextAttemptAt:n.nextAttemptAt??null,queuedAt:n.queuedAt??n.createdAt,lastAttemptAt:n.lastAttemptAt??null,attemptCount:n.attemptCount??0,commitStage:n.commitStage??null}))}function ye(e){const t=ne(e.limit,20,1,100),n=P(e.product.ledgerPath,e.product.runtime.runtimeId),o=[];for(const r of e.product.runtime.bindings){const a=n.bindings[r.bindingId]?.recent??[];for(const c of a.slice(-t)){const l=M(e.config,r,c);l&&o.push(l)}}return o}function be(e){const t=f(e.config.workDir,e.config.id),n=b(t,e.config.id),o=String(e.idempotencyKey??"").trim(),r=String(e.replyId??"").trim();if(!o&&!r)return{cancelled:!1};const a=n.records.find(l=>o&&l.idempotencyKey===o||r&&l.replyId===r);if(!a)return{cancelled:!1};const c=a.sendStatus;return T(a,e.reason||"user_cancelled"),a.sendStatus!==c?(S(t,n),{cancelled:!0,status:a.sendStatus,record:a}):{cancelled:!1,status:a.sendStatus,record:a}}function we(e){return e===j}function L(e){return d("wechat-conversation",i(e))}function _(e,t){return d("wechat-channel-binding",`${e}
4
+ ${s.join(`
5
+ `)}`),bindingId:n,runtimeId:t,sessionId:e.config.sessionId||e.config.managerSessionId,conversationName:e.conversationName,replyBaseRevision:o.bindings[n]?.revision??0,text:e.reply.text,attachmentLocalRefs:s});return S(r,a),l}function ge(e){return b(f(e.config.workDir,e.config.id),e.config.id).records.filter(n=>["queued","sending","sent_unconfirmed"].includes(n.sendStatus)).length}function pe(e){return b(f(e.config.workDir,e.config.id),e.config.id).records.filter(n=>["queued","sending","sent_unconfirmed"].includes(n.sendStatus)).slice(0,100).map(n=>({replyId:n.replyId,idempotencyKey:n.idempotencyKey,status:n.sendStatus,conversationName:n.conversationName,reasonCode:n.deferReason||n.failureCode||null,nextAttemptAt:n.nextAttemptAt??null,queuedAt:n.queuedAt??n.createdAt,lastAttemptAt:n.lastAttemptAt??null,attemptCount:n.attemptCount??0,commitStage:n.commitStage??null}))}function ye(e){const t=ne(e.limit,20,1,100),n=P(e.product.ledgerPath,e.product.runtime.runtimeId),o=[];for(const r of e.product.runtime.bindings){const a=(n.bindings[r.bindingId]?.recent??[]).filter(s=>s.senderRole!=="self");for(const s of a.slice(-t)){const l=A(e.config,r,s);l&&o.push(l)}}return o}function be(e){const t=f(e.config.workDir,e.config.id),n=b(t,e.config.id),o=String(e.idempotencyKey??"").trim(),r=String(e.replyId??"").trim();if(!o&&!r)return{cancelled:!1};const a=n.records.find(l=>o&&l.idempotencyKey===o||r&&l.replyId===r);if(!a)return{cancelled:!1};const s=a.sendStatus;return T(a,e.reason||"user_cancelled"),a.sendStatus!==s?(S(t,n),{cancelled:!0,status:a.sendStatus,record:a}):{cancelled:!1,status:a.sendStatus,record:a}}function we(e){return e===j}function L(e){return d("wechat-conversation",i(e))}function _(e,t){return d("wechat-channel-binding",`${e}
6
6
  ${i(t)}`)}function x(e,t){return y.join(e,"wechat-channel",`${I(t)}.ledger.json`)}function f(e,t){return y.join(e,"wechat-channel",`${I(t)}.outbound-ledger.json`)}function F(e,t){return y.join(e,"wechat-channel",`${I(t)}.helper-trace.jsonl`)}function I(e){return String(e||"").trim().replace(/[^a-zA-Z0-9._-]+/g,"_").replace(/^_+|_+$/g,"")||"wechat-channel-runtime"}function B(e){return t=>{p.appendFileSync(e,`${JSON.stringify(H(t))}
7
- `)}}function H(e){const t={phase:e.phase,at:e.at,id:e.id,command:e.command};return e.traceId&&(t.traceId=e.traceId),e.timeoutMs!=null&&(t.timeoutMs=e.timeoutMs),e.durationMs!=null&&(t.durationMs=e.durationMs),e.params!==void 0&&(t.params=w(e.params)),e.ok!==void 0&&(t.ok=e.ok),e.errorCode&&(t.errorCode=e.errorCode),e.errorSummary&&(t.errorSummary=e.errorSummary),e.latencyMs!=null&&(t.latencyMs=e.latencyMs),e.result!==void 0&&(t.result=w(e.result)),t}function w(e,t="",n=0){if(e==null||typeof e=="number"||typeof e=="boolean")return e;if(typeof e=="string")return e.length>240||/(base64|bytes|data|image|screenshot|png|jpeg|jpg|buffer)/i.test(t)&&e.length>48?{type:"string",length:e.length,sha256:C.createHash("sha256").update(e).digest("hex").slice(0,16),preview:e.slice(0,120)}:e;if(Array.isArray(e)){const a=e.slice(0,10).map(c=>w(c,t,n+1));return e.length>a.length?{type:"array",length:e.length,items:a}:a}if(typeof e!="object")return String(e);if(n>=5)return{type:"object",truncated:!0};const o=e,r={};for(const[a,c]of Object.entries(o))r[a]=w(c,a,n+1);return r}function q(e,t){return Y(t).map(n=>({bindingId:A(e,n),sessionId:e.sessionId||e.managerSessionId,conversationDisplayName:n,enabled:!0,allowReply:t.canReply!==!1,downloadMedia:t.downloadAttachments!==!1,selfTriggerMarker:i(t.selfTriggerMarker||"")||null}))}function A(e,t){return e.managedBy==="session-sync"||e.id.startsWith("wechat-binding:")?e.id:_(e.id,t)}function M(e,t,n){const o=i(n.normalizedText||n.anchorText||n.textExcerpt||""),r=V(n);if(!o&&r.length===0)return null;const a=i(n.senderName||"");return{type:"external.message",channelId:e.id,channelType:"wechat-rpa",conversationId:L(t.conversationDisplayName),conversationName:t.conversationDisplayName,messageId:d("wechat-message",`${t.bindingId}
7
+ `)}}function H(e){const t={phase:e.phase,at:e.at,id:e.id,command:e.command};return e.traceId&&(t.traceId=e.traceId),e.timeoutMs!=null&&(t.timeoutMs=e.timeoutMs),e.durationMs!=null&&(t.durationMs=e.durationMs),e.params!==void 0&&(t.params=w(e.params)),e.ok!==void 0&&(t.ok=e.ok),e.errorCode&&(t.errorCode=e.errorCode),e.errorSummary&&(t.errorSummary=e.errorSummary),e.latencyMs!=null&&(t.latencyMs=e.latencyMs),e.result!==void 0&&(t.result=w(e.result)),t}function w(e,t="",n=0){if(e==null||typeof e=="number"||typeof e=="boolean")return e;if(typeof e=="string")return e.length>240||/(base64|bytes|data|image|screenshot|png|jpeg|jpg|buffer)/i.test(t)&&e.length>48?{type:"string",length:e.length,sha256:C.createHash("sha256").update(e).digest("hex").slice(0,16),preview:e.slice(0,120)}:e;if(Array.isArray(e)){const a=e.slice(0,10).map(s=>w(s,t,n+1));return e.length>a.length?{type:"array",length:e.length,items:a}:a}if(typeof e!="object")return String(e);if(n>=5)return{type:"object",truncated:!0};const o=e,r={};for(const[a,s]of Object.entries(o))r[a]=w(s,a,n+1);return r}function q(e,t){return Y(t).map(n=>({bindingId:R(e,n),sessionId:e.sessionId||e.managerSessionId,conversationDisplayName:n,enabled:!0,allowReply:t.canReply!==!1,downloadMedia:t.downloadAttachments!==!1,selfTriggerMarker:i(t.selfTriggerMarker||"")||null}))}function R(e,t){return e.managedBy==="session-sync"||e.id.startsWith("wechat-binding:")?e.id:_(e.id,t)}function A(e,t,n){if(n.senderRole==="self")return null;const o=i(n.normalizedText||n.anchorText||n.textExcerpt||""),r=V(n);if(!o&&r.length===0)return null;const a=i(n.senderName||"");return{type:"external.message",channelId:e.id,channelType:"wechat-rpa",conversationId:L(t.conversationDisplayName),conversationName:t.conversationDisplayName,messageId:d("wechat-message",`${t.bindingId}
8
8
  ${n.stableMessageKey}`),sender:{id:d("wechat-sender",`${t.bindingId}
9
- ${a||n.senderRole}`),name:a||null},text:o,attachments:r,receivedAt:te(n.observedAt),isMentioned:!1,replyTarget:"",rawRef:n.stableMessageKey}}function v(e,t,n){const o=n.map(s=>M(e,t,s)).filter(s=>s!=null);if(o.length<=1)return o[0]??null;const r=o[0],a=n.map(s=>i(s.stableMessageKey||"")).filter(Boolean),l=new Set(o.map(s=>i(s.sender.name||""))).size===1?r.sender.name??null:null,u=new Set(o.map(s=>s.sender.id)).size===1?r.sender.id:d("wechat-sender-batch",`${t.bindingId}
9
+ ${a||n.senderRole}`),name:a||null},text:o,attachments:r,receivedAt:te(n.observedAt),isMentioned:!1,replyTarget:"",rawRef:n.stableMessageKey}}function v(e,t,n){const o=n.map(c=>A(e,t,c)).filter(c=>c!=null);if(o.length<=1)return o[0]??null;const r=o[0],a=n.map(c=>i(c.stableMessageKey||"")).filter(Boolean),l=new Set(o.map(c=>i(c.sender.name||""))).size===1?r.sender.name??null:null,u=new Set(o.map(c=>c.sender.id)).size===1?r.sender.id:d("wechat-sender-batch",`${t.bindingId}
10
10
  ${a.join(`
11
- `)}`),h=o.map(s=>{const m=i(s.text);return m?l!==null?m:`${i(s.sender.name||"")||"\u5BF9\u65B9"}: ${m}`:""}).filter(Boolean).join(`
11
+ `)}`),h=o.map(c=>{const m=i(c.text);return m?l!==null?m:`${i(c.sender.name||"")||"\u5BF9\u65B9"}: ${m}`:""}).filter(Boolean).join(`
12
12
  `);return{...r,messageId:d("wechat-message-batch",`${t.bindingId}
13
13
  ${a.join(`
14
- `)}`),sender:{id:u,name:l},text:h,attachments:G(o.flatMap(s=>s.attachments)),receivedAt:J(o.map(s=>s.receivedAt)),rawRef:a.join(`
15
- `)}}function V(e){const t=e.mediaMetadata;return U(t).map(o=>{const r=o,a=typeof r.localPath=="string"?i(r.localPath):"",c=typeof r.thumbnailPath=="string"?i(r.thumbnailPath):"",l=typeof r.url=="string"?i(r.url):"",u=Number(r.size??r.sizeBytes??0);return{type:i(String(r.type||r.kind||e.kind||"file"))||"file",...r.name||r.fileName?{name:i(String(r.name||r.fileName))}:{},...l?{url:l}:{},...typeof r.mimeType=="string"?{mimeType:i(r.mimeType)}:{},...typeof r.extension=="string"?{extension:i(r.extension)}:{},...Number.isFinite(u)&&u>0?{size:u}:{},...a?{localPath:a}:{},...c?{thumbnailPath:c}:{},...typeof r.hash=="string"||typeof r.sha256=="string"?{hash:i(String(r.hash||r.sha256))}:{},availability:Z(r.availability,{localPath:a,url:l}),...typeof r.providerError=="string"?{providerError:i(r.providerError)}:{},...typeof r.sourceAction=="string"?{sourceAction:i(r.sourceAction)}:{},...Q(r.materializationKind)?{materializationKind:r.materializationKind}:{},...typeof r.isOriginal=="boolean"?{isOriginal:r.isOriginal}:{},...typeof r.mimeKindMatches=="boolean"?{mimeKindMatches:r.mimeKindMatches}:{}}})}function U(e){if(!e||typeof e!="object")return[];const t=e;return Array.isArray(t.attachments)?t.attachments:t.attachment&&typeof t.attachment=="object"?[t.attachment]:t.localPath||t.url||t.name||t.fileName||t.availability?[t]:[]}function G(e){const t=new Set,n=[];for(const o of e){const r=[o.localPath||"",o.url||"",o.thumbnailPath||"",o.hash||"",o.name||"",o.mimeType||"",o.type||""].join(`
16
- `);t.has(r)||(t.add(r),n.push(o))}return n}function J(e){let t=0;for(const n of e){const o=Date.parse(n);Number.isFinite(o)&&(t=Math.max(t,o))}return t?new Date(t).toISOString():new Date().toISOString()}function Z(e,t){return e==="edge-local"||e==="server-url"||e==="pending-download"||e==="metadata-only"||e==="unavailable-large"?e:t.localPath?"edge-local":t.url?"server-url":"metadata-only"}function Q(e){return e==="original-file"||e==="clipboard-image"||e==="preview-crop"||e==="metadata"}function X(e){const t=i(e.localPath||"");if(!t)throw new Error("WeChat channel product send requires a local attachment path on this machine");if(!p.statSync(t).isFile())throw new Error(`WeChat channel product attachment is not a file: ${t}`);return t}function Y(e){return Array.isArray(e.groups)?e.groups.map(t=>i(t?.name||"")).filter(Boolean):[]}function ee(){return process.env.SHENNIAN_MACHINE_ID?.trim()||W().machineId||"local"}function te(e){if(!e)return new Date().toISOString();const t=new Date(e);return Number.isNaN(t.getTime())?new Date().toISOString():t.toISOString()}function i(e){return e.replace(/\s+/g," ").trim()}function ne(e,t,n,o){const r=Number(e);return Number.isFinite(r)?Math.max(n,Math.min(o,Math.floor(r))):t}function d(e,t){return`${e}:${C.createHash("sha256").update(t).digest("hex").slice(0,24)}`}export{j as WECHAT_RPA_PRODUCT_SOURCE,be as cancelWeChatRpaProductOutboundReply,ge as countPendingWeChatRpaProductOutbound,fe as createWeChatRpaProductSourceConnection,he as enqueueWeChatRpaProductOutboundReply,we as isWeChatRpaProductSource,pe as listPendingWeChatRpaProductOutbound,ye as listWeChatRpaProductRecentMessages,M as observedMessageToExternalEvent,v as observedMessagesToExternalEvent,I as safeWeChatChannelRuntimePathSegment,_ as weChatChannelBindingId,L as weChatChannelConversationId,F as weChatChannelHelperTracePath,x as weChatChannelLedgerPath,f as weChatChannelOutboundLedgerPath};
14
+ `)}`),sender:{id:u,name:l},text:h,attachments:G(o.flatMap(c=>c.attachments)),receivedAt:J(o.map(c=>c.receivedAt)),rawRef:a.join(`
15
+ `)}}function V(e){const t=e.mediaMetadata;return U(t).map(o=>{const r=o,a=typeof r.localPath=="string"?i(r.localPath):"",s=typeof r.thumbnailPath=="string"?i(r.thumbnailPath):"",l=typeof r.url=="string"?i(r.url):"",u=Number(r.size??r.sizeBytes??0);return{type:i(String(r.type||r.kind||e.kind||"file"))||"file",...r.name||r.fileName?{name:i(String(r.name||r.fileName))}:{},...l?{url:l}:{},...typeof r.mimeType=="string"?{mimeType:i(r.mimeType)}:{},...typeof r.extension=="string"?{extension:i(r.extension)}:{},...Number.isFinite(u)&&u>0?{size:u}:{},...a?{localPath:a}:{},...s?{thumbnailPath:s}:{},...typeof r.hash=="string"||typeof r.sha256=="string"?{hash:i(String(r.hash||r.sha256))}:{},availability:Z(r.availability,{localPath:a,url:l}),...typeof r.providerError=="string"?{providerError:i(r.providerError)}:{},...typeof r.sourceAction=="string"?{sourceAction:i(r.sourceAction)}:{},...Q(r.materializationKind)?{materializationKind:r.materializationKind}:{},...typeof r.isOriginal=="boolean"?{isOriginal:r.isOriginal}:{},...typeof r.mimeKindMatches=="boolean"?{mimeKindMatches:r.mimeKindMatches}:{}}})}function U(e){if(!e||typeof e!="object")return[];const t=e;return Array.isArray(t.attachments)?t.attachments:t.attachment&&typeof t.attachment=="object"?[t.attachment]:t.localPath||t.url||t.name||t.fileName||t.availability?[t]:[]}function G(e){const t=new Set,n=[];for(const o of e){const r=[o.localPath||"",o.url||"",o.thumbnailPath||"",o.hash||"",o.name||"",o.mimeType||"",o.type||""].join(`
16
+ `);t.has(r)||(t.add(r),n.push(o))}return n}function J(e){let t=0;for(const n of e){const o=Date.parse(n);Number.isFinite(o)&&(t=Math.max(t,o))}return t?new Date(t).toISOString():new Date().toISOString()}function Z(e,t){return e==="edge-local"||e==="server-url"||e==="pending-download"||e==="metadata-only"||e==="unavailable-large"?e:t.localPath?"edge-local":t.url?"server-url":"metadata-only"}function Q(e){return e==="original-file"||e==="clipboard-image"||e==="preview-crop"||e==="metadata"}function X(e){const t=i(e.localPath||"");if(!t)throw new Error("WeChat channel product send requires a local attachment path on this machine");if(!p.statSync(t).isFile())throw new Error(`WeChat channel product attachment is not a file: ${t}`);return t}function Y(e){return Array.isArray(e.groups)?e.groups.map(t=>i(t?.name||"")).filter(Boolean):[]}function ee(){return process.env.SHENNIAN_MACHINE_ID?.trim()||W().machineId||"local"}function te(e){if(!e)return new Date().toISOString();const t=new Date(e);return Number.isNaN(t.getTime())?new Date().toISOString():t.toISOString()}function i(e){return e.replace(/\s+/g," ").trim()}function ne(e,t,n,o){const r=Number(e);return Number.isFinite(r)?Math.max(n,Math.min(o,Math.floor(r))):t}function d(e,t){return`${e}:${C.createHash("sha256").update(t).digest("hex").slice(0,24)}`}export{j as WECHAT_RPA_PRODUCT_SOURCE,be as cancelWeChatRpaProductOutboundReply,ge as countPendingWeChatRpaProductOutbound,fe as createWeChatRpaProductSourceConnection,he as enqueueWeChatRpaProductOutboundReply,we as isWeChatRpaProductSource,pe as listPendingWeChatRpaProductOutbound,ye as listWeChatRpaProductRecentMessages,A as observedMessageToExternalEvent,v as observedMessagesToExternalEvent,I as safeWeChatChannelRuntimePathSegment,_ as weChatChannelBindingId,L as weChatChannelConversationId,F as weChatChannelHelperTracePath,x as weChatChannelLedgerPath,f as weChatChannelOutboundLedgerPath};
@@ -1,8 +1,6 @@
1
- import I from"node:crypto";import m from"node:fs";import f from"node:path";import{fileURLToPath as ee,pathToFileURL as te}from"node:url";import{ChannelSecretRegistry as re}from"./secret-registry.js";import{resolveShennianPath as ne}from"../config/index.js";import{probeMacWeChat as q,observedMessageFromProbe as ae}from"./wechat-rpa/macos.js";import{runMacWeChatRpaFlow as F}from"./wechat-rpa/macos-flow.js";import{normalizeWeChatRpaMessage as se,WeChatRpaDeduper as K,weChatRpaConversationId as x}from"./wechat-rpa/normalizer.js";import{cancelWeChatRpaProductOutboundReply as ie,countPendingWeChatRpaProductOutbound as oe,createWeChatRpaProductSourceConnection as ue,enqueueWeChatRpaProductOutboundReply as de,isWeChatRpaProductSource as y,listWeChatRpaProductRecentMessages as ce,listPendingWeChatRpaProductOutbound as le,weChatChannelConversationId as N}from"./wechat-rpa/product-channel.js";import{createWeChatChannelApiClient as H}from"./wechat-channel/client.js";const pe=5e3,A=5,fe=8,S=Number(process.env.SHENNIAN_WECHAT_RPA_OUTBOUND_ATTACHMENT_MAX_BYTES||20*1024*1024),W=Number(process.env.SHENNIAN_WECHAT_RPA_INTERRUPTION_COOLDOWN_THRESHOLD||3),E=Number(process.env.SHENNIAN_WECHAT_RPA_INTERRUPTION_COOLDOWN_MS||300*1e3),he=10;function me(t){return P(t,A,1,50)>=fe?"scroll-read":"read-latest"}class it{onMessage;options;type="wechat-rpa";secrets=new re;connections=new Map;constructor(e,r={}){this.onMessage=e,this.options=r}async connect(e){if(!e.enabled)return;const r=this.readSecret(e),n=this.ensureConnection(e);if(n.stopped=!1,n.config=e,M(n,e),O(n,e),this.seedConfiguredConversations(n,r),y(r.source)){const a=await this.stopProductWhenControlPlaneDisabled(n,r);if(a||await this.ensureProductConnection(n,r),n.timer)return;n.timer=setInterval(()=>{this.enqueueOperation(n,()=>this.pollProductOnce(n,this.readSecret(n.config))).catch(i=>{console.error(`[wechat-rpa] autonomous poll failed id=${e.id}: ${i instanceof Error?i.message:String(i)}`)})},n.product?.runtime.policy.pollIntervalMs??j(r.pollIntervalMs)),n.timer.unref(),!r.deferInitialPoll&&!a&&this.enqueueOperation(n,()=>this.pollProductOnce(n,this.readSecret(n.config))).catch(i=>{console.error(`[wechat-rpa] initial poll failed id=${e.id}: ${i instanceof Error?i.message:String(i)}`)});return}n.timer||(n.timer=setInterval(()=>{this.enqueueOperation(n,()=>this.pollOnce(n,this.readSecret(n.config))).catch(()=>{})},j(r.pollIntervalMs)),n.timer.unref(),r.deferInitialPoll||this.enqueueOperation(n,()=>this.pollOnce(n,this.readSecret(n.config))).catch(()=>{}))}async disconnect(e){const r=this.connections.get(e.id);if(!r)return;r.stopped=!0,r.timer&&clearInterval(r.timer),r.timer=null;const n=r.product;await this.enqueueOperation(r,async()=>{n&&await n.runner.stop()}).catch(()=>{}),this.connections.delete(e.id)}async syncNow(e){if(!e.enabled)throw new Error("WeChat RPA channel is disabled");const r=this.readSecret(e),n=this.ensureConnection(e);return n.stopped=!1,n.config=e,M(n,e),O(n,e),this.seedConfiguredConversations(n,r),y(r.source)?this.enqueueOperation(n,async()=>{const a=this.readSecret(n.config);if(await this.stopProductWhenControlPlaneDisabled(n,a))return[];const i=await this.pollProductOnce(n,a,{forceObserve:!0}),s=n.product;if(!s)return i;const u=ce({config:n.config,product:s,limit:a.recentLimit});return Xe(u,i)}):this.enqueueOperation(n,()=>this.pollOnce(n,this.readSecret(n.config)))}async cancelOutbound(e,r){const n=this.readSecret(e);return y(n.source)?ie({config:e,idempotencyKey:r.idempotencyKey,replyId:r.replyId,reason:r.reason}):{cancelled:!1,status:"unsupported_source"}}async send(e,r){const n=this.readSecret(e);if(n.canReply===!1)throw new Error("WeChat RPA channel does not allow replies");const a=this.ensureConnection(e);if(a.config=e,M(a,e),O(a,e),this.seedConfiguredConversations(a,n),y(n.source)){const d=this.resolveConversationName(e,n,r.conversationId);if(!d)throw new Error(`Unknown WeChat RPA conversation: ${r.conversationId}`);const h=r.text.trim();if(!h&&!r.attachment)throw new Error("Reply text or attachment is required");if(await this.stopProductWhenControlPlaneDisabled(a,n))throw new Error("WeChat RPA channel is disabled");return de({config:a.config,secret:n,reply:{...r,text:h},conversationName:d}),a.runtimeState="retrying",w(a,{status:"sent",runId:a.lastRunId??null,summary:`queued ${d}`}),this.enqueueOperation(a,()=>this.pollProductOnce(a,this.readSecret(a.config))).catch(v=>{a.lastError=v instanceof Error?v.message:String(v)}),{status:"queued"}}if(!Ee(n.source))throw new Error("WeChat RPA reply requires source=macos-flow, windows-visual-flow, or wechat-rpa-lab");const i=this.resolveConversationName(e,n,r.conversationId);if(!i)throw new Error(`Unknown WeChat RPA conversation: ${r.conversationId}`);const s=Te(e,r);if(a.completedPendingReplyKeys.has(s))return{status:"sent"};const u=a.manualReviewReplies.get(s);if(u)return{status:"manual-review",reason:u.reason};const o=r.attachment?await Be(e.workDir,r.attachment):void 0,c=r.text.trim();if(!c&&!o)throw new Error("Reply text or attachment is required");if(Z(a,n)){const d="WeChat RPA is cooling down after repeated user activity";return a.runtimeState="cooldown",T(a,{key:s,conversationId:r.conversationId,conversationName:i,text:c,attachmentPath:o,reason:d}),{status:"queued",reason:d}}let l;try{a.runtimeState="syncing",l=await this.enqueueOperation(a,()=>(a.lastRunAt=new Date().toISOString(),V(e,n,i,c,o,s)))}catch(d){throw a.lastError=d instanceof Error?d.message:String(d),w(a,{status:b(a.lastError),runId:a.lastRunId??null,summary:a.lastError}),d}$(a,l);const p=Y(l,{text:c,attachmentPath:o});if(p.status==="queued"){const d=p.reason;return T(a,{key:s,conversationId:r.conversationId,conversationName:i,text:p.text,attachmentPath:p.attachmentPath,reason:d,skipText:p.skipText}),a.lastError=d,{status:"queued",reason:d}}if(l.interrupted){const d=l.error||"WeChat RPA send was interrupted by user activity";return _(a,l,d),T(a,{key:s,conversationId:r.conversationId,conversationName:i,text:p.text,attachmentPath:o,reason:d,skipText:p.skipText}),{status:"queued",reason:d}}if(!l.ok){const d=l.error||"WeChat RPA send validator failed; manual review required before retry";return Q(a,s,d),{status:"manual-review",reason:d}}return w(a,{status:"sent",runId:a.lastRunId??null,summary:l.rpaTraceSummary||`sent ${i}`}),C(a),a.lastError=null,{status:"sent"}}async health(e){const r=this.readSecret(e);if((r.source??"macos-probe")==="fixture-jsonl")return r.fixturePath&&m.existsSync(r.fixturePath)?{ok:!0}:{ok:!1,message:"WeChat RPA fixture file is missing"};if(r.source==="macos-flow"){if(process.platform!=="darwin")return{ok:!1,message:"WeChat RPA macOS flow requires macOS"};if(!g(r).length)return{ok:!1,message:"WeChat RPA macOS flow requires at least one group"}}if(y(r.source))return Se(r,process.platform);if(r.source==="wechat-rpa-lab"){if(process.platform!=="darwin")return{ok:!1,message:"WeChat RPA Lab source requires macOS for live runs"};if(!g(r).length)return{ok:!1,message:"WeChat RPA Lab source requires at least one group"};try{J(e.workDir)}catch(a){return{ok:!1,message:a instanceof Error?a.message:String(a)}}return{ok:!0,message:"WeChat RPA Lab source configured"}}if(r.source==="windows-visual-flow")return ve(r,process.platform);const n=await q();return n.ok?{ok:!0,message:n.wechatRunning?"WeChat detected":"WeChat is not running"}:{ok:!1,message:n.message}}async defaultConversation(e){const r=this.readSecret(e),n=g(r)[0];if(!n)throw new Error("WeChat RPA channel has no configured groups");return y(r.source)?{conversationId:N(n),conversationName:n}:{conversationId:x(n),conversationName:n}}runtimeStatus(e){const r=this.connections.get(e.id);return r?{wechatRpaRuntimeState:r.runtimeState,wechatRpaLastRunAt:r.lastRunAt??null,wechatRpaLastMessageAt:r.lastMessageAt??null,wechatRpaLastInterruptedAt:r.lastInterruptedAt??null,wechatRpaLastError:r.lastError??null,wechatRpaLastRunId:r.lastRunId??null,wechatRpaLastTracePath:r.lastTracePath??r.product?.helperTracePath??null,wechatRpaLastTraceSummary:r.lastTraceSummary??null,wechatRpaPendingReplyCount:r.pendingReplies.size+oe({config:e}),wechatRpaPendingReplies:Me(r,e),wechatRpaRecentTaskSummaries:r.recentTaskSummaries}:{}}ensureConnection(e){let r=this.connections.get(e.id);return r||(r={config:e,timer:null,deduper:new K,stopped:!1,conversations:new Map,pendingReplies:new Map,manualReviewReplies:new Map,completedPendingReplyKeys:new Set,pendingStatePath:void 0,messageStatePath:void 0,operation:Promise.resolve(),recentTaskSummaries:[],runtimeState:"idle_waiting",consecutiveInterruptions:0},this.connections.set(e.id,r)),r}enqueueOperation(e,r){const n=this.options.automationLane?()=>this.options.automationLane?.run(`wechat-rpa:${e.config.id}`,r):r,a=e.operation.then(n,n);return e.operation=a.then(()=>{},()=>{}),a}readSecret(e){const r=this.secrets.get(e.secretRef);if(!r||r.type!=="wechat-rpa")throw new Error("WeChat RPA channel is not configured on this daemon");return r}async pollOnce(e,r){if(e.stopped)return[];const n=[];e.lastRunAt=new Date().toISOString();try{if(Z(e,r))return e.runtimeState="cooldown",[];if(await this.drainPendingReplies(e,r))return[];let i=!1;e.runtimeState="syncing";const s=await this.readObservedMessages(e.config,r,u=>{$(e,u),u.interrupted&&(i=!0,_(e,u,u.error||"WeChat RPA poll was interrupted by user activity"))});e.lastError=null;for(const u of s){const o=se(u,{selfNicknames:U(r)});if(!o||!e.deduper.accept(o.messageId))continue;De(e),e.conversations.set(o.conversationId,o.conversationName),e.lastMessageAt=o.receivedAt;const c={type:"external.message",managerSessionId:e.config.managerSessionId,channelId:e.config.id,channelType:"wechat-rpa",conversationId:o.conversationId,conversationName:o.conversationName,messageId:o.messageId,sender:o.sender,text:o.text,attachments:o.attachments,receivedAt:o.receivedAt,isMentioned:o.isMentioned,replyTarget:"",rawRef:o.rawRef};n.push(this.onMessage?.(c)??c),w(e,{status:"received",runId:e.lastRunId??null,summary:`received ${o.conversationName}${o.sender.name?` from ${o.sender.name}`:""}: ${L(o.text||o.attachments[0]?.name||"attachment")}`})}return i||C(e),n}catch(a){throw e.lastError=a instanceof Error?a.message:String(a),w(e,{status:b(e.lastError),runId:e.lastRunId??null,summary:e.lastError}),a}}async pollProductOnce(e,r,n){if(e.stopped)return[];if(!y(r.source))return[];if(await this.stopProductWhenControlPlaneDisabled(e,r))return[];const a=await this.ensureProductConnection(e,r);await B(a).catch(()=>null);const i=a.emitted.length;e.lastRunAt=new Date().toISOString(),e.runtimeState="syncing";try{await a.runner.tick(n);const s=a.emitted.slice(i);e.lastError=null;for(const u of s)e.conversations.set(u.conversationId,u.conversationName||""),e.lastMessageAt=u.receivedAt,w(e,{status:"received",runId:e.lastRunId??null,summary:`received ${u.conversationName||u.conversationId}${u.sender.name?` from ${u.sender.name}`:""}: ${L(u.text||u.attachments[0]?.name||"attachment")}`});return C(e),s}catch(s){throw e.lastError=s instanceof Error?s.message:String(s),e.runtimeState=b(e.lastError)==="blocked"?"interrupted":"idle_waiting",w(e,{status:b(e.lastError),runId:e.lastRunId??null,summary:e.lastError}),s}}async ensureProductConnection(e,r){const n=ge(e.config,r);return e.product&&e.productConfigKey===n||(e.product&&await e.product.runner.stop().catch(()=>{}),e.product=ue({config:e.config,secret:r,createRunner:this.options.createProductRunner,onMessage:this.onMessage}),await B(e.product).catch(()=>{}),e.productConfigKey=n,e.lastTracePath=e.product.helperTracePath),e.product}async stopProductWhenControlPlaneDisabled(e,r){if(!y(r.source))return!1;let n=null;try{n=await we(e.config,r)}catch(a){return e.productDisabled?(e.lastRunAt=new Date().toISOString(),e.runtimeState="idle_waiting",e.lastError=a instanceof Error?a.message:String(a),!0):!1}if(n?.enabled!==!1)return e.productDisabled=!1,!1;if(e.productDisabled=!0,e.lastRunAt=new Date().toISOString(),e.runtimeState="idle_waiting",e.lastError=null,e.product){const a=e.product;e.product=void 0,e.productConfigKey=void 0,await a.runner.stop().catch(()=>{})}return!0}async drainPendingReplies(e,r){if(!e.pendingReplies.size)return!1;for(const n of Array.from(e.pendingReplies.values())){e.runtimeState="retrying",n.attempts+=1,n.lastAttemptAt=new Date().toISOString(),R(e);const a=await V(e.config,r,n.conversationName,n.skipText?"":n.text,n.attachmentPath,n.key);$(e,a);const i=Y(a,{text:n.skipText?"":n.text,attachmentPath:n.attachmentPath});if(i.status==="queued")return n.text=i.text,n.attachmentPath=i.attachmentPath,n.skipText=i.skipText,n.lastInterruptedReason=i.reason,e.lastError=i.reason,R(e),!0;if(a.interrupted){const s=a.error||"WeChat RPA pending send was interrupted by user activity";return n.text=i.text,n.attachmentPath=i.attachmentPath,n.skipText=i.skipText,n.lastInterruptedReason=s,_(e,a,s),R(e),!0}if(!a.ok){const s=a.error||"WeChat RPA pending send validator failed; manual review required before retry";return e.pendingReplies.delete(n.key),Q(e,n.key,s),R(e),!0}e.pendingReplies.delete(n.key),e.completedPendingReplyKeys.add(n.key),R(e),e.lastError=null,w(e,{status:"sent",runId:e.lastRunId??null,summary:a.rpaTraceSummary||`sent ${n.conversationName}`}),C(e)}return!1}async readObservedMessages(e,r,n){if(r.source==="fixture-jsonl")return He(r.fixturePath);if(r.source==="macos-flow")return Ne(e,r,n);if(r.source==="wechat-rpa-lab")return Pe(e,r,n);if(r.source==="windows-visual-flow")return We(e,r,n);const a=await q(),i=ae(a);return i?[i]:[]}seedConfiguredConversations(e,r){for(const n of g(r))e.conversations.set(x(n),n),e.conversations.set(N(n),n)}resolveConversationName(e,r,n){const i=this.connections.get(e.id)?.conversations.get(n);return i||(g(r).find(s=>x(s)===n||N(s)===n)??null)}}function ge(t,e){return JSON.stringify({id:t.id,workDir:t.workDir,managerSessionId:t.managerSessionId,pollIntervalMs:e.pollIntervalMs,forceForeground:k(e),downloadAttachments:e.downloadAttachments!==!1,canReply:e.canReply!==!1,selfTriggerMarker:e.selfTriggerMarker||"",groups:g(e)})}async function we(t,e,r){const n=r??H(),a=await n.getRuntimePolicy();if(a.feature?.enabled===!1)return{enabled:!1,reasonCode:a.feature.reasonCode||"feature_disabled"};if(a.runtime?.current?.status==="disabled")return{enabled:!1,reasonCode:"runtime_disabled"};const i=t.sessionId||t.managerSessionId;if(!i)return{enabled:!1,reasonCode:"session_missing"};let s;try{s=(await n.getBinding({sessionId:i})).binding}catch(o){const c=ye(o);if(c==="binding_not_found"||c==="session_not_found"||c==="machine_not_found")return{enabled:!1,reasonCode:c};throw o}if(!s||s.enabled===!1)return{enabled:!1,reasonCode:"no_enabled_binding"};const u=g(e);return u.length?u.includes(s.conversationName)?{enabled:!0}:{enabled:!1,reasonCode:"binding_conversation_changed"}:{enabled:!1,reasonCode:"no_configured_conversation"}}async function B(t){const r=(await H().getRuntimePolicy()).runtime?.current;return t.runtime.foregroundPolicy="work",{enabled:r?.status!=="disabled"}}function ye(t){if(t&&typeof t=="object"){const e=t.reasonCode;if(typeof e=="string")return e}return""}function j(t){return Number.isFinite(t)?Math.min(6e4,Math.max(1e3,Number(t))):pe}function g(t){return Array.isArray(t.groups)?t.groups.map(e=>String(e?.name||"").trim()).filter(Boolean):[]}function U(t){return String(t.selfNickname||"").split(/[,\n,、]/).map(e=>e.trim()).filter(Boolean)}function Re(t,e){return e.length?e.some(r=>{const n=r.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");return new RegExp(`@\\s*${n}(?=$|\\s|[\uFF0C\u3002\uFF01\uFF1F,.!?:\uFF1A\uFF1B;\u3001)])`).test(t)}):!1}function ve(t,e=process.platform){return e!=="win32"?{ok:!1,message:"WeChat RPA Windows visual flow requires Windows"}:{ok:!1,message:"WeChat RPA Windows visual flow is archived; redesign Windows support before enabling it"}}function Se(t,e=process.platform){return e!=="darwin"&&e!=="win32"?{ok:!1,message:"WeChat channel product source requires macOS or Windows"}:(Array.isArray(t.groups)?t.groups.map(n=>String(n?.name||"").trim()).filter(Boolean):[]).length?{ok:!0,message:"WeChat channel product source configured"}:{ok:!1,message:"WeChat channel product source requires at least one group"}}async function Pe(t,e,r){const n=await z(t.workDir),a=[];for(const i of g(e)){const s=await Ie(n,t,e,i);if(r?.(s),!(!s.ok||s.interrupted))for(const u of s.newMessages??[]){const o=G(i,u,e);o&&a.push(o)}}return a}async function Ie(t,e,r,n){const a=me(r.recentLimit),i={kind:a,requestId:`wechat-rpa:${n}:read:${Date.now()}`,targetGroup:n,policy:r.forceForeground?"work":"polite",limit:P(r.recentLimit,A,1,50),attachmentsDir:D(e,r)},s=await t.runWechatRpaReadLatest(i),u=Array.isArray(s.data?.structuredMessages)?s.data.structuredMessages:[],o=s.validation?.deterministic?.latestMessages??[],c=u.length?u.map((l,p)=>Ae(l,s.runId||"lab-read",p)):o.map((l,p)=>({id:`${s.runId||"lab-read"}:${p}`,text:l,confidence:.8}));return{ok:!!s.ok,groupName:n,interrupted:s.status==="interrupted",reason:s.status,rpaRunId:s.runId,rpaTraceSummary:`${a} ${s.status||(s.ok?"success":"failed")} ${n}`,recentMessages:c,newMessages:c,screenshotPath:s.tracePath,error:X(s)}}function Ae(t,e,r){return{id:`${e}:${t.index??r}`,text:String(t.text||t.card?.title||"").trim(),confidence:typeof t.confidence=="number"?t.confidence:.75,senderName:typeof t.senderName=="string"?t.senderName:null,attachments:Ce(t)}}function Ce(t){const e=String(t.kind||"");if(!["image","file","video-file","video-card","link-card","official-account-card","mini-program-card"].includes(e))return[];const r=typeof t.localPath=="string"?t.localPath:void 0;return[{type:e==="video-file"||e==="video-card"?"video":e==="link-card"||e==="official-account-card"||e==="mini-program-card"?"file":e,localPath:r,availability:r?"edge-local":"metadata-only"}]}async function be(t,e,r,n,a,i){const s=await z(t.workDir),u=ke({groupName:r,text:n,attachmentPath:a,dedupeKey:i,requestId:i,policy:"work"}),o=[];for(const h of u){const v=await s.runWechatRpaTask(h);if(o.push(v),!v.ok)break}const c=o.find(h=>h.kind==="send-text"),l=o.find(h=>h.kind==="send-image"||h.kind==="send-video"||h.kind==="send-file"),p=u.length>0&&o.length===u.length&&o.every(h=>h.ok),d=o.some(h=>h.status==="interrupted");return{ok:p,groupName:r,interrupted:d,reason:o.at(-1)?.status,rpaRunId:o.at(-1)?.runId,rpaTraceSummary:`send ${p?"success":o.at(-1)?.status||"failed"} ${r} (${o.length}/${u.length})`,sentReply:!!n,sentReplyObserved:n?!!c?.ok:!1,sentAttachment:!!a,sentAttachmentObserved:a?!!l?.ok:!1,postSendScreenshotPath:o.at(-1)?.tracePath,error:p?void 0:X(o.at(-1))}}function ke(t){const e=[],r=String(t.text||"").trim(),n=t.policy||"work";if(r&&e.push({kind:"send-text",requestId:t.requestId?`${t.requestId}:text`:void 0,targetGroup:t.groupName,policy:n,text:r,...t.dedupeKey?{dedupeKey:`${t.dedupeKey}:text`}:{}}),t.attachmentPath){const a=xe(t.attachmentPath);e.push({kind:a,requestId:t.requestId?`${t.requestId}:attachment`:void 0,targetGroup:t.groupName,policy:n,filePath:t.attachmentPath,...t.dedupeKey?{dedupeKey:`${t.dedupeKey}:attachment`}:{}})}return e}function xe(t){const e=f.extname(t).toLowerCase();return[".png",".jpg",".jpeg",".gif",".webp",".heic"].includes(e)?"send-image":[".mp4",".mov",".avi",".mkv"].includes(e)?"send-video":"send-file"}async function z(t){const e=J(t);return await import(te(e).href)}function J(t){const e=f.dirname(ee(import.meta.url)),n=[process.env.SHENNIAN_WECHAT_RPA_LAB_INDEX,f.resolve(e,"../../../../scripts/wechat-rpa-lab/index.mjs"),t?f.join(t,"scripts/wechat-rpa-lab/index.mjs"):"",f.resolve(process.cwd(),"scripts/wechat-rpa-lab/index.mjs")].filter(Boolean).find(a=>m.existsSync(a));if(!n)throw new Error("WeChat RPA Lab API is missing; set SHENNIAN_WECHAT_RPA_LAB_INDEX or run from the repository root");return f.resolve(n)}function X(t){return t?typeof t.error=="string"?t.error:t.error?.message||(t.ok?void 0:`WeChat RPA Lab task ${t.kind||"<unknown>"} ${t.status||"failed"}`):"WeChat RPA Lab did not return a result"}async function Ne(t,e,r){const n=[];for(const a of g(e)){const i=await F({groupName:a,scriptPath:e.flowScriptPath,workDir:t.workDir,forceForeground:k(e),noRestore:e.noRestore!==!1,idleSeconds:P(e.idleSeconds,15,0,3600),recentLimit:P(e.recentLimit,A,0,50),downloadAttachmentsDir:D(t,e)});if(r?.(i),!i.interrupted)for(const s of i.newMessages??[]){const u=G(a,s,e);u&&n.push(u)}}return n}async function We(t,e,r){const n=g(e)[0]||"<unbound>";return r?.({ok:!1,groupName:n,interrupted:!0,reason:"windows-visual-flow-archived",newMessages:[],recentMessages:[],error:"WeChat RPA Windows visual flow is archived; redesign Windows support before enabling it"}),[]}function G(t,e,r){const n=String(e.text||"").trim(),a=Array.isArray(e.attachments)?e.attachments:[];if(!n&&a.length===0)return null;const i=typeof e.senderName=="string"?e.senderName.trim():"",s=String(e.observedAt||e.timestampIso||"").trim()||new Date().toISOString();return{conversationName:t,senderName:i||null,text:n,attachments:Je(a),observedAt:s,isMentioned:e.isMentioned===!0||Re(n,U(r)),rawId:String(e.id||`${t}:${i}:${n}:${s}`)}}async function V(t,e,r,n,a,i){return e.source==="windows-visual-flow"?{ok:!1,groupName:r,interrupted:!0,reason:"windows-visual-flow-archived",newMessages:[],recentMessages:[],error:"WeChat RPA Windows visual flow is archived; redesign Windows support before enabling it"}:e.source==="wechat-rpa-lab"?be(t,e,r,n,a,i):F({groupName:r,replyText:n||void 0,attachmentPath:a,scriptPath:e.flowScriptPath,workDir:t.workDir,forceForeground:k(e),noRestore:e.noRestore!==!1,idleSeconds:P(e.idleSeconds,15,0,3600),recentLimit:P(e.recentLimit,A,0,50),downloadAttachmentsDir:D(t,e)})}function Ee(t){return t==="macos-flow"||t==="wechat-rpa-lab"}function Y(t,e){return e.text&&e.attachmentPath&&t.sentReplyObserved&&!t.sentAttachmentObserved?{status:t.interrupted?"continue":"queued",text:"",attachmentPath:e.attachmentPath,skipText:!0,reason:t.error||"WeChat RPA sent the text but did not confirm the attachment; queued attachment-only retry"}:{status:"continue",text:e.text,attachmentPath:e.attachmentPath,reason:t.error||""}}function Te(t,e){return e.idempotencyKey?e.idempotencyKey:I.createHash("sha256").update(`${t.id}
1
+ import I from"node:crypto";import m from"node:fs";import h from"node:path";import{fileURLToPath as ee,pathToFileURL as te}from"node:url";import{ChannelSecretRegistry as re}from"./secret-registry.js";import{resolveShennianPath as ne}from"../config/index.js";import{probeMacWeChat as q,observedMessageFromProbe as ae}from"./wechat-rpa/macos.js";import{runMacWeChatRpaFlow as F}from"./wechat-rpa/macos-flow.js";import{normalizeWeChatRpaMessage as se,WeChatRpaDeduper as K,weChatRpaConversationId as x}from"./wechat-rpa/normalizer.js";import{cancelWeChatRpaProductOutboundReply as ie,countPendingWeChatRpaProductOutbound as oe,createWeChatRpaProductSourceConnection as ue,enqueueWeChatRpaProductOutboundReply as de,isWeChatRpaProductSource as y,listPendingWeChatRpaProductOutbound as ce,weChatChannelConversationId as N}from"./wechat-rpa/product-channel.js";import{createWeChatChannelApiClient as H}from"./wechat-channel/client.js";const le=5e3,A=5,pe=8,S=Number(process.env.SHENNIAN_WECHAT_RPA_OUTBOUND_ATTACHMENT_MAX_BYTES||20*1024*1024),W=Number(process.env.SHENNIAN_WECHAT_RPA_INTERRUPTION_COOLDOWN_THRESHOLD||3),E=Number(process.env.SHENNIAN_WECHAT_RPA_INTERRUPTION_COOLDOWN_MS||300*1e3),he=10;function fe(t){return P(t,A,1,50)>=pe?"scroll-read":"read-latest"}class at{onMessage;options;type="wechat-rpa";secrets=new re;connections=new Map;constructor(e,r={}){this.onMessage=e,this.options=r}async connect(e){if(!e.enabled)return;const r=this.readSecret(e),n=this.ensureConnection(e);if(n.stopped=!1,n.config=e,O(n,e),M(n,e),this.seedConfiguredConversations(n,r),y(r.source)){const a=await this.stopProductWhenControlPlaneDisabled(n,r);if(a||await this.ensureProductConnection(n,r),n.timer)return;n.timer=setInterval(()=>{this.enqueueOperation(n,()=>this.pollProductOnce(n,this.readSecret(n.config))).catch(i=>{console.error(`[wechat-rpa] autonomous poll failed id=${e.id}: ${i instanceof Error?i.message:String(i)}`)})},n.product?.runtime.policy.pollIntervalMs??j(r.pollIntervalMs)),n.timer.unref(),!r.deferInitialPoll&&!a&&this.enqueueOperation(n,()=>this.pollProductOnce(n,this.readSecret(n.config))).catch(i=>{console.error(`[wechat-rpa] initial poll failed id=${e.id}: ${i instanceof Error?i.message:String(i)}`)});return}n.timer||(n.timer=setInterval(()=>{this.enqueueOperation(n,()=>this.pollOnce(n,this.readSecret(n.config))).catch(()=>{})},j(r.pollIntervalMs)),n.timer.unref(),r.deferInitialPoll||this.enqueueOperation(n,()=>this.pollOnce(n,this.readSecret(n.config))).catch(()=>{}))}async disconnect(e){const r=this.connections.get(e.id);if(!r)return;r.stopped=!0,r.timer&&clearInterval(r.timer),r.timer=null;const n=r.product;await this.enqueueOperation(r,async()=>{n&&await n.runner.stop()}).catch(()=>{}),this.connections.delete(e.id)}async syncNow(e){if(!e.enabled)throw new Error("WeChat RPA channel is disabled");const r=this.readSecret(e),n=this.ensureConnection(e);return n.stopped=!1,n.config=e,O(n,e),M(n,e),this.seedConfiguredConversations(n,r),y(r.source)?this.enqueueOperation(n,async()=>{const a=this.readSecret(n.config);return await this.stopProductWhenControlPlaneDisabled(n,a)?[]:this.pollProductOnce(n,a,{forceObserve:!0})}):this.enqueueOperation(n,()=>this.pollOnce(n,this.readSecret(n.config)))}async cancelOutbound(e,r){const n=this.readSecret(e);return y(n.source)?ie({config:e,idempotencyKey:r.idempotencyKey,replyId:r.replyId,reason:r.reason}):{cancelled:!1,status:"unsupported_source"}}async send(e,r){const n=this.readSecret(e);if(n.canReply===!1)throw new Error("WeChat RPA channel does not allow replies");const a=this.ensureConnection(e);if(a.config=e,O(a,e),M(a,e),this.seedConfiguredConversations(a,n),y(n.source)){const d=this.resolveConversationName(e,n,r.conversationId);if(!d)throw new Error(`Unknown WeChat RPA conversation: ${r.conversationId}`);const f=r.text.trim();if(!f&&!r.attachment)throw new Error("Reply text or attachment is required");if(await this.stopProductWhenControlPlaneDisabled(a,n))throw new Error("WeChat RPA channel is disabled");return de({config:a.config,secret:n,reply:{...r,text:f},conversationName:d}),a.runtimeState="retrying",w(a,{status:"sent",runId:a.lastRunId??null,summary:`queued ${d}`}),this.enqueueOperation(a,()=>this.pollProductOnce(a,this.readSecret(a.config))).catch(v=>{a.lastError=v instanceof Error?v.message:String(v)}),{status:"queued"}}if(!We(n.source))throw new Error("WeChat RPA reply requires source=macos-flow, windows-visual-flow, or wechat-rpa-lab");const i=this.resolveConversationName(e,n,r.conversationId);if(!i)throw new Error(`Unknown WeChat RPA conversation: ${r.conversationId}`);const s=Ee(e,r);if(a.completedPendingReplyKeys.has(s))return{status:"sent"};const u=a.manualReviewReplies.get(s);if(u)return{status:"manual-review",reason:u.reason};const o=r.attachment?await He(e.workDir,r.attachment):void 0,c=r.text.trim();if(!c&&!o)throw new Error("Reply text or attachment is required");if(Z(a,n)){const d="WeChat RPA is cooling down after repeated user activity";return a.runtimeState="cooldown",T(a,{key:s,conversationId:r.conversationId,conversationName:i,text:c,attachmentPath:o,reason:d}),{status:"queued",reason:d}}let l;try{a.runtimeState="syncing",l=await this.enqueueOperation(a,()=>(a.lastRunAt=new Date().toISOString(),V(e,n,i,c,o,s)))}catch(d){throw a.lastError=d instanceof Error?d.message:String(d),w(a,{status:b(a.lastError),runId:a.lastRunId??null,summary:a.lastError}),d}$(a,l);const p=Y(l,{text:c,attachmentPath:o});if(p.status==="queued"){const d=p.reason;return T(a,{key:s,conversationId:r.conversationId,conversationName:i,text:p.text,attachmentPath:p.attachmentPath,reason:d,skipText:p.skipText}),a.lastError=d,{status:"queued",reason:d}}if(l.interrupted){const d=l.error||"WeChat RPA send was interrupted by user activity";return _(a,l,d),T(a,{key:s,conversationId:r.conversationId,conversationName:i,text:p.text,attachmentPath:o,reason:d,skipText:p.skipText}),{status:"queued",reason:d}}if(!l.ok){const d=l.error||"WeChat RPA send validator failed; manual review required before retry";return Q(a,s,d),{status:"manual-review",reason:d}}return w(a,{status:"sent",runId:a.lastRunId??null,summary:l.rpaTraceSummary||`sent ${i}`}),C(a),a.lastError=null,{status:"sent"}}async health(e){const r=this.readSecret(e);if((r.source??"macos-probe")==="fixture-jsonl")return r.fixturePath&&m.existsSync(r.fixturePath)?{ok:!0}:{ok:!1,message:"WeChat RPA fixture file is missing"};if(r.source==="macos-flow"){if(process.platform!=="darwin")return{ok:!1,message:"WeChat RPA macOS flow requires macOS"};if(!g(r).length)return{ok:!1,message:"WeChat RPA macOS flow requires at least one group"}}if(y(r.source))return ve(r,process.platform);if(r.source==="wechat-rpa-lab"){if(process.platform!=="darwin")return{ok:!1,message:"WeChat RPA Lab source requires macOS for live runs"};if(!g(r).length)return{ok:!1,message:"WeChat RPA Lab source requires at least one group"};try{J(e.workDir)}catch(a){return{ok:!1,message:a instanceof Error?a.message:String(a)}}return{ok:!0,message:"WeChat RPA Lab source configured"}}if(r.source==="windows-visual-flow")return Re(r,process.platform);const n=await q();return n.ok?{ok:!0,message:n.wechatRunning?"WeChat detected":"WeChat is not running"}:{ok:!1,message:n.message}}async defaultConversation(e){const r=this.readSecret(e),n=g(r)[0];if(!n)throw new Error("WeChat RPA channel has no configured groups");return y(r.source)?{conversationId:N(n),conversationName:n}:{conversationId:x(n),conversationName:n}}runtimeStatus(e){const r=this.connections.get(e.id);return r?{wechatRpaRuntimeState:r.runtimeState,wechatRpaLastRunAt:r.lastRunAt??null,wechatRpaLastMessageAt:r.lastMessageAt??null,wechatRpaLastInterruptedAt:r.lastInterruptedAt??null,wechatRpaLastError:r.lastError??null,wechatRpaLastRunId:r.lastRunId??null,wechatRpaLastTracePath:r.lastTracePath??r.product?.helperTracePath??null,wechatRpaLastTraceSummary:r.lastTraceSummary??null,wechatRpaPendingReplyCount:r.pendingReplies.size+oe({config:e}),wechatRpaPendingReplies:Te(r,e),wechatRpaRecentTaskSummaries:r.recentTaskSummaries}:{}}ensureConnection(e){let r=this.connections.get(e.id);return r||(r={config:e,timer:null,deduper:new K,stopped:!1,conversations:new Map,pendingReplies:new Map,manualReviewReplies:new Map,completedPendingReplyKeys:new Set,pendingStatePath:void 0,messageStatePath:void 0,operation:Promise.resolve(),recentTaskSummaries:[],runtimeState:"idle_waiting",consecutiveInterruptions:0},this.connections.set(e.id,r)),r}enqueueOperation(e,r){const n=this.options.automationLane?()=>this.options.automationLane?.run(`wechat-rpa:${e.config.id}`,r):r,a=e.operation.then(n,n);return e.operation=a.then(()=>{},()=>{}),a}readSecret(e){const r=this.secrets.get(e.secretRef);if(!r||r.type!=="wechat-rpa")throw new Error("WeChat RPA channel is not configured on this daemon");return r}async pollOnce(e,r){if(e.stopped)return[];const n=[];e.lastRunAt=new Date().toISOString();try{if(Z(e,r))return e.runtimeState="cooldown",[];if(await this.drainPendingReplies(e,r))return[];let i=!1;e.runtimeState="syncing";const s=await this.readObservedMessages(e.config,r,u=>{$(e,u),u.interrupted&&(i=!0,_(e,u,u.error||"WeChat RPA poll was interrupted by user activity"))});e.lastError=null;for(const u of s){const o=se(u,{selfNicknames:U(r)});if(!o||!e.deduper.accept(o.messageId))continue;De(e),e.conversations.set(o.conversationId,o.conversationName),e.lastMessageAt=o.receivedAt;const c={type:"external.message",managerSessionId:e.config.managerSessionId,channelId:e.config.id,channelType:"wechat-rpa",conversationId:o.conversationId,conversationName:o.conversationName,messageId:o.messageId,sender:o.sender,text:o.text,attachments:o.attachments,receivedAt:o.receivedAt,isMentioned:o.isMentioned,replyTarget:"",rawRef:o.rawRef};n.push(this.onMessage?.(c)??c),w(e,{status:"received",runId:e.lastRunId??null,summary:`received ${o.conversationName}${o.sender.name?` from ${o.sender.name}`:""}: ${D(o.text||o.attachments[0]?.name||"attachment")}`})}return i||C(e),n}catch(a){throw e.lastError=a instanceof Error?a.message:String(a),w(e,{status:b(e.lastError),runId:e.lastRunId??null,summary:e.lastError}),a}}async pollProductOnce(e,r,n){if(e.stopped)return[];if(!y(r.source))return[];if(await this.stopProductWhenControlPlaneDisabled(e,r))return[];const a=await this.ensureProductConnection(e,r);await B(a).catch(()=>null);const i=a.emitted.length;e.lastRunAt=new Date().toISOString(),e.runtimeState="syncing";try{await a.runner.tick(n);const s=a.emitted.slice(i);e.lastError=null;for(const u of s)e.conversations.set(u.conversationId,u.conversationName||""),e.lastMessageAt=u.receivedAt,w(e,{status:"received",runId:e.lastRunId??null,summary:`received ${u.conversationName||u.conversationId}${u.sender.name?` from ${u.sender.name}`:""}: ${D(u.text||u.attachments[0]?.name||"attachment")}`});return C(e),s}catch(s){throw e.lastError=s instanceof Error?s.message:String(s),e.runtimeState=b(e.lastError)==="blocked"?"interrupted":"idle_waiting",w(e,{status:b(e.lastError),runId:e.lastRunId??null,summary:e.lastError}),s}}async ensureProductConnection(e,r){const n=me(e.config,r);return e.product&&e.productConfigKey===n||(e.product&&await e.product.runner.stop().catch(()=>{}),e.product=ue({config:e.config,secret:r,createRunner:this.options.createProductRunner,onMessage:this.onMessage}),await B(e.product).catch(()=>{}),e.productConfigKey=n,e.lastTracePath=e.product.helperTracePath),e.product}async stopProductWhenControlPlaneDisabled(e,r){if(!y(r.source))return!1;let n=null;try{n=await ge(e.config,r)}catch(a){return e.productDisabled?(e.lastRunAt=new Date().toISOString(),e.runtimeState="idle_waiting",e.lastError=a instanceof Error?a.message:String(a),!0):!1}if(n?.enabled!==!1)return e.productDisabled=!1,!1;if(e.productDisabled=!0,e.lastRunAt=new Date().toISOString(),e.runtimeState="idle_waiting",e.lastError=null,e.product){const a=e.product;e.product=void 0,e.productConfigKey=void 0,await a.runner.stop().catch(()=>{})}return!0}async drainPendingReplies(e,r){if(!e.pendingReplies.size)return!1;for(const n of Array.from(e.pendingReplies.values())){e.runtimeState="retrying",n.attempts+=1,n.lastAttemptAt=new Date().toISOString(),R(e);const a=await V(e.config,r,n.conversationName,n.skipText?"":n.text,n.attachmentPath,n.key);$(e,a);const i=Y(a,{text:n.skipText?"":n.text,attachmentPath:n.attachmentPath});if(i.status==="queued")return n.text=i.text,n.attachmentPath=i.attachmentPath,n.skipText=i.skipText,n.lastInterruptedReason=i.reason,e.lastError=i.reason,R(e),!0;if(a.interrupted){const s=a.error||"WeChat RPA pending send was interrupted by user activity";return n.text=i.text,n.attachmentPath=i.attachmentPath,n.skipText=i.skipText,n.lastInterruptedReason=s,_(e,a,s),R(e),!0}if(!a.ok){const s=a.error||"WeChat RPA pending send validator failed; manual review required before retry";return e.pendingReplies.delete(n.key),Q(e,n.key,s),R(e),!0}e.pendingReplies.delete(n.key),e.completedPendingReplyKeys.add(n.key),R(e),e.lastError=null,w(e,{status:"sent",runId:e.lastRunId??null,summary:a.rpaTraceSummary||`sent ${n.conversationName}`}),C(e)}return!1}async readObservedMessages(e,r,n){if(r.source==="fixture-jsonl")return Ke(r.fixturePath);if(r.source==="macos-flow")return xe(e,r,n);if(r.source==="wechat-rpa-lab")return Se(e,r,n);if(r.source==="windows-visual-flow")return Ne(e,r,n);const a=await q(),i=ae(a);return i?[i]:[]}seedConfiguredConversations(e,r){for(const n of g(r))e.conversations.set(x(n),n),e.conversations.set(N(n),n)}resolveConversationName(e,r,n){const i=this.connections.get(e.id)?.conversations.get(n);return i||(g(r).find(s=>x(s)===n||N(s)===n)??null)}}function me(t,e){return JSON.stringify({id:t.id,workDir:t.workDir,managerSessionId:t.managerSessionId,pollIntervalMs:e.pollIntervalMs,forceForeground:k(e),downloadAttachments:e.downloadAttachments!==!1,canReply:e.canReply!==!1,selfTriggerMarker:e.selfTriggerMarker||"",groups:g(e)})}async function ge(t,e,r){const n=r??H(),a=await n.getRuntimePolicy();if(a.feature?.enabled===!1)return{enabled:!1,reasonCode:a.feature.reasonCode||"feature_disabled"};if(a.runtime?.current?.status==="disabled")return{enabled:!1,reasonCode:"runtime_disabled"};const i=t.sessionId||t.managerSessionId;if(!i)return{enabled:!1,reasonCode:"session_missing"};let s;try{s=(await n.getBinding({sessionId:i})).binding}catch(o){const c=we(o);if(c==="binding_not_found"||c==="session_not_found"||c==="machine_not_found")return{enabled:!1,reasonCode:c};throw o}if(!s||s.enabled===!1)return{enabled:!1,reasonCode:"no_enabled_binding"};const u=g(e);return u.length?u.includes(s.conversationName)?{enabled:!0}:{enabled:!1,reasonCode:"binding_conversation_changed"}:{enabled:!1,reasonCode:"no_configured_conversation"}}async function B(t){const r=(await H().getRuntimePolicy()).runtime?.current;return t.runtime.foregroundPolicy="work",{enabled:r?.status!=="disabled"}}function we(t){if(t&&typeof t=="object"){const e=t.reasonCode;if(typeof e=="string")return e}return""}function j(t){return Number.isFinite(t)?Math.min(6e4,Math.max(1e3,Number(t))):le}function g(t){return Array.isArray(t.groups)?t.groups.map(e=>String(e?.name||"").trim()).filter(Boolean):[]}function U(t){return String(t.selfNickname||"").split(/[,\n,、]/).map(e=>e.trim()).filter(Boolean)}function ye(t,e){return e.length?e.some(r=>{const n=r.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");return new RegExp(`@\\s*${n}(?=$|\\s|[\uFF0C\u3002\uFF01\uFF1F,.!?:\uFF1A\uFF1B;\u3001)])`).test(t)}):!1}function Re(t,e=process.platform){return e!=="win32"?{ok:!1,message:"WeChat RPA Windows visual flow requires Windows"}:{ok:!1,message:"WeChat RPA Windows visual flow is archived; redesign Windows support before enabling it"}}function ve(t,e=process.platform){return e!=="darwin"&&e!=="win32"?{ok:!1,message:"WeChat channel product source requires macOS or Windows"}:(Array.isArray(t.groups)?t.groups.map(n=>String(n?.name||"").trim()).filter(Boolean):[]).length?{ok:!0,message:"WeChat channel product source configured"}:{ok:!1,message:"WeChat channel product source requires at least one group"}}async function Se(t,e,r){const n=await z(t.workDir),a=[];for(const i of g(e)){const s=await Pe(n,t,e,i);if(r?.(s),!(!s.ok||s.interrupted))for(const u of s.newMessages??[]){const o=G(i,u,e);o&&a.push(o)}}return a}async function Pe(t,e,r,n){const a=fe(r.recentLimit),i={kind:a,requestId:`wechat-rpa:${n}:read:${Date.now()}`,targetGroup:n,policy:r.forceForeground?"work":"polite",limit:P(r.recentLimit,A,1,50),attachmentsDir:L(e,r)},s=await t.runWechatRpaReadLatest(i),u=Array.isArray(s.data?.structuredMessages)?s.data.structuredMessages:[],o=s.validation?.deterministic?.latestMessages??[],c=u.length?u.map((l,p)=>Ie(l,s.runId||"lab-read",p)):o.map((l,p)=>({id:`${s.runId||"lab-read"}:${p}`,text:l,confidence:.8}));return{ok:!!s.ok,groupName:n,interrupted:s.status==="interrupted",reason:s.status,rpaRunId:s.runId,rpaTraceSummary:`${a} ${s.status||(s.ok?"success":"failed")} ${n}`,recentMessages:c,newMessages:c,screenshotPath:s.tracePath,error:X(s)}}function Ie(t,e,r){return{id:`${e}:${t.index??r}`,text:String(t.text||t.card?.title||"").trim(),confidence:typeof t.confidence=="number"?t.confidence:.75,senderName:typeof t.senderName=="string"?t.senderName:null,attachments:Ae(t)}}function Ae(t){const e=String(t.kind||"");if(!["image","file","video-file","video-card","link-card","official-account-card","mini-program-card"].includes(e))return[];const r=typeof t.localPath=="string"?t.localPath:void 0;return[{type:e==="video-file"||e==="video-card"?"video":e==="link-card"||e==="official-account-card"||e==="mini-program-card"?"file":e,localPath:r,availability:r?"edge-local":"metadata-only"}]}async function Ce(t,e,r,n,a,i){const s=await z(t.workDir),u=be({groupName:r,text:n,attachmentPath:a,dedupeKey:i,requestId:i,policy:"work"}),o=[];for(const f of u){const v=await s.runWechatRpaTask(f);if(o.push(v),!v.ok)break}const c=o.find(f=>f.kind==="send-text"),l=o.find(f=>f.kind==="send-image"||f.kind==="send-video"||f.kind==="send-file"),p=u.length>0&&o.length===u.length&&o.every(f=>f.ok),d=o.some(f=>f.status==="interrupted");return{ok:p,groupName:r,interrupted:d,reason:o.at(-1)?.status,rpaRunId:o.at(-1)?.runId,rpaTraceSummary:`send ${p?"success":o.at(-1)?.status||"failed"} ${r} (${o.length}/${u.length})`,sentReply:!!n,sentReplyObserved:n?!!c?.ok:!1,sentAttachment:!!a,sentAttachmentObserved:a?!!l?.ok:!1,postSendScreenshotPath:o.at(-1)?.tracePath,error:p?void 0:X(o.at(-1))}}function be(t){const e=[],r=String(t.text||"").trim(),n=t.policy||"work";if(r&&e.push({kind:"send-text",requestId:t.requestId?`${t.requestId}:text`:void 0,targetGroup:t.groupName,policy:n,text:r,...t.dedupeKey?{dedupeKey:`${t.dedupeKey}:text`}:{}}),t.attachmentPath){const a=ke(t.attachmentPath);e.push({kind:a,requestId:t.requestId?`${t.requestId}:attachment`:void 0,targetGroup:t.groupName,policy:n,filePath:t.attachmentPath,...t.dedupeKey?{dedupeKey:`${t.dedupeKey}:attachment`}:{}})}return e}function ke(t){const e=h.extname(t).toLowerCase();return[".png",".jpg",".jpeg",".gif",".webp",".heic"].includes(e)?"send-image":[".mp4",".mov",".avi",".mkv"].includes(e)?"send-video":"send-file"}async function z(t){const e=J(t);return await import(te(e).href)}function J(t){const e=h.dirname(ee(import.meta.url)),n=[process.env.SHENNIAN_WECHAT_RPA_LAB_INDEX,h.resolve(e,"../../../../scripts/wechat-rpa-lab/index.mjs"),t?h.join(t,"scripts/wechat-rpa-lab/index.mjs"):"",h.resolve(process.cwd(),"scripts/wechat-rpa-lab/index.mjs")].filter(Boolean).find(a=>m.existsSync(a));if(!n)throw new Error("WeChat RPA Lab API is missing; set SHENNIAN_WECHAT_RPA_LAB_INDEX or run from the repository root");return h.resolve(n)}function X(t){return t?typeof t.error=="string"?t.error:t.error?.message||(t.ok?void 0:`WeChat RPA Lab task ${t.kind||"<unknown>"} ${t.status||"failed"}`):"WeChat RPA Lab did not return a result"}async function xe(t,e,r){const n=[];for(const a of g(e)){const i=await F({groupName:a,scriptPath:e.flowScriptPath,workDir:t.workDir,forceForeground:k(e),noRestore:e.noRestore!==!1,idleSeconds:P(e.idleSeconds,15,0,3600),recentLimit:P(e.recentLimit,A,0,50),downloadAttachmentsDir:L(t,e)});if(r?.(i),!i.interrupted)for(const s of i.newMessages??[]){const u=G(a,s,e);u&&n.push(u)}}return n}async function Ne(t,e,r){const n=g(e)[0]||"<unbound>";return r?.({ok:!1,groupName:n,interrupted:!0,reason:"windows-visual-flow-archived",newMessages:[],recentMessages:[],error:"WeChat RPA Windows visual flow is archived; redesign Windows support before enabling it"}),[]}function G(t,e,r){const n=String(e.text||"").trim(),a=Array.isArray(e.attachments)?e.attachments:[];if(!n&&a.length===0)return null;const i=typeof e.senderName=="string"?e.senderName.trim():"",s=String(e.observedAt||e.timestampIso||"").trim()||new Date().toISOString();return{conversationName:t,senderName:i||null,text:n,attachments:ze(a),observedAt:s,isMentioned:e.isMentioned===!0||ye(n,U(r)),rawId:String(e.id||`${t}:${i}:${n}:${s}`)}}async function V(t,e,r,n,a,i){return e.source==="windows-visual-flow"?{ok:!1,groupName:r,interrupted:!0,reason:"windows-visual-flow-archived",newMessages:[],recentMessages:[],error:"WeChat RPA Windows visual flow is archived; redesign Windows support before enabling it"}:e.source==="wechat-rpa-lab"?Ce(t,e,r,n,a,i):F({groupName:r,replyText:n||void 0,attachmentPath:a,scriptPath:e.flowScriptPath,workDir:t.workDir,forceForeground:k(e),noRestore:e.noRestore!==!1,idleSeconds:P(e.idleSeconds,15,0,3600),recentLimit:P(e.recentLimit,A,0,50),downloadAttachmentsDir:L(t,e)})}function We(t){return t==="macos-flow"||t==="wechat-rpa-lab"}function Y(t,e){return e.text&&e.attachmentPath&&t.sentReplyObserved&&!t.sentAttachmentObserved?{status:t.interrupted?"continue":"queued",text:"",attachmentPath:e.attachmentPath,skipText:!0,reason:t.error||"WeChat RPA sent the text but did not confirm the attachment; queued attachment-only retry"}:{status:"continue",text:e.text,attachmentPath:e.attachmentPath,reason:t.error||""}}function Ee(t,e){return e.idempotencyKey?e.idempotencyKey:I.createHash("sha256").update(`${t.id}
2
2
  ${e.conversationId}
3
3
  ${e.text}
4
4
  ${e.attachment?.name||""}
5
- ${e.attachment?.size||0}`).digest("hex").slice(0,24)}function Me(t,e){return[...Array.from(t.pendingReplies.values()).map(n=>({replyId:n.key,idempotencyKey:n.key,status:"queued",conversationName:n.conversationName,reasonCode:n.lastInterruptedReason??null,nextAttemptAt:null,queuedAt:n.queuedAt,lastAttemptAt:n.lastAttemptAt??null,attemptCount:n.attempts,commitStage:null})),...le({config:e})].slice(0,100)}function T(t,e){const r=t.pendingReplies.get(e.key);t.pendingReplies.set(e.key,{key:e.key,conversationId:e.conversationId,conversationName:e.conversationName,text:e.text,attachmentPath:e.attachmentPath,skipText:e.skipText,queuedAt:r?.queuedAt??new Date().toISOString(),attempts:r?.attempts??0,lastAttemptAt:r?.lastAttemptAt,lastInterruptedReason:e.reason}),R(t)}function Q(t,e,r){t.manualReviewReplies.set(e,{key:e,reason:r,createdAt:t.manualReviewReplies.get(e)?.createdAt??new Date().toISOString()}),t.lastError=r,w(t,{status:"failed",runId:t.lastRunId??null,summary:`manual review required: ${r}`}),R(t)}function M(t,e){const r=Le(e);if(t.pendingStatePath===r)return;t.pendingStatePath=r;const n=Oe(r);for(const a of n.pending??[])_e(a)&&(t.pendingReplies.has(a.key)||t.pendingReplies.set(a.key,a));for(const a of n.manualReview??[])$e(a)&&(t.manualReviewReplies.has(a.key)||t.manualReviewReplies.set(a.key,a));for(const a of n.completedKeys??[])typeof a=="string"&&a&&t.completedPendingReplyKeys.add(a)}function R(t){if(!t.pendingStatePath)return;const e=Array.from(t.completedPendingReplyKeys).slice(-500),r=Array.from(t.pendingReplies.values()).slice(0,500),n=Array.from(t.manualReviewReplies.values()).slice(-500);try{m.mkdirSync(f.dirname(t.pendingStatePath),{recursive:!0}),m.writeFileSync(t.pendingStatePath,JSON.stringify({version:1,pending:r,manualReview:n,completedKeys:e},null,2))}catch{}}function Oe(t){try{const e=JSON.parse(m.readFileSync(t,"utf8"));return e&&e.version===1?e:{version:1,pending:[],completedKeys:[]}}catch{return{version:1,pending:[],completedKeys:[]}}}function _e(t){if(!t||typeof t!="object")return!1;const e=t;return typeof e.key=="string"&&typeof e.conversationId=="string"&&typeof e.conversationName=="string"&&typeof e.text=="string"&&typeof e.queuedAt=="string"&&typeof e.attempts=="number"&&Number.isFinite(e.attempts)&&(e.attachmentPath===void 0||typeof e.attachmentPath=="string")&&(e.skipText===void 0||typeof e.skipText=="boolean")}function $e(t){if(!t||typeof t!="object")return!1;const e=t;return typeof e.key=="string"&&typeof e.reason=="string"&&typeof e.createdAt=="string"}function Le(t){const e=I.createHash("sha256").update(t.id).digest("hex").slice(0,16);return f.join(t.workDir,".shennian","wechat-rpa-pending-replies",`${e}.json`)}function O(t,e){const r=Fe(e);if(t.messageStatePath===r)return;t.messageStatePath=r;const n=qe(r);t.deduper=new K((n.messageIds??[]).filter(a=>typeof a=="string"&&a.length>0))}function De(t){if(t.messageStatePath)try{m.mkdirSync(f.dirname(t.messageStatePath),{recursive:!0}),m.writeFileSync(t.messageStatePath,JSON.stringify({version:1,messageIds:t.deduper.snapshot()},null,2))}catch{}}function qe(t){try{const e=JSON.parse(m.readFileSync(t,"utf8"));return e&&e.version===1?e:{version:1,messageIds:[]}}catch{return{version:1,messageIds:[]}}}function Fe(t){const e=I.createHash("sha256").update(t.id).digest("hex").slice(0,16);return f.join(t.workDir,".shennian","wechat-rpa-seen-messages",`${e}.json`)}function _(t,e,r){t.lastInterruptedAt=new Date().toISOString(),t.lastError=null,t.consecutiveInterruptions+=1,t.runtimeState="interrupted",Ke(t)&&(t.interruptionCooldownUntil=Date.now()+E,t.runtimeState="cooldown"),w(t,{status:"interrupted",runId:e.rpaRunId||t.lastRunId||null,summary:r})}function C(t){t.consecutiveInterruptions=0,t.interruptionCooldownUntil=void 0,t.runtimeState="idle_waiting"}function $(t,e){const r=e.postSendScreenshotPath||e.screenshotPath;e.rpaRunId&&(t.lastRunId=e.rpaRunId),r&&(t.lastTracePath=r),t.lastTraceSummary=e.rpaTraceSummary||[e.groupName?`group=${e.groupName}`:"",e.ok===!1?"failed":e.interrupted?"interrupted":"ok",e.reason?`reason=${e.reason}`:""].filter(Boolean).join(" ")}function w(t,e){const r=e.runId||t.lastRunId||`local:${Date.now().toString(36)}`;t.lastRunId=r,t.recentTaskSummaries.unshift({at:new Date().toISOString(),status:e.status,runId:r,summary:L(e.summary)}),t.recentTaskSummaries=t.recentTaskSummaries.slice(0,he)}function b(t){const e=String(t||"").toLowerCase();return/permission|accessibility|screen recording|automation|window|foreground|target group|refusing|requires|安全|权限|窗口|目标群/.test(e)?"blocked":"failed"}function L(t){return String(t||"").replace(/\s+/g," ").trim().slice(0,180)}function Z(t,e){if(k(e))return!1;const r=t.interruptionCooldownUntil;return r?Date.now()<r?!0:(t.interruptionCooldownUntil=void 0,t.consecutiveInterruptions=0,!1):!1}function k(t){return t.forceForeground!==!1}function Ke(t){return!Number.isFinite(W)||W<=0||!Number.isFinite(E)||E<=0?!1:t.consecutiveInterruptions>=W}function He(t){return!t||!m.existsSync(t)?[]:m.readFileSync(t,"utf-8").split(`
6
- `).map(e=>e.trim()).filter(Boolean).map(e=>{try{return JSON.parse(e)}catch{return null}}).filter(e=>!!e)}async function Be(t,e){if(e.localPath){if(!m.existsSync(e.localPath))throw new Error(`WeChat RPA local attachment does not exist: ${e.localPath}`);return e.localPath}if(e.url)return je(t,e);throw new Error("WeChat RPA attachment requires localPath or url; dataBase64 is not accepted over Manager IPC")}async function je(t,e){if(!e.url||!/^https?:\/\//i.test(e.url))throw new Error("WeChat RPA attachment url must be http(s)");if(Number.isFinite(e.size)&&e.size>S)throw new Error(`WeChat RPA cross-machine attachment is too large: ${e.size} bytes. Max: ${S} bytes.`);const r=await fetch(e.url);if(!r.ok)throw new Error(`WeChat RPA attachment url download failed: ${r.status}`);const n=Number(r.headers.get("content-length")||e.size||0);if(n>S)throw new Error(`WeChat RPA cross-machine attachment is too large: ${n} bytes. Max: ${S} bytes.`);const a=Buffer.from(await r.arrayBuffer());return Ue(t,e,a)}function Ue(t,e,r){if(!r.byteLength)throw new Error("WeChat RPA attachment is empty");if(r.byteLength>S)throw new Error(`WeChat RPA cross-machine attachment is too large: ${r.byteLength} bytes. Max: ${S} bytes.`);const n=f.join(t,".uploads","wechat-rpa","outbound");m.mkdirSync(n,{recursive:!0});const a=ze(e.name||"attachment"),i=I.createHash("sha256").update(r).digest("hex").slice(0,12),s=f.extname(a),u=s?a.slice(0,-s.length):a,o=f.join(n,`${u}-${i}${s}`);return m.existsSync(o)||m.writeFileSync(o,r),o}function ze(t){return f.basename(t||"attachment").normalize("NFKC").replace(/[<>:"/\\|?*\x00-\x1F]/g,"_").replace(/\s+/g," ").replace(/^[ ._]+|[ ._]+$/g,"")||"attachment"}function D(t,e){if(e.downloadAttachments===!1)return;const r=e.downloadAttachmentsDir?.trim();if(r)return f.resolve(r);const n=I.createHash("sha256").update(t.id).digest("hex").slice(0,16);return ne("wechat-rpa","attachments","inbound",n)}function Je(t){if(!Array.isArray(t))return[];const e=process.env.SHENNIAN_MACHINE_ID?.trim();return t.map(r=>r?.localPath?{...r,availability:r.availability||"edge-local",...r.machineId||!e?{}:{machineId:e}}:r)}function P(t,e,r,n){const a=Number(t);return Number.isFinite(a)?Math.min(n,Math.max(r,a)):e}function Xe(...t){const e=new Set,r=[];for(const n of t.flat()){const a=`${n.channelId}
7
- ${n.conversationId}
8
- ${n.messageId}`;e.has(a)||(e.add(a),r.push(n))}return r}export{it as WeChatRpaChannelAdapter,Je as annotateWeChatRpaInboundAttachments,Be as materializeWeChatRpaOutboundAttachment,ke as planWeChatRpaLabSendTasks,D as resolveWeChatRpaInboundAttachmentDir,me as selectWeChatRpaLabReadKind,xe as weChatRpaLabTaskKindForAttachment,Se as weChatRpaProductSourceHealth,ve as windowsVisualFlowHealth};
5
+ ${e.attachment?.size||0}`).digest("hex").slice(0,24)}function Te(t,e){return[...Array.from(t.pendingReplies.values()).map(n=>({replyId:n.key,idempotencyKey:n.key,status:"queued",conversationName:n.conversationName,reasonCode:n.lastInterruptedReason??null,nextAttemptAt:null,queuedAt:n.queuedAt,lastAttemptAt:n.lastAttemptAt??null,attemptCount:n.attempts,commitStage:null})),...ce({config:e})].slice(0,100)}function T(t,e){const r=t.pendingReplies.get(e.key);t.pendingReplies.set(e.key,{key:e.key,conversationId:e.conversationId,conversationName:e.conversationName,text:e.text,attachmentPath:e.attachmentPath,skipText:e.skipText,queuedAt:r?.queuedAt??new Date().toISOString(),attempts:r?.attempts??0,lastAttemptAt:r?.lastAttemptAt,lastInterruptedReason:e.reason}),R(t)}function Q(t,e,r){t.manualReviewReplies.set(e,{key:e,reason:r,createdAt:t.manualReviewReplies.get(e)?.createdAt??new Date().toISOString()}),t.lastError=r,w(t,{status:"failed",runId:t.lastRunId??null,summary:`manual review required: ${r}`}),R(t)}function O(t,e){const r=$e(e);if(t.pendingStatePath===r)return;t.pendingStatePath=r;const n=Oe(r);for(const a of n.pending??[])Me(a)&&(t.pendingReplies.has(a.key)||t.pendingReplies.set(a.key,a));for(const a of n.manualReview??[])_e(a)&&(t.manualReviewReplies.has(a.key)||t.manualReviewReplies.set(a.key,a));for(const a of n.completedKeys??[])typeof a=="string"&&a&&t.completedPendingReplyKeys.add(a)}function R(t){if(!t.pendingStatePath)return;const e=Array.from(t.completedPendingReplyKeys).slice(-500),r=Array.from(t.pendingReplies.values()).slice(0,500),n=Array.from(t.manualReviewReplies.values()).slice(-500);try{m.mkdirSync(h.dirname(t.pendingStatePath),{recursive:!0}),m.writeFileSync(t.pendingStatePath,JSON.stringify({version:1,pending:r,manualReview:n,completedKeys:e},null,2))}catch{}}function Oe(t){try{const e=JSON.parse(m.readFileSync(t,"utf8"));return e&&e.version===1?e:{version:1,pending:[],completedKeys:[]}}catch{return{version:1,pending:[],completedKeys:[]}}}function Me(t){if(!t||typeof t!="object")return!1;const e=t;return typeof e.key=="string"&&typeof e.conversationId=="string"&&typeof e.conversationName=="string"&&typeof e.text=="string"&&typeof e.queuedAt=="string"&&typeof e.attempts=="number"&&Number.isFinite(e.attempts)&&(e.attachmentPath===void 0||typeof e.attachmentPath=="string")&&(e.skipText===void 0||typeof e.skipText=="boolean")}function _e(t){if(!t||typeof t!="object")return!1;const e=t;return typeof e.key=="string"&&typeof e.reason=="string"&&typeof e.createdAt=="string"}function $e(t){const e=I.createHash("sha256").update(t.id).digest("hex").slice(0,16);return h.join(t.workDir,".shennian","wechat-rpa-pending-replies",`${e}.json`)}function M(t,e){const r=qe(e);if(t.messageStatePath===r)return;t.messageStatePath=r;const n=Le(r);t.deduper=new K((n.messageIds??[]).filter(a=>typeof a=="string"&&a.length>0))}function De(t){if(t.messageStatePath)try{m.mkdirSync(h.dirname(t.messageStatePath),{recursive:!0}),m.writeFileSync(t.messageStatePath,JSON.stringify({version:1,messageIds:t.deduper.snapshot()},null,2))}catch{}}function Le(t){try{const e=JSON.parse(m.readFileSync(t,"utf8"));return e&&e.version===1?e:{version:1,messageIds:[]}}catch{return{version:1,messageIds:[]}}}function qe(t){const e=I.createHash("sha256").update(t.id).digest("hex").slice(0,16);return h.join(t.workDir,".shennian","wechat-rpa-seen-messages",`${e}.json`)}function _(t,e,r){t.lastInterruptedAt=new Date().toISOString(),t.lastError=null,t.consecutiveInterruptions+=1,t.runtimeState="interrupted",Fe(t)&&(t.interruptionCooldownUntil=Date.now()+E,t.runtimeState="cooldown"),w(t,{status:"interrupted",runId:e.rpaRunId||t.lastRunId||null,summary:r})}function C(t){t.consecutiveInterruptions=0,t.interruptionCooldownUntil=void 0,t.runtimeState="idle_waiting"}function $(t,e){const r=e.postSendScreenshotPath||e.screenshotPath;e.rpaRunId&&(t.lastRunId=e.rpaRunId),r&&(t.lastTracePath=r),t.lastTraceSummary=e.rpaTraceSummary||[e.groupName?`group=${e.groupName}`:"",e.ok===!1?"failed":e.interrupted?"interrupted":"ok",e.reason?`reason=${e.reason}`:""].filter(Boolean).join(" ")}function w(t,e){const r=e.runId||t.lastRunId||`local:${Date.now().toString(36)}`;t.lastRunId=r,t.recentTaskSummaries.unshift({at:new Date().toISOString(),status:e.status,runId:r,summary:D(e.summary)}),t.recentTaskSummaries=t.recentTaskSummaries.slice(0,he)}function b(t){const e=String(t||"").toLowerCase();return/permission|accessibility|screen recording|automation|window|foreground|target group|refusing|requires|安全|权限|窗口|目标群/.test(e)?"blocked":"failed"}function D(t){return String(t||"").replace(/\s+/g," ").trim().slice(0,180)}function Z(t,e){if(k(e))return!1;const r=t.interruptionCooldownUntil;return r?Date.now()<r?!0:(t.interruptionCooldownUntil=void 0,t.consecutiveInterruptions=0,!1):!1}function k(t){return t.forceForeground!==!1}function Fe(t){return!Number.isFinite(W)||W<=0||!Number.isFinite(E)||E<=0?!1:t.consecutiveInterruptions>=W}function Ke(t){return!t||!m.existsSync(t)?[]:m.readFileSync(t,"utf-8").split(`
6
+ `).map(e=>e.trim()).filter(Boolean).map(e=>{try{return JSON.parse(e)}catch{return null}}).filter(e=>!!e)}async function He(t,e){if(e.localPath){if(!m.existsSync(e.localPath))throw new Error(`WeChat RPA local attachment does not exist: ${e.localPath}`);return e.localPath}if(e.url)return Be(t,e);throw new Error("WeChat RPA attachment requires localPath or url; dataBase64 is not accepted over Manager IPC")}async function Be(t,e){if(!e.url||!/^https?:\/\//i.test(e.url))throw new Error("WeChat RPA attachment url must be http(s)");if(Number.isFinite(e.size)&&e.size>S)throw new Error(`WeChat RPA cross-machine attachment is too large: ${e.size} bytes. Max: ${S} bytes.`);const r=await fetch(e.url);if(!r.ok)throw new Error(`WeChat RPA attachment url download failed: ${r.status}`);const n=Number(r.headers.get("content-length")||e.size||0);if(n>S)throw new Error(`WeChat RPA cross-machine attachment is too large: ${n} bytes. Max: ${S} bytes.`);const a=Buffer.from(await r.arrayBuffer());return je(t,e,a)}function je(t,e,r){if(!r.byteLength)throw new Error("WeChat RPA attachment is empty");if(r.byteLength>S)throw new Error(`WeChat RPA cross-machine attachment is too large: ${r.byteLength} bytes. Max: ${S} bytes.`);const n=h.join(t,".uploads","wechat-rpa","outbound");m.mkdirSync(n,{recursive:!0});const a=Ue(e.name||"attachment"),i=I.createHash("sha256").update(r).digest("hex").slice(0,12),s=h.extname(a),u=s?a.slice(0,-s.length):a,o=h.join(n,`${u}-${i}${s}`);return m.existsSync(o)||m.writeFileSync(o,r),o}function Ue(t){return h.basename(t||"attachment").normalize("NFKC").replace(/[<>:"/\\|?*\x00-\x1F]/g,"_").replace(/\s+/g," ").replace(/^[ ._]+|[ ._]+$/g,"")||"attachment"}function L(t,e){if(e.downloadAttachments===!1)return;const r=e.downloadAttachmentsDir?.trim();if(r)return h.resolve(r);const n=I.createHash("sha256").update(t.id).digest("hex").slice(0,16);return ne("wechat-rpa","attachments","inbound",n)}function ze(t){if(!Array.isArray(t))return[];const e=process.env.SHENNIAN_MACHINE_ID?.trim();return t.map(r=>r?.localPath?{...r,availability:r.availability||"edge-local",...r.machineId||!e?{}:{machineId:e}}:r)}function P(t,e,r,n){const a=Number(t);return Number.isFinite(a)?Math.min(n,Math.max(r,a)):e}export{at as WeChatRpaChannelAdapter,ze as annotateWeChatRpaInboundAttachments,He as materializeWeChatRpaOutboundAttachment,be as planWeChatRpaLabSendTasks,L as resolveWeChatRpaInboundAttachmentDir,fe as selectWeChatRpaLabReadKind,ke as weChatRpaLabTaskKindForAttachment,ve as weChatRpaProductSourceHealth,Re as windowsVisualFlowHealth};
@@ -0,0 +1,8 @@
1
+ import type { DaemonStatus } from './daemon.js';
2
+ export declare function isDaemonReadyStatus(status: Pick<DaemonStatus, 'running' | 'stale'>): boolean;
3
+ export declare function waitForDaemonReadyStatus(readStatus: () => DaemonStatus, opts?: {
4
+ timeoutMs?: number;
5
+ intervalMs?: number;
6
+ now?: () => number;
7
+ sleep?: (ms: number) => void;
8
+ }): DaemonStatus;
@@ -0,0 +1 @@
1
+ const T=2e4,c=500;function M(n){return n.running===!0&&n.stale!==!0}function s(n){Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,n)}function u(n,t={}){const r=Math.max(0,t.timeoutMs??2e4),i=Math.max(1,t.intervalMs??500),e=t.now??Date.now,A=t.sleep??s,o=e()+r;let a=n();for(;!M(a)&&e()<o;){const _=Math.max(1,o-e());A(Math.min(i,_)),a=n()}return a}export{M as isDaemonReadyStatus,u as waitForDaemonReadyStatus};
@@ -1,13 +1,13 @@
1
- import s from"chalk";import o from"node:fs";import d from"node:path";import a from"node:os";import{execSync as c,spawn as cn}from"node:child_process";import{randomUUID as an}from"node:crypto";import{fileURLToPath as un}from"node:url";import{getShennianDir as dn,loadConfig as M,resolveShennianPath as h,saveConfig as ln}from"../config/index.js";import{buildAugmentedPath as pn}from"../env-path.js";import{buildWindowsLauncherCommand as mn,buildWindowsScheduledTaskXml as fn,buildWindowsStartupVbs as Sn,escapeXml as N,isWindowsCmdScript as hn,quoteCmdArg as j,quoteSystemdArg as gn}from"./daemon-windows.js";import{findRunningWindowsDaemonProcessIds as yn,isShennianRunServiceCommand as wn}from"./daemon-process.js";import{buildWindowsLauncherCommand as pe,buildWindowsScheduledTaskXml as me,buildWindowsStartupVbs as fe}from"./daemon-windows.js";import{isShennianRunServiceCommand as he}from"./daemon-process.js";const vn=d.dirname(un(import.meta.url)),y=dn(),p=h("daemon.pid"),u=h("daemon.log"),b=h("remote-access.disabled"),I=h("daemon-launcher.json"),W=d.resolve(vn,"../../bin/shennian.js"),F=process.execPath,B=new Set(["PATH","HOME","USERPROFILE","HOMEDRIVE","HOMEPATH","USERNAME","USERDOMAIN","COMPUTERNAME","USER","LOGNAME","SHELL","TMPDIR","LANG","LC_ALL","LC_CTYPE","SSH_AUTH_SOCK","XDG_CONFIG_HOME","XDG_DATA_HOME","TEMP","TMP","APPDATA","LOCALAPPDATA","SHENNIAN_DESKTOP_SERVER_URL","SHENNIAN_WECHAT_CHANNEL_HELPER_DIR","SHENNIAN_HOME","SHENNIAN_NATIVE_FUSION_DISABLED","SHENNIAN_WECHAT_DELIVERABILITY_TRACE"]);function En(n){return B.has(n)}function l(){const n=a.platform();return n==="darwin"||n==="linux"||n==="win32"?n:"linux"}function L(n){const e=n.replace(/\\/g,"/").toLowerCase(),t=a.tmpdir().replace(/\\/g,"/").toLowerCase();return e.includes("/_npx/")||e.includes("/npm-cache/_npx/")||e.includes("/pnpm/dlx/")||e.startsWith(t.endsWith("/")?t:`${t}/`)}function kn(n){return o.existsSync(n.scriptPath)&&!L(n.scriptPath)?{command:n.nodeExec,args:[n.scriptPath,"run-service"],mode:"direct"}:n.shennianCommandPath&&!L(n.shennianCommandPath)?{command:n.shennianCommandPath,args:["run-service"],mode:"global-shim"}:n.npxPath?{command:n.npxPath,args:["--yes","shennian","run-service"],mode:"npx"}:{command:n.nodeExec,args:[n.scriptPath,"run-service"],mode:"direct"}}function Pn(n){const e=n.trim();if(!e)return null;if(e.startsWith("{"))try{const r=JSON.parse(e),i=Number(r.pid);return!Number.isInteger(i)||i<=0?null:{pid:i,...typeof r.instanceId=="string"?{instanceId:r.instanceId}:{},...typeof r.version=="string"?{version:r.version}:{},...r.launcher==="desktop-managed"||r.launcher==="global-cli"||r.launcher==="unknown"?{launcher:r.launcher}:{},...typeof r.startedAt=="string"?{startedAt:r.startedAt}:{},...r.adopted===!0?{adopted:!0}:{}}}catch{return null}const t=parseInt(e,10);return isNaN(t)?null:{pid:t}}function R(){try{return Pn(o.readFileSync(p,"utf-8"))}catch{return null}}function An(){return R()?.pid??null}function w(n){try{return process.kill(n,0),!0}catch{return!1}}function In(n,e=3e3){const t=Date.now();for(;Date.now()-t<e;){if(!w(n))return!0;Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,100)}return!w(n)}function Dn(){return o.existsSync(b)}function Nn(){try{o.unlinkSync(b)}catch{}}function V(){return"global-cli"}function bn(){return`${process.pid}-${Date.now()}-${an()}`}function Ln(n,e,t={}){o.mkdirSync(y,{recursive:!0}),o.writeFileSync(p,JSON.stringify({pid:n,instanceId:e,...t.version?{version:t.version}:{},launcher:t.launcher??V(),...t.adopted?{adopted:!0}:{},startedAt:new Date().toISOString()},null,2))}function ae(n,e){const t=R();if(!(t?.pid!==n||t.instanceId!==e))try{o.unlinkSync(p)}catch{}}function J(n,e=V(),t){o.mkdirSync(y,{recursive:!0}),o.writeFileSync(I,JSON.stringify({pid:n,launcher:e,...t?{instanceId:t}:{},updatedAt:new Date().toISOString()},null,2))}function O(n){if(n)try{if(JSON.parse(o.readFileSync(I,"utf-8")).instanceId!==n)return}catch{return}try{o.unlinkSync(I)}catch{}}function Rn(n){n&&J(n)}function On(n,e){if(!n||!e)return"unknown";try{const t=JSON.parse(o.readFileSync(I,"utf-8"));if(t.pid!==n)return"unknown";if(t.launcher==="desktop-managed"||t.launcher==="global-cli")return t.launcher}catch{}return T(n)}function T(n){if(l()==="win32")return"unknown";try{const e=c(`ps -p ${n} -o command=`,{encoding:"utf-8",stdio:["ignore","pipe","ignore"],timeout:1e3,windowsHide:!0}).replace(/\\/g,"/");if(e.includes("/node_modules/shennian/")||e.includes("/bin/shennian")||e.includes(" shennian run-service")||e.includes(" shennian.js run-service"))return"global-cli"}catch{}return"unknown"}function G(n=process.pid){if(l()==="win32")return yn(n);try{return c("ps -axo pid=,command=",{encoding:"utf-8",stdio:["ignore","pipe","ignore"],timeout:1e3,windowsHide:!0}).split(/\r?\n/).map(t=>{const r=t.trim().match(/^(\d+)\s+(.+)$/);if(!r)return null;const i=Number(r[1]),S=r[2];return!Number.isInteger(i)||i===n?null:wn(S)?i:null}).filter(t=>typeof t=="number")}catch{return[]}}function f(n={}){let e=R(),t=e?.pid??null,r=t!==null&&w(t),i=t!==null&&!r,S=e?.adopted===!0;const v=M();if(i&&n.cleanupStale){try{o.unlinkSync(p)}catch{}e=null,t=null,r=!1,i=!1}if(!r){const g=G()[0];if(g&&(t=g,r=!0,i=!1,S=!0,n.cleanupStale)){const C=bn();Ln(g,C,{launcher:T(g),adopted:!0}),J(g,T(g)),e={pid:g,instanceId:C,adopted:!0}}}return{running:r,pid:t,stale:i,remoteAccessDisabled:Dn(),launcher:On(t,r),platform:l(),shennianDir:y,pidFile:p,logFile:u,...e?.instanceId?{instanceId:e.instanceId}:{},...S?{adopted:!0}:{},...v.machineId?{machineId:v.machineId}:{},paired:!!(v.machineToken&&v.machineId),...v.serverUrl?{serverUrl:v.serverUrl}:{}}}function E(n){process.stdout.write(`${JSON.stringify(n,null,2)}
2
- `)}function K(n){const e=n?.trim();if(!e)return;const t=M();t.serverUrl=e,ln(t)}function X(n){return n?.trim()||process.env.SHENNIAN_DESKTOP_SERVER_URL?.trim()||void 0}const x="com.shennian.agent",m=d.join(a.homedir(),"Library/LaunchAgents",`${x}.plist`),Tn=d.join(a.homedir(),"AppData","Roaming","Microsoft","Windows","Start Menu","Programs","Startup"),$=h("autostart.cmd"),H=h("autostart.vbs"),_=h("autostart.xml"),xn=d.join(Tn,"ShennianAgent.vbs");function q(n){const e=l()==="win32"?`where ${n}`:`command -v ${n}`;try{return c(e,{stdio:["ignore","pipe","pipe"],encoding:"utf-8",windowsHide:!0}).split(/\r?\n/).map(i=>i.trim()).find(Boolean)??null}catch{return null}}function $n(){const n=l()==="win32"?"npx.cmd":"npx",e=d.join(d.dirname(F),n);return o.existsSync(e)?e:q("npx")}function k(){return kn({nodeExec:F,scriptPath:W,shennianCommandPath:q("shennian"),npxPath:$n()})}function Y(){try{c(`schtasks /delete /tn "${P}" /f`,{stdio:"pipe",windowsHide:!0})}catch{}}function z(){try{o.unlinkSync(xn)}catch{}}function Hn(){const n=process.env.USERDOMAIN?.trim(),e=process.env.COMPUTERNAME?.trim(),t=process.env.USERNAME?.trim()||a.userInfo().username;return n&&n.toUpperCase()!=="WORKGROUP"?`${n}\\${t}`:e?`${e}\\${t}`:t}function _n(n){if(l()==="win32"&&hn(n.command)){const e=[n.command,...n.args].map(j).join(" ");return{command:process.env.ComSpec||"cmd.exe",args:["/d","/s","/c",`"${e}"`],windowsVerbatimArguments:!0}}return{command:n.command,args:n.args}}function Un(n,e,t=process.env){return{detached:!0,stdio:["ignore",e,e],env:t,windowsHide:!0,...n.windowsVerbatimArguments?{windowsVerbatimArguments:!0}:{}}}function Cn(){const n=k();o.writeFileSync($,mn(n,u)),o.writeFileSync(H,Sn($)),z(),Y();const e=fn({userId:Hn(),command:d.join(process.env.SystemRoot||"C:\\Windows","System32","wscript.exe"),arguments:[j(H)]});o.writeFileSync(_,e,"utf8");try{return c(`schtasks /create /tn "${P}" /xml "${_}" /f`,{stdio:"pipe",windowsHide:!0}),c(`schtasks /run /tn "${P}"`,{stdio:"pipe",windowsHide:!0}),!0}catch{return!1}}function Q(){const n={};for(const e of B)process.env[e]&&(n[e]=process.env[e]);if(n.HOME??=a.homedir(),n.PATH=pn({pathValue:n.PATH,env:n}),n.USER??=a.userInfo().username,l()==="win32"){const e=process.env.TEMP||process.env.TMP||a.tmpdir(),t=a.homedir();n.USERPROFILE??=t;const r=d.win32.parse(t);n.HOMEDRIVE??=r.root.replace(/[\\/]$/,"")||process.env.HOMEDRIVE||"",n.HOMEPATH??=t.slice((n.HOMEDRIVE||"").length)||process.env.HOMEPATH||"",n.USERNAME??=process.env.USERNAME||a.userInfo().username,n.COMPUTERNAME??=process.env.COMPUTERNAME||"",n.TMPDIR??=e,n.TEMP??=e,n.TMP??=e}else n.TMPDIR??="/tmp";return n}function Mn(){const n=Q(),e=k(),t=Object.entries(n).map(([i,S])=>` <key>${N(i)}</key>
1
+ import s from"chalk";import o from"node:fs";import d from"node:path";import a from"node:os";import{execSync as c,spawn as ce}from"node:child_process";import{randomUUID as ae}from"node:crypto";import{fileURLToPath as ue}from"node:url";import{getShennianDir as de,loadConfig as j,resolveShennianPath as h,saveConfig as le}from"../config/index.js";import{buildAugmentedPath as pe}from"../env-path.js";import{buildWindowsLauncherCommand as me,buildWindowsScheduledTaskXml as fe,buildWindowsStartupVbs as Se,escapeXml as N,isWindowsCmdScript as he,quoteCmdArg as W,quoteSystemdArg as ge}from"./daemon-windows.js";import{waitForDaemonReadyStatus as b}from"./daemon-start-wait.js";import{findRunningWindowsDaemonProcessIds as ye,isShennianRunServiceCommand as we}from"./daemon-process.js";import{buildWindowsLauncherCommand as fn,buildWindowsScheduledTaskXml as Sn,buildWindowsStartupVbs as hn}from"./daemon-windows.js";import{isShennianRunServiceCommand as yn}from"./daemon-process.js";const ve=d.dirname(ue(import.meta.url)),y=de(),p=h("daemon.pid"),u=h("daemon.log"),L=h("remote-access.disabled"),I=h("daemon-launcher.json"),F=d.resolve(ve,"../../bin/shennian.js"),B=process.execPath,V=new Set(["PATH","HOME","USERPROFILE","HOMEDRIVE","HOMEPATH","USERNAME","USERDOMAIN","COMPUTERNAME","USER","LOGNAME","SHELL","TMPDIR","LANG","LC_ALL","LC_CTYPE","SSH_AUTH_SOCK","XDG_CONFIG_HOME","XDG_DATA_HOME","TEMP","TMP","APPDATA","LOCALAPPDATA","SHENNIAN_DESKTOP_SERVER_URL","SHENNIAN_WECHAT_CHANNEL_HELPER_DIR","SHENNIAN_HOME","SHENNIAN_NATIVE_FUSION_DISABLED","SHENNIAN_WECHAT_DELIVERABILITY_TRACE"]);function Ee(e){return V.has(e)}function l(){const e=a.platform();return e==="darwin"||e==="linux"||e==="win32"?e:"linux"}function R(e){const n=e.replace(/\\/g,"/").toLowerCase(),t=a.tmpdir().replace(/\\/g,"/").toLowerCase();return n.includes("/_npx/")||n.includes("/npm-cache/_npx/")||n.includes("/pnpm/dlx/")||n.startsWith(t.endsWith("/")?t:`${t}/`)}function ke(e){return o.existsSync(e.scriptPath)&&!R(e.scriptPath)?{command:e.nodeExec,args:[e.scriptPath,"run-service"],mode:"direct"}:e.shennianCommandPath&&!R(e.shennianCommandPath)?{command:e.shennianCommandPath,args:["run-service"],mode:"global-shim"}:e.npxPath?{command:e.npxPath,args:["--yes","shennian","run-service"],mode:"npx"}:{command:e.nodeExec,args:[e.scriptPath,"run-service"],mode:"direct"}}function Pe(e){const n=e.trim();if(!n)return null;if(n.startsWith("{"))try{const r=JSON.parse(n),i=Number(r.pid);return!Number.isInteger(i)||i<=0?null:{pid:i,...typeof r.instanceId=="string"?{instanceId:r.instanceId}:{},...typeof r.version=="string"?{version:r.version}:{},...r.launcher==="desktop-managed"||r.launcher==="global-cli"||r.launcher==="unknown"?{launcher:r.launcher}:{},...typeof r.startedAt=="string"?{startedAt:r.startedAt}:{},...r.adopted===!0?{adopted:!0}:{}}}catch{return null}const t=parseInt(n,10);return isNaN(t)?null:{pid:t}}function O(){try{return Pe(o.readFileSync(p,"utf-8"))}catch{return null}}function Ae(){return O()?.pid??null}function w(e){try{return process.kill(e,0),!0}catch{return!1}}function Ie(e,n=3e3){const t=Date.now();for(;Date.now()-t<n;){if(!w(e))return!0;Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,100)}return!w(e)}function De(){return o.existsSync(L)}function Ne(){try{o.unlinkSync(L)}catch{}}function J(){return"global-cli"}function be(){return`${process.pid}-${Date.now()}-${ae()}`}function Le(e,n,t={}){o.mkdirSync(y,{recursive:!0}),o.writeFileSync(p,JSON.stringify({pid:e,instanceId:n,...t.version?{version:t.version}:{},launcher:t.launcher??J(),...t.adopted?{adopted:!0}:{},startedAt:new Date().toISOString()},null,2))}function dn(e,n){const t=O();if(!(t?.pid!==e||t.instanceId!==n))try{o.unlinkSync(p)}catch{}}function G(e,n=J(),t){o.mkdirSync(y,{recursive:!0}),o.writeFileSync(I,JSON.stringify({pid:e,launcher:n,...t?{instanceId:t}:{},updatedAt:new Date().toISOString()},null,2))}function T(e){if(e)try{if(JSON.parse(o.readFileSync(I,"utf-8")).instanceId!==e)return}catch{return}try{o.unlinkSync(I)}catch{}}function Re(e){e&&G(e)}function Oe(e,n){if(!e||!n)return"unknown";try{const t=JSON.parse(o.readFileSync(I,"utf-8"));if(t.pid!==e)return"unknown";if(t.launcher==="desktop-managed"||t.launcher==="global-cli")return t.launcher}catch{}return x(e)}function x(e){if(l()==="win32")return"unknown";try{const n=c(`ps -p ${e} -o command=`,{encoding:"utf-8",stdio:["ignore","pipe","ignore"],timeout:1e3,windowsHide:!0}).replace(/\\/g,"/");if(n.includes("/node_modules/shennian/")||n.includes("/bin/shennian")||n.includes(" shennian run-service")||n.includes(" shennian.js run-service"))return"global-cli"}catch{}return"unknown"}function K(e=process.pid){if(l()==="win32")return ye(e);try{return c("ps -axo pid=,command=",{encoding:"utf-8",stdio:["ignore","pipe","ignore"],timeout:1e3,windowsHide:!0}).split(/\r?\n/).map(t=>{const r=t.trim().match(/^(\d+)\s+(.+)$/);if(!r)return null;const i=Number(r[1]),S=r[2];return!Number.isInteger(i)||i===e?null:we(S)?i:null}).filter(t=>typeof t=="number")}catch{return[]}}function f(e={}){let n=O(),t=n?.pid??null,r=t!==null&&w(t),i=t!==null&&!r,S=n?.adopted===!0;const v=j();if(i&&e.cleanupStale){try{o.unlinkSync(p)}catch{}n=null,t=null,r=!1,i=!1}if(!r){const g=K()[0];if(g&&(t=g,r=!0,i=!1,S=!0,e.cleanupStale)){const M=be();Le(g,M,{launcher:x(g),adopted:!0}),G(g,x(g)),n={pid:g,instanceId:M,adopted:!0}}}return{running:r,pid:t,stale:i,remoteAccessDisabled:De(),launcher:Oe(t,r),platform:l(),shennianDir:y,pidFile:p,logFile:u,...n?.instanceId?{instanceId:n.instanceId}:{},...S?{adopted:!0}:{},...v.machineId?{machineId:v.machineId}:{},paired:!!(v.machineToken&&v.machineId),...v.serverUrl?{serverUrl:v.serverUrl}:{}}}function E(e){process.stdout.write(`${JSON.stringify(e,null,2)}
2
+ `)}function X(e){const n=e?.trim();if(!n)return;const t=j();t.serverUrl=n,le(t)}function q(e){return e?.trim()||process.env.SHENNIAN_DESKTOP_SERVER_URL?.trim()||void 0}const $="com.shennian.agent",m=d.join(a.homedir(),"Library/LaunchAgents",`${$}.plist`),Te=d.join(a.homedir(),"AppData","Roaming","Microsoft","Windows","Start Menu","Programs","Startup"),H=h("autostart.cmd"),_=h("autostart.vbs"),U=h("autostart.xml"),xe=d.join(Te,"ShennianAgent.vbs");function Y(e){const n=l()==="win32"?`where ${e}`:`command -v ${e}`;try{return c(n,{stdio:["ignore","pipe","pipe"],encoding:"utf-8",windowsHide:!0}).split(/\r?\n/).map(i=>i.trim()).find(Boolean)??null}catch{return null}}function $e(){const e=l()==="win32"?"npx.cmd":"npx",n=d.join(d.dirname(B),e);return o.existsSync(n)?n:Y("npx")}function k(){return ke({nodeExec:B,scriptPath:F,shennianCommandPath:Y("shennian"),npxPath:$e()})}function z(){try{c(`schtasks /delete /tn "${P}" /f`,{stdio:"pipe",windowsHide:!0})}catch{}}function Q(){try{o.unlinkSync(xe)}catch{}}function He(){const e=process.env.USERDOMAIN?.trim(),n=process.env.COMPUTERNAME?.trim(),t=process.env.USERNAME?.trim()||a.userInfo().username;return e&&e.toUpperCase()!=="WORKGROUP"?`${e}\\${t}`:n?`${n}\\${t}`:t}function _e(e){if(l()==="win32"&&he(e.command)){const n=[e.command,...e.args].map(W).join(" ");return{command:process.env.ComSpec||"cmd.exe",args:["/d","/s","/c",`"${n}"`],windowsVerbatimArguments:!0}}return{command:e.command,args:e.args}}function Ue(e,n,t=process.env){return{detached:!0,stdio:["ignore",n,n],env:t,windowsHide:!0,...e.windowsVerbatimArguments?{windowsVerbatimArguments:!0}:{}}}function Ce(){const e=k();o.writeFileSync(H,me(e,u)),o.writeFileSync(_,Se(H)),Q(),z();const n=fe({userId:He(),command:d.join(process.env.SystemRoot||"C:\\Windows","System32","wscript.exe"),arguments:[W(_)]});o.writeFileSync(U,n,"utf8");try{return c(`schtasks /create /tn "${P}" /xml "${U}" /f`,{stdio:"pipe",windowsHide:!0}),c(`schtasks /run /tn "${P}"`,{stdio:"pipe",windowsHide:!0}),!0}catch{return!1}}function Z(){const e={};for(const n of V)process.env[n]&&(e[n]=process.env[n]);if(e.HOME??=a.homedir(),e.PATH=pe({pathValue:e.PATH,env:e}),e.USER??=a.userInfo().username,l()==="win32"){const n=process.env.TEMP||process.env.TMP||a.tmpdir(),t=a.homedir();e.USERPROFILE??=t;const r=d.win32.parse(t);e.HOMEDRIVE??=r.root.replace(/[\\/]$/,"")||process.env.HOMEDRIVE||"",e.HOMEPATH??=t.slice((e.HOMEDRIVE||"").length)||process.env.HOMEPATH||"",e.USERNAME??=process.env.USERNAME||a.userInfo().username,e.COMPUTERNAME??=process.env.COMPUTERNAME||"",e.TMPDIR??=n,e.TEMP??=n,e.TMP??=n}else e.TMPDIR??="/tmp";return e}function Me(){const e=Z(),n=k(),t=Object.entries(e).map(([i,S])=>` <key>${N(i)}</key>
3
3
  <string>${N(S)}</string>`).join(`
4
- `),r=[e.command,...e.args].map(i=>` <string>${N(i)}</string>`).join(`
4
+ `),r=[n.command,...n.args].map(i=>` <string>${N(i)}</string>`).join(`
5
5
  `);return`<?xml version="1.0" encoding="UTF-8"?>
6
6
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
7
7
  <plist version="1.0">
8
8
  <dict>
9
9
  <key>Label</key>
10
- <string>${x}</string>
10
+ <string>${$}</string>
11
11
  <key>ProgramArguments</key>
12
12
  <array>
13
13
  ${r}
@@ -27,8 +27,8 @@ ${t}
27
27
  <key>StandardErrorPath</key>
28
28
  <string>${u}</string>
29
29
  </dict>
30
- </plist>`}const D=d.join(a.homedir(),".config/systemd/user/shennian.service");function jn(){const n=Q(),e=k(),t=Object.entries(n).map(([i,S])=>`Environment=${i}=${S}`).join(`
31
- `),r=[e.command,...e.args].map(gn).join(" ");return`[Unit]
30
+ </plist>`}const D=d.join(a.homedir(),".config/systemd/user/shennian.service");function je(){const e=Z(),n=k(),t=Object.entries(e).map(([i,S])=>`Environment=${i}=${S}`).join(`
31
+ `),r=[n.command,...n.args].map(ge).join(" ");return`[Unit]
32
32
  Description=Shennian Agent Daemon
33
33
  After=network.target
34
34
 
@@ -41,6 +41,6 @@ StandardOutput=append:${u}
41
41
  StandardError=append:${u}
42
42
 
43
43
  [Install]
44
- WantedBy=default.target`}const P="ShennianAgent";function Wn(n){c(n,{stdio:"pipe",windowsHide:!0})}function Fn(){const n=process.getuid?.();if(typeof n=="number")return n;const e=a.userInfo();return typeof e.uid=="number"?e.uid:e.username}function Z(n=Fn()){return`gui/${n}/${x}`}function Bn(n=Wn){try{return n(`launchctl unload "${m}" 2>/dev/null; launchctl load -w "${m}"`),n(`launchctl kickstart -k "${Z()}"`),!0}catch{try{return n(`launchctl load -w "${m}"`),n(`launchctl kickstart -k "${Z()}"`),!0}catch{return!1}}}function Vn(){o.mkdirSync(y,{recursive:!0});const n={};for(const[e,t]of Object.entries(process.env))t!==void 0&&En(e)&&(n[e]=t);o.writeFileSync(h("env.json"),JSON.stringify(n,null,2))}function nn(n={}){o.mkdirSync(y,{recursive:!0});const e=f({cleanupStale:!0});if(e.pid!==null&&e.running&&!e.adopted){n.quiet||console.log(s.green(`\u2713 Background service already running (PID ${e.pid})`));return}en(e);const t=o.openSync(u,"a"),r=_n(k()),i=cn(r.command,r.args,Un(r,t));i.unref(),o.closeSync(t),Rn(i.pid),n.quiet||(console.log(s.green(`\u2713 Background service started (PID ${i.pid})`)),console.log(s.gray(` Logs: ${u}`)))}function en(n=f({cleanupStale:!0})){if(!(n.pid===null||!n.running||!n.adopted))try{process.kill(n.pid,"SIGTERM"),In(n.pid);try{o.unlinkSync(p)}catch{}}catch{}}function tn(){o.mkdirSync(y,{recursive:!0});const n=l();switch(k().mode==="direct"&&L(W)&&console.warn(s.yellow("\u26A0 Warning: Current CLI path is temporary (npx). Auto-start may not work after reboot.\n Run `npm install -g shennian@latest` for reliable auto-start.")),n){case"darwin":{const t=d.dirname(m);return o.mkdirSync(t,{recursive:!0}),o.writeFileSync(m,Mn()),Bn()}case"linux":{const t=d.dirname(D);o.mkdirSync(t,{recursive:!0}),o.writeFileSync(D,jn());try{c("systemctl --user daemon-reload && systemctl --user enable shennian",{stdio:"pipe",windowsHide:!0})}catch{return!1}try{c(`loginctl enable-linger ${a.userInfo().username}`,{stdio:"pipe",windowsHide:!0})}catch{}try{return c("systemctl --user restart shennian",{stdio:"pipe",windowsHide:!0}),!0}catch{return!1}}case"win32":return Cn()}}function Jn(){const e=f({cleanupStale:!0}).pid;if(e===null)return{};if(!w(e)){try{o.unlinkSync(p)}catch{}return{stalePid:e}}try{return process.kill(e,"SIGTERM"),{stoppedPid:e}}catch(t){return{error:t instanceof Error?t.message:String(t)}}}async function Gn(n=5e3){const e=Jn();if(!e.stoppedPid)return e;if(!await A(e.stoppedPid,n))try{process.kill(e.stoppedPid,"SIGKILL"),await A(e.stoppedPid,2e3)}catch(r){return{...e,error:r instanceof Error?r.message:String(r)}}try{o.unlinkSync(p)}catch{}O();for(const r of G())if(r!==e.stoppedPid)try{process.kill(r,"SIGTERM"),await A(r,2e3)}catch{}return e}function Kn(n={}){K(X(n.api)),Nn(),en(),tn()||nn({quiet:n.json}),n.json?E(f()):console.log(s.green("\u2713 Background service started"))}async function Xn(n={}){switch(o.mkdirSync(y,{recursive:!0}),o.writeFileSync(b,new Date().toISOString()),l()){case"darwin":{if(o.existsSync(m))try{c(`launchctl unload -w "${m}"`,{stdio:"pipe",windowsHide:!0})}catch{}break}case"linux":{try{c("systemctl --user disable --now shennian",{stdio:"pipe",windowsHide:!0})}catch{}break}case"win32":{try{c(`schtasks /change /tn "${P}" /disable`,{stdio:"pipe",windowsHide:!0})}catch{}try{c(`schtasks /end /tn "${P}"`,{stdio:"pipe",windowsHide:!0})}catch{}break}}const t=await Gn(),r=f({cleanupStale:!0});if(n.json){E(t.error?{...r,error:t.error}:r);return}t.error?console.error(s.red(`\u2717 Failed to stop: ${t.error}`)):console.log(s.green("\u2713 Background service stopped"))}function rn(n){Vn(),Kn(n)}async function U(n={}){await Xn(n)}async function A(n,e=5e3){const t=Date.now();for(;Date.now()-t<e;){if(!w(n))return!0;await new Promise(r=>setTimeout(r,100))}return!w(n)}async function qn(n={}){K(X(n.api));const e=An();if(e!==null&&w(e))try{process.kill(e,"SIGTERM"),await A(e)||(process.kill(e,"SIGKILL"),await A(e,2e3));try{o.unlinkSync(p)}catch{}O(),n.json||console.log(s.green(`\u2713 Background service stopped (PID ${e})`))}catch(t){const r=t instanceof Error?t.message:String(t);n.json?E({...f(),error:r}):console.error(s.red(`\u2717 Failed to stop: ${r}`));return}else if(e!==null){try{o.unlinkSync(p)}catch{}O(),n.json||console.log(s.yellow(`\u26A0 Service process no longer exists, cleaned up stale PID ${e}`))}else n.json||console.log(s.gray("\u25CF Background service not running, starting it now"));if(l()==="linux"&&tn()){n.json&&E(f());return}nn({quiet:n.json}),n.json&&E(f())}function on(n={}){const e=f({cleanupStale:!0});if(n.json){E(e);return}if(e.pid===null){console.log(s.gray("\u25CF Background service not running"));return}e.running?(console.log(s.green(`\u25CF Background service running (PID ${e.pid})`)),console.log(s.gray(` Logs: ${u}`))):console.log(s.yellow(`\u25CF Background service stopped (PID ${e.pid} is stale)`))}function sn(n){if(!o.existsSync(u)){console.log(s.gray("No logs yet"));return}try{const e=c(a.platform()==="win32"?`powershell Get-Content -Tail ${n.lines} "${u}"`:`tail -n ${n.lines} "${u}"`,{encoding:"utf-8",windowsHide:!0});process.stdout.write(e)}catch{const e=o.readFileSync(u,"utf-8").split(`
45
- `).slice(-n.lines);console.log(e.join(`
46
- `))}}async function Yn(){switch(await U(),l()){case"darwin":{if(o.existsSync(m)){try{c(`launchctl unload -w "${m}"`,{stdio:"pipe",windowsHide:!0})}catch{}o.unlinkSync(m),console.log(s.green("\u2713 Auto-start uninstalled (launchd)"))}break}case"linux":{try{c("systemctl --user disable --now shennian",{stdio:"pipe",windowsHide:!0})}catch{}if(o.existsSync(D)){o.unlinkSync(D);try{c("systemctl --user daemon-reload",{stdio:"pipe",windowsHide:!0})}catch{}}console.log(s.green("\u2713 Auto-start uninstalled (systemd)"));break}case"win32":{Y();try{o.unlinkSync($)}catch{}try{o.unlinkSync(H)}catch{}try{o.unlinkSync(_)}catch{}z(),console.log(s.green("\u2713 Auto-start uninstalled (Task Scheduler)"));break}}}function ue(n){n.command("start").description("Start the background service").option("--json","Print machine-readable status JSON").option("--api <url>","Server URL override").action(r=>rn(r)),n.command("stop").description("Stop the background service").option("--json","Print machine-readable status JSON").action(r=>U(r)),n.command("status").description("Show background service status").option("--json","Print machine-readable status JSON").action(r=>on(r)),n.command("logs").description("Show recent logs").option("-n, --lines <n>","Number of lines","50").action(r=>sn({lines:parseInt(r.lines,10)}));const e=n.command("daemon",{hidden:!0}).description("Deprecated: use top-level start/stop/status/logs"),t=(r,i)=>{i||console.error(s.yellow(`\u26A0 Deprecated command. Use \`shennian ${r}\` instead.`))};e.command("start").description("Start the background service").option("--json","Print machine-readable status JSON").option("--api <url>","Server URL override").action(r=>{t("start",r.json),rn(r)}),e.command("stop").description("Stop the background service").option("--json","Print machine-readable status JSON").action(async r=>{t("stop",r.json),await U(r)}),e.command("restart").description("Restart the background service").option("--json","Print machine-readable status JSON").option("--api <url>","Server URL override").action(r=>{t("stop && shennian start",r.json),qn(r)}),e.command("status").description("Show background service status").option("--json","Print machine-readable status JSON").action(r=>{t("status",r.json),on(r)}),e.command("logs").description("Show recent logs").option("-n, --lines <n>","Number of lines","50").action(r=>{t("logs"),sn({lines:parseInt(r.lines,10)})}),e.command("uninstall").description("Uninstall auto-start service").action(Yn)}export{Un as buildDaemonSpawnOptions,pe as buildWindowsLauncherCommand,me as buildWindowsScheduledTaskXml,fe as buildWindowsStartupVbs,Q as captureEnvForService,O as clearDaemonLauncher,ae as clearDaemonPidIfOwner,Nn as clearRemoteAccessDisabled,bn as createDaemonInstanceId,G as findRunningDaemonProcessIds,V as getCurrentProcessDaemonLauncher,f as getDaemonStatus,Z as getLaunchdServiceTarget,tn as installService,L as isEphemeralCliPath,Dn as isRemoteAccessDisabled,En as isSafeSnapshotEnvKey,he as isShennianRunServiceCommand,Rn as recordStartedDaemon,ue as registerDaemonCommand,Bn as reloadLaunchdAgent,kn as resolveServiceLaunchSpec,Vn as saveEnvSnapshot,nn as startDaemonProcess,J as writeDaemonLauncher,Ln as writeDaemonPid};
44
+ WantedBy=default.target`}const P="ShennianAgent";function We(e){c(e,{stdio:"pipe",windowsHide:!0})}function Fe(){const e=process.getuid?.();if(typeof e=="number")return e;const n=a.userInfo();return typeof n.uid=="number"?n.uid:n.username}function ee(e=Fe()){return`gui/${e}/${$}`}function Be(e=We){try{return e(`launchctl unload "${m}" 2>/dev/null; launchctl load -w "${m}"`),e(`launchctl kickstart -k "${ee()}"`),!0}catch{try{return e(`launchctl load -w "${m}"`),e(`launchctl kickstart -k "${ee()}"`),!0}catch{return!1}}}function Ve(){o.mkdirSync(y,{recursive:!0});const e={};for(const[n,t]of Object.entries(process.env))t!==void 0&&Ee(n)&&(e[n]=t);o.writeFileSync(h("env.json"),JSON.stringify(e,null,2))}function ne(e={}){o.mkdirSync(y,{recursive:!0});const n=f({cleanupStale:!0});if(n.pid!==null&&n.running&&!n.adopted){e.quiet||console.log(s.green(`\u2713 Background service already running (PID ${n.pid})`));return}te(n);const t=o.openSync(u,"a"),r=_e(k()),i=ce(r.command,r.args,Ue(r,t));i.unref(),o.closeSync(t),Re(i.pid),e.quiet||(console.log(s.green(`\u2713 Background service started (PID ${i.pid})`)),console.log(s.gray(` Logs: ${u}`)))}function te(e=f({cleanupStale:!0})){if(!(e.pid===null||!e.running||!e.adopted))try{process.kill(e.pid,"SIGTERM"),Ie(e.pid);try{o.unlinkSync(p)}catch{}}catch{}}function re(){o.mkdirSync(y,{recursive:!0});const e=l();switch(k().mode==="direct"&&R(F)&&console.warn(s.yellow("\u26A0 Warning: Current CLI path is temporary (npx). Auto-start may not work after reboot.\n Run `npm install -g shennian@latest` for reliable auto-start.")),e){case"darwin":{const t=d.dirname(m);return o.mkdirSync(t,{recursive:!0}),o.writeFileSync(m,Me()),Be()}case"linux":{const t=d.dirname(D);o.mkdirSync(t,{recursive:!0}),o.writeFileSync(D,je());try{c("systemctl --user daemon-reload && systemctl --user enable shennian",{stdio:"pipe",windowsHide:!0})}catch{return!1}try{c(`loginctl enable-linger ${a.userInfo().username}`,{stdio:"pipe",windowsHide:!0})}catch{}try{return c("systemctl --user restart shennian",{stdio:"pipe",windowsHide:!0}),!0}catch{return!1}}case"win32":return Ce()}}function Je(){const n=f({cleanupStale:!0}).pid;if(n===null)return{};if(!w(n)){try{o.unlinkSync(p)}catch{}return{stalePid:n}}try{return process.kill(n,"SIGTERM"),{stoppedPid:n}}catch(t){return{error:t instanceof Error?t.message:String(t)}}}async function Ge(e=5e3){const n=Je();if(!n.stoppedPid)return n;if(!await A(n.stoppedPid,e))try{process.kill(n.stoppedPid,"SIGKILL"),await A(n.stoppedPid,2e3)}catch(r){return{...n,error:r instanceof Error?r.message:String(r)}}try{o.unlinkSync(p)}catch{}T();for(const r of K())if(r!==n.stoppedPid)try{process.kill(r,"SIGTERM"),await A(r,2e3)}catch{}return n}function Ke(e={}){X(q(e.api)),Ne(),te(),re()||ne({quiet:e.json}),e.json?E(b(()=>f({cleanupStale:!0}))):console.log(s.green("\u2713 Background service started"))}async function Xe(e={}){switch(o.mkdirSync(y,{recursive:!0}),o.writeFileSync(L,new Date().toISOString()),l()){case"darwin":{if(o.existsSync(m))try{c(`launchctl unload -w "${m}"`,{stdio:"pipe",windowsHide:!0})}catch{}break}case"linux":{try{c("systemctl --user disable --now shennian",{stdio:"pipe",windowsHide:!0})}catch{}break}case"win32":{try{c(`schtasks /change /tn "${P}" /disable`,{stdio:"pipe",windowsHide:!0})}catch{}try{c(`schtasks /end /tn "${P}"`,{stdio:"pipe",windowsHide:!0})}catch{}break}}const t=await Ge(),r=f({cleanupStale:!0});if(e.json){E(t.error?{...r,error:t.error}:r);return}t.error?console.error(s.red(`\u2717 Failed to stop: ${t.error}`)):console.log(s.green("\u2713 Background service stopped"))}function oe(e){Ve(),Ke(e)}async function C(e={}){await Xe(e)}async function A(e,n=5e3){const t=Date.now();for(;Date.now()-t<n;){if(!w(e))return!0;await new Promise(r=>setTimeout(r,100))}return!w(e)}async function qe(e={}){X(q(e.api));const n=Ae();if(n!==null&&w(n))try{process.kill(n,"SIGTERM"),await A(n)||(process.kill(n,"SIGKILL"),await A(n,2e3));try{o.unlinkSync(p)}catch{}T(),e.json||console.log(s.green(`\u2713 Background service stopped (PID ${n})`))}catch(t){const r=t instanceof Error?t.message:String(t);e.json?E({...f(),error:r}):console.error(s.red(`\u2717 Failed to stop: ${r}`));return}else if(n!==null){try{o.unlinkSync(p)}catch{}T(),e.json||console.log(s.yellow(`\u26A0 Service process no longer exists, cleaned up stale PID ${n}`))}else e.json||console.log(s.gray("\u25CF Background service not running, starting it now"));if(l()==="linux"&&re()){e.json&&E(b(()=>f({cleanupStale:!0})));return}ne({quiet:e.json}),e.json&&E(b(()=>f({cleanupStale:!0})))}function ie(e={}){const n=f({cleanupStale:!0});if(e.json){E(n);return}if(n.pid===null){console.log(s.gray("\u25CF Background service not running"));return}n.running?(console.log(s.green(`\u25CF Background service running (PID ${n.pid})`)),console.log(s.gray(` Logs: ${u}`))):console.log(s.yellow(`\u25CF Background service stopped (PID ${n.pid} is stale)`))}function se(e){if(!o.existsSync(u)){console.log(s.gray("No logs yet"));return}try{const n=c(a.platform()==="win32"?`powershell Get-Content -Tail ${e.lines} "${u}"`:`tail -n ${e.lines} "${u}"`,{encoding:"utf-8",windowsHide:!0});process.stdout.write(n)}catch{const n=o.readFileSync(u,"utf-8").split(`
45
+ `).slice(-e.lines);console.log(n.join(`
46
+ `))}}async function Ye(){switch(await C(),l()){case"darwin":{if(o.existsSync(m)){try{c(`launchctl unload -w "${m}"`,{stdio:"pipe",windowsHide:!0})}catch{}o.unlinkSync(m),console.log(s.green("\u2713 Auto-start uninstalled (launchd)"))}break}case"linux":{try{c("systemctl --user disable --now shennian",{stdio:"pipe",windowsHide:!0})}catch{}if(o.existsSync(D)){o.unlinkSync(D);try{c("systemctl --user daemon-reload",{stdio:"pipe",windowsHide:!0})}catch{}}console.log(s.green("\u2713 Auto-start uninstalled (systemd)"));break}case"win32":{z();try{o.unlinkSync(H)}catch{}try{o.unlinkSync(_)}catch{}try{o.unlinkSync(U)}catch{}Q(),console.log(s.green("\u2713 Auto-start uninstalled (Task Scheduler)"));break}}}function ln(e){e.command("start").description("Start the background service").option("--json","Print machine-readable status JSON").option("--api <url>","Server URL override").action(r=>oe(r)),e.command("stop").description("Stop the background service").option("--json","Print machine-readable status JSON").action(r=>C(r)),e.command("status").description("Show background service status").option("--json","Print machine-readable status JSON").action(r=>ie(r)),e.command("logs").description("Show recent logs").option("-n, --lines <n>","Number of lines","50").action(r=>se({lines:parseInt(r.lines,10)}));const n=e.command("daemon",{hidden:!0}).description("Deprecated: use top-level start/stop/status/logs"),t=(r,i)=>{i||console.error(s.yellow(`\u26A0 Deprecated command. Use \`shennian ${r}\` instead.`))};n.command("start").description("Start the background service").option("--json","Print machine-readable status JSON").option("--api <url>","Server URL override").action(r=>{t("start",r.json),oe(r)}),n.command("stop").description("Stop the background service").option("--json","Print machine-readable status JSON").action(async r=>{t("stop",r.json),await C(r)}),n.command("restart").description("Restart the background service").option("--json","Print machine-readable status JSON").option("--api <url>","Server URL override").action(r=>{t("stop && shennian start",r.json),qe(r)}),n.command("status").description("Show background service status").option("--json","Print machine-readable status JSON").action(r=>{t("status",r.json),ie(r)}),n.command("logs").description("Show recent logs").option("-n, --lines <n>","Number of lines","50").action(r=>{t("logs"),se({lines:parseInt(r.lines,10)})}),n.command("uninstall").description("Uninstall auto-start service").action(Ye)}export{Ue as buildDaemonSpawnOptions,fn as buildWindowsLauncherCommand,Sn as buildWindowsScheduledTaskXml,hn as buildWindowsStartupVbs,Z as captureEnvForService,T as clearDaemonLauncher,dn as clearDaemonPidIfOwner,Ne as clearRemoteAccessDisabled,be as createDaemonInstanceId,K as findRunningDaemonProcessIds,J as getCurrentProcessDaemonLauncher,f as getDaemonStatus,ee as getLaunchdServiceTarget,re as installService,R as isEphemeralCliPath,De as isRemoteAccessDisabled,Ee as isSafeSnapshotEnvKey,yn as isShennianRunServiceCommand,Re as recordStartedDaemon,ln as registerDaemonCommand,Be as reloadLaunchdAgent,ke as resolveServiceLaunchSpec,Ve as saveEnvSnapshot,ne as startDaemonProcess,G as writeDaemonLauncher,Le as writeDaemonPid};
@@ -1,9 +1,9 @@
1
- import a from"chalk";import y from"node:os";import U from"node:readline";import{loadConfig as d,saveConfig as T}from"../config/index.js";import{detectAgents as S}from"../agents/detect.js";import{clearRemoteAccessDisabled as v,startDaemonProcess as P,installService as N,saveEnvSnapshot as R}from"./daemon.js";import{detectAndChooseServer as I}from"../region.js";import{buildPairQrPayload as O,PAIR_QR_RENDER_OPTIONS as $}from"./pair-qr.js";const j=3e3,E=600*1e3;function s(o){process.stdout.write(`${JSON.stringify(o)}
2
- `)}function w(o,e){s({type:"error",code:o,message:e}),process.exit(1)}function _(o){const e="\u2500".repeat(o.length+4);console.log(a.cyan(`\u250C${e}\u2510`)),console.log(a.cyan("\u2502 ")+a.bold.yellow(o)+a.cyan(" \u2502")),console.log(a.cyan(`\u2514${e}\u2518`))}async function C(o){const e=(await import("qrcode-terminal")).default;return new Promise(n=>{e.generate(o,$,r=>{console.log(r),n()})})}async function D(o,e,n={}){const r=Date.now()+E;for(n.json&&s({type:"pair.waiting"});Date.now()<r;){await new Promise(m=>setTimeout(m,j));let i;try{i=await fetch(`${o}/api/machines/pair/status?token=${encodeURIComponent(e)}`)}catch{continue}if(i.status===410&&(n.json?w("PAIR_EXPIRED","Pairing code expired"):(console.error(a.red(`
3
- \u2717 Pairing code expired. Please re-run: shennian pair`)),process.exit(1))),!i.ok)continue;const t=await i.json();if(t.status==="claimed"&&t.machineToken&&t.machineId)return n.json&&s({type:"pair.claimed"}),{machineToken:t.machineToken,machineId:t.machineId,machineName:t.machineName??""}}return null}function L(o){const e=U.createInterface({input:process.stdin,output:process.stdout});return new Promise(n=>{e.question(o,r=>{e.close(),n(r.trim().toLowerCase()==="y")})})}async function A(o){const{serverUrl:e,machineName:n,force:r,json:i}=o,t=d();if(t.machineToken&&t.machineId&&!r){i?s({type:"done",status:"already-paired",machineId:t.machineId}):(console.log(a.green(`\u2713 Already paired (machine ID: ${t.machineId})`)),console.log(a.gray(' Run "shennian pair" to re-pair to another account')));return}const c=S().map(g=>g.type),l=y.platform(),u=l==="win32"?"windows":l==="darwin"?"macos":"linux";i||(console.log(a.bold(`
1
+ import i from"chalk";import y from"node:os";import U from"node:readline";import{loadConfig as d,saveConfig as S}from"../config/index.js";import{detectAgents as T}from"../agents/detect.js";import{clearRemoteAccessDisabled as v,getDaemonStatus as O,startDaemonProcess as P,installService as R,saveEnvSnapshot as N}from"./daemon.js";import{waitForDaemonReadyStatus as $}from"./daemon-start-wait.js";import{detectAndChooseServer as I}from"../region.js";import{buildPairQrPayload as j,PAIR_QR_RENDER_OPTIONS as D}from"./pair-qr.js";const _=3e3,E=600*1e3;function s(o){process.stdout.write(`${JSON.stringify(o)}
2
+ `)}function w(o,e){s({type:"error",code:o,message:e}),process.exit(1)}function C(o){const e="\u2500".repeat(o.length+4);console.log(i.cyan(`\u250C${e}\u2510`)),console.log(i.cyan("\u2502 ")+i.bold.yellow(o)+i.cyan(" \u2502")),console.log(i.cyan(`\u2514${e}\u2518`))}async function L(o){const e=(await import("qrcode-terminal")).default;return new Promise(n=>{e.generate(o,D,r=>{console.log(r),n()})})}async function b(o,e,n={}){const r=Date.now()+E;for(n.json&&s({type:"pair.waiting"});Date.now()<r;){await new Promise(m=>setTimeout(m,_));let a;try{a=await fetch(`${o}/api/machines/pair/status?token=${encodeURIComponent(e)}`)}catch{continue}if(a.status===410&&(n.json?w("PAIR_EXPIRED","Pairing code expired"):(console.error(i.red(`
3
+ \u2717 Pairing code expired. Please re-run: shennian pair`)),process.exit(1))),!a.ok)continue;const t=await a.json();if(t.status==="claimed"&&t.machineToken&&t.machineId)return n.json&&s({type:"pair.claimed"}),{machineToken:t.machineToken,machineId:t.machineId,machineName:t.machineName??""}}return null}function M(o){const e=U.createInterface({input:process.stdin,output:process.stdout});return new Promise(n=>{e.question(o,r=>{e.close(),n(r.trim().toLowerCase()==="y")})})}async function A(o){const{serverUrl:e,machineName:n,force:r,json:a}=o,t=d();if(t.machineToken&&t.machineId&&!r){a?s({type:"done",status:"already-paired",machineId:t.machineId}):(console.log(i.green(`\u2713 Already paired (machine ID: ${t.machineId})`)),console.log(i.gray(' Run "shennian pair" to re-pair to another account')));return}const c=T().map(g=>g.type),l=y.platform(),u=l==="win32"?"windows":l==="darwin"?"macos":"linux";a||(console.log(i.bold(`
4
4
  Shennian CLI \u2014 Pairing
5
- `)),console.log(a.gray(` Machine: ${n} | OS: ${u} | Agents: ${c.join(", ")||"none"}`)),console.log());let h;try{h=await fetch(`${e}/api/machines/pair/create`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:n,os:u,agentList:c})})}catch{const g=`Network error: cannot connect to ${e}`;i&&w("NETWORK_ERROR",g),console.error(a.red(`\u2717 ${g}`)),process.exit(1)}if(!h.ok){const k=`Failed to create machine: ${(await h.json().catch(()=>({}))).error??h.statusText}`;i&&w("PAIR_CREATE_FAILED",k),console.error(a.red(`\u2717 ${k}`)),process.exit(1)}const p=await h.json(),x=O(p.pairToken);i?s({type:"pair.created",code:p.pairToken,machineId:p.machineId,expiresAt:new Date(Date.now()+E).toISOString()}):(console.log(a.white("Scan QR code or enter the pairing code in the Shennian app:")),console.log(),await C(x),_(p.pairToken),console.log(),console.log(a.gray("Waiting for pairing... (Ctrl+C to cancel)")));const f=await D(e,p.pairToken,{json:i});f||(i&&w("PAIR_TIMEOUT","Timed out waiting for pairing"),console.error(a.red(`
6
- \u2717 Timed out. Pairing code expired, please try again.`)),process.exit(1)),T({...t,machineToken:f.machineToken,machineId:f.machineId,serverUrl:e}),i&&s({type:"pair.saved",machineId:f.machineId}),i||console.log(a.green(`
7
- \u2713 Paired successfully! Machine "${f.machineName}" is now linked.`))}async function b(o){const e=d(),r=S().map(l=>l.type),i=y.platform(),t=i==="win32"?"windows":i==="darwin"?"macos":"linux",m=await fetch(`${o.serverUrl}/api/machines/desktop/register`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${o.authToken}`},body:JSON.stringify({machineId:e.machineId,name:o.machineName,os:t,agentList:r})});if(!m.ok){const u=(await m.json().catch(()=>({}))).error??m.statusText;throw new Error(`Failed to register desktop machine: ${u}`)}const c=await m.json();if(!c.machine?.id||!c.machineToken)throw new Error("Failed to register desktop machine: missing machine token");return e.machineId=c.machine.id,e.machineToken=c.machineToken,e.serverUrl=o.serverUrl,T(e),{machineId:c.machine.id,machineName:c.machine.name}}async function M(o){const e=o?.trim();if(e)return e;const n=process.env.SHENNIAN_DESKTOP_SERVER_URL?.trim();if(n)return n;const r=d().serverUrl?.trim();return r||I()}async function W(o,e){const n=d();if(!n.machineToken||!n.machineId){const i=o??await I();await A({serverUrl:i,machineName:e,force:!1})}else console.log(a.green(`\u2713 Already paired (machine ID: ${n.machineId})`));R(),v(),console.log(a.gray(`
8
- Starting background service...`)),N()||P()}function z(o){o.command("desktop-register",{hidden:!0}).description("Register this desktop-managed machine to the authenticated account").option("--api <url>","Server URL").option("--name <name>","Machine name",y.hostname()).action(async e=>{try{const n=process.env.SHENNIAN_DESKTOP_AUTH_TOKEN;if(!n)throw new Error("Missing desktop auth token");const r=await M(e.api),i=await b({serverUrl:r,authToken:n,machineName:e.name});console.log(JSON.stringify({ok:!0,...i}))}catch(n){console.error(n instanceof Error?n.message:String(n)),process.exit(1)}}),o.command("pair").description("Pair this machine to a Shennian account (re-pair if already paired)").option("--api <url>","Server URL override").option("--server <url>","Server URL override").option("--name <name>","Machine name",y.hostname()).option("--json","Emit machine-readable JSONL events").option("--force","Force re-pair even when a machine token exists").action(async e=>{const n=d();if(n.machineToken&&n.machineId&&!e.force){if(e.json){s({type:"done",status:"already-paired",machineId:n.machineId});return}if(console.log(a.yellow(`Currently paired (machine ID: ${n.machineId})`)),!await L("Re-pairing will unlink the current account. Continue? (y/N) ")){console.log(a.gray("Cancelled"));return}}const r=e.server??e.api??await I(),i=!!(e.force||n.machineToken&&n.machineId);await A({serverUrl:r,machineName:e.name,force:i,json:!!e.json}),R(),v(),e.json?s({type:"daemon.starting"}):console.log(a.gray(`
9
- Starting background service...`)),N()||P({quiet:!!e.json}),e.json&&s({type:"done",status:"paired",machineId:d().machineId})})}export{b as registerDesktopMachine,z as registerPairCommand,A as runPairFlow,W as runSmartStart};
5
+ `)),console.log(i.gray(` Machine: ${n} | OS: ${u} | Agents: ${c.join(", ")||"none"}`)),console.log());let h;try{h=await fetch(`${e}/api/machines/pair/create`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:n,os:u,agentList:c})})}catch{const g=`Network error: cannot connect to ${e}`;a&&w("NETWORK_ERROR",g),console.error(i.red(`\u2717 ${g}`)),process.exit(1)}if(!h.ok){const k=`Failed to create machine: ${(await h.json().catch(()=>({}))).error??h.statusText}`;a&&w("PAIR_CREATE_FAILED",k),console.error(i.red(`\u2717 ${k}`)),process.exit(1)}const p=await h.json(),x=j(p.pairToken);a?s({type:"pair.created",code:p.pairToken,machineId:p.machineId,expiresAt:new Date(Date.now()+E).toISOString()}):(console.log(i.white("Scan QR code or enter the pairing code in the Shennian app:")),console.log(),await L(x),C(p.pairToken),console.log(),console.log(i.gray("Waiting for pairing... (Ctrl+C to cancel)")));const f=await b(e,p.pairToken,{json:a});f||(a&&w("PAIR_TIMEOUT","Timed out waiting for pairing"),console.error(i.red(`
6
+ \u2717 Timed out. Pairing code expired, please try again.`)),process.exit(1)),S({...t,machineToken:f.machineToken,machineId:f.machineId,serverUrl:e}),a&&s({type:"pair.saved",machineId:f.machineId}),a||console.log(i.green(`
7
+ \u2713 Paired successfully! Machine "${f.machineName}" is now linked.`))}async function B(o){const e=d(),r=T().map(l=>l.type),a=y.platform(),t=a==="win32"?"windows":a==="darwin"?"macos":"linux",m=await fetch(`${o.serverUrl}/api/machines/desktop/register`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${o.authToken}`},body:JSON.stringify({machineId:e.machineId,name:o.machineName,os:t,agentList:r})});if(!m.ok){const u=(await m.json().catch(()=>({}))).error??m.statusText;throw new Error(`Failed to register desktop machine: ${u}`)}const c=await m.json();if(!c.machine?.id||!c.machineToken)throw new Error("Failed to register desktop machine: missing machine token");return e.machineId=c.machine.id,e.machineToken=c.machineToken,e.serverUrl=o.serverUrl,S(e),{machineId:c.machine.id,machineName:c.machine.name}}async function F(o){const e=o?.trim();if(e)return e;const n=process.env.SHENNIAN_DESKTOP_SERVER_URL?.trim();if(n)return n;const r=d().serverUrl?.trim();return r||I()}async function G(o,e){const n=d();if(!n.machineToken||!n.machineId){const a=o??await I();await A({serverUrl:a,machineName:e,force:!1})}else console.log(i.green(`\u2713 Already paired (machine ID: ${n.machineId})`));N(),v(),console.log(i.gray(`
8
+ Starting background service...`)),R()||P()}function Y(o){o.command("desktop-register",{hidden:!0}).description("Register this desktop-managed machine to the authenticated account").option("--api <url>","Server URL").option("--name <name>","Machine name",y.hostname()).action(async e=>{try{const n=process.env.SHENNIAN_DESKTOP_AUTH_TOKEN;if(!n)throw new Error("Missing desktop auth token");const r=await F(e.api),a=await B({serverUrl:r,authToken:n,machineName:e.name});console.log(JSON.stringify({ok:!0,...a}))}catch(n){console.error(n instanceof Error?n.message:String(n)),process.exit(1)}}),o.command("pair").description("Pair this machine to a Shennian account (re-pair if already paired)").option("--api <url>","Server URL override").option("--server <url>","Server URL override").option("--name <name>","Machine name",y.hostname()).option("--json","Emit machine-readable JSONL events").option("--force","Force re-pair even when a machine token exists").action(async e=>{const n=d();if(n.machineToken&&n.machineId&&!e.force){if(e.json){s({type:"done",status:"already-paired",machineId:n.machineId});return}if(console.log(i.yellow(`Currently paired (machine ID: ${n.machineId})`)),!await M("Re-pairing will unlink the current account. Continue? (y/N) ")){console.log(i.gray("Cancelled"));return}}const r=e.server??e.api??await I(),a=!!(e.force||n.machineToken&&n.machineId);await A({serverUrl:r,machineName:e.name,force:a,json:!!e.json}),N(),v(),e.json?s({type:"daemon.starting"}):console.log(i.gray(`
9
+ Starting background service...`)),R()||P({quiet:!!e.json}),e.json&&($(()=>O({cleanupStale:!0})),s({type:"done",status:"paired",machineId:d().machineId}))})}export{B as registerDesktopMachine,Y as registerPairCommand,A as runPairFlow,G as runSmartStart};
@@ -27,5 +27,7 @@ export declare function runHelperRuntimeAction(input: {
27
27
  channel?: string;
28
28
  manifestUrl?: string;
29
29
  probePermissions?: boolean;
30
+ env?: NodeJS.ProcessEnv;
31
+ homedir?: string;
30
32
  }): Promise<HelperRuntimeActionResult>;
31
33
  export {};
@@ -1 +1 @@
1
- import{loadConfig as d}from"../../config/index.js";import{getHelperRuntimeStatus as p,installHelperRuntimeWithOfficialDownload as h}from"../../commands/runtime.js";import{createWeChatChannelApiClient as w}from"../../channels/wechat-channel/client.js";import{WECHAT_CHANNEL_HELPER_VERSION as g}from"../../channels/wechat-channel/helper-assets.js";import{WeChatChannelHelperClient as y}from"../../channels/wechat-channel/helper-client.js";import{requiredWeChatChannelHelperCapabilitiesForProfile as R}from"../../channels/wechat-channel/helper-protocol.js";import{createWeChatChannelRuntime as V}from"../../channels/wechat-channel/runtime.js";import{getCurrentVersion as b}from"../../upgrade/engine.js";async function I(e,o){const r=o.params??{},t=k(r.action);if(!t){e.client.sendRes({type:"res",id:o.id,ok:!1,error:'action must be "status", "install", "upgrade", or "repair"'});return}const n=typeof r.platform=="string"?r.platform:void 0,i=typeof r.source=="string"&&r.source.trim()?r.source:void 0,a=typeof r.channel=="string"&&r.channel.trim()?r.channel:void 0,c=typeof r.manifestUrl=="string"&&r.manifestUrl.trim()?r.manifestUrl:void 0,u=await C({action:t,platform:n,source:i,channel:a,manifestUrl:c});e.client.sendRes({type:"res",id:o.id,ok:!0,payload:u})}async function C(e){if(e.action==="status"){const i=p({platform:e.platform}),a=e.probePermissions===!1?void 0:await f(i),c=await m(i,a);return{action:e.action,status:i,permissions:a,projection:c}}const o=await h({source:e.source,platform:e.platform,force:e.action==="upgrade"||e.action==="repair",preferOfficialDownload:(e.action==="upgrade"||e.action==="repair")&&!e.source,officialDownload:{channel:e.channel,manifestUrl:e.manifestUrl}}),r=p({platform:e.platform}),t=e.probePermissions===!1?void 0:await f(r),n=await m(r,t);return{action:e.action,install:o,status:r,permissions:t,projection:n}}function k(e){return e==="status"||e==="install"||e==="upgrade"||e==="repair"?e:null}async function f(e){const o=new Date().toISOString();if(!e.ok||!e.helperPath)return{checkedAt:o,helperReady:!1,helperVersion:e.helperVersion??null,protocolVersion:e.protocolVersion??null,platform:e.platform};const r=new y({helperPath:e.helperPath,expectedHelperVersion:e.helperVersion||g,requiredCapabilities:R("send")});try{await r.start();const t=await r.request("permissions.check",{},`helper-runtime-status-${Date.now()}`),n=t.ok&&t.result&&typeof t.result=="object"?t.result:{};return{checkedAt:o,helperReady:t.ok,helperVersion:e.helperVersion??H(n.helperVersion),protocolVersion:e.protocolVersion??A(n.protocolVersion),platform:e.platform,screenRecording:l(n.screenRecording),accessibility:l(n.accessibility),inputMonitoring:l(n.inputMonitoring),automation:l(n.automation),wechatRunning:s(n.wechatRunning),wechatAppDetected:s(n.wechatAppDetected??n.wechatRunning),wechatWindowAvailable:s(n.wechatWindowAvailable)}}catch{return{checkedAt:o,helperReady:!1,helperVersion:e.helperVersion??null,protocolVersion:e.protocolVersion??null,platform:e.platform}}finally{await r.stop().catch(()=>{})}}async function m(e,o){if(!o||e.platform!=="darwin"&&e.platform!=="win32")return;const r=d(),t=r.machineId?.trim(),n=r.machineToken?.trim();if(!t||!n)return{ok:!1,error:"machine_not_paired"};try{const i=V({runtimeId:`wechat-machine:${t}`,machineId:t,platform:e.platform,bindings:[]});return await w({serverUrl:r.serverUrl,machineToken:n}).upsertRuntime(i,void 0,{helperVersion:e.helperVersion??void 0,clientRuntimeVersion:b(),preflight:o}),{ok:!0}}catch(i){return{ok:!1,error:i instanceof Error?i.message:String(i)}}}function l(e){return typeof e=="boolean"?e:"unknown"}function s(e){return typeof e=="boolean"?e:void 0}function H(e){return(typeof e=="string"?e.trim():"")||null}function A(e){const o=Number(e);return Number.isFinite(o)?o:null}export{I as handleWeChatHelperRuntime,C as runHelperRuntimeAction};
1
+ import{loadConfig as h}from"../../config/index.js";import{getHelperRuntimeStatus as f,installHelperRuntimeWithOfficialDownload as u}from"../../commands/runtime.js";import{createWeChatChannelApiClient as w}from"../../channels/wechat-channel/client.js";import{WECHAT_CHANNEL_HELPER_VERSION as g}from"../../channels/wechat-channel/helper-assets.js";import{WeChatChannelHelperClient as y}from"../../channels/wechat-channel/helper-client.js";import{requiredWeChatChannelHelperCapabilitiesForProfile as R}from"../../channels/wechat-channel/helper-protocol.js";import{createWeChatChannelRuntime as V}from"../../channels/wechat-channel/runtime.js";import{getCurrentVersion as b}from"../../upgrade/engine.js";async function I(e,o){const r=o.params??{},t=k(r.action);if(!t){e.client.sendRes({type:"res",id:o.id,ok:!1,error:'action must be "status", "install", "upgrade", or "repair"'});return}const n=typeof r.platform=="string"?r.platform:void 0,i=typeof r.source=="string"&&r.source.trim()?r.source:void 0,a=typeof r.channel=="string"&&r.channel.trim()?r.channel:void 0,c=typeof r.manifestUrl=="string"&&r.manifestUrl.trim()?r.manifestUrl:void 0,d=await C({action:t,platform:n,source:i,channel:a,manifestUrl:c});e.client.sendRes({type:"res",id:o.id,ok:!0,payload:d})}async function C(e){if(e.action==="status"){const i=f({platform:e.platform,env:e.env,homedir:e.homedir}),a=e.probePermissions===!1?void 0:await m(i),c=await p(i,a);return{action:e.action,status:i,permissions:a,projection:c}}const o=await u({source:e.source,platform:e.platform,env:e.env,homedir:e.homedir,force:e.action==="upgrade"||e.action==="repair",preferOfficialDownload:(e.action==="upgrade"||e.action==="repair")&&!e.source,officialDownload:{channel:e.channel,manifestUrl:e.manifestUrl}}),r=f({platform:e.platform,env:e.env,homedir:e.homedir}),t=e.probePermissions===!1?void 0:await m(r),n=await p(r,t);return{action:e.action,install:o,status:r,permissions:t,projection:n}}function k(e){return e==="status"||e==="install"||e==="upgrade"||e==="repair"?e:null}async function m(e){const o=new Date().toISOString();if(!e.ok||!e.helperPath)return{checkedAt:o,helperReady:!1,helperVersion:e.helperVersion??null,protocolVersion:e.protocolVersion??null,platform:e.platform};const r=new y({helperPath:e.helperPath,expectedHelperVersion:e.helperVersion||g,requiredCapabilities:R("send")});try{await r.start();const t=await r.request("permissions.check",{},`helper-runtime-status-${Date.now()}`),n=t.ok&&t.result&&typeof t.result=="object"?t.result:{};return{checkedAt:o,helperReady:t.ok,helperVersion:e.helperVersion??H(n.helperVersion),protocolVersion:e.protocolVersion??A(n.protocolVersion),platform:e.platform,screenRecording:l(n.screenRecording),accessibility:l(n.accessibility),inputMonitoring:l(n.inputMonitoring),automation:l(n.automation),wechatRunning:s(n.wechatRunning),wechatAppDetected:s(n.wechatAppDetected??n.wechatRunning),wechatWindowAvailable:s(n.wechatWindowAvailable)}}catch{return{checkedAt:o,helperReady:!1,helperVersion:e.helperVersion??null,protocolVersion:e.protocolVersion??null,platform:e.platform}}finally{await r.stop().catch(()=>{})}}async function p(e,o){if(!o||e.platform!=="darwin"&&e.platform!=="win32")return;const r=h(),t=r.machineId?.trim(),n=r.machineToken?.trim();if(!t||!n)return{ok:!1,error:"machine_not_paired"};try{const i=V({runtimeId:`wechat-machine:${t}`,machineId:t,platform:e.platform,bindings:[]});return await w({serverUrl:r.serverUrl,machineToken:n}).upsertRuntime(i,void 0,{helperVersion:e.helperVersion??void 0,clientRuntimeVersion:b(),preflight:o}),{ok:!0}}catch(i){return{ok:!1,error:i instanceof Error?i.message:String(i)}}}function l(e){return typeof e=="boolean"?e:"unknown"}function s(e){return typeof e=="boolean"?e:void 0}function H(e){return(typeof e=="string"?e.trim():"")||null}function A(e){const o=Number(e);return Number.isFinite(o)?o:null}export{I as handleWeChatHelperRuntime,C as runHelperRuntimeAction};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shennian",
3
- "version": "0.2.134",
3
+ "version": "0.2.136",
4
4
  "description": "Shennian — AI Agent Control Plane CLI",
5
5
  "type": "module",
6
6
  "bin": {