shennian 0.2.113 → 0.2.115

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": 133,
5
+ "fileCount": 134,
6
6
  "files": [
7
7
  {
8
8
  "file": "bin/shennian.js",
@@ -176,8 +176,8 @@
176
176
  },
177
177
  {
178
178
  "file": "src/channels/wechat-channel/automation-lane.js",
179
- "beforeBytes": 4299,
180
- "afterBytes": 2233
179
+ "beforeBytes": 5498,
180
+ "afterBytes": 2547
181
181
  },
182
182
  {
183
183
  "file": "src/channels/wechat-channel/client.js",
@@ -221,13 +221,13 @@
221
221
  },
222
222
  {
223
223
  "file": "src/channels/wechat-channel/helper-client.js",
224
- "beforeBytes": 31826,
225
- "afterBytes": 16459
224
+ "beforeBytes": 36712,
225
+ "afterBytes": 17546
226
226
  },
227
227
  {
228
228
  "file": "src/channels/wechat-channel/helper-protocol.js",
229
- "beforeBytes": 8198,
230
- "afterBytes": 3837
229
+ "beforeBytes": 8581,
230
+ "afterBytes": 4026
231
231
  },
232
232
  {
233
233
  "file": "src/channels/wechat-channel/human-coordination.js",
@@ -261,8 +261,8 @@
261
261
  },
262
262
  {
263
263
  "file": "src/channels/wechat-channel/observer.js",
264
- "beforeBytes": 102627,
265
- "afterBytes": 47836
264
+ "beforeBytes": 104356,
265
+ "afterBytes": 47760
266
266
  },
267
267
  {
268
268
  "file": "src/channels/wechat-channel/outbound-ledger.js",
@@ -276,8 +276,8 @@
276
276
  },
277
277
  {
278
278
  "file": "src/channels/wechat-channel/pacing.js",
279
- "beforeBytes": 1673,
280
- "afterBytes": 941
279
+ "beforeBytes": 2561,
280
+ "afterBytes": 1070
281
281
  },
282
282
  {
283
283
  "file": "src/channels/wechat-channel/preflight.js",
@@ -286,8 +286,8 @@
286
286
  },
287
287
  {
288
288
  "file": "src/channels/wechat-channel/runner.js",
289
- "beforeBytes": 14776,
290
- "afterBytes": 7286
289
+ "beforeBytes": 15187,
290
+ "afterBytes": 7537
291
291
  },
292
292
  {
293
293
  "file": "src/channels/wechat-channel/runtime.js",
@@ -321,8 +321,8 @@
321
321
  },
322
322
  {
323
323
  "file": "src/channels/wechat-rpa/product-channel.js",
324
- "beforeBytes": 17962,
325
- "afterBytes": 9649
324
+ "beforeBytes": 18089,
325
+ "afterBytes": 9740
326
326
  },
327
327
  {
328
328
  "file": "src/channels/wechat-rpa.js",
@@ -339,6 +339,11 @@
339
339
  "beforeBytes": 6216,
340
340
  "afterBytes": 3443
341
341
  },
342
+ {
343
+ "file": "src/commands/daemon-process.js",
344
+ "beforeBytes": 3288,
345
+ "afterBytes": 1780
346
+ },
342
347
  {
343
348
  "file": "src/commands/daemon-windows.js",
344
349
  "beforeBytes": 3570,
@@ -346,8 +351,8 @@
346
351
  },
347
352
  {
348
353
  "file": "src/commands/daemon.js",
349
- "beforeBytes": 37382,
350
- "afterBytes": 17983
354
+ "beforeBytes": 37149,
355
+ "afterBytes": 17834
351
356
  },
352
357
  {
353
358
  "file": "src/commands/external-attachments.js",
@@ -381,8 +386,8 @@
381
386
  },
382
387
  {
383
388
  "file": "src/commands/runtime.js",
384
- "beforeBytes": 34179,
385
- "afterBytes": 19155
389
+ "beforeBytes": 34591,
390
+ "afterBytes": 19171
386
391
  },
387
392
  {
388
393
  "file": "src/commands/tools.js",
@@ -396,8 +401,8 @@
396
401
  },
397
402
  {
398
403
  "file": "src/commands/wechat/command.js",
399
- "beforeBytes": 7941,
400
- "afterBytes": 4755
404
+ "beforeBytes": 7986,
405
+ "afterBytes": 4767
401
406
  },
402
407
  {
403
408
  "file": "src/commands/wechat/direct.js",
@@ -1,5 +1,8 @@
1
+ export type WeChatAutomationLaneRunOptions = {
2
+ waitMs?: number;
3
+ };
1
4
  export type WeChatAutomationLane = {
2
- run<T>(label: string, operation: () => Promise<T>): Promise<T>;
5
+ run<T>(label: string, operation: () => Promise<T>, options?: WeChatAutomationLaneRunOptions): Promise<T>;
3
6
  snapshot(): WeChatAutomationLaneSnapshot;
4
7
  };
5
8
  export type WeChatAutomationLaneSnapshot = {
@@ -12,6 +15,7 @@ export type WeChatAutomationLaneOptions = {
12
15
  lockPath?: string;
13
16
  staleMs?: number;
14
17
  owner?: string;
18
+ defaultWaitMs?: number;
15
19
  };
16
20
  export declare class SerialWeChatAutomationLane implements WeChatAutomationLane {
17
21
  private tail;
@@ -21,10 +25,11 @@ export declare class SerialWeChatAutomationLane implements WeChatAutomationLane
21
25
  private readonly lockPath?;
22
26
  private readonly staleMs;
23
27
  private readonly owner;
28
+ private readonly defaultWaitMs;
24
29
  constructor(options?: WeChatAutomationLaneOptions);
25
- run<T>(label: string, operation: () => Promise<T>): Promise<T>;
30
+ run<T>(label: string, operation: () => Promise<T>, options?: WeChatAutomationLaneRunOptions): Promise<T>;
26
31
  snapshot(): WeChatAutomationLaneSnapshot;
27
- private acquireLock;
32
+ private acquireLockWithWait;
28
33
  }
29
- export declare function createWeChatAutomationLane(): WeChatAutomationLane;
34
+ export declare function createWeChatAutomationLane(options?: WeChatAutomationLaneOptions): WeChatAutomationLane;
30
35
  export declare function defaultWeChatAutomationLockPath(): string;
@@ -1,2 +1,2 @@
1
- import r from"node:fs";import l from"node:os";import s from"node:path";import{resolveShennianPath as u}from"../../config/index.js";const h=900*1e3;class f{tail=Promise.resolve();active=!1;queued=0;lastLabel=null;lockPath;staleMs;owner;constructor(t={}){this.lockPath=t.lockPath,this.staleMs=t.staleMs??h,this.owner=t.owner??`${process.platform}:${process.pid}`}async run(t,a){this.queued+=1;const i=this.tail;let o;this.tail=new Promise(c=>{o=c}),await i.catch(()=>{}),this.queued-=1,this.active=!0,this.lastLabel=t;let n=null;try{return n=this.acquireLock(t),await a()}finally{n?.release(),this.active=!1,o()}}snapshot(){return{active:this.active,queued:this.queued,lastLabel:this.lastLabel,lockPath:this.lockPath}}acquireLock(t){if(!this.lockPath)return null;r.mkdirSync(s.dirname(this.lockPath),{recursive:!0});try{r.mkdirSync(this.lockPath)}catch(a){if(!w(a))throw a;const i=d(this.lockPath);if(p(this.lockPath,i,this.staleMs))r.rmSync(this.lockPath,{recursive:!0,force:!0}),r.mkdirSync(this.lockPath);else throw new Error(P(this.lockPath,i))}return k(this.lockPath,{pid:process.pid,label:t,owner:this.owner,hostname:l.hostname(),startedAt:new Date().toISOString()}),{release:()=>{r.rmSync(this.lockPath,{recursive:!0,force:!0})}}}}function b(){return new f({lockPath:m()})}function m(){return u("runtime","wechat-automation.lock")}function k(e,t){r.writeFileSync(s.join(e,"owner.json"),`${JSON.stringify(t,null,2)}
2
- `,"utf8")}function d(e){try{const t=JSON.parse(r.readFileSync(s.join(e,"owner.json"),"utf8"));return t&&typeof t=="object"?t:null}catch{return null}}function p(e,t,a){if(t?.pid&&y(t.pid))return!1;try{return Date.now()-r.statSync(e).mtimeMs>a}catch{return!0}}function y(e){if(!Number.isInteger(e)||e<=0)return!1;try{return process.kill(e,0),!0}catch{return!1}}function P(e,t){return`wechat_automation_busy: another WeChat automation run is active (${[`lock=${e}`,t?.pid?`pid=${t.pid}`:"",t?.label?`label=${t.label}`:"",t?.startedAt?`startedAt=${t.startedAt}`:"",t?.owner?`owner=${t.owner}`:""].filter(Boolean).join(" ")})`}function w(e){return typeof e=="object"&&e!==null&&"code"in e&&e.code==="EEXIST"}export{f as SerialWeChatAutomationLane,b as createWeChatAutomationLane,m as defaultWeChatAutomationLockPath};
1
+ import a from"node:fs";import f from"node:os";import l from"node:path";import{resolveShennianPath as m}from"../../config/index.js";const d=900*1e3,k=3e4,w=100,L=1e3;class M{tail=Promise.resolve();active=!1;queued=0;lastLabel=null;lockPath;staleMs;owner;defaultWaitMs;constructor(t={}){this.lockPath=t.lockPath,this.staleMs=t.staleMs??d,this.owner=t.owner??`${process.platform}:${process.pid}`,this.defaultWaitMs=t.defaultWaitMs??k}async run(t,i,r={}){const n=Math.max(0,r.waitMs??this.defaultWaitMs);this.queued+=1;const o=this.tail;let s;this.tail=new Promise(h=>{s=h}),await o.catch(()=>{}),this.queued-=1,this.active=!0,this.lastLabel=t;const c=Date.now()+n;let u=null;try{return u=await this.acquireLockWithWait(t,c),await i()}finally{u?.release(),this.active=!1,s()}}snapshot(){return{active:this.active,queued:this.queued,lastLabel:this.lastLabel,lockPath:this.lockPath}}async acquireLockWithWait(t,i){if(!this.lockPath)return null;a.mkdirSync(l.dirname(this.lockPath),{recursive:!0});let r=w,n=null;for(;;)try{return a.mkdirSync(this.lockPath),A(this.lockPath,{pid:process.pid,label:t,owner:this.owner,hostname:f.hostname(),startedAt:new Date().toISOString()}),{release:()=>{a.rmSync(this.lockPath,{recursive:!0,force:!0})}}}catch(o){if(!W(o))throw o;const s=P(this.lockPath);if(n=s,_(this.lockPath,s,this.staleMs)){a.rmSync(this.lockPath,{recursive:!0,force:!0});continue}const c=i-Date.now();if(c<=0)throw new Error(O(this.lockPath,n));await y(Math.min(r,c)),r=Math.min(r*2,L)}}}function C(e={}){return new M({lockPath:p(),...e})}function p(){return m("runtime","wechat-automation.lock")}function y(e){return new Promise(t=>setTimeout(t,e))}function A(e,t){a.writeFileSync(l.join(e,"owner.json"),`${JSON.stringify(t,null,2)}
2
+ `,"utf8")}function P(e){try{const t=JSON.parse(a.readFileSync(l.join(e,"owner.json"),"utf8"));return t&&typeof t=="object"?t:null}catch{return null}}function _(e,t,i){if(t?.pid&&S(t.pid))return!1;try{return Date.now()-a.statSync(e).mtimeMs>i}catch{return!0}}function S(e){if(!Number.isInteger(e)||e<=0)return!1;try{return process.kill(e,0),!0}catch{return!1}}function O(e,t){return`wechat_automation_busy: another WeChat automation run is active (${[`lock=${e}`,t?.pid?`pid=${t.pid}`:"",t?.label?`label=${t.label}`:"",t?.startedAt?`startedAt=${t.startedAt}`:"",t?.owner?`owner=${t.owner}`:""].filter(Boolean).join(" ")})`}function W(e){return typeof e=="object"&&e!==null&&"code"in e&&e.code==="EEXIST"}export{M as SerialWeChatAutomationLane,C as createWeChatAutomationLane,p as defaultWeChatAutomationLockPath};
@@ -38,6 +38,7 @@ export declare class WeChatChannelHelperClient {
38
38
  private stderrTail;
39
39
  private startPromise;
40
40
  private requestQueueTail;
41
+ private lastGuardPassAtMs;
41
42
  private pending;
42
43
  constructor(options: WeChatChannelHelperClientOptions);
43
44
  start(): Promise<WeChatChannelHelperReady>;
@@ -48,6 +49,7 @@ export declare class WeChatChannelHelperClient {
48
49
  private sendRequest;
49
50
  private sendRawRequest;
50
51
  private guardUnsafeWindowsCommand;
52
+ private tryRecoverFromStuckShell;
51
53
  private requestFirstMissingMacPermissionPrompt;
52
54
  private cleanupWindowsOverlaysAfterCommand;
53
55
  private cleanupWindowsOverlays;
@@ -66,7 +68,7 @@ export declare class WeChatChannelHelperClient {
66
68
  private stopAfterCommandTimeout;
67
69
  private connectDarwinHelperSocket;
68
70
  }
69
- export declare function stopWindowsWeChatChannelHelperProcesses(): Promise<void>;
71
+ export declare function stopWindowsWeChatChannelHelperProcesses(pids?: number[]): Promise<void>;
70
72
  type LegacyRawHelperLaunchSpec = {
71
73
  transport: 'legacy-stdio';
72
74
  command: string;
@@ -1,3 +1,3 @@
1
- import{spawn as w}from"node:child_process";import{randomUUID as R}from"node:crypto";import f from"node:fs";import v from"node:net";import M from"node:os";import d from"node:path";import{createInterface as g}from"node:readline";import{decideWeChatChannelActivityGate as x,normalizeWeChatChannelActivitySnapshot as A}from"./human-coordination.js";import{createWeChatChannelHelperHello as S,extractWeChatChannelHelperWarmupSnapshot as H,timeoutForWeChatChannelHelperCommand as $,validateWeChatChannelHelperReady as k}from"./helper-protocol.js";const q=["en","v"].join(""),F="shennian-wechat-channel-helper.exe",m=1500;class ue{options;child=null;socket=null;lines=null;readyState=null;warmupState=null;stderrTail="";startPromise=null;requestQueueTail=Promise.resolve();pending=new Map;constructor(e){this.options=e}async start(){return this.hasTransport()&&this.readyState?this.readyState:this.startPromise?this.startPromise:(this.hasTransport()&&await this.stop(),this.startPromise=this.startFresh().catch(async e=>{throw await this.stop().catch(()=>{}),e}).finally(()=>{this.startPromise=null}),this.startPromise)}async startFresh(){const e=U(this.options.helperPath,this.options.args??[]);return e.transport==="macos-socket"?this.startDarwinAppSocketFresh(e):this.startStdioFresh(e)}async startStdioFresh(e){const r=w(e.command,e.args,{cwd:this.options.cwd,stdio:["pipe","pipe","pipe"],windowsHide:!0});this.child=r,this.stderrTail="",this.lines=g({input:r.stdout}),this.lines.on("line",i=>this.handleLine(i)),r.stderr.on("data",i=>{this.captureStderr(i)}),r.stdin.on("error",i=>{this.rejectAllPending(i instanceof Error?i:new Error(String(i)))}),r.once("error",i=>{this.child===r&&(this.child=null,this.readyState=null,this.lines?.close(),this.lines=null),this.rejectAllPending(i instanceof Error?i:new Error(String(i)))}),r.once("exit",(i,n)=>{this.child===r&&(this.child=null,this.readyState=null,this.lines?.close(),this.lines=null),this.rejectAllPending(new Error(this.formatHelperExitMessage(i,n)))});const s=new Promise((i,n)=>{const a=setTimeout(()=>n(new Error("WeChat channel helper handshake timed out")),1e4),l=u=>{clearTimeout(a),this.lines?.off("line",c),r.off("exit",o),n(new Error(`WeChat channel helper failed to start: ${u.message}`))},o=(u,h)=>{clearTimeout(a),r.off("error",l),this.lines?.off("line",c),n(new Error(this.formatHelperExitMessage(u,h)))},c=u=>{let h;try{h=JSON.parse(u)}catch{clearTimeout(a),r.off("error",l),this.lines?.off("line",c),n(new Error("WeChat channel helper sent invalid handshake JSON"));return}if(!P(h))return;clearTimeout(a),r.off("error",l),r.off("exit",o),this.lines?.off("line",c);const p=k(h,this.options.expectedHelperVersion,this.options.requiredCapabilities);p.ok?(this.readyState=h,this.captureWarmupSnapshot(h),i(h)):n(new Error(`${p.errorCode}: ${p.errorSummary}`))};r.once("error",l),r.once("exit",o),this.lines?.on("line",c)});try{r.stdin.write(`${JSON.stringify(S(this.options.expectedHelperVersion,this.options.requiredCapabilities))}
2
- `)}catch(i){throw await this.stop().catch(()=>{}),i}return s}async startDarwinAppSocketFresh(e){const r=await this.connectDarwinHelperSocket(e);this.socket=r,this.stderrTail="",this.lines=g({input:r}),this.lines.on("line",i=>this.handleLine(i)),r.on("error",i=>{this.socket===r&&(this.socket=null,this.readyState=null,this.lines?.close(),this.lines=null),this.rejectAllPending(i instanceof Error?i:new Error(String(i)))}),r.once("close",()=>{this.socket===r&&(this.socket=null,this.readyState=null,this.lines?.close(),this.lines=null),this.rejectAllPending(new Error("WeChat channel helper socket closed"))});const s=new Promise((i,n)=>{const a=setTimeout(()=>n(new Error("WeChat channel helper socket handshake timed out")),1e4),l=u=>{clearTimeout(a),this.lines?.off("line",c),r.off("close",o),n(new Error(`WeChat channel helper socket failed: ${u.message}`))},o=()=>{clearTimeout(a),r.off("error",l),this.lines?.off("line",c),n(new Error("WeChat channel helper socket closed before handshake"))},c=u=>{let h;try{h=JSON.parse(u)}catch{clearTimeout(a),r.off("error",l),r.off("close",o),this.lines?.off("line",c),n(new Error("WeChat channel helper sent invalid socket handshake JSON"));return}if(!P(h))return;clearTimeout(a),r.off("error",l),r.off("close",o),this.lines?.off("line",c);const p=k(h,this.options.expectedHelperVersion,this.options.requiredCapabilities);p.ok?(this.readyState=h,this.captureWarmupSnapshot(h),i(h)):n(new Error(`${p.errorCode}: ${p.errorSummary}`))};r.once("error",l),r.once("close",o),this.lines?.on("line",c)});try{this.writeFrame(S(this.options.expectedHelperVersion,this.options.requiredCapabilities))}catch(i){throw await this.stop().catch(()=>{}),i}return s}async request(e,r,s){const i=this.requestQueueTail.catch(()=>{}).then(()=>this.sendRequest(e,r,s));return this.requestQueueTail=i.then(()=>{},()=>{}),i}async sendRequest(e,r,s){await this.guardUnsafeWindowsCommand(e,s);try{return await this.sendRawRequest(e,r,s)}finally{await this.cleanupWindowsOverlaysAfterCommand(e,s).catch(()=>{})}}async sendRawRequest(e,r,s){if((!this.hasTransport()||!this.readyState)&&await this.start(),!this.hasTransport())throw new Error("WeChat channel helper is not started");const i=R(),n=$(e),a=Date.now();this.emitRequestTrace({phase:"request",at:new Date(a).toISOString(),id:i,command:e,traceId:s,params:r,timeoutMs:n});const l=new Promise((o,c)=>{const u=setTimeout(()=>{this.pending.get(i)&&(this.pending.delete(i),this.stopAfterCommandTimeout(e).finally(()=>{c(new Error(`helper_command_timeout: ${e}`))}))},n);this.pending.set(i,{resolve:o,reject:c,timer:u})});try{this.writeFrame({id:i,command:e,params:r,traceId:s})}catch(o){const c=this.pending.get(i);throw c&&clearTimeout(c.timer),this.pending.delete(i),this.emitRequestTrace({phase:"error",at:new Date().toISOString(),id:i,command:e,traceId:s,durationMs:Date.now()-a,errorSummary:o instanceof Error?o.message:String(o)}),o}try{const o=await l;return this.emitRequestTrace({phase:"response",at:new Date().toISOString(),id:i,command:e,traceId:s,durationMs:Date.now()-a,ok:o.ok,errorCode:o.errorCode,errorSummary:o.errorSummary,latencyMs:o.latencyMs,result:o.result}),this.captureWarmupSnapshot(o),this.captureWarmupSnapshot(o.result),o}catch(o){throw this.emitRequestTrace({phase:"error",at:new Date().toISOString(),id:i,command:e,traceId:s,durationMs:Date.now()-a,errorSummary:o instanceof Error?o.message:String(o)}),o}}async guardUnsafeWindowsCommand(e,r){if(!this.options.guardUnsafeWindowsCommands||!Z(e))return;const s=await this.sendRawRequest("permissions.check",{},r);if(!s.ok)throw new Error(`${s.errorCode??"permissions_check_failed"}: ${s.errorSummary??e}`);const i=s.result??{},n=await this.requestFirstMissingMacPermissionPrompt(i,r);if(n==="screen-recording")throw new Error("permission_screen_recording_missing");if(n==="accessibility")throw new Error("permission_accessibility_missing");if(n==="input-monitoring")throw new Error("permission_input_monitoring_missing");if(i.automation===!1)throw new Error("permission_automation_missing");if(i.wechatRunning===!1)throw new Error("wechat_not_running");if(O(i))throw new Error("wechat_login_required");if(N(i))throw new Error("windows_visible_desktop_unavailable");if(i.dpiMappingAvailable===!1||i.displayTopologySupported===!1)throw new Error("dpi_mapping_failed");if(i.wechatWindowAvailable===!1)throw new Error("wechat_window_unavailable");if(i.wechatMainWindowResponsive===!1)throw new Error("wechat_window_unresponsive");if(!this.options.skipUserActivityGuard&&te(e)){const a=await this.sendRawRequest("activity.snapshot",{},r);if(!a.ok)throw new Error(`${a.errorCode??"user_activity_unknown"}: ${a.errorSummary??e}`);const l=x({snapshot:A(a.result),stage:"dangerous_action"});if(!l.ok)throw new Error(`user_active:${l.reasonCode}`)}await this.cleanupWindowsOverlays("before",e,r)}async requestFirstMissingMacPermissionPrompt(e,r){const s=K(e);if(!s||process.platform!=="darwin")return s;const i=Y(s);try{await this.sendRawRequest(i,{},r)}catch{}return s}async cleanupWindowsOverlaysAfterCommand(e,r){!this.options.guardUnsafeWindowsCommands||!ee(e)||await this.cleanupWindowsOverlays("after",e,r)}async cleanupWindowsOverlays(e,r,s){if(this.options.cleanupWindowsOverlays===!1)return;if(!this.readyState?.capabilities.includes("overlayCleanup"))throw new Error("helper_capability_missing: overlayCleanup");const i=await this.sendRawRequest("windows.cleanupOverlays",{stage:e,command:r,includeGeneratedMediaPreviews:!0,includeToolingTerminals:!1,waitMs:e==="after"?220:120},s);if(!i.ok)throw new Error(`${i.errorCode??"overlay_cleanup_failed"}: ${i.errorSummary??r}`)}async healthCheck(e){return this.request("health.check",{},e)}getReadyState(){return this.readyState?{...this.readyState,capabilities:[...this.readyState.capabilities]}:null}getWarmupState(){return this.warmupState?{warmState:this.warmupState.warmState,metrics:this.warmupState.metrics?{...this.warmupState.metrics}:void 0}:null}async stop(){const e=this.child,r=this.socket;this.child=null,this.socket=null,this.readyState=null,this.lines?.close(),this.lines=null,this.rejectAllPending(new Error("WeChat channel helper stopped")),r&&!r.destroyed&&r.destroy(),e&&await J(e)}hasTransport(){return!!this.child||!!this.socket}writeFrame(e){const r=`${JSON.stringify(e)}
3
- `;if(this.socket){this.socket.write(r);return}if(this.child){this.child.stdin.write(r);return}throw new Error("WeChat channel helper is not started")}handleLine(e){let r;try{r=JSON.parse(e)}catch{return}if(!X(r))return;const s=this.pending.get(r.id);s&&(clearTimeout(s.timer),this.pending.delete(r.id),s.resolve(r))}captureWarmupSnapshot(e){const r=H(e);r&&(this.warmupState=r)}captureStderr(e){const r=Buffer.isBuffer(e)?e.toString("utf8"):String(e??"");r&&(this.stderrTail=(this.stderrTail+r).slice(-2e3))}formatHelperExitMessage(e,r){const s=[e===null?null:`code=${e}`,r?`signal=${r}`:null,this.stderrTail.trim()?`stderr=${this.stderrTail.trim()}`:null].filter(Boolean);return s.length?`WeChat channel helper process exited (${s.join(", ")})`:"WeChat channel helper process exited"}rejectAllPending(e){for(const[r,s]of this.pending)clearTimeout(s.timer),s.reject(e),this.pending.delete(r)}emitRequestTrace(e){try{this.options.requestLogger?.(e)}catch{}}async stopAfterCommandTimeout(e){await this.stop().catch(()=>{}),process.platform==="win32"&&await D().catch(()=>{}),this.emitRequestTrace({phase:"error",at:new Date().toISOString(),id:`timeout-cleanup:${e}`,command:e,errorSummary:"helper process stopped after command timeout"})}async connectDarwinHelperSocket(e){try{return await _(e.runtimeFile)}catch{f.rmSync(e.runtimeFile,{force:!0})}await W(e,{},this.options.openHelperAppForTest);const r=await T(e.runtimeFile,this.options.macosRuntimeInitialReadyTimeoutMs??5e3);if(r.ok)return r.socket;f.rmSync(e.runtimeFile,{force:!0}),await W(e,{forceNewInstance:!0},this.options.openHelperAppForTest);const s=await T(e.runtimeFile,this.options.macosRuntimeRelaunchReadyTimeoutMs??1e4);if(s.ok)return s.socket;throw new Error(`WeChat channel Helper.app socket did not become ready: ${s.error.message}`)}}async function D(){process.platform==="win32"&&await C(["taskkill","/IM",F,"/T","/F"],[0,128])}function O(t){return t.wechatWindowAvailable!==!1?!1:![...t.captureCandidates??[],...t.restoreCandidates??[],...t.hiddenRestoreCandidates??[]].some(L)}function N(t){return t.windowsVisibleDesktopAvailable===!1||t.rdpVisibleDesktopAvailable===!1||t.desktopSessionVisible===!1||t.screenLocked===!0||t.rdpDisconnected===!0}function I(t){const e=String(t.appName||"").toLowerCase();return e==="wechat"||e==="weixin"}function L(t){return!(!I(t)||t.visible===!1||t.minimized===!0)}function U(t,e,r=process.platform){if(r==="darwin"){const s=G(t);if(s&&e.length===0){const i=V();return{transport:"macos-socket",command:"/usr/bin/open",args:["-g",s,"--args","--socket-runtime",i],appPath:s,runtimeDir:i,runtimeFile:d.join(i,"runtime.json")}}}return{transport:"legacy-stdio",command:t,args:e}}function G(t){const e=d.resolve(t),r=`${d.sep}Contents${d.sep}MacOS${d.sep}`,s=e.lastIndexOf(r);if(s<0)return null;const i=e.slice(0,s);return i.endsWith(".app")?i:null}function V(){const t=j().SHENNIAN_HELPER_RUNTIME_DIR?.trim();return t?d.resolve(t):d.join(M.homedir(),"Library","Application Support","Shennian","Helper")}async function J(t){if(E(t))return;if(process.platform==="win32"&&t.pid){t.kill("SIGTERM"),await C(["taskkill","/PID",String(t.pid),"/T","/F"],[0,128]).catch(()=>{}),await y(t,m);return}t.kill("SIGTERM"),!await y(t,m)&&t.pid&&b(t.pid)&&(t.kill("SIGKILL"),await y(t,m))}function E(t){return t.exitCode!==null||t.signalCode!==null}function y(t,e){return E(t)?Promise.resolve(!0):new Promise(r=>{const s=setTimeout(()=>{t.off("exit",i),r(!1)},e),i=()=>{clearTimeout(s),r(!0)};t.once("exit",i)})}function C(t,e){return new Promise((r,s)=>{const[i,...n]=t,a=w(i,n,{stdio:"ignore",windowsHide:!0});a.once("error",s),a.once("exit",l=>{l!==null&&e.includes(l)?r():s(new Error(`${i} exited ${l}`))})})}function j(){return globalThis.process?.[q]??{}}function z(t){let e;try{e=JSON.parse(f.readFileSync(t,"utf8"))}catch(i){throw new Error(`runtime_file_unreadable: ${i instanceof Error?i.message:String(i)}`)}if(!e||typeof e!="object")throw new Error("runtime_file_invalid");const r=e.socketPath;if(typeof r!="string"||!r.trim())throw new Error("runtime_socket_missing");const s=e.pid;return{socketPath:r,...typeof s=="number"&&Number.isInteger(s)&&s>0?{pid:s}:{}}}async function _(t){const{socketPath:e,pid:r}=z(t);if(r&&!b(r))throw new Error(`runtime_process_stale: ${r}`);return new Promise((s,i)=>{const n=v.createConnection(e),a=setTimeout(()=>{n.destroy(),i(new Error(`runtime_socket_connect_timeout: ${e}`))},1e3);n.once("connect",()=>{clearTimeout(a),n.off("error",l),s(n)});const l=o=>{clearTimeout(a),n.destroy(),i(o)};n.once("error",l)})}async function T(t,e){const r=Date.now()+e;let s=null;for(;Date.now()<r;)try{return{ok:!0,socket:await _(t)}}catch(i){s=i instanceof Error?i:new Error(String(i));const n=r-Date.now();n>0&&await Q(Math.min(150,n))}return{ok:!1,error:s??new Error("runtime file missing")}}async function W(t,e={},r=B){f.mkdirSync(t.runtimeDir,{recursive:!0,mode:448});const s=e.forceNewInstance?["-n",...t.args]:t.args;try{await r(t.command,s)}catch(i){if(!(i instanceof Error?i.message:String(i)).includes("-1712"))throw i;await r(t.command,["-n",...t.args])}}function b(t){try{return process.kill(t,0),!0}catch{return!1}}async function B(t,e){await new Promise((r,s)=>{const i=w(t,e,{stdio:["ignore","ignore","pipe"],windowsHide:!0});let n="";i.stderr.on("data",a=>{n=(n+String(a)).slice(-2e3)}),i.once("error",s),i.once("exit",(a,l)=>{a===0?r():s(new Error(`open Helper.app failed (${l?`signal=${l}`:`code=${a}`}${n.trim()?`, stderr=${n.trim()}`:""})`))})})}function Q(t){return new Promise(e=>setTimeout(e,t))}function K(t){return t.screenRecording===!1?"screen-recording":t.accessibility===!1?"accessibility":t.inputMonitoring===!1?"input-monitoring":null}function Y(t){return t==="screen-recording"?"permissions.requestScreenRecording":t==="accessibility"?"permissions.requestAccessibility":"permissions.requestInputMonitoring"}function P(t){if(!t||typeof t!="object")return!1;const e=t;return e.type==="ready"&&typeof e.helperVersion=="string"&&typeof e.protocolVersion=="number"&&Array.isArray(e.capabilities)&&typeof e.pid=="number"}function X(t){if(!t||typeof t!="object")return!1;const e=t;return typeof e.id=="string"&&typeof e.ok=="boolean"&&typeof e.latencyMs=="number"}function Z(t){return t==="windows.focus"||t==="windows.capture"||t==="screen.capture"||t==="ocr.recognize"||t==="image.cropHash"||t==="wechat.searchConversation"||t==="wechat.focusMessageInput"||t==="wechat.pasteAndSubmit"||t==="keyboard.primeTextPaste"||t.startsWith("mouse.")||t.startsWith("keyboard.")||t.startsWith("clipboard.")||t==="menu.pickItem"||t==="savePanel.saveToPath"}function ee(t){return t.startsWith("mouse.")||t.startsWith("keyboard.")||t.startsWith("clipboard.")||t==="menu.pickItem"||t==="savePanel.saveToPath"}function te(t){return t==="windows.focus"||t==="wechat.searchConversation"||t==="wechat.focusMessageInput"||t==="wechat.pasteAndSubmit"||t==="keyboard.primeTextPaste"||t.startsWith("mouse.")||t.startsWith("keyboard.")||t.startsWith("clipboard.set")||t==="clipboard.restore"||t==="menu.pickItem"||t==="savePanel.saveToPath"}export{ue as WeChatChannelHelperClient,U as helperLaunchSpec,D as stopWindowsWeChatChannelHelperProcesses};
1
+ import{spawn as w}from"node:child_process";import{randomUUID as W}from"node:crypto";import f from"node:fs";import v from"node:net";import M from"node:os";import d from"node:path";import{createInterface as g}from"node:readline";import{decideWeChatChannelActivityGate as A,normalizeWeChatChannelActivitySnapshot as x}from"./human-coordination.js";import{createWeChatChannelHelperHello as S,extractWeChatChannelHelperWarmupSnapshot as $,timeoutForWeChatChannelHelperCommand as q,validateWeChatChannelHelperReady as k}from"./helper-protocol.js";const H=["en","v"].join(""),m=1500,D=1500;class de{options;child=null;socket=null;lines=null;readyState=null;warmupState=null;stderrTail="";startPromise=null;requestQueueTail=Promise.resolve();lastGuardPassAtMs=0;pending=new Map;constructor(t){this.options=t}async start(){return this.hasTransport()&&this.readyState?this.readyState:this.startPromise?this.startPromise:(this.hasTransport()&&await this.stop(),this.startPromise=this.startFresh().catch(async t=>{throw await this.stop().catch(()=>{}),t}).finally(()=>{this.startPromise=null}),this.startPromise)}async startFresh(){const t=U(this.options.helperPath,this.options.args??[]);return t.transport==="macos-socket"?this.startDarwinAppSocketFresh(t):this.startStdioFresh(t)}async startStdioFresh(t){const r=w(t.command,t.args,{cwd:this.options.cwd,stdio:["pipe","pipe","pipe"],windowsHide:!0});this.child=r,this.stderrTail="",this.lines=g({input:r.stdout}),this.lines.on("line",i=>this.handleLine(i)),r.stderr.on("data",i=>{this.captureStderr(i)}),r.stdin.on("error",i=>{this.rejectAllPending(i instanceof Error?i:new Error(String(i)))}),r.once("error",i=>{this.child===r&&(this.child=null,this.readyState=null,this.lines?.close(),this.lines=null),this.rejectAllPending(i instanceof Error?i:new Error(String(i)))}),r.once("exit",(i,n)=>{this.child===r&&(this.child=null,this.readyState=null,this.lines?.close(),this.lines=null),this.rejectAllPending(new Error(this.formatHelperExitMessage(i,n)))});const s=new Promise((i,n)=>{const o=setTimeout(()=>n(new Error("WeChat channel helper handshake timed out")),1e4),l=u=>{clearTimeout(o),this.lines?.off("line",c),r.off("exit",a),n(new Error(`WeChat channel helper failed to start: ${u.message}`))},a=(u,h)=>{clearTimeout(o),r.off("error",l),this.lines?.off("line",c),n(new Error(this.formatHelperExitMessage(u,h)))},c=u=>{let h;try{h=JSON.parse(u)}catch{clearTimeout(o),r.off("error",l),this.lines?.off("line",c),n(new Error("WeChat channel helper sent invalid handshake JSON"));return}if(!R(h))return;clearTimeout(o),r.off("error",l),r.off("exit",a),this.lines?.off("line",c);const p=k(h,this.options.expectedHelperVersion,this.options.requiredCapabilities);p.ok?(this.readyState=h,this.captureWarmupSnapshot(h),i(h)):n(new Error(`${p.errorCode}: ${p.errorSummary}`))};r.once("error",l),r.once("exit",a),this.lines?.on("line",c)});try{r.stdin.write(`${JSON.stringify(S(this.options.expectedHelperVersion,this.options.requiredCapabilities))}
2
+ `)}catch(i){throw await this.stop().catch(()=>{}),i}return s}async startDarwinAppSocketFresh(t){const r=await this.connectDarwinHelperSocket(t);this.socket=r,this.stderrTail="",this.lines=g({input:r}),this.lines.on("line",i=>this.handleLine(i)),r.on("error",i=>{this.socket===r&&(this.socket=null,this.readyState=null,this.lines?.close(),this.lines=null),this.rejectAllPending(i instanceof Error?i:new Error(String(i)))}),r.once("close",()=>{this.socket===r&&(this.socket=null,this.readyState=null,this.lines?.close(),this.lines=null),this.rejectAllPending(new Error("WeChat channel helper socket closed"))});const s=new Promise((i,n)=>{const o=setTimeout(()=>n(new Error("WeChat channel helper socket handshake timed out")),1e4),l=u=>{clearTimeout(o),this.lines?.off("line",c),r.off("close",a),n(new Error(`WeChat channel helper socket failed: ${u.message}`))},a=()=>{clearTimeout(o),r.off("error",l),this.lines?.off("line",c),n(new Error("WeChat channel helper socket closed before handshake"))},c=u=>{let h;try{h=JSON.parse(u)}catch{clearTimeout(o),r.off("error",l),r.off("close",a),this.lines?.off("line",c),n(new Error("WeChat channel helper sent invalid socket handshake JSON"));return}if(!R(h))return;clearTimeout(o),r.off("error",l),r.off("close",a),this.lines?.off("line",c);const p=k(h,this.options.expectedHelperVersion,this.options.requiredCapabilities);p.ok?(this.readyState=h,this.captureWarmupSnapshot(h),i(h)):n(new Error(`${p.errorCode}: ${p.errorSummary}`))};r.once("error",l),r.once("close",a),this.lines?.on("line",c)});try{this.writeFrame(S(this.options.expectedHelperVersion,this.options.requiredCapabilities))}catch(i){throw await this.stop().catch(()=>{}),i}return s}async request(t,r,s){const i=this.requestQueueTail.catch(()=>{}).then(()=>this.sendRequest(t,r,s));return this.requestQueueTail=i.then(()=>{},()=>{}),i}async sendRequest(t,r,s){await this.guardUnsafeWindowsCommand(t,s);try{return await this.sendRawRequest(t,r,s)}finally{await this.cleanupWindowsOverlaysAfterCommand(t,s).catch(()=>{})}}async sendRawRequest(t,r,s){if((!this.hasTransport()||!this.readyState)&&await this.start(),!this.hasTransport())throw new Error("WeChat channel helper is not started");const i=W(),n=q(t),o=Date.now();this.emitRequestTrace({phase:"request",at:new Date(o).toISOString(),id:i,command:t,traceId:s,params:r,timeoutMs:n});const l=new Promise((a,c)=>{const u=setTimeout(()=>{this.pending.get(i)&&(this.pending.delete(i),this.stopAfterCommandTimeout(t).finally(()=>{c(new Error(`helper_command_timeout: ${t}`))}))},n);this.pending.set(i,{resolve:a,reject:c,timer:u})});try{this.writeFrame({id:i,command:t,params:r,traceId:s})}catch(a){const c=this.pending.get(i);throw c&&clearTimeout(c.timer),this.pending.delete(i),this.emitRequestTrace({phase:"error",at:new Date().toISOString(),id:i,command:t,traceId:s,durationMs:Date.now()-o,errorSummary:a instanceof Error?a.message:String(a)}),a}try{const a=await l;return this.emitRequestTrace({phase:"response",at:new Date().toISOString(),id:i,command:t,traceId:s,durationMs:Date.now()-o,ok:a.ok,errorCode:a.errorCode,errorSummary:a.errorSummary,latencyMs:a.latencyMs,result:a.result}),this.captureWarmupSnapshot(a),this.captureWarmupSnapshot(a.result),a}catch(a){throw this.emitRequestTrace({phase:"error",at:new Date().toISOString(),id:i,command:t,traceId:s,durationMs:Date.now()-o,errorSummary:a instanceof Error?a.message:String(a)}),a}}async guardUnsafeWindowsCommand(t,r){if(!this.options.guardUnsafeWindowsCommands||!ee(t)||te(t)&&this.lastGuardPassAtMs>0&&Date.now()-this.lastGuardPassAtMs<D)return;let s=await this.sendRawRequest("permissions.check",{},r);if(!s.ok)throw new Error(`${s.errorCode??"permissions_check_failed"}: ${s.errorSummary??t}`);let i=s.result??{};if(i.wechatRunning!==!1&&O(i)&&await this.tryRecoverFromStuckShell(r)){if(s=await this.sendRawRequest("permissions.check",{},r),!s.ok)throw new Error(`${s.errorCode??"permissions_check_failed"}: ${s.errorSummary??t}`);i=s.result??{}}const n=await this.requestFirstMissingMacPermissionPrompt(i,r);if(n==="screen-recording")throw new Error("permission_screen_recording_missing");if(n==="accessibility")throw new Error("permission_accessibility_missing");if(n==="input-monitoring")throw new Error("permission_input_monitoring_missing");if(i.automation===!1)throw new Error("permission_automation_missing");if(i.wechatRunning===!1)throw new Error("wechat_not_running");if(N(i))throw new Error("wechat_login_required");if(L(i))throw new Error("windows_visible_desktop_unavailable");if(i.dpiMappingAvailable===!1||i.displayTopologySupported===!1)throw new Error("dpi_mapping_failed");if(i.wechatWindowAvailable===!1)throw new Error("wechat_window_unavailable");if(i.wechatMainWindowResponsive===!1)throw new Error("wechat_window_unresponsive");if(!this.options.skipUserActivityGuard&&ie(t)){const o=await this.sendRawRequest("activity.snapshot",{},r);if(!o.ok)throw new Error(`${o.errorCode??"user_activity_unknown"}: ${o.errorSummary??t}`);const l=A({snapshot:x(o.result),stage:"dangerous_action"});if(!l.ok)throw new Error(`user_active:${l.reasonCode}`)}await this.cleanupWindowsOverlays("before",t,r),this.lastGuardPassAtMs=Date.now()}async tryRecoverFromStuckShell(t){try{const r=await this.sendRawRequest("wechat.healthProbe",{},t);if(!r.ok||r.result?.stuckShell!==!0)return!1;const s=await this.sendRawRequest("windows.recoverWeChat",{allowRestart:!0,onlyIfUnresponsive:!1,waitAfterStartMs:4e3},t);return s.ok===!0&&s.result?.started===!0}catch{return!1}}async requestFirstMissingMacPermissionPrompt(t,r){const s=Y(t);if(!s||process.platform!=="darwin")return s;const i=X(s);try{await this.sendRawRequest(i,{},r)}catch{}return s}async cleanupWindowsOverlaysAfterCommand(t,r){!this.options.guardUnsafeWindowsCommands||!re(t)||await this.cleanupWindowsOverlays("after",t,r)}async cleanupWindowsOverlays(t,r,s){if(this.options.cleanupWindowsOverlays===!1)return;if(!this.readyState?.capabilities.includes("overlayCleanup"))throw new Error("helper_capability_missing: overlayCleanup");const i=await this.sendRawRequest("windows.cleanupOverlays",{stage:t,command:r,includeGeneratedMediaPreviews:!0,includeToolingTerminals:!1,waitMs:t==="after"?220:120},s);if(!i.ok)throw new Error(`${i.errorCode??"overlay_cleanup_failed"}: ${i.errorSummary??r}`)}async healthCheck(t){return this.request("health.check",{},t)}getReadyState(){return this.readyState?{...this.readyState,capabilities:[...this.readyState.capabilities]}:null}getWarmupState(){return this.warmupState?{warmState:this.warmupState.warmState,metrics:this.warmupState.metrics?{...this.warmupState.metrics}:void 0}:null}async stop(){const t=this.child,r=this.socket;this.child=null,this.socket=null,this.readyState=null,this.lines?.close(),this.lines=null,this.rejectAllPending(new Error("WeChat channel helper stopped")),r&&!r.destroyed&&r.destroy(),t&&await z(t)}hasTransport(){return!!this.child||!!this.socket}writeFrame(t){const r=`${JSON.stringify(t)}
3
+ `;if(this.socket){this.socket.write(r);return}if(this.child){this.child.stdin.write(r);return}throw new Error("WeChat channel helper is not started")}handleLine(t){let r;try{r=JSON.parse(t)}catch{return}if(!Z(r))return;const s=this.pending.get(r.id);s&&(clearTimeout(s.timer),this.pending.delete(r.id),s.resolve(r))}captureWarmupSnapshot(t){const r=$(t);r&&(this.warmupState=r)}captureStderr(t){const r=Buffer.isBuffer(t)?t.toString("utf8"):String(t??"");r&&(this.stderrTail=(this.stderrTail+r).slice(-2e3))}formatHelperExitMessage(t,r){const s=[t===null?null:`code=${t}`,r?`signal=${r}`:null,this.stderrTail.trim()?`stderr=${this.stderrTail.trim()}`:null].filter(Boolean);return s.length?`WeChat channel helper process exited (${s.join(", ")})`:"WeChat channel helper process exited"}rejectAllPending(t){for(const[r,s]of this.pending)clearTimeout(s.timer),s.reject(t),this.pending.delete(r)}emitRequestTrace(t){try{this.options.requestLogger?.(t)}catch{}}async stopAfterCommandTimeout(t){const r=this.child?.pid;if(await this.stop().catch(()=>{}),process.platform==="win32"){const s=typeof r=="number"&&r>0?[r]:[];await F(s).catch(()=>{})}this.emitRequestTrace({phase:"error",at:new Date().toISOString(),id:`timeout-cleanup:${t}`,command:t,errorSummary:"helper process stopped after command timeout"})}async connectDarwinHelperSocket(t){try{return await _(t.runtimeFile)}catch{f.rmSync(t.runtimeFile,{force:!0})}await b(t,{},this.options.openHelperAppForTest);const r=await T(t.runtimeFile,this.options.macosRuntimeInitialReadyTimeoutMs??5e3);if(r.ok)return r.socket;f.rmSync(t.runtimeFile,{force:!0}),await b(t,{forceNewInstance:!0},this.options.openHelperAppForTest);const s=await T(t.runtimeFile,this.options.macosRuntimeRelaunchReadyTimeoutMs??1e4);if(s.ok)return s.socket;throw new Error(`WeChat channel Helper.app socket did not become ready: ${s.error.message}`)}}async function F(e=[]){if(process.platform!=="win32")return;const t=e.filter(r=>Number.isInteger(r)&&r>0);if(t.length!==0)for(const r of t)await E(["taskkill","/PID",String(r),"/T","/F"],[0,128])}function O(e){return e.wechatWindowAvailable===!1&&(e.captureCandidateCount??0)===0&&(e.restoreCandidateCount??0)===0&&(e.hiddenRestoreCandidateCount??0)===0}function N(e){return e.wechatWindowAvailable!==!1?!1:![...e.captureCandidates??[],...e.restoreCandidates??[],...e.hiddenRestoreCandidates??[]].some(I)}function L(e){return e.windowsVisibleDesktopAvailable===!1||e.rdpVisibleDesktopAvailable===!1||e.desktopSessionVisible===!1||e.screenLocked===!0||e.rdpDisconnected===!0}function G(e){const t=String(e.appName||"").toLowerCase();return t==="wechat"||t==="weixin"}function I(e){return!(!G(e)||e.visible===!1||e.minimized===!0)}function U(e,t,r=process.platform){if(r==="darwin"){const s=V(e);if(s&&t.length===0){const i=J();return{transport:"macos-socket",command:"/usr/bin/open",args:["-g",s,"--args","--socket-runtime",i],appPath:s,runtimeDir:i,runtimeFile:d.join(i,"runtime.json")}}}return{transport:"legacy-stdio",command:e,args:t}}function V(e){const t=d.resolve(e),r=`${d.sep}Contents${d.sep}MacOS${d.sep}`,s=t.lastIndexOf(r);if(s<0)return null;const i=t.slice(0,s);return i.endsWith(".app")?i:null}function J(){const e=j().SHENNIAN_HELPER_RUNTIME_DIR?.trim();return e?d.resolve(e):d.join(M.homedir(),"Library","Application Support","Shennian","Helper")}async function z(e){if(C(e))return;if(process.platform==="win32"&&e.pid){e.kill("SIGTERM"),await E(["taskkill","/PID",String(e.pid),"/T","/F"],[0,128]).catch(()=>{}),await y(e,m);return}e.kill("SIGTERM"),!await y(e,m)&&e.pid&&P(e.pid)&&(e.kill("SIGKILL"),await y(e,m))}function C(e){return e.exitCode!==null||e.signalCode!==null}function y(e,t){return C(e)?Promise.resolve(!0):new Promise(r=>{const s=setTimeout(()=>{e.off("exit",i),r(!1)},t),i=()=>{clearTimeout(s),r(!0)};e.once("exit",i)})}function E(e,t){return new Promise((r,s)=>{const[i,...n]=e,o=w(i,n,{stdio:"ignore",windowsHide:!0});o.once("error",s),o.once("exit",l=>{l!==null&&t.includes(l)?r():s(new Error(`${i} exited ${l}`))})})}function j(){return globalThis.process?.[H]??{}}function B(e){let t;try{t=JSON.parse(f.readFileSync(e,"utf8"))}catch(i){throw new Error(`runtime_file_unreadable: ${i instanceof Error?i.message:String(i)}`)}if(!t||typeof t!="object")throw new Error("runtime_file_invalid");const r=t.socketPath;if(typeof r!="string"||!r.trim())throw new Error("runtime_socket_missing");const s=t.pid;return{socketPath:r,...typeof s=="number"&&Number.isInteger(s)&&s>0?{pid:s}:{}}}async function _(e){const{socketPath:t,pid:r}=B(e);if(r&&!P(r))throw new Error(`runtime_process_stale: ${r}`);return new Promise((s,i)=>{const n=v.createConnection(t),o=setTimeout(()=>{n.destroy(),i(new Error(`runtime_socket_connect_timeout: ${t}`))},1e3);n.once("connect",()=>{clearTimeout(o),n.off("error",l),s(n)});const l=a=>{clearTimeout(o),n.destroy(),i(a)};n.once("error",l)})}async function T(e,t){const r=Date.now()+t;let s=null;for(;Date.now()<r;)try{return{ok:!0,socket:await _(e)}}catch(i){s=i instanceof Error?i:new Error(String(i));const n=r-Date.now();n>0&&await K(Math.min(150,n))}return{ok:!1,error:s??new Error("runtime file missing")}}async function b(e,t={},r=Q){f.mkdirSync(e.runtimeDir,{recursive:!0,mode:448});const s=t.forceNewInstance?["-n",...e.args]:e.args;try{await r(e.command,s)}catch(i){if(!(i instanceof Error?i.message:String(i)).includes("-1712"))throw i;await r(e.command,["-n",...e.args])}}function P(e){try{return process.kill(e,0),!0}catch{return!1}}async function Q(e,t){await new Promise((r,s)=>{const i=w(e,t,{stdio:["ignore","ignore","pipe"],windowsHide:!0});let n="";i.stderr.on("data",o=>{n=(n+String(o)).slice(-2e3)}),i.once("error",s),i.once("exit",(o,l)=>{o===0?r():s(new Error(`open Helper.app failed (${l?`signal=${l}`:`code=${o}`}${n.trim()?`, stderr=${n.trim()}`:""})`))})})}function K(e){return new Promise(t=>setTimeout(t,e))}function Y(e){return e.screenRecording===!1?"screen-recording":e.accessibility===!1?"accessibility":e.inputMonitoring===!1?"input-monitoring":null}function X(e){return e==="screen-recording"?"permissions.requestScreenRecording":e==="accessibility"?"permissions.requestAccessibility":"permissions.requestInputMonitoring"}function R(e){if(!e||typeof e!="object")return!1;const t=e;return t.type==="ready"&&typeof t.helperVersion=="string"&&typeof t.protocolVersion=="number"&&Array.isArray(t.capabilities)&&typeof t.pid=="number"}function Z(e){if(!e||typeof e!="object")return!1;const t=e;return typeof t.id=="string"&&typeof t.ok=="boolean"&&typeof t.latencyMs=="number"}function ee(e){return e==="windows.focus"||e==="windows.capture"||e==="screen.capture"||e==="ocr.recognize"||e==="image.cropHash"||e==="wechat.searchConversation"||e==="wechat.focusMessageInput"||e==="wechat.pasteAndSubmit"||e==="keyboard.primeTextPaste"||e.startsWith("mouse.")||e.startsWith("keyboard.")||e.startsWith("clipboard.")||e==="menu.pickItem"||e==="savePanel.saveToPath"}function te(e){return e==="windows.capture"||e==="screen.capture"||e==="ocr.recognize"||e==="image.cropHash"}function re(e){return e.startsWith("mouse.")||e.startsWith("keyboard.")||e.startsWith("clipboard.")||e==="menu.pickItem"||e==="savePanel.saveToPath"}function ie(e){return e==="windows.focus"||e==="wechat.searchConversation"||e==="wechat.focusMessageInput"||e==="wechat.pasteAndSubmit"||e==="keyboard.primeTextPaste"||e.startsWith("mouse.")||e.startsWith("keyboard.")||e.startsWith("clipboard.set")||e==="clipboard.restore"||e==="menu.pickItem"||e==="savePanel.saveToPath"}export{de as WeChatChannelHelperClient,U as helperLaunchSpec,F as stopWindowsWeChatChannelHelperProcesses};
@@ -13,6 +13,7 @@ export declare const WECHAT_CHANNEL_HELPER_COMMAND_TIMEOUT_MS: {
13
13
  readonly menuPickItem: 5000;
14
14
  readonly savePanel: 10000;
15
15
  readonly wechatSearch: 6000;
16
+ readonly wechatInteraction: 15000;
16
17
  };
17
18
  export type WeChatChannelHelperCapability = 'screenCapture' | 'visionOcr' | 'windowList' | 'windowFocus' | 'mouseKeyboard' | 'clipboard' | 'contextMenu' | 'imageCropHash' | 'wechatSearch' | 'humanActivity' | 'automationLease' | 'overlayCleanup' | 'wechatRecovery';
18
19
  export type WeChatChannelHelperCapabilityProfile = 'observe' | 'download' | 'send';
@@ -90,7 +91,7 @@ export type WeChatChannelHumanActivitySnapshot = {
90
91
  capturesMousePath?: boolean;
91
92
  };
92
93
  };
93
- export type WeChatChannelHelperCommandName = 'health.check' | 'processes.list' | 'permissions.check' | 'permissions.requestScreenRecording' | 'permissions.requestAccessibility' | 'permissions.requestInputMonitoring' | 'activity.snapshot' | 'automation.lease.acquire' | 'automation.lease.release' | 'automation.lease.status' | 'automation.lease.simulateInterruption' | 'windows.ensureReady' | 'windows.list' | 'windows.enumerateRaw' | 'windows.focus' | 'windows.capture' | 'windows.captureAndOcr' | 'windows.cleanupOverlays' | 'windows.recoverWeChat' | 'screen.capture' | 'ocr.recognize' | 'mouse.click' | 'mouse.rightClick' | 'mouse.scroll' | 'keyboard.type' | 'keyboard.shortcut' | 'keyboard.primeTextPaste' | 'clipboard.snapshot' | 'clipboard.restore' | 'clipboard.setText' | 'clipboard.setFiles' | 'clipboard.setImage' | 'clipboard.readFileUrls' | 'clipboard.readAttachment' | 'menu.pickItem' | 'savePanel.saveToPath' | 'image.cropHash' | 'wechat.searchConversation' | 'wechat.focusMessageInput' | 'wechat.pasteAndSubmit';
94
+ export type WeChatChannelHelperCommandName = 'health.check' | 'processes.list' | 'permissions.check' | 'permissions.requestScreenRecording' | 'permissions.requestAccessibility' | 'permissions.requestInputMonitoring' | 'activity.snapshot' | 'automation.lease.acquire' | 'automation.lease.release' | 'automation.lease.status' | 'automation.lease.simulateInterruption' | 'windows.ensureReady' | 'windows.list' | 'windows.enumerateRaw' | 'windows.focus' | 'windows.closeWindow' | 'windows.capture' | 'windows.captureAndOcr' | 'windows.cleanupOverlays' | 'windows.recoverWeChat' | 'screen.capture' | 'ocr.recognize' | 'mouse.click' | 'mouse.rightClick' | 'mouse.scroll' | 'keyboard.type' | 'keyboard.shortcut' | 'keyboard.primeTextPaste' | 'clipboard.snapshot' | 'clipboard.restore' | 'clipboard.setText' | 'clipboard.setFiles' | 'clipboard.setImage' | 'clipboard.readFileUrls' | 'clipboard.readAttachment' | 'menu.pickItem' | 'savePanel.saveToPath' | 'image.cropHash' | 'wechat.searchConversation' | 'wechat.focusMessageInput' | 'wechat.pasteAndSubmit' | 'wechat.healthProbe';
94
95
  export type WeChatChannelHelperCommand = {
95
96
  id: string;
96
97
  command: WeChatChannelHelperCommandName;
@@ -1 +1 @@
1
- const w=1,i={healthCheck:2e3,permissionsCheck:1e4,activitySnapshot:1e3,automationLease:6e3,windowList:1e4,windowsEnsureReady:2e4,ocrRecognize:1e4,windowsCapture:5e3,mouseKeyboard:3e3,clipboard:5e3,menuPickItem:5e3,savePanel:1e4,wechatSearch:6e3},a=["screenCapture","visionOcr","windowList","windowFocus","mouseKeyboard","clipboard","imageCropHash","wechatSearch","humanActivity"],u=[...a,"contextMenu"],c=[...u,"automationLease"],p=c,l=[...a,"overlayCleanup","wechatRecovery"],C=[...l,"contextMenu"],h=[...C,"automationLease"];function H(e){return e==="observe"?[...a]:e==="download"?[...u]:[...c]}function A(e){return e==="observe"?[...l]:e==="download"?[...C]:[...h]}function L(e,t=p){const r=String(e||"").trim();if(!r)throw new Error("WeChat channel helper expected version is required");return{type:"hello",protocolVersion:1,expectedHelperVersion:r,capabilities:[...t]}}function S(e,t,r=p){if(!e||e.type!=="ready")return{ok:!1,errorCode:"helper_invalid_response",errorSummary:"Helper did not send a ready frame"};if(e.protocolVersion!==1)return{ok:!1,errorCode:"helper_protocol_mismatch",errorSummary:`Helper protocol ${e.protocolVersion} does not match 1`};if(e.helperVersion!==t)return{ok:!1,errorCode:"helper_version_mismatch",errorSummary:`Helper version ${e.helperVersion} does not match ${t}`};const n=r.filter(_=>!e.capabilities.includes(_));return n.length?{ok:!1,errorCode:"helper_capability_missing",errorSummary:`Helper missing capabilities: ${n.join(", ")}`}:{ok:!0}}function d(e){if(!e||typeof e!="object")return null;const t=e;if(!E(t.warmState))return null;const r=f(t.warmup);return r?{warmState:t.warmState,metrics:r}:{warmState:t.warmState}}function W(e){return e==="health.check"?i.healthCheck:e==="processes.list"?i.windowList:e==="permissions.check"||e==="permissions.requestScreenRecording"||e==="permissions.requestAccessibility"||e==="permissions.requestInputMonitoring"?i.permissionsCheck:e==="activity.snapshot"?i.activitySnapshot:e.startsWith("automation.lease.")?i.automationLease:e==="windows.list"||e==="windows.enumerateRaw"?i.windowList:e==="windows.ensureReady"||e==="windows.cleanupOverlays"||e==="windows.recoverWeChat"?i.windowsEnsureReady:e==="ocr.recognize"||e==="windows.captureAndOcr"?i.ocrRecognize:e==="windows.capture"||e==="screen.capture"?i.windowsCapture:e.startsWith("mouse.")||e.startsWith("keyboard.")?i.mouseKeyboard:e.startsWith("clipboard.")?i.clipboard:e==="menu.pickItem"?i.menuPickItem:e==="savePanel.saveToPath"?i.savePanel:e.startsWith("wechat.")?i.wechatSearch:i.healthCheck}function E(e){return e==="cold"||e==="warming"||e==="warm"||e==="failed"}function f(e){if(!e||typeof e!="object")return;const t=e,r={};return o(t,r,"startedAt"),o(t,r,"readyAt"),o(t,r,"warmupStartedAt"),o(t,r,"warmupCompletedAt"),s(t,r,"coldStartMs"),s(t,r,"warmupMs"),s(t,r,"firstOcrMs"),s(t,r,"warmOcrMs"),s(t,r,"lastOcrMs"),s(t,r,"ocrSampleCount"),o(t,r,"errorCode"),o(t,r,"errorSummary"),Object.keys(r).length>0?r:void 0}function o(e,t,r){typeof e[r]=="string"&&(t[r]=e[r])}function s(e,t,r){const n=e[r];typeof n=="number"&&Number.isFinite(n)&&n>=0&&(t[r]=n)}export{u as WECHAT_CHANNEL_DOWNLOAD_HELPER_CAPABILITIES,i as WECHAT_CHANNEL_HELPER_COMMAND_TIMEOUT_MS,w as WECHAT_CHANNEL_HELPER_PROTOCOL_VERSION,a as WECHAT_CHANNEL_OBSERVE_HELPER_CAPABILITIES,p as WECHAT_CHANNEL_REQUIRED_HELPER_CAPABILITIES,c as WECHAT_CHANNEL_SEND_HELPER_CAPABILITIES,C as WECHAT_CHANNEL_WINDOWS_DOWNLOAD_HELPER_CAPABILITIES,l as WECHAT_CHANNEL_WINDOWS_OBSERVE_HELPER_CAPABILITIES,h as WECHAT_CHANNEL_WINDOWS_SEND_HELPER_CAPABILITIES,L as createWeChatChannelHelperHello,d as extractWeChatChannelHelperWarmupSnapshot,H as requiredWeChatChannelHelperCapabilitiesForProfile,A as requiredWindowsWeChatChannelHelperCapabilitiesForProfile,W as timeoutForWeChatChannelHelperCommand,S as validateWeChatChannelHelperReady};
1
+ const f=1,i={healthCheck:2e3,permissionsCheck:1e4,activitySnapshot:1e3,automationLease:6e3,windowList:1e4,windowsEnsureReady:2e4,ocrRecognize:1e4,windowsCapture:5e3,mouseKeyboard:3e3,clipboard:5e3,menuPickItem:5e3,savePanel:1e4,wechatSearch:6e3,wechatInteraction:15e3},a=["screenCapture","visionOcr","windowList","windowFocus","mouseKeyboard","clipboard","imageCropHash","wechatSearch","humanActivity"],u=[...a,"contextMenu"],c=[...u,"automationLease"],p=c,l=[...a,"overlayCleanup","wechatRecovery"],h=[...l,"contextMenu"],_=[...h,"automationLease"];function H(e){return e==="observe"?[...a]:e==="download"?[...u]:[...c]}function A(e){return e==="observe"?[...l]:e==="download"?[...h]:[..._]}function L(e,t=p){const r=String(e||"").trim();if(!r)throw new Error("WeChat channel helper expected version is required");return{type:"hello",protocolVersion:1,expectedHelperVersion:r,capabilities:[...t]}}function S(e,t,r=p){if(!e||e.type!=="ready")return{ok:!1,errorCode:"helper_invalid_response",errorSummary:"Helper did not send a ready frame"};if(e.protocolVersion!==1)return{ok:!1,errorCode:"helper_protocol_mismatch",errorSummary:`Helper protocol ${e.protocolVersion} does not match 1`};if(e.helperVersion!==t)return{ok:!1,errorCode:"helper_version_mismatch",errorSummary:`Helper version ${e.helperVersion} does not match ${t}`};const n=r.filter(C=>!e.capabilities.includes(C));return n.length?{ok:!1,errorCode:"helper_capability_missing",errorSummary:`Helper missing capabilities: ${n.join(", ")}`}:{ok:!0}}function d(e){if(!e||typeof e!="object")return null;const t=e;if(!E(t.warmState))return null;const r=w(t.warmup);return r?{warmState:t.warmState,metrics:r}:{warmState:t.warmState}}function W(e){return e==="health.check"?i.healthCheck:e==="processes.list"?i.windowList:e==="permissions.check"||e==="permissions.requestScreenRecording"||e==="permissions.requestAccessibility"||e==="permissions.requestInputMonitoring"?i.permissionsCheck:e==="activity.snapshot"?i.activitySnapshot:e.startsWith("automation.lease.")?i.automationLease:e==="windows.list"||e==="windows.enumerateRaw"||e==="wechat.healthProbe"?i.windowList:e==="windows.ensureReady"||e==="windows.cleanupOverlays"||e==="windows.recoverWeChat"||e==="windows.closeWindow"?i.windowsEnsureReady:e==="ocr.recognize"||e==="windows.captureAndOcr"?i.ocrRecognize:e==="windows.capture"||e==="screen.capture"?i.windowsCapture:e.startsWith("mouse.")||e.startsWith("keyboard.")?i.mouseKeyboard:e.startsWith("clipboard.")?i.clipboard:e==="menu.pickItem"?i.menuPickItem:e==="savePanel.saveToPath"?i.savePanel:e==="wechat.pasteAndSubmit"||e==="wechat.focusMessageInput"||e==="wechat.searchConversation"?i.wechatInteraction:e.startsWith("wechat.")?i.wechatSearch:i.healthCheck}function E(e){return e==="cold"||e==="warming"||e==="warm"||e==="failed"}function w(e){if(!e||typeof e!="object")return;const t=e,r={};return o(t,r,"startedAt"),o(t,r,"readyAt"),o(t,r,"warmupStartedAt"),o(t,r,"warmupCompletedAt"),s(t,r,"coldStartMs"),s(t,r,"warmupMs"),s(t,r,"firstOcrMs"),s(t,r,"warmOcrMs"),s(t,r,"lastOcrMs"),s(t,r,"ocrSampleCount"),o(t,r,"errorCode"),o(t,r,"errorSummary"),Object.keys(r).length>0?r:void 0}function o(e,t,r){typeof e[r]=="string"&&(t[r]=e[r])}function s(e,t,r){const n=e[r];typeof n=="number"&&Number.isFinite(n)&&n>=0&&(t[r]=n)}export{u as WECHAT_CHANNEL_DOWNLOAD_HELPER_CAPABILITIES,i as WECHAT_CHANNEL_HELPER_COMMAND_TIMEOUT_MS,f as WECHAT_CHANNEL_HELPER_PROTOCOL_VERSION,a as WECHAT_CHANNEL_OBSERVE_HELPER_CAPABILITIES,p as WECHAT_CHANNEL_REQUIRED_HELPER_CAPABILITIES,c as WECHAT_CHANNEL_SEND_HELPER_CAPABILITIES,h as WECHAT_CHANNEL_WINDOWS_DOWNLOAD_HELPER_CAPABILITIES,l as WECHAT_CHANNEL_WINDOWS_OBSERVE_HELPER_CAPABILITIES,_ as WECHAT_CHANNEL_WINDOWS_SEND_HELPER_CAPABILITIES,L as createWeChatChannelHelperHello,d as extractWeChatChannelHelperWarmupSnapshot,H as requiredWeChatChannelHelperCapabilitiesForProfile,A as requiredWindowsWeChatChannelHelperCapabilitiesForProfile,W as timeoutForWeChatChannelHelperCommand,S as validateWeChatChannelHelperReady};
@@ -1,7 +1,7 @@
1
- import W from"node:fs";import U from"node:path";import{WeChatChannelApiError as Re}from"./client.js";import{defaultWeChatChannelAttachmentDir as Fe,resolveVisibleWeChatChannelMedia as Ae}from"./media-resolver.js";import{updateWeChatChannelBindingLedger as Oe}from"./ledger.js";import{defaultWeChatChannelVectorStorePath as Le,loadWeChatChannelVectorStore as De,saveWeChatChannelVectorStore as Ke,upsertWeChatChannelVectorReferences as $e}from"./vector-store.js";import{normalizeWeChatChannelAttachmentAvailability as X,normalizeWeChatChannelMessageKind as ze}from"./core/schema.js";import{waitForWeChatChannelPacing as J,weChatChannelPacingDelayMs as Ve}from"./pacing.js";import{matchWeChatConversationFingerprints as qe}from"./fingerprint.js";import{normalizeWeChatAnchorText as v,weChatTextSimilarity as Z}from"./anchor.js";import{decideWeChatChannelActivityGate as je,normalizeWeChatChannelActivitySnapshot as Ge}from"./human-coordination.js";const He=3,Ye=5,Ue="wechat_login_required",Xe=5500;class N extends Error{reasonCode;stage;action;constructor(t){super(`user_active:${t.reasonCode}:${t.stage}:${t.action}`),this.name="WeChatChannelUserActivityAbort",this.reasonCode=t.reasonCode,this.stage=t.stage,this.action=t.action}}async function Tn(e){return await ct(e.helper,e.activityGuard,e.traceId).withLease("observe","observe-binding",async n=>Je({...e,helper:n}))}async function Je(e){const t=e.foregroundMode??"required";let n=await Ze(e,{foregroundMode:t});if(t!=="background"&&(n=await ve(e.helper,n,e.traceId)),t==="background"){const{capture:s,ocr:a}=await p({helper:e.helper,window:n,traceId:e.traceId}),c=B({capture:s,ocr:a,window:n});if(c)throw new Error(c);if(!O(a,s,e.binding.conversationDisplayName))throw new Error("conversation_not_visible_background");if(G({capture:s,ocr:a,binding:e.binding}))throw new Error("wechat_window_obstructed_background");return e.onObservationEvidence?.({window:n,capture:s,ocr:a}),se(e)?ce({...e,window:n,capture:s,ocr:a}):K({...e,window:n,capture:s,ocr:a})}const r=await z({helper:e.helper,window:n,binding:e.binding,runtime:e.runtime,api:e.api,workDir:e.workDir,traceId:e.traceId});if(!r.opened){const s=await oe({...e,fallback:n,requireTargetConversation:!1,force:!0});if(s.windowId!==n.windowId){n=await ve(e.helper,s,e.traceId);const a=await z({helper:e.helper,window:n,binding:e.binding,runtime:e.runtime,api:e.api,workDir:e.workDir,traceId:e.traceId});if(!a.opened)throw new Error(a.reason||r.reason||"conversation_not_opened")}else throw new Error(r.reason||"conversation_not_opened")}let{capture:i,ocr:o}=await p({helper:e.helper,window:n,traceId:e.traceId});if(G({capture:i,ocr:o,binding:e.binding})){n=await $t(e.helper,e.traceId);const s=await z({helper:e.helper,window:n,binding:e.binding,runtime:e.runtime,api:e.api,workDir:e.workDir,traceId:e.traceId});if(!s.opened)throw new Error(s.reason||"wechat_window_obstructed");if({capture:i,ocr:o}=await p({helper:e.helper,window:n,traceId:e.traceId}),G({capture:i,ocr:o,binding:e.binding}))throw new Error("wechat_window_obstructed")}return e.onObservationEvidence?.({window:n,capture:i,ocr:o}),se(e)?ce({...e,window:n,capture:i,ocr:o}):K({...e,window:n,capture:i,ocr:o})}async function Ze(e,t={}){const n=t.foregroundMode??e.foregroundMode??"required",r=await Dt(e.helper,e.traceId,{foreground:n});return n==="background"?r:oe({...e,fallback:r,requireTargetConversation:!1,force:t.forceVision})}const k=new Map;async function Bn(e){const t=await Q(e,"messageInputPoint");if(!t.messageInputPoint)throw new Error("wechat_message_input_not_found:vision_rect_missing");return t.messageInputPoint}async function Qe(e){const t=await Q(e,"searchInputPoint");if(!t.searchInputPoint)throw new Error("wechat_search_input_not_found:vision_rect_missing");return t.searchInputPoint}async function Q(e,t){const n=t==="searchInputPoint"?"wechat_search_input":"wechat_message_input";if(!e.api.classifyWindow)throw new Error(`${n}_detector_unavailable`);const r=e.window.windowId,i=tt(e.window),o=`${e.runtime.runtimeId}:${e.binding.bindingId}:${r||"unknown"}`,s=`${e.runtime.runtimeId}:window:${r||"unknown"}:${i}`,a=k.get(o)??k.get(s)??te(e,o)??te(e,s);if(a&&a.windowId===r&&a.boundsKey===i&&(!t||a[t]))return k.set(o,a),k.set(s,a),a;const c=e.screenshot??await F(e.helper,r,e.traceId,e.window.bounds),u=await e.api.classifyWindow(e.runtime,e.binding,{screenshot:{mimeType:c.mimeType,dataBase64:c.dataBase64,width:c.width,height:c.height,windowId:r},traceId:e.traceId});if(u.windowKind!=="chat_main")throw new Error(`${n}_not_found:${u.windowKind||"unknown_window"}`);const w=ie(u.layout?.messageInputRect,c,e.window),l=ie(u.layout?.searchInputRect,c,e.window),d={windowId:r||"",boundsKey:i,messageInputPoint:w,searchInputPoint:l,updatedAt:new Date().toISOString()};return k.set(o,d),k.set(s,d),ne(e,o,d),ne(e,s,d),d}function ee(e){const t=e.workDir;return t?U.join(t,"wechat-channel",e.runtime.runtimeId,"vision-layout-point-cache.json"):null}function te(e,t){const n=ee(e);if(!n)return null;try{const r=JSON.parse(W.readFileSync(n,"utf8"));return et(r[t])}catch{return null}}function ne(e,t,n){const r=ee(e);if(r)try{let i={};try{i=JSON.parse(W.readFileSync(r,"utf8"))}catch{i={}}i[t]=n,W.mkdirSync(U.dirname(r),{recursive:!0}),W.writeFileSync(r,`${JSON.stringify(i,null,2)}
2
- `,"utf8")}catch{}}function et(e){if(!e||typeof e!="object")return null;const t=e,n=typeof t.windowId=="string"?t.windowId:"",r=typeof t.boundsKey=="string"?t.boundsKey:"";if(!r)return null;const i=re(t.messageInputPoint??t.point),o=re(t.searchInputPoint);return{windowId:n,boundsKey:r,messageInputPoint:i,searchInputPoint:o,...typeof t.updatedAt=="string"?{updatedAt:t.updatedAt}:{}}}function re(e){if(!e||typeof e!="object")return null;const t=e,n=Number(t.x),r=Number(t.y);return!Number.isFinite(n)||!Number.isFinite(r)?null:{x:Math.round(n),y:Math.round(r),coordinateSpace:"screen"}}function tt(e){const t=e.bounds;return[Math.round(Number(t?.x??0)),Math.round(Number(t?.y??0)),Math.round(Number(t?.width??0)),Math.round(Number(t?.height??0))].join(",")}function ie(e,t,n){const r=Number(e?.x),i=Number(e?.y),o=Number(e?.width),s=Number(e?.height);if(![r,i,o,s].every(Number.isFinite)||o<=0||s<=0)return null;const a=e?.coordinateSpace==="screen"?{x:r,y:i,width:o,height:s,coordinateSpace:"screen"}:{x:r/999*t.width,y:i/999*t.height,width:o/999*t.width,height:s/999*t.height,coordinateSpace:"screenshotPixel"},c=nt(a,t,n);return c?{x:Math.round(c.x),y:Math.round(c.y),coordinateSpace:"screen"}:null}function nt(e,t,n){const r=rt({x:e.x+e.width*.54,y:e.y+e.height*.55,coordinateSpace:e.coordinateSpace},t,n);return r?{x:r.x,y:r.y,coordinateSpace:"screen"}:null}function rt(e,t,n){if(![e.x,e.y].every(Number.isFinite))return null;if(e.coordinateSpace==="screen"||!n.bounds)return{x:e.x,y:e.y};const r=t.width&&n.bounds.width?t.width/n.bounds.width:1,i=t.height&&n.bounds.height?t.height/n.bounds.height:r,o=Math.max(0,(t.width-n.bounds.width*r)/2),s=Math.max(0,(t.height-n.bounds.height*i)/2);return{x:n.bounds.x+(e.x-o)/r,y:n.bounds.y+(e.y-s)/i}}async function oe(e){if(!e.api.classifyWindow)throw new Error("wechat_window_classifier_unavailable");const t=await e.helper.request("windows.list",{},e.traceId);m(t,"windows.list");const n=(t.result?.windows??[]).filter(q).filter(s=>s.visible!==!1&&s.minimized!==!0&&!!s.windowId),r=at(n.length?n:[e.fallback],e.fallback);if(!r.length)throw new Error("wechat_window_not_found");const i=[];for(const s of r)try{const a=await F(e.helper,s.windowId,e.traceId,s.bounds),c=await e.api.classifyWindow(e.runtime,e.binding,{screenshot:{mimeType:a.mimeType,dataBase64:a.dataBase64,width:a.width,height:a.height,windowId:s.windowId},traceId:e.traceId});i.push({window:s,classification:c,score:st(c,e.requireTargetConversation===!0)})}catch(a){if(it(a))throw a;i.push({window:s,classification:null,errorSummary:a instanceof Error?a.message:String(a||"window classification failed"),score:-1})}const o=i.filter(s=>s.score>0).sort((s,a)=>a.score-s.score)[0];if(!o){const s=i.map(a=>({windowId:a.window.windowId,appName:a.window.appName,title:a.window.title,score:a.score,errorSummary:a.errorSummary,classification:a.classification?{windowKind:a.classification.windowKind,reasonCode:a.classification.reasonCode,confidence:a.classification.confidence,conversationTitle:a.classification.conversationTitle,isTargetConversation:a.classification.isTargetConversation}:null}));throw new Error(`wechat_chat_main_window_not_found: ${JSON.stringify(s)}`)}return o.window}function it(e){const t=ot(e);return t==="insufficient_credits"||t==="entitlement_required"||t==="manual_review_required"||t==="wechat_channel_network_failed"||t==="wechat_channel_invalid_response"}function ot(e){if(e instanceof Re||e&&typeof e=="object"&&typeof e.reasonCode=="string")return e.reasonCode.trim().toLowerCase();const t=e instanceof Error?e.message:String(e||"");return/classify-window (?:network )?failed:\s*([a-z][a-z0-9_]*)/i.exec(t)?.[1]?.trim().toLowerCase()||""}function at(e,t){const n=[],r=i=>{i?.windowId&&(n.some(o=>o.windowId===i.windowId)||n.push(i))};r(e.find(i=>i.windowId===t.windowId));for(const i of e)r(i);return n}function st(e,t){if(!e||e.windowKind!=="chat_main")return 0;const n=Number(e.confidence??0);return t&&e.isTargetConversation!==!0?0:100+(e.isTargetConversation?50:0)+Math.round(Math.max(0,Math.min(1,n))*100)}function ct(e,t,n){if(!t)return{helper:e,withLease:async(a,c,u)=>await u(e)};let r=null;const i=a=>{t.onEvent?.({...a,at:new Date().toISOString()})},o=async(a,c,u={})=>{if(r){const l=await e.request("automation.lease.status",{},n),d=l.result??{},f=l.ok&&d.interrupted?ae(d.interruptReason):l.ok?void 0:l.errorCode||"user_activity_unknown";if(i({phase:"lease-status",stage:a,action:c,ok:l.ok===!0&&!d.interrupted,...f?{reasonCode:f}:{}}),!l.ok)throw new N({reasonCode:l.errorCode||"user_activity_unknown",stage:a,action:c});if(d.active===!1||d.leaseId!==r||!d.interrupted)return;throw new N({reasonCode:ae(d.interruptReason),stage:a,action:c})}const w=Date.now()+lt(t.preflightIdleSettleTimeoutMs);for(;;){const l=await e.request("activity.snapshot",{},n);if(!l.ok)throw i({phase:"snapshot",stage:a,action:c,ok:!1,reasonCode:l.errorCode||"user_activity_unknown"}),new N({reasonCode:l.errorCode||"user_activity_unknown",stage:a,action:c});const d=je({snapshot:Ge(l.result),stage:a,policy:t.policy});if(i({phase:"snapshot",stage:a,action:c,ok:d.ok,...d.ok?{}:{reasonCode:d.reasonCode,waitMs:d.waitMs}}),d.ok)return;const f=new N({reasonCode:d.reasonCode,stage:a,action:c});if(!u.settle)throw f;const Y=w-Date.now();if(Y<=0)throw f;await j(Math.max(50,Math.min(d.waitMs,Y)))}},s={request:async(a,c,u)=>{const w=dt(a);w&&await o(w.stage,`${a}:before`);const l=await e.request(a,c,u);return w&&l.ok&&await o(w.stage,`${a}:after`),l}};return{helper:s,async withLease(a,c,u){if(await Ft(e,n),await o(a,`${c}:preflight`,{settle:!0}),t.useAutomationLease===!1)return await u(s);const w=await e.request("automation.lease.acquire",{owner:"wechat-channel",purpose:`observe:${c}`,ttlMs:12e4},n);if(r=typeof w.result?.leaseId=="string"?w.result.leaseId:null,i({phase:"lease-acquire",stage:a,action:c,ok:w.ok===!0,...w.ok?{}:{reasonCode:w.errorCode||"user_activity_unknown"}}),!w.ok)throw r=null,new N({reasonCode:w.errorCode||"user_activity_unknown",stage:a,action:c});try{const l=await u(s);return await o(a,`${c}:complete`),l}finally{const l=r;if(r=null,l){const d=await e.request("automation.lease.release",{leaseId:l},n).catch(f=>({ok:!1,errorCode:"automation_lease_release_failed",errorSummary:f instanceof Error?f.message:String(f)}));i({phase:"lease-release",stage:a,action:c,ok:d.ok===!0,...d.ok?{}:{reasonCode:d.errorCode||"automation_lease_release_failed"}})}}}}}function dt(e){return e==="windows.ensureReady"||e==="windows.focus"||e==="wechat.searchConversation"?{stage:"open_conversation"}:e==="keyboard.shortcut"||e==="mouse.click"||e==="mouse.rightClick"||e==="mouse.scroll"||e==="clipboard.snapshot"||e==="clipboard.readAttachment"||e==="menu.pickItem"?{stage:"dangerous_action"}:null}function ae(e){return e==="recent_mouse_activity"||e==="recent_mouse_click"||e==="recent_scroll_activity"||e==="recent_keyboard_activity"||e==="frontmost_app_changed"?e:"user_activity_unknown"}function lt(e){return typeof e=="number"&&Number.isFinite(e)&&e>=0?e:Xe}function se(e){return e.observePipeline==="edge-structure"?!0:e.observePipeline==="server-observe"?!1:!!e.api.structureWindow}async function K(e){const t=await _e(e),n=await e.api.observe(e.runtime,e.binding,{screenshots:[{captureIndex:0,mimeType:t.mimeType,dataBase64:t.dataBase64,width:t.width,height:t.height}],edgeOcrBlocks:e.ocr.blocks??[],visibleConversationFingerprints:e.ocr.visibleConversationFingerprints??[],localLedgerTailAnchors:e.localLedgerTailAnchors??[],traceId:e.traceId}),r=await we({helper:e.helper,runtime:e.runtime,binding:e.binding,messages:n.observedMessages??[],workDir:e.workDir,attachmentsDir:e.attachmentsDir,window:e.window,screenshot:t,windowId:e.window.windowId,traceId:e.traceId,mediaAnchorText:e.mediaAnchorText});return Be(r,Te({ocr:e.ocr,screenshot:t,binding:e.binding,anchors:e.localOutboundEchoAnchors??[]}))}async function ce(e){if(!e.api.structureWindow)throw new Error("wechat_structure_window_unavailable");const t=await _e(e);let n;try{n=await e.api.structureWindow(e.runtime,e.binding,{screenshots:[{captureIndex:0,mimeType:t.mimeType,dataBase64:t.dataBase64,width:t.width,height:t.height}],edgeOcrBlocks:e.ocr.blocks??[],visibleConversationFingerprints:e.ocr.visibleConversationFingerprints??[],localLedgerTailAnchors:e.localLedgerTailAnchors??[],traceId:e.traceId})}catch(c){if(ut(c))return K(e);throw c}const r=vt(n.structuredMessages??n.observedMessages??[]),i=await wt({...e,messages:r,screenshot:t}),o=await we({helper:e.helper,runtime:e.runtime,binding:e.binding,messages:i.messages,workDir:e.workDir,attachmentsDir:e.attachmentsDir,window:e.window,screenshot:t,windowId:e.window.windowId,traceId:e.traceId,mediaAnchorText:e.mediaAnchorText}),s=Be(o,Te({ocr:e.ocr,screenshot:t,binding:e.binding,anchors:e.localOutboundEchoAnchors??[]}));return e.localLedger?Oe({ledger:e.localLedger,bindingId:e.binding.bindingId,observedMessages:s,baselineOnly:e.baselineOnly,vectorReferences:i.vectorReferences}).newMessages:s}function ut(e){return e instanceof Error&&/\bedge_runtime_disabled\b/.test(e.message)}async function wt(e){const t=ht(e.messages);if(!t.length)return{messages:e.messages,vectorReferences:[]};const n=e.api.embedVisual?await mt({...e,seedBlocks:t}):t.filter(s=>h(s.vectorBase64)||h(s.signature));if(!n.length)return{messages:e.messages,vectorReferences:[]};const r=e.vectorStorePath??Le(e.workDir||process.cwd(),e.runtime.runtimeId),i=De(r,e.runtime.runtimeId),o=$e({store:i,bindingId:e.binding.bindingId,blocks:n});return o.length?(Ke(r,i),{messages:yt(e.messages,o),vectorReferences:o}):{messages:e.messages,vectorReferences:[]}}function ht(e){const t=[];for(const n of e)for(const[r,i]of(n.visualBlocks??[]).entries()){const o=h(i.blockId)||`${n.stableMessageKey}:visual-${r}`,s=h(i.blockKind)||n.kind||"visual";t.push({...i,stableMessageKey:n.stableMessageKey,blockId:o,blockKind:s,...i.bbox!==void 0?{bbox:i.bbox}:n.bbox!==void 0?{bbox:n.bbox}:{}})}return t}async function ft(e){if(!e.api.embedVisual)return[];const t=await e.api.embedVisual(e.runtime,e.binding,{screenshots:[{captureIndex:0,mimeType:e.screenshot.mimeType,dataBase64:e.screenshot.dataBase64,width:e.screenshot.width,height:e.screenshot.height}],visualBlocks:e.seedBlocks,traceId:e.traceId});return gt(t.visualBlocks??t.embeddedBlocks??[],e.seedBlocks)}async function mt(e){try{return await ft(e)}catch(t){if(bt(t))throw t;return[]}}function bt(e){return!e||typeof e!="object"?!1:e.reasonCode==="insufficient_credits"?!0:e instanceof Error&&/\binsufficient_credits\b/.test(e.message)}function gt(e,t){return e.map((n,r)=>{const i=t[r];return{...n,stableMessageKey:h(n.stableMessageKey)||h(i?.stableMessageKey),blockId:h(n.blockId)||h(i?.blockId),blockKind:h(n.blockKind)||h(i?.blockKind)||"visual",...n.bbox!==void 0?{bbox:n.bbox}:i?.bbox!==void 0?{bbox:i.bbox}:{}}}).filter(n=>h(n.stableMessageKey)&&h(n.blockId))}function yt(e,t){const n=new Map;for(const r of t){const i=n.get(r.stableMessageKey)??[];i.push(r),n.set(r.stableMessageKey,i)}return e.map(r=>{const i=n.get(r.stableMessageKey);return i?.length?{...r,visualBlocks:xt(r,i)}:r})}function xt(e,t){const r=(e.visualBlocks??[]).map((o,s)=>{const a=h(o.blockId)||`${e.stableMessageKey}:visual-${s}`,c=t.find(u=>u.blockId===a);return c?de(c,o):le(o)}),i=new Set(r.map(o=>o.blockId));for(const o of t)i.has(o.blockId)||r.push(de(o));return r}function de(e,t){return{...t?le(t):{},stableMessageKey:e.stableMessageKey,blockId:e.blockId,blockKind:e.blockKind,vectorStoreKey:e.vectorStoreKey,model:e.model,...e.modelVersion!==void 0?{modelVersion:e.modelVersion}:{},dims:e.dims,...e.signature?{signature:e.signature}:{},...e.bbox!==void 0?{bbox:e.bbox}:{}}}function le(e){const{vectorBase64:t,...n}=e;return n}function vt(e){return e.map((t,n)=>pt(t,n)).filter(t=>t!=null)}function pt(e,t){if(!b(e))return null;const n=ze(e.kind),r=It(e.senderRole),i=g(e.normalizedText),o=g(e.anchorText)??i??g(e.textExcerpt),s=h(e.stableMessageKey)||Ct({message:e,index:t,senderRole:r,kind:n,anchorText:o});return{stableMessageKey:s,senderRole:r,...g(e.senderName)!==null?{senderName:g(e.senderName)}:{},kind:n,...i!==null?{normalizedText:i}:{},...o!==null?{anchorText:o}:{},...S(e,"anchorMetadata")?{anchorMetadata:e.anchorMetadata}:{},...S(e,"neighborContext")?{neighborContext:e.neighborContext}:{},...g(e.textExcerpt)!==null?{textExcerpt:g(e.textExcerpt)}:{},...S(e,"bbox")?{bbox:e.bbox}:{},...S(e,"mediaMetadata")?{mediaMetadata:_t(e.mediaMetadata)}:{},...typeof e.isBaseline=="boolean"?{isBaseline:e.isBaseline}:{},...g(e.deliveryStatus)!==null?{deliveryStatus:g(e.deliveryStatus)??void 0}:{},...g(e.observedAt)!==null?{observedAt:g(e.observedAt)??void 0}:{},...Array.isArray(e.visualBlocks)?{visualBlocks:e.visualBlocks}:{},...o?{}:{anchorText:s}}}function Ct(e){const t=b(e.message.bbox)?JSON.stringify(e.message.bbox):"";return`structured-window:${[e.index,e.senderRole,e.kind,e.anchorText||h(e.message.textExcerpt)||"",t].join(":").slice(0,180)}`}function _t(e){if(!b(e))return e;const t={...e};return S(t,"availability")&&(t.availability=X(t.availability)),b(t.attachment)&&(t.attachment=ue(t.attachment)),Array.isArray(t.attachments)&&(t.attachments=t.attachments.map(n=>b(n)?ue(n):n)),t}function ue(e){const t={...e};return S(t,"availability")&&(t.availability=X(t.availability)),t}function It(e){return e==="self"||e==="contact"||e==="system"||e==="unknown"?e:"unknown"}async function we(e){if(e.binding.downloadMedia===!1)return e.messages;const t=e.messages.map((o,s)=>Et(o,e.screenshot,e.window,Bt(e.messages,s))).filter((o,s)=>kt(e.messages,s,e.mediaAnchorText)).filter(o=>o!=null);if(!t.length)return e.messages;const n=e.attachmentsDir??Fe(e.workDir||process.cwd(),e.runtime.runtimeId,e.binding.bindingId),r=await Ae({helper:e.helper,candidates:t,attachmentsDir:n,screenshot:e.screenshot,windowId:e.windowId,window:e.window,traceId:e.traceId,platform:e.runtime.policy.platform,stabilityCheck:async()=>{const o=await _({helper:e.helper,window:e.window,binding:e.binding,traceId:e.traceId});return o.ok?{ok:!0}:{ok:!1,reasonCode:o.reason}}});if(!r.length)return e.messages;const i=new Map(r.map(o=>[o.messageKey,o]));return e.messages.map(o=>{const s=i.get(o.stableMessageKey);return s?{...o,mediaMetadata:Pt(o.mediaMetadata,s.attachment,s.reasonCode,s.attemptReasonCodes,s.resolveTrace)}:o})}function kt(e,t,n){const r=fe(n);if(!r)return!0;const i=St(r),o=e.map((s,a)=>fe(Tt(s)).includes(r)?a:-1).filter(s=>s>=0);if(!o.length)return t===Mt(e,i);for(const s of o)if(t>=s&&t<=s+8||t===s-1&&!e.slice(s+1,Math.min(e.length,s+9)).some(he))return!0;return!1}function St(e){return e.includes("video")||e.includes("vide0")?"video":e.includes("image")||e.includes("photo")||e.includes("ph0t0")||e.includes("picture")?"image":e.includes("file")||e.includes("document")?"file":null}function Mt(e,t=null){for(let n=e.length-1;n>=0;n-=1)if(he(e[n]))return Nt(e[n],t)?n:-1;return-1}function he(e){if(!e)return!1;const t=String(e.kind||"").toLowerCase(),n=b(e.mediaMetadata)?e.mediaMetadata:{},r=b(n.attachment)?n.attachment:{};return me(t,n,r)}function Nt(e,t){if(!e||!t)return!0;const n=String(e.kind||"").toLowerCase(),r=b(e.mediaMetadata)?e.mediaMetadata:{},i=b(r.attachment)?r.attachment:{},o=String(i.type||"").toLowerCase(),s=String(i.mimeType||"").toLowerCase(),a=String(i.name||i.localPath||e.anchorText||e.textExcerpt||"").toLowerCase();return t==="video"?n.includes("video")||o==="video"||s.startsWith("video/")||/\.(mp4|mov|m4v|avi|mkv|webm)$/i.test(a):t==="image"?/image|photo|picture/.test(n)||o==="image"||s.startsWith("image/")||/\.(png|jpe?g|gif|webp|heic|tiff?|bmp)$/i.test(a):/file|document/.test(n)||o==="file"||!/image|photo|picture|video/.test(n)&&o!=="image"&&o!=="video"&&!s.startsWith("image/")&&!s.startsWith("video/")}function Tt(e){if(!e)return"";const t=e;return[e.normalizedText,e.anchorText,e.textExcerpt,t.text].map(n=>String(n||"")).join(`
1
+ import P from"node:fs";import U from"node:path";import{WeChatChannelApiError as Re}from"./client.js";import{defaultWeChatChannelAttachmentDir as Fe,resolveVisibleWeChatChannelMedia as Ae}from"./media-resolver.js";import{updateWeChatChannelBindingLedger as De}from"./ledger.js";import{defaultWeChatChannelVectorStorePath as Le,loadWeChatChannelVectorStore as Oe,saveWeChatChannelVectorStore as Ke,upsertWeChatChannelVectorReferences as $e}from"./vector-store.js";import{normalizeWeChatChannelAttachmentAvailability as X,normalizeWeChatChannelMessageKind as ze}from"./core/schema.js";import{waitForWeChatChannelPacing as J,weChatChannelPacingDelayMs as Ve}from"./pacing.js";import{matchWeChatConversationFingerprints as qe}from"./fingerprint.js";import{normalizeWeChatAnchorText as x,weChatTextSimilarity as Z}from"./anchor.js";import{decideWeChatChannelActivityGate as je,normalizeWeChatChannelActivitySnapshot as Ge}from"./human-coordination.js";const He=3,Ye=5,Ue="wechat_login_required",Xe=5500;class N extends Error{reasonCode;stage;action;constructor(t){super(`user_active:${t.reasonCode}:${t.stage}:${t.action}`),this.name="WeChatChannelUserActivityAbort",this.reasonCode=t.reasonCode,this.stage=t.stage,this.action=t.action}}async function Mn(e){return await ct(e.helper,e.activityGuard,e.traceId).withLease("observe","observe-binding",async n=>Je({...e,helper:n}))}async function Je(e){const t=e.foregroundMode??"required";let n=await Ze(e,{foregroundMode:t});if(t!=="background"&&(n=await pe(e.helper,n,e.traceId)),t==="background"){const{capture:s,ocr:a}=await k({helper:e.helper,window:n,traceId:e.traceId}),c=B({capture:s,ocr:a,window:n});if(c)throw new Error(c);if(!D(a,s,e.binding.conversationDisplayName))throw new Error("conversation_not_visible_background");if(G({capture:s,ocr:a,binding:e.binding}))throw new Error("wechat_window_obstructed_background");return e.onObservationEvidence?.({window:n,capture:s,ocr:a}),se(e)?ce({...e,window:n,capture:s,ocr:a}):K({...e,window:n,capture:s,ocr:a})}const r=await z({helper:e.helper,window:n,binding:e.binding,runtime:e.runtime,api:e.api,workDir:e.workDir,traceId:e.traceId});if(!r.opened){const s=await oe({...e,fallback:n,requireTargetConversation:!1,force:!0});if(s.windowId!==n.windowId){n=await pe(e.helper,s,e.traceId);const a=await z({helper:e.helper,window:n,binding:e.binding,runtime:e.runtime,api:e.api,workDir:e.workDir,traceId:e.traceId});if(!a.opened)throw new Error(a.reason||r.reason||"conversation_not_opened")}else throw new Error(r.reason||"conversation_not_opened")}let{capture:i,ocr:o}=await k({helper:e.helper,window:n,traceId:e.traceId});if(G({capture:i,ocr:o,binding:e.binding})){n=await $t(e.helper,e.traceId);const s=await z({helper:e.helper,window:n,binding:e.binding,runtime:e.runtime,api:e.api,workDir:e.workDir,traceId:e.traceId});if(!s.opened)throw new Error(s.reason||"wechat_window_obstructed");if({capture:i,ocr:o}=await k({helper:e.helper,window:n,traceId:e.traceId}),G({capture:i,ocr:o,binding:e.binding}))throw new Error("wechat_window_obstructed")}return e.onObservationEvidence?.({window:n,capture:i,ocr:o}),se(e)?ce({...e,window:n,capture:i,ocr:o}):K({...e,window:n,capture:i,ocr:o})}async function Ze(e,t={}){const n=t.foregroundMode??e.foregroundMode??"required",r=await Ot(e.helper,e.traceId,{foreground:n});return n==="background"?r:oe({...e,fallback:r,requireTargetConversation:!1,force:t.forceVision})}const C=new Map;async function Nn(e){const t=await Q(e,"messageInputPoint");if(!t.messageInputPoint)throw new Error("wechat_message_input_not_found:vision_rect_missing");return t.messageInputPoint}async function Qe(e){const t=await Q(e,"searchInputPoint");if(!t.searchInputPoint)throw new Error("wechat_search_input_not_found:vision_rect_missing");return t.searchInputPoint}async function Q(e,t){const n=t==="searchInputPoint"?"wechat_search_input":"wechat_message_input";if(!e.api.classifyWindow)throw new Error(`${n}_detector_unavailable`);const r=e.window.windowId,i=tt(e.window),o=`${e.runtime.runtimeId}:${e.binding.bindingId}:${r||"unknown"}`,s=`${e.runtime.runtimeId}:window:${r||"unknown"}:${i}`,a=C.get(o)??C.get(s)??te(e,o)??te(e,s);if(a&&a.windowId===r&&a.boundsKey===i&&(!t||a[t]))return C.set(o,a),C.set(s,a),a;const c=e.screenshot??await R(e.helper,r,e.traceId,e.window.bounds),l=await e.api.classifyWindow(e.runtime,e.binding,{screenshot:{mimeType:c.mimeType,dataBase64:c.dataBase64,width:c.width,height:c.height,windowId:r},traceId:e.traceId});if(l.windowKind!=="chat_main")throw new Error(`${n}_not_found:${l.windowKind||"unknown_window"}`);const w=ie(l.layout?.messageInputRect,c,e.window,"message-input"),u=ie(l.layout?.searchInputRect,c,e.window,"search-input"),d={windowId:r||"",boundsKey:i,messageInputPoint:w,searchInputPoint:u,updatedAt:new Date().toISOString()};return C.set(o,d),C.set(s,d),ne(e,o,d),ne(e,s,d),d}function ee(e){const t=e.workDir;return t?U.join(t,"wechat-channel",e.runtime.runtimeId,"vision-layout-point-cache.json"):null}function te(e,t){const n=ee(e);if(!n)return null;try{const r=JSON.parse(P.readFileSync(n,"utf8"));return et(r[t])}catch{return null}}function ne(e,t,n){const r=ee(e);if(r)try{let i={};try{i=JSON.parse(P.readFileSync(r,"utf8"))}catch{i={}}i[t]=n,P.mkdirSync(U.dirname(r),{recursive:!0}),P.writeFileSync(r,`${JSON.stringify(i,null,2)}
2
+ `,"utf8")}catch{}}function et(e){if(!e||typeof e!="object")return null;const t=e,n=typeof t.windowId=="string"?t.windowId:"",r=typeof t.boundsKey=="string"?t.boundsKey:"";if(!r)return null;const i=re(t.messageInputPoint??t.point),o=re(t.searchInputPoint);return{windowId:n,boundsKey:r,messageInputPoint:i,searchInputPoint:o,...typeof t.updatedAt=="string"?{updatedAt:t.updatedAt}:{}}}function re(e){if(!e||typeof e!="object")return null;const t=e,n=Number(t.x),r=Number(t.y);return!Number.isFinite(n)||!Number.isFinite(r)?null:{x:Math.round(n),y:Math.round(r),coordinateSpace:"screen"}}function tt(e){const t=e.bounds;return[Math.round(Number(t?.x??0)),Math.round(Number(t?.y??0)),Math.round(Number(t?.width??0)),Math.round(Number(t?.height??0))].join(",")}function ie(e,t,n,r="message-input"){const i=Number(e?.x),o=Number(e?.y),s=Number(e?.width),a=Number(e?.height);if(![i,o,s,a].every(Number.isFinite)||s<=0||a<=0)return null;const c=e?.coordinateSpace==="screen"?{x:i,y:o,width:s,height:a,coordinateSpace:"screen"}:{x:i/999*t.width,y:o/999*t.height,width:s/999*t.width,height:a/999*t.height,coordinateSpace:"screenshotPixel"},l=nt(c,t,n,r);return l?{x:Math.round(l.x),y:Math.round(l.y),coordinateSpace:"screen"}:null}function nt(e,t,n,r="message-input"){const i=r==="message-input"?.28:.55,o=rt({x:e.x+e.width*.54,y:e.y+e.height*i,coordinateSpace:e.coordinateSpace},t,n);return o?{x:o.x,y:o.y,coordinateSpace:"screen"}:null}function rt(e,t,n){if(![e.x,e.y].every(Number.isFinite))return null;if(e.coordinateSpace==="screen"||!n.bounds)return{x:e.x,y:e.y};const r=t.width&&n.bounds.width?t.width/n.bounds.width:1,i=t.height&&n.bounds.height?t.height/n.bounds.height:r,o=Math.max(0,(t.width-n.bounds.width*r)/2),s=Math.max(0,(t.height-n.bounds.height*i)/2);return{x:n.bounds.x+(e.x-o)/r,y:n.bounds.y+(e.y-s)/i}}async function oe(e){if(!e.api.classifyWindow)throw new Error("wechat_window_classifier_unavailable");const t=await e.helper.request("windows.list",{},e.traceId);m(t,"windows.list");const n=(t.result?.windows??[]).filter(q).filter(s=>s.visible!==!1&&s.minimized!==!0&&!!s.windowId),r=at(n.length?n:[e.fallback],e.fallback);if(!r.length)throw new Error("wechat_window_not_found");const i=[];for(const s of r)try{const a=await R(e.helper,s.windowId,e.traceId,s.bounds),c=await e.api.classifyWindow(e.runtime,e.binding,{screenshot:{mimeType:a.mimeType,dataBase64:a.dataBase64,width:a.width,height:a.height,windowId:s.windowId},traceId:e.traceId});i.push({window:s,classification:c,score:st(c,e.requireTargetConversation===!0)})}catch(a){if(it(a))throw a;i.push({window:s,classification:null,errorSummary:a instanceof Error?a.message:String(a||"window classification failed"),score:-1})}const o=i.filter(s=>s.score>0).sort((s,a)=>a.score-s.score)[0];if(!o){const s=i.map(a=>({windowId:a.window.windowId,appName:a.window.appName,title:a.window.title,score:a.score,errorSummary:a.errorSummary,classification:a.classification?{windowKind:a.classification.windowKind,reasonCode:a.classification.reasonCode,confidence:a.classification.confidence,conversationTitle:a.classification.conversationTitle,isTargetConversation:a.classification.isTargetConversation}:null}));throw new Error(`wechat_chat_main_window_not_found: ${JSON.stringify(s)}`)}return o.window}async function Tn(e,t,n){if(e.runtime.policy.platform!=="win32")return;const r=t.filter(i=>{if(i.window.windowId===n.window.windowId||!i.window.windowId)return!1;const o=i.classification?.windowKind;return o==="chat_main"||o==="login"});if(r.length!==0)for(const i of r)try{await e.helper.request("windows.closeWindow",{windowId:i.window.windowId},e.traceId)}catch{}}function it(e){const t=ot(e);return t==="insufficient_credits"||t==="entitlement_required"||t==="manual_review_required"||t==="wechat_channel_network_failed"||t==="wechat_channel_invalid_response"}function ot(e){if(e instanceof Re||e&&typeof e=="object"&&typeof e.reasonCode=="string")return e.reasonCode.trim().toLowerCase();const t=e instanceof Error?e.message:String(e||"");return/classify-window (?:network )?failed:\s*([a-z][a-z0-9_]*)/i.exec(t)?.[1]?.trim().toLowerCase()||""}function at(e,t){const n=[],r=i=>{i?.windowId&&(n.some(o=>o.windowId===i.windowId)||n.push(i))};r(e.find(i=>i.windowId===t.windowId));for(const i of e)r(i);return n}function st(e,t){if(!e||e.windowKind!=="chat_main")return 0;const n=Number(e.confidence??0);return t&&e.isTargetConversation!==!0?0:100+(e.isTargetConversation?50:0)+Math.round(Math.max(0,Math.min(1,n))*100)}function ct(e,t,n){if(!t)return{helper:e,withLease:async(a,c,l)=>await l(e)};let r=null;const i=a=>{t.onEvent?.({...a,at:new Date().toISOString()})},o=async(a,c,l={})=>{if(r){const u=await e.request("automation.lease.status",{},n),d=u.result??{},f=u.ok&&d.interrupted?ae(d.interruptReason):u.ok?void 0:u.errorCode||"user_activity_unknown";if(i({phase:"lease-status",stage:a,action:c,ok:u.ok===!0&&!d.interrupted,...f?{reasonCode:f}:{}}),!u.ok)throw new N({reasonCode:u.errorCode||"user_activity_unknown",stage:a,action:c});if(d.active===!1||d.leaseId!==r||!d.interrupted)return;throw new N({reasonCode:ae(d.interruptReason),stage:a,action:c})}const w=Date.now()+lt(t.preflightIdleSettleTimeoutMs);for(;;){const u=await e.request("activity.snapshot",{},n);if(!u.ok)throw i({phase:"snapshot",stage:a,action:c,ok:!1,reasonCode:u.errorCode||"user_activity_unknown"}),new N({reasonCode:u.errorCode||"user_activity_unknown",stage:a,action:c});const d=je({snapshot:Ge(u.result),stage:a,policy:t.policy});if(i({phase:"snapshot",stage:a,action:c,ok:d.ok,...d.ok?{}:{reasonCode:d.reasonCode,waitMs:d.waitMs}}),d.ok)return;const f=new N({reasonCode:d.reasonCode,stage:a,action:c});if(!l.settle)throw f;const Y=w-Date.now();if(Y<=0)throw f;await j(Math.max(50,Math.min(d.waitMs,Y)))}},s={request:async(a,c,l)=>{const w=dt(a);w&&await o(w.stage,`${a}:before`);const u=await e.request(a,c,l);return w&&u.ok&&await o(w.stage,`${a}:after`),u}};return{helper:s,async withLease(a,c,l){if(await Ft(e,n),await o(a,`${c}:preflight`,{settle:!0}),t.useAutomationLease===!1)return await l(s);const w=await e.request("automation.lease.acquire",{owner:"wechat-channel",purpose:`observe:${c}`,ttlMs:12e4},n);if(r=typeof w.result?.leaseId=="string"?w.result.leaseId:null,i({phase:"lease-acquire",stage:a,action:c,ok:w.ok===!0,...w.ok?{}:{reasonCode:w.errorCode||"user_activity_unknown"}}),!w.ok)throw r=null,new N({reasonCode:w.errorCode||"user_activity_unknown",stage:a,action:c});try{const u=await l(s);return await o(a,`${c}:complete`),u}finally{const u=r;if(r=null,u){const d=await e.request("automation.lease.release",{leaseId:u},n).catch(f=>({ok:!1,errorCode:"automation_lease_release_failed",errorSummary:f instanceof Error?f.message:String(f)}));i({phase:"lease-release",stage:a,action:c,ok:d.ok===!0,...d.ok?{}:{reasonCode:d.errorCode||"automation_lease_release_failed"}})}}}}}function dt(e){return e==="windows.ensureReady"||e==="windows.focus"||e==="wechat.searchConversation"?{stage:"open_conversation"}:e==="keyboard.shortcut"||e==="mouse.click"||e==="mouse.rightClick"||e==="mouse.scroll"||e==="clipboard.snapshot"||e==="clipboard.readAttachment"||e==="menu.pickItem"?{stage:"dangerous_action"}:null}function ae(e){return e==="recent_mouse_activity"||e==="recent_mouse_click"||e==="recent_scroll_activity"||e==="recent_keyboard_activity"||e==="frontmost_app_changed"?e:"user_activity_unknown"}function lt(e){return typeof e=="number"&&Number.isFinite(e)&&e>=0?e:Xe}function se(e){return e.observePipeline==="edge-structure"?!0:e.observePipeline==="server-observe"?!1:!!e.api.structureWindow}async function K(e){const t=await Ie(e),n=await e.api.observe(e.runtime,e.binding,{screenshots:[{captureIndex:0,mimeType:t.mimeType,dataBase64:t.dataBase64,width:t.width,height:t.height}],edgeOcrBlocks:e.ocr.blocks??[],visibleConversationFingerprints:e.ocr.visibleConversationFingerprints??[],localLedgerTailAnchors:e.localLedgerTailAnchors??[],traceId:e.traceId}),r=await we({helper:e.helper,runtime:e.runtime,binding:e.binding,messages:n.observedMessages??[],workDir:e.workDir,attachmentsDir:e.attachmentsDir,window:e.window,screenshot:t,windowId:e.window.windowId,traceId:e.traceId,mediaAnchorText:e.mediaAnchorText});return Be(r,Te({ocr:e.ocr,screenshot:t,binding:e.binding,anchors:e.localOutboundEchoAnchors??[]}))}async function ce(e){if(!e.api.structureWindow)throw new Error("wechat_structure_window_unavailable");const t=await Ie(e);let n;try{n=await e.api.structureWindow(e.runtime,e.binding,{screenshots:[{captureIndex:0,mimeType:t.mimeType,dataBase64:t.dataBase64,width:t.width,height:t.height}],edgeOcrBlocks:e.ocr.blocks??[],visibleConversationFingerprints:e.ocr.visibleConversationFingerprints??[],localLedgerTailAnchors:e.localLedgerTailAnchors??[],traceId:e.traceId})}catch(c){if(ut(c))return K(e);throw c}const r=pt(n.structuredMessages??n.observedMessages??[]),i=await wt({...e,messages:r,screenshot:t}),o=await we({helper:e.helper,runtime:e.runtime,binding:e.binding,messages:i.messages,workDir:e.workDir,attachmentsDir:e.attachmentsDir,window:e.window,screenshot:t,windowId:e.window.windowId,traceId:e.traceId,mediaAnchorText:e.mediaAnchorText}),s=Be(o,Te({ocr:e.ocr,screenshot:t,binding:e.binding,anchors:e.localOutboundEchoAnchors??[]}));return e.localLedger?De({ledger:e.localLedger,bindingId:e.binding.bindingId,observedMessages:s,baselineOnly:e.baselineOnly,vectorReferences:i.vectorReferences}).newMessages:s}function ut(e){return e instanceof Error&&/\bedge_runtime_disabled\b/.test(e.message)}async function wt(e){const t=ht(e.messages);if(!t.length)return{messages:e.messages,vectorReferences:[]};const n=e.api.embedVisual?await mt({...e,seedBlocks:t}):t.filter(s=>h(s.vectorBase64)||h(s.signature));if(!n.length)return{messages:e.messages,vectorReferences:[]};const r=e.vectorStorePath??Le(e.workDir||process.cwd(),e.runtime.runtimeId),i=Oe(r,e.runtime.runtimeId),o=$e({store:i,bindingId:e.binding.bindingId,blocks:n});return o.length?(Ke(r,i),{messages:yt(e.messages,o),vectorReferences:o}):{messages:e.messages,vectorReferences:[]}}function ht(e){const t=[];for(const n of e)for(const[r,i]of(n.visualBlocks??[]).entries()){const o=h(i.blockId)||`${n.stableMessageKey}:visual-${r}`,s=h(i.blockKind)||n.kind||"visual";t.push({...i,stableMessageKey:n.stableMessageKey,blockId:o,blockKind:s,...i.bbox!==void 0?{bbox:i.bbox}:n.bbox!==void 0?{bbox:n.bbox}:{}})}return t}async function ft(e){if(!e.api.embedVisual)return[];const t=await e.api.embedVisual(e.runtime,e.binding,{screenshots:[{captureIndex:0,mimeType:e.screenshot.mimeType,dataBase64:e.screenshot.dataBase64,width:e.screenshot.width,height:e.screenshot.height}],visualBlocks:e.seedBlocks,traceId:e.traceId});return gt(t.visualBlocks??t.embeddedBlocks??[],e.seedBlocks)}async function mt(e){try{return await ft(e)}catch(t){if(bt(t))throw t;return[]}}function bt(e){return!e||typeof e!="object"?!1:e.reasonCode==="insufficient_credits"?!0:e instanceof Error&&/\binsufficient_credits\b/.test(e.message)}function gt(e,t){return e.map((n,r)=>{const i=t[r];return{...n,stableMessageKey:h(n.stableMessageKey)||h(i?.stableMessageKey),blockId:h(n.blockId)||h(i?.blockId),blockKind:h(n.blockKind)||h(i?.blockKind)||"visual",...n.bbox!==void 0?{bbox:n.bbox}:i?.bbox!==void 0?{bbox:i.bbox}:{}}}).filter(n=>h(n.stableMessageKey)&&h(n.blockId))}function yt(e,t){const n=new Map;for(const r of t){const i=n.get(r.stableMessageKey)??[];i.push(r),n.set(r.stableMessageKey,i)}return e.map(r=>{const i=n.get(r.stableMessageKey);return i?.length?{...r,visualBlocks:xt(r,i)}:r})}function xt(e,t){const r=(e.visualBlocks??[]).map((o,s)=>{const a=h(o.blockId)||`${e.stableMessageKey}:visual-${s}`,c=t.find(l=>l.blockId===a);return c?de(c,o):le(o)}),i=new Set(r.map(o=>o.blockId));for(const o of t)i.has(o.blockId)||r.push(de(o));return r}function de(e,t){return{...t?le(t):{},stableMessageKey:e.stableMessageKey,blockId:e.blockId,blockKind:e.blockKind,vectorStoreKey:e.vectorStoreKey,model:e.model,...e.modelVersion!==void 0?{modelVersion:e.modelVersion}:{},dims:e.dims,...e.signature?{signature:e.signature}:{},...e.bbox!==void 0?{bbox:e.bbox}:{}}}function le(e){const{vectorBase64:t,...n}=e;return n}function pt(e){return e.map((t,n)=>vt(t,n)).filter(t=>t!=null)}function vt(e,t){if(!b(e))return null;const n=ze(e.kind),r=_t(e.senderRole),i=g(e.normalizedText),o=g(e.anchorText)??i??g(e.textExcerpt),s=h(e.stableMessageKey)||Ct({message:e,index:t,senderRole:r,kind:n,anchorText:o});return{stableMessageKey:s,senderRole:r,...g(e.senderName)!==null?{senderName:g(e.senderName)}:{},kind:n,...i!==null?{normalizedText:i}:{},...o!==null?{anchorText:o}:{},...I(e,"anchorMetadata")?{anchorMetadata:e.anchorMetadata}:{},...I(e,"neighborContext")?{neighborContext:e.neighborContext}:{},...g(e.textExcerpt)!==null?{textExcerpt:g(e.textExcerpt)}:{},...I(e,"bbox")?{bbox:e.bbox}:{},...I(e,"mediaMetadata")?{mediaMetadata:It(e.mediaMetadata)}:{},...typeof e.isBaseline=="boolean"?{isBaseline:e.isBaseline}:{},...g(e.deliveryStatus)!==null?{deliveryStatus:g(e.deliveryStatus)??void 0}:{},...g(e.observedAt)!==null?{observedAt:g(e.observedAt)??void 0}:{},...Array.isArray(e.visualBlocks)?{visualBlocks:e.visualBlocks}:{},...o?{}:{anchorText:s}}}function Ct(e){const t=b(e.message.bbox)?JSON.stringify(e.message.bbox):"";return`structured-window:${[e.index,e.senderRole,e.kind,e.anchorText||h(e.message.textExcerpt)||"",t].join(":").slice(0,180)}`}function It(e){if(!b(e))return e;const t={...e};return I(t,"availability")&&(t.availability=X(t.availability)),b(t.attachment)&&(t.attachment=ue(t.attachment)),Array.isArray(t.attachments)&&(t.attachments=t.attachments.map(n=>b(n)?ue(n):n)),t}function ue(e){const t={...e};return I(t,"availability")&&(t.availability=X(t.availability)),t}function _t(e){return e==="self"||e==="contact"||e==="system"||e==="unknown"?e:"unknown"}async function we(e){if(e.binding.downloadMedia===!1)return e.messages;const t=e.messages.map((o,s)=>Et(o,e.screenshot,e.window,Bt(e.messages,s))).filter((o,s)=>kt(e.messages,s,e.mediaAnchorText)).filter(o=>o!=null);if(!t.length)return e.messages;const n=e.attachmentsDir??Fe(e.workDir||process.cwd(),e.runtime.runtimeId,e.binding.bindingId),r=await Ae({helper:e.helper,candidates:t,attachmentsDir:n,screenshot:e.screenshot,windowId:e.windowId,window:e.window,traceId:e.traceId,platform:e.runtime.policy.platform,stabilityCheck:async()=>{const o=await M({helper:e.helper,window:e.window,binding:e.binding,traceId:e.traceId});return o.ok?{ok:!0}:{ok:!1,reasonCode:o.reason}}});if(!r.length)return e.messages;const i=new Map(r.map(o=>[o.messageKey,o]));return e.messages.map(o=>{const s=i.get(o.stableMessageKey);return s?{...o,mediaMetadata:Pt(o.mediaMetadata,s.attachment,s.reasonCode,s.attemptReasonCodes,s.resolveTrace)}:o})}function kt(e,t,n){const r=fe(n);if(!r)return!0;const i=St(r),o=e.map((s,a)=>fe(Tt(s)).includes(r)?a:-1).filter(s=>s>=0);if(!o.length)return t===Mt(e,i);for(const s of o)if(t>=s&&t<=s+8||t===s-1&&!e.slice(s+1,Math.min(e.length,s+9)).some(he))return!0;return!1}function St(e){return e.includes("video")||e.includes("vide0")?"video":e.includes("image")||e.includes("photo")||e.includes("ph0t0")||e.includes("picture")?"image":e.includes("file")||e.includes("document")?"file":null}function Mt(e,t=null){for(let n=e.length-1;n>=0;n-=1)if(he(e[n]))return Nt(e[n],t)?n:-1;return-1}function he(e){if(!e)return!1;const t=String(e.kind||"").toLowerCase(),n=b(e.mediaMetadata)?e.mediaMetadata:{},r=b(n.attachment)?n.attachment:{};return me(t,n,r)}function Nt(e,t){if(!e||!t)return!0;const n=String(e.kind||"").toLowerCase(),r=b(e.mediaMetadata)?e.mediaMetadata:{},i=b(r.attachment)?r.attachment:{},o=String(i.type||"").toLowerCase(),s=String(i.mimeType||"").toLowerCase(),a=String(i.name||i.localPath||e.anchorText||e.textExcerpt||"").toLowerCase();return t==="video"?n.includes("video")||o==="video"||s.startsWith("video/")||/\.(mp4|mov|m4v|avi|mkv|webm)$/i.test(a):t==="image"?/image|photo|picture/.test(n)||o==="image"||s.startsWith("image/")||/\.(png|jpe?g|gif|webp|heic|tiff?|bmp)$/i.test(a):/file|document/.test(n)||o==="file"||!/image|photo|picture|video/.test(n)&&o!=="image"&&o!=="video"&&!s.startsWith("image/")&&!s.startsWith("video/")}function Tt(e){if(!e)return"";const t=e;return[e.normalizedText,e.anchorText,e.textExcerpt,t.text].map(n=>String(n||"")).join(`
3
3
  `)}function fe(e){return String(e||"").normalize("NFKC").replace(/[o]/giu,"0").replace(/[^\p{L}\p{N}]/gu,"").toLowerCase()}function Bt(e,t){return[e[t-1],e[t],e[t+1]].map(r=>r&&(r.normalizedText||r.anchorText||r.textExcerpt)||"").map(r=>String(r||"").trim()).filter(Boolean).join(`
4
- `)||null}function Et(e,t,n,r){const i=String(e.kind||"").toLowerCase(),o=b(e.mediaMetadata)?e.mediaMetadata:{},s=b(o.attachment)?o.attachment:{},a=h(s.availability)||h(o.availability),c=h(s.localPath)||h(o.localPath);if(a==="edge-local"&&c||!me(i,o,s))return null;const u=R(e.bbox)??R(o.bbox)??R(o.downloadActionBbox),w=R(o.downloadActionBbox),l=be(u,t,n),d=Rt(u,t,n),f=be(w,t,n);return{messageKey:e.stableMessageKey,kind:h(s.type)||h(s.kind)||h(o.messageType)||i,fileName:h(s.name)||h(s.fileName)||h(o.fileName)||null,mimeType:h(s.mimeType)||h(o.mimeType)||null,size:M(s.size)??M(o.size),mediaStatus:Wt(o,s),observedAt:e.observedAt??null,contextText:r??e.normalizedText??e.anchorText??e.textExcerpt??null,...l?{bbox:l}:{},...d?{screenshotBbox:d}:{},...f?{downloadActionBbox:f}:{}}}function Pt(e,t,n,r,i){const o=b(e)?{...e}:{};return{...o,availability:b(t)?t.availability:o.availability,mediaStatus:b(t)&&t.availability==="edge-local"?"downloaded":o.mediaStatus,attachment:t,edgeResolveReasonCode:n,...r?.length?{edgeResolveAttempts:r}:{},...i?{edgeResolveTrace:i}:{}}}function Wt(e,t){const n=h(e.mediaStatus);if(n==="not_downloaded")return"not_downloaded";if(n==="downloaded"||n==="available")return"available";const r=h(t.availability)||h(e.availability);return r==="pending-download"?"not_downloaded":r==="metadata-only"?"metadata_only":n||null}function me(e,t,n){const r=`${e} ${h(t.messageType)} ${h(n.type)} ${h(n.kind)}`.toLowerCase();return/image|photo|video|file|document/.test(r)}function R(e){if(!b(e))return null;const t=M(e.x),n=M(e.y),r=M(e.width),i=M(e.height);return t==null||n==null||r==null||i==null?null:{x:t,y:n,width:r,height:i,...typeof e.coordinateSpace=="string"?{coordinateSpace:e.coordinateSpace}:{}}}function be(e,t,n){if(!e)return null;if(e.coordinateSpace==="screen")return e;const r=L(e,t,n);if(!r)return e;const i=n.bounds;if(!i?.width||!i?.height)return{...e,x:r.x-e.width/2,y:r.y-e.height/2,coordinateSpace:"screen"};const o=i.width/t.width,s=i.height/t.height,a=r.x,c=r.y;return{x:a-e.width*o/2,y:c-e.height*s/2,width:e.width*o,height:e.height*s,coordinateSpace:"screen"}}function Rt(e,t,n){if(!e)return null;if(e.coordinateSpace!=="screen")return{...e,coordinateSpace:"screenshotPixel"};const r=n.bounds;if(!r?.width||!r?.height)return null;const i=t.width/r.width,o=t.height/r.height;return{x:(e.x-r.x)*i,y:(e.y-r.y)*o,width:e.width*i,height:e.height*o,coordinateSpace:"screenshotPixel"}}function b(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}function S(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function h(e){return typeof e=="string"?e.trim():""}function g(e){return h(e)||null}function M(e){const t=Number(e);return Number.isFinite(t)?t:null}async function En(e,t){await ye(e,t)}async function Ft(e,t){await ge(e,t)}async function ge(e,t){const n=await e.request("permissions.check",{},t);m(n,"permissions.check");const r=n.result??{},i=await At(e,r,t);if(i==="screen-recording")throw new Error("permission_screen_recording_missing");if(i==="accessibility")throw new Error("permission_accessibility_missing");if(i==="input-monitoring")throw new Error("permission_input_monitoring_missing");if(r.automation===!1)throw new Error("permission_automation_missing");return r}async function ye(e,t,n={}){const r=await ge(e,t);if(r.wechatRunning===!1)throw new Error("wechat_not_running");if(Kt(r))throw new Error("windows_visible_desktop_unavailable");if(r.dpiMappingAvailable===!1||r.displayTopologySupported===!1)throw new Error("dpi_mapping_failed");if(r.wechatMainWindowResponsive===!1)throw new Error("wechat_window_unresponsive");if(r.wechatWindowAvailable===!1&&String(r.platform??"").toLowerCase()!=="win32"&&n.allowUnavailableWindow!==!0)throw new Error("wechat_window_unavailable");return r}async function At(e,t,n){const r=Ot(t);if(!r||process.platform!=="darwin")return r;const i=Lt(r);try{await e.request(i,{},n)}catch{}return r}function Ot(e){return e.screenRecording===!1?"screen-recording":e.accessibility===!1?"accessibility":e.inputMonitoring===!1?"input-monitoring":null}function Lt(e){return e==="screen-recording"?"permissions.requestScreenRecording":e==="accessibility"?"permissions.requestAccessibility":"permissions.requestInputMonitoring"}async function Dt(e,t,n={}){const i=(n.foreground??"required")!=="background",o=await ye(e,t,{allowUnavailableWindow:i});if(String(o.platform??"").toLowerCase()==="win32"){const c=await e.request("windows.ensureReady",{activate:i,allowRecovery:!1,allowLaunch:!1},t);m(c,"windows.ensureReady");const u=c.result;if(!u?.windowId)throw new Error("helper_invalid_response: windows.ensureReady missing WeChat window data");return u}const s=await e.request("windows.ensureReady",{restore:i,focus:i},t);m(s,"windows.ensureReady");const a=s.result;if(!a?.windowId)throw new Error("helper_invalid_response: windows.ensureReady missing WeChat window data");return a}function Kt(e){return e.windowsVisibleDesktopAvailable===!1||e.rdpVisibleDesktopAvailable===!1||e.desktopSessionVisible===!1||e.screenLocked===!0||e.rdpDisconnected===!0}function xe(e){const t=e.filter(q),n=t.filter(i=>i.visible!==!1&&i.minimized!==!0);return(n.length?n:t)[0]??null}async function Pn(e,t){let n=await e.request("windows.list",{},t);m(n,"windows.list");let r=n.result?.windows??[],i=xe(r);if(!i)throw new Error("wechat_window_not_found");const o=await e.request("windows.focus",{windowId:i.windowId},t);return m(o,"windows.focus"),await pe(e,i,t)}async function ve(e,t,n){if(!t.windowId)throw new Error("wechat_window_not_found");const r=await e.request("windows.focus",{windowId:t.windowId},n);return m(r,"windows.focus"),t}async function $t(e,t){const n=await e.request("windows.list",{},t);m(n,"windows.list");const r=n.result?.windows??[],i=xe(r);if(!i)throw new Error("wechat_window_not_found");const o=await e.request("windows.focus",{windowId:i.windowId},t);return m(o,"windows.focus"),await pe(e,i,t)}async function pe(e,t,n){let r;try{r=await e.request("windows.list",{},n)}catch{return t}if(!r.ok)return t;const i=(r.result?.windows??[]).filter(q);return i.find(o=>o.windowId===t.windowId)??i[0]??t}async function $(e){const{capture:t,ocr:n}=e.observation??await p(e),r=B({capture:t,ocr:n,window:e.window});if(r)return{opened:!1,reason:r};const i=qe({visibleItems:Zt(n,t,e.allowOcrTitleRows!==!1),targets:[{bindingId:e.binding.bindingId,conversationDisplayName:e.binding.conversationDisplayName}]})[0];if(!i)return{opened:!1,reason:"conversation_not_visible"};const o=bn(i.item.bbox,t,e.window);if(!o)return{opened:!1,reason:"conversation_bbox_missing"};const s=await e.helper.request("mouse.click",{x:o.x,y:o.y,coordinateSpace:"screen",windowId:e.window.windowId},e.traceId);return m(s,"mouse.click"),{opened:!0,reason:i.reason}}async function z(e){let t={opened:!1,reason:"conversation_not_opened"};for(let r=0;r<He;r+=1){if(t=await zt(e),t.opened||t.reason!=="conversation_title_not_confirmed")return t;await C(e,"open-retry",`retry:${r}`)}const n=await Ce(e);return n.opened?{opened:!0,reason:`${n.reason}:final-visible-recovery`}:t}async function zt(e){const t=await p(e),n=B({capture:t.capture,ocr:t.ocr,window:e.window});if(n)return{opened:!1,reason:n};let r=!1;if(O(t.ocr,t.capture,e.binding.conversationDisplayName)){e.settleToBottom===!0&&await T({helper:e.helper,window:e.window,traceId:e.traceId,observation:t});const l=await Vt(e,t);return l.ok?{opened:!0,reason:"current-conversation-title-confirmed"}:{opened:!1,reason:l.reason}}if(!r){const l=await $({...e,observation:t});if(l.opened){if(await C(e,"open-click-settle","visible-initial"),(await _(e)).ok)return{opened:!0,reason:l.reason};const f=await Ce(e);if(f.opened)return f}}const i=await Ut(e,t.capture);if(!i.ok)return{opened:!1,reason:i.reason};const o=await e.helper.request("wechat.searchConversation",{conversationName:e.binding.conversationDisplayName,windowId:e.window.windowId,waitMs:e.searchDelayMs??A(e,"search-input-settle","search-input"),searchPoint:i.point},e.traceId);m(o,"wechat.searchConversation");const s=await qt(e);if(s.opened){await C(e,"open-click-settle","search-result-click");const l=await _(e);return l.ok?(e.settleToBottom===!0&&await T({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:s.reason}):{opened:!1,reason:l.reason}}let a=s.observation;const c=y(e.binding.conversationDisplayName)&&s.reason==="search_result_not_visible";if(!y(e.binding.conversationDisplayName)){await Jt(e.helper,e.traceId),await C(e,"open-click-settle","search-return");const l=await _(e);if(l.ok)return e.settleToBottom===!0&&await T({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:l.reason};const d=await e.helper.request("wechat.searchConversation",{conversationName:e.binding.conversationDisplayName,windowId:e.window.windowId,waitMs:e.searchDelayMs??A(e,"search-input-settle","search-input-retry-after-return"),searchPoint:i.point},e.traceId);m(d,"wechat.searchConversation"),a=void 0}const u=await $({...e,...a?{observation:a}:{},allowOcrTitleRows:!c});if(!u.opened)return u;await C(e,"open-click-settle","visible-final");const w=await _(e);return w.ok?(e.settleToBottom===!0&&await T({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:u.reason}):{opened:!1,reason:w.reason}}async function Vt(e,t){const n=t??await p(e);if(!rn(n.ocr))return{ok:!0};const r=await e.helper.request("keyboard.shortcut",{key:"escape",modifiers:[]},e.traceId);m(r,"keyboard.shortcut"),await C(e,"open-click-settle","escape-after-title-confirmed");const i=await _(e);return i.ok?{ok:!0}:{ok:!1,reason:`search_overlay_dismiss_failed:${i.reason}`}}async function Ce(e){const t=await $(e);if(!t.opened)return t;await C(e,"open-click-settle","visible-retry");const n=await _(e);return n.ok?(e.settleToBottom===!0&&await T({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:`${t.reason}:retry-visible-title-confirmed`}):{opened:!1,reason:n.reason}}async function qt(e){let t;const n=e.searchDelayMs===0?0:A(e,"search-result-probe","search-result-probe"),r=y(e.binding.conversationDisplayName)?Ye:1;for(let i=0;i<r;i+=1){i>0&&n>0&&await j(n);const o=await jt(e);t=o;const{capture:s,ocr:a}=o,c=B({capture:s,ocr:a,window:e.window});if(c)return{opened:!1,reason:c,observation:o};const u=tn(a,s,e.binding.conversationDisplayName);if(!u){if(nn(a)||on(a,e.binding.conversationDisplayName))break;continue}const w=L(u.bbox,s,Gt(s,e.window));if(!w)return{opened:!1,reason:"search_result_bbox_missing",observation:o};const l=await e.helper.request("mouse.click",{x:w.x,y:w.y,coordinateSpace:"screen",windowId:e.window.windowId},e.traceId);return m(l,"mouse.click"),{opened:!0,reason:u.reason,observation:o}}return{opened:!1,reason:"search_result_not_visible",observation:t}}async function jt(e){if(e.window.bounds&&y(e.binding.conversationDisplayName)){const t=await e.helper.request("screen.capture",{bounds:e.window.bounds},e.traceId);if(t.ok&&t.result?.dataBase64&&t.result.width&&t.result.height){const n={...t.result,windowId:e.window.windowId,bounds:t.result.bounds??e.window.bounds},r=await Ie(e.helper,n,e.traceId);return{capture:n,ocr:r}}t.errorCode&&t.errorCode!=="helper_unknown_command"&&t.errorCode!=="helper_command_unsupported"&&m(t,"screen.capture")}return p(e)}function Gt(e,t){return e.bounds?{...t,bounds:e.bounds}:t}async function p(e){let{capture:t,ocr:n}=await V(e.helper,e.window.windowId,e.traceId,e.window.bounds);return await Ht({helper:e.helper,window:e.window,capture:t,ocr:n,traceId:e.traceId})&&(await J("preview-dismiss-settle",`${e.traceId||""}:${e.window.windowId}:preview-dismiss`),{capture:t,ocr:n}=await V(e.helper,e.window.windowId,e.traceId,e.window.bounds)),{capture:t,ocr:n}}async function Ht(e){const t=Yt(e.ocr,e.capture,e.window);if(!t)return!1;for(const n of[async()=>e.helper.request("keyboard.shortcut",{key:"escape",modifiers:[]},e.traceId),async()=>e.helper.request("mouse.click",{x:t.x,y:t.y,coordinateSpace:"screen",windowId:e.window.windowId},e.traceId)])if((await n()).ok)return!0;return!1}function Yt(e,t,n){const r=e.blocks??[],i=r.find(c=>{const u=String(c.text||""),w=c.bbox;if(!w)return!1;const l=Number(w.x),d=Number(w.y);return Number.isFinite(l)&&Number.isFinite(d)&&d>t.height*.06&&d<t.height*.22&&l>t.width*.3&&/查看原视频|查看原图|原视频|原图|视频|图片|图像/.test(u)});if(!i)return null;const o=i.bbox,s=Number(o?.y);if(!Number.isFinite(s))return null;const a=r.filter(c=>{const u=String(c.text||"").trim(),w=c.bbox;if(!w||u!=="\xD7")return!1;const l=Number(w.x),d=Number(w.y);return Number.isFinite(l)&&Number.isFinite(d)&&l>t.width*.55&&l<t.width*.9&&Math.abs(d-s)<t.height*.08}).sort((c,u)=>Number(u.bbox?.x??0)-Number(c.bbox?.x??0))[0];return L(a?.bbox,t,n)}async function Ut(e,t){if(!e.runtime||!e.api?.classifyWindow)return{ok:!1,reason:"wechat_search_input_detector_unavailable"};try{return{ok:!0,point:await Qe({runtime:e.runtime,binding:e.binding,helper:e.helper,api:e.api,workDir:e.workDir,traceId:e.traceId,window:e.window,...t.dataBase64?{screenshot:t}:{}})}}catch(n){return{ok:!1,reason:n instanceof Error?n.message:"wechat_search_input_not_found"}}}async function F(e,t,n,r){const i=await e.request("windows.capture",{windowId:t,scope:"full-window",...r?{bounds:r}:{}},n);m(i,"windows.capture");const o=i.result;if(!o?.dataBase64||!o.mimeType||!o.width||!o.height)throw new Error("helper_invalid_response: windows.capture missing screenshot data");return o}async function _e(e){return e.capture.dataBase64?e.capture:F(e.helper,e.window.windowId,e.traceId,e.window.bounds)}async function V(e,t,n,r){const i=await e.request("windows.captureAndOcr",{windowId:t,scope:"full-window",...r?{bounds:r}:{}},n);if(i.ok&&i.result?.capture&&i.result?.ocr)return{capture:i.result.capture,ocr:i.result.ocr};!i.ok&&i.errorCode&&i.errorCode!=="helper_unknown_command"&&i.errorCode!=="helper_command_unsupported"&&m(i,"windows.captureAndOcr");const o=await F(e,t,n,r),s=await Ie(e,o,n);return{capture:o,ocr:s}}async function Ie(e,t,n){const r=await e.request("ocr.recognize",{mimeType:t.mimeType,dataBase64:t.dataBase64,width:t.width,height:t.height},n);return m(r,"ocr.recognize"),r.result??{}}function m(e,t){if(!e.ok)throw new Error(`${e.errorCode||"helper_command_failed"}: ${e.errorSummary||t}`)}function q(e){const t=String(e.appName||"").toLowerCase();return t==="wechat"||t==="weixin"||t==="\u5FAE\u4FE1"}function j(e){return new Promise(t=>setTimeout(t,e))}function A(e,t,n){return e.searchDelayMs===0?0:Ve(t,Xt(e,n))}async function C(e,t,n){const r=A(e,t,n);r>0&&await j(r)}function Xt(e,t){return`${e.traceId||""}:${e.binding.conversationDisplayName}:${t}`}async function Jt(e,t){const n=await e.request("keyboard.shortcut",{key:"return",modifiers:[]},t);m(n,"keyboard.shortcut")}async function T(e){if(!e.window.bounds)return;const t=e.observation,n=gn(t?.ocr,t?.capture,e.window);if(n){const r=await e.helper.request("mouse.click",{x:n.x,y:n.y,coordinateSpace:"screen",windowId:e.window.windowId},e.traceId);m(r,"mouse.click"),await J("jump-latest-settle",`${e.traceId||""}:${e.window.windowId}:jump-latest`)}}async function _(e){const{capture:t,ocr:n}=await V(e.helper,e.window.windowId,e.traceId,e.window.bounds),r=B({capture:t,ocr:n,window:e.window});return r?{ok:!1,reason:r}:O(n,t,e.binding.conversationDisplayName)?{ok:!0,reason:"target-conversation-title-confirmed"}:{ok:!1,reason:"conversation_title_not_confirmed"}}function B(e){const n=(e.ocr.blocks??[]).map(r=>sn(r.text)).filter(Boolean).join("");return/(扫码登录|仅传输文件|重新登录|为了安全|安全验证|登录微信|微信登录|该账号已登录|帐号已登录|账号已登录|当前账号已登录|当前帐号已登录|已登录|进入微信|進入微信)/u.test(n)?Ue:null}function Zt(e,t,n=!0){const r=n?Qt(e.blocks??[],t):[];return r.length?r:e.visibleConversationFingerprints??[]}function Qt(e,t){const n=e.map(i=>ke(i)).filter(i=>!!i).filter(i=>i.text.length>=2).filter(i=>i.x>t.width*.08&&i.x<t.width*.38).filter(i=>i.y>t.height*.06&&i.y<t.height*.96).filter(i=>!Se(i.text)),r=new Set;return n.filter(i=>!en(i,n,t)).sort((i,o)=>i.y-o.y||i.x-o.x).map(i=>{const o=i.text.trim();return{title:o,bbox:{x:Math.max(0,i.x-24),y:Math.max(0,i.y-Math.max(24,i.height)),width:Math.max(120,t.width*.3),height:Math.max(48,i.height+40),coordinateSpace:"screenshotPixel"},fingerprint:o.toLowerCase()}}).filter(i=>{const o=P(i.title);return!o||r.has(o)?!1:(r.add(o),!0)})}function ke(e){const t=e.bbox,n=String(e.text||"").normalize("NFKC").trim(),r=Number(t?.x),i=Number(t?.y),o=Number(t?.width),s=Number(t?.height);return!n||![r,i,o,s].every(Number.isFinite)?null:{text:n,x:r,y:i,width:o,height:s}}function en(e,t,n){const r=Math.max(36,Math.min(64,n.height*.058));return t.some(i=>i!==e&&i.y<e.y&&e.y-i.y<=r&&Math.abs(i.x-e.x)<=16&&i.height>=e.height*.85)}function Se(e){const t=x(e);return/^(搜索|微信|通讯录|收藏|设置|文件传输助手|search)$/iu.test(t)||E(t)||an(t)||/^包含[::]?/u.test(t)||/搜索网络结果/u.test(t)}function tn(e,t,n){const r=(e.blocks??[]).map(a=>ke(a)).filter(a=>!!a),i=r.filter(a=>E(x(a.text))).sort((a,c)=>a.y-c.y||a.x-c.x),o=["\u7FA4\u804A","GroupChats","\u8054\u7CFB\u4EBA","Contacts"];for(const a of o){const c=i.findIndex(d=>x(d.text)===a);if(c<0)continue;const u=i[c],w=i[c+1]?.y??Number.POSITIVE_INFINITY,l=r.filter(d=>d.y>u.y&&d.y<w).filter(d=>d.x<t.width*.7).filter(d=>d.text.length>=2).filter(d=>!E(x(d.text))).filter(d=>!Ne(d,r)).filter(d=>H(d.text,n)).sort((d,f)=>d.y-f.y||d.x-f.x)[0];if(l)return{bbox:Me(l,t),reason:"search-result-title"}}const s=r.filter(a=>a.y>t.height*.12&&a.y<t.height*.82).filter(a=>a.x>t.width*.08&&a.x<t.width*.45).filter(a=>a.text.length>=2).filter(a=>!Se(a.text)).filter(a=>!Ne(a,r)).filter(a=>H(a.text,n)).sort((a,c)=>a.y-c.y||a.x-c.x)[0];return y(n)?null:s?{bbox:Me(s,t),reason:"search-result-title"}:null}function Me(e,t){return{x:Math.max(0,e.x-24),y:Math.max(0,e.y-12),width:Math.max(e.width+t.width*.18,160),height:Math.max(e.height+28,44),coordinateSpace:"screenshotPixel"}}function E(e){return/^(群聊|联系人|聊天记录|公众号|小程序|GroupChats|Contacts|ChatHistory|OfficialAccounts|MiniPrograms)$/u.test(e)}function nn(e){return(e.blocks??[]).some(t=>E(x(t.text)))}function rn(e){return(e.blocks??[]).some(t=>{const n=x(t.text);return E(n)||n==="\u641C\u7D22\u7F51\u7EDC\u7ED3\u679C"})}function on(e,t){return(e.visibleConversationFingerprints??[]).some(n=>H(n.title,t))}function x(e){return String(e||"").normalize("NFKC").replace(/[^\p{L}\p{N}_-]/gu,"").trim()}function Ne(e,t){return/^包含[::]?/u.test(e.text.trim())?!0:t.some(n=>n!==e&&Math.abs(n.y-e.y)<=Math.max(18,e.height)&&n.x<e.x&&/^包含[::]?/u.test(n.text.trim()))}function an(e){const t=String(e||"").trim();return!!(/^(星期[一二三四五六日天]|周[一二三四五六日天])\s*\d{1,2}:\d{2}$/u.test(t)||/^\d{1,2}:\d{2}(?::\d{2})?$/u.test(t)||/^\d{4}[-/.年]\d{1,2}(?:[-/.月]\d{1,2}日?)?$/u.test(t)||/^\d{1,2}[-/.月]\d{1,2}日?$/u.test(t))}function sn(e){return String(e||"").normalize("NFKC").replace(/\s+/g,"").trim()}function O(e,t,n){const r=y(n)?P(n):D(n);return r?(e.blocks??[]).some(i=>{const o=y(n)?P(i.text):D(i.text);if(!o||!Ee(o,r))return!1;const s=i.bbox;if(!s)return!1;const a=Number(s.x),c=Number(s.y);if(!Number.isFinite(a)||!Number.isFinite(c))return!1;const u=a>t.width*.32&&a<t.width*.78&&c<t.height*.16,w=a<t.width*.18&&c<t.height*.12;return u||w}):!1}function G(e){if(O(e.ocr,e.capture,e.binding.conversationDisplayName))return!1;const t=(e.ocr.blocks??[]).map(a=>String(a.text||"").normalize("NFKC").replace(/\s+/g," ").trim()).filter(Boolean),n=t.join(`
5
- `),i=[{pattern:/UTF-?8/iu,source:n},{pattern:/Unix\s*\(LF\)/iu,source:n},{pattern:/纯文本/u,source:n},{pattern:/\d+\s*个字符/u,source:n},{pattern:/\bH1\b/u,source:n},{pattern:/^查看$/u,source:t}].reduce((a,c)=>{const u=Array.isArray(c.source)?c.source.some(w=>c.pattern.test(w)):c.pattern.test(c.source);return a+(u?1:0)},0);if(i>=2)return!0;const o=(e.ocr.blocks??[]).filter(a=>Number(a.bbox?.y)<e.capture.height*.14).map(a=>String(a.text||"")).join(`
6
- `);return/\.(txt|md|rtf|docx?|pdf)\b/i.test(o)&&i>=1||/\.(png|jpe?g|gif|webp|bmp|heic|mp4|mov|m4v|avi|mkv|webm)\b/i.test(o)&&cn(e.capture,e.ocr)?!0:((e.ocr.visibleConversationFingerprints??[]).some(a=>{const c=y(e.binding.conversationDisplayName)?P(a.title):D(a.title),u=y(e.binding.conversationDisplayName)?P(e.binding.conversationDisplayName):D(e.binding.conversationDisplayName);return!!(c&&u&&Ee(c,u))}),!1)}function cn(e,t){const n=(t.blocks??[]).map(o=>String(o.text||"").normalize("NFKC").trim()).filter(Boolean),r=n.join(`
7
- `);if(/\b\d+\s*x\s*\d+\b/i.test(r)&&/\b\d+(\.\d+)?\s*(B|KB|MB|GB)\b/i.test(r)||/\b100%\b/.test(r)&&n.length<=20)return!0;const i=n.filter(o=>!/^(□|×|\+|…|\.\.\.|100%|\d+|发送)$/u.test(o));return e.width>600&&e.height>500&&i.length<=6}function Te(e){const t=e.anchors.map(o=>({...o,normalized:v(o.expectedEchoAnchor)})).filter(o=>o.normalized);if(!t.length)return[];const n=[],r=new Set,i=dn(e.ocr,e.screenshot).sort((o,s)=>v(s.text).length-v(o.text).length);for(const o of i){const s=v(o.text);if(!s)continue;const a=t.find(u=>ln(u.normalized,s));if(!a)continue;const c=`local-outbound-echo:${e.binding.bindingId}:${a.idempotencyKey||a.replyId||a.normalized}`;r.has(c)||(r.add(c),n.push({stableMessageKey:c,senderRole:"self",kind:"text",normalizedText:s,anchorText:s,textExcerpt:o.text.trim(),bbox:o.bbox??null,observedAt:new Date().toISOString(),deliveryStatus:"suppressed"}))}return n}function dn(e,t){const n=(e.blocks??[]).filter(i=>mn(i,t)&&v(i.text)).sort((i,o)=>I(i.bbox,"y")-I(o.bbox,"y")),r=n.map(i=>({text:String(i.text||"").trim(),bbox:i.bbox}));for(let i=0;i<n.length;i+=1){const o=[n[i]];for(let s=i+1;s<n.length&&o.length<5;s+=1){const a=n[s];if(!un(o[o.length-1],a,t))break;o.push(a),r.push({text:wn(o.map(c=>String(c.text||"").trim())),bbox:fn(o.map(c=>c.bbox))})}}return r}function ln(e,t){if(!e||!t)return!1;if(Z(e,t)>=.88)return!0;const n=e.replace(/\s+/g,""),r=t.replace(/\s+/g,"");return r.length>=8&&n.includes(r)}function un(e,t,n){const r=I(e.bbox,"x"),i=I(e.bbox,"y"),o=I(e.bbox,"height"),s=I(t.bbox,"x"),a=I(t.bbox,"y");if(![r,i,o,s,a].every(Number.isFinite)||r<n.width*.5||s<n.width*.5)return!1;const c=a-(i+o);return c>=-8&&c<=Math.max(64,n.height*.04)&&Math.abs(s-r)<=120}function wn(e){return e.reduce((t,n)=>t?n?`${t}${hn(t,n)?" ":""}${n}`:t:n,"")}function hn(e,t){return/[A-Za-z0-9]$/u.test(e)&&/^[A-Za-z0-9]/u.test(t)}function fn(e){const t=e.filter(c=>!!c);if(!t.length)return;const n=t.map(c=>Number(c.x)).filter(Number.isFinite),r=t.map(c=>Number(c.y)).filter(Number.isFinite),i=t.map(c=>Number(c.x)+Number(c.width)).filter(Number.isFinite),o=t.map(c=>Number(c.y)+Number(c.height)).filter(Number.isFinite);if(!n.length||!r.length||!i.length||!o.length)return t[0];const s=Math.min(...n),a=Math.min(...r);return{x:s,y:a,width:Math.max(...i)-s,height:Math.max(...o)-a,coordinateSpace:t.find(c=>c.coordinateSpace)?.coordinateSpace}}function I(e,t){const n=Number(e?.[t]);return Number.isFinite(n)?n:Number.NaN}function Be(e,t){if(!t.length)return e;const n=[...e];for(const r of t){const i=v(r.anchorText||r.normalizedText||r.textExcerpt);n.some(s=>{if(s.senderRole!=="self")return!1;const a=v(s.anchorText||s.normalizedText||s.textExcerpt);return a&&i&&Z(a,i)>=.9})||n.push(r)}return n}function mn(e,t){const n=e.bbox;if(!n)return!1;const r=Number(n.x),i=Number(n.y);return!Number.isFinite(r)||!Number.isFinite(i)?!1:r>t.width*.34&&r<t.width*.96&&i>t.height*.12&&i<t.height*.86}function bn(e,t,n){if(!e||typeof e!="object")return null;const r=e,i=Number(r.x),o=Number(r.y),s=Number(r.width),a=Number(r.height);if(![i,o,s,a].every(Number.isFinite))return null;const c=typeof r.coordinateSpace=="string"?r.coordinateSpace:void 0,u=t.width&&n.bounds?.width?t.width/n.bounds.width:1,w=t.height&&n.bounds?.height?t.height/n.bounds.height:u,l={x:i+Math.min(s*.35,110*u),y:o+Math.min(a-8*w,Math.max(18*w,a/2))};if(c==="screen"||!n.bounds)return l;const d=Math.max(0,(t.width-n.bounds.width*u)/2),f=Math.max(0,(t.height-n.bounds.height*w)/2);return{x:n.bounds.x+(l.x-d)/u,y:n.bounds.y+(l.y-f)/w}}function L(e,t,n){if(!e||typeof e!="object")return null;const r=e,i=Number(r.x),o=Number(r.y),s=Number(r.width),a=Number(r.height);if(![i,o,s,a].every(Number.isFinite))return null;const c=typeof r.coordinateSpace=="string"?r.coordinateSpace:void 0,u={x:i+s/2,y:o+a/2};if(c==="screen"||!n.bounds)return u;const w=t.width&&n.bounds.width?t.width/n.bounds.width:1,l=t.height&&n.bounds.height?t.height/n.bounds.height:w,d=Math.max(0,(t.width-n.bounds.width*w)/2),f=Math.max(0,(t.height-n.bounds.height*l)/2);return{x:n.bounds.x+(u.x-d)/w,y:n.bounds.y+(u.y-f)/l}}function gn(e,t,n){if(!e?.blocks?.length||!t)return null;const r=e.blocks.find(i=>{const o=String(i.text||"").trim(),s=i.bbox;if(!s||!o)return!1;const a=Number(s.x),c=Number(s.y);return!Number.isFinite(a)||!Number.isFinite(c)||!/跳转到最新消息|回到最新消息|jump\s+to\s+latest/i.test(o)?!1:a>t.width*.55&&c>t.height*.45&&c<t.height*.92});return L(r?.bbox,t,n)}function Ee(e,t){return y(t)?We(e,t):e===t||e.includes(t)||t.includes(e)}function H(e,t){if(y(t))return We(e,t);const n=Pe(x(e)),r=Pe(x(t));return!n||!r?!1:n===r?!0:n.replace(/\d+$/u,"")===r}function Pe(e){return e.toLowerCase().replace(/[l1]/g,"i")}function D(e){return String(e||"").normalize("NFKC").replace(/[((]\d+[))]/g,"").replace(/\s+/g,"").replace(/[l1]/gi,"i").toLowerCase().trim()}function P(e){return String(e||"").normalize("NFKC").replace(/[((]\d+[))]/g,"").replace(/\s+/g,"").trim()}function y(e){const t=String(e||"").trim();return/^[A-Z0-9_-]{1,6}$/.test(t)&&/[A-Z]/.test(t)}function We(e,t){const n=String(e||"").normalize("NFKC").replace(/[((]\d+[))]/g,"").replace(/\s+/g,"").trim(),r=String(t||"").normalize("NFKC").replace(/[((]\d+[))]/g,"").replace(/\s+/g,"").trim();return!!(n&&r&&(n===r||n.replace(/\d+$/u,"")===r))}export{N as WeChatChannelUserActivityAbort,V as captureAndRecognizeWeChatWindow,F as captureWeChatWindow,Bn as detectWeChatMessageInputPointByServerVision,Qe as detectWeChatSearchInputPointByServerVision,Ft as ensureHelperPermissionPreflight,En as ensureHelperPreflight,Dt as ensureWeChatWindowReady,$t as focusExistingWeChatWindow,ve as focusKnownWeChatWindow,Pn as focusWeChatWindow,Tn as observeWeChatChannelBindingViaHelper,z as openConversationBySearch,$ as openConversationInVisibleList,Ie as recognizeWeChatScreenshot,Ze as selectWeChatWindowForBinding};
4
+ `)||null}function Et(e,t,n,r){const i=String(e.kind||"").toLowerCase(),o=b(e.mediaMetadata)?e.mediaMetadata:{},s=b(o.attachment)?o.attachment:{},a=h(s.availability)||h(o.availability),c=h(s.localPath)||h(o.localPath);if(a==="edge-local"&&c||!me(i,o,s))return null;const l=W(e.bbox)??W(o.bbox)??W(o.downloadActionBbox),w=W(o.downloadActionBbox),u=be(l,t,n),d=Rt(l,t,n),f=be(w,t,n);return{messageKey:e.stableMessageKey,kind:h(s.type)||h(s.kind)||h(o.messageType)||i,fileName:h(s.name)||h(s.fileName)||h(o.fileName)||null,mimeType:h(s.mimeType)||h(o.mimeType)||null,size:_(s.size)??_(o.size),mediaStatus:Wt(o,s),observedAt:e.observedAt??null,contextText:r??e.normalizedText??e.anchorText??e.textExcerpt??null,...u?{bbox:u}:{},...d?{screenshotBbox:d}:{},...f?{downloadActionBbox:f}:{}}}function Pt(e,t,n,r,i){const o=b(e)?{...e}:{};return{...o,availability:b(t)?t.availability:o.availability,mediaStatus:b(t)&&t.availability==="edge-local"?"downloaded":o.mediaStatus,attachment:t,edgeResolveReasonCode:n,...r?.length?{edgeResolveAttempts:r}:{},...i?{edgeResolveTrace:i}:{}}}function Wt(e,t){const n=h(e.mediaStatus);if(n==="not_downloaded")return"not_downloaded";if(n==="downloaded"||n==="available")return"available";const r=h(t.availability)||h(e.availability);return r==="pending-download"?"not_downloaded":r==="metadata-only"?"metadata_only":n||null}function me(e,t,n){const r=`${e} ${h(t.messageType)} ${h(n.type)} ${h(n.kind)}`.toLowerCase();return/image|photo|video|file|document/.test(r)}function W(e){if(!b(e))return null;const t=_(e.x),n=_(e.y),r=_(e.width),i=_(e.height);return t==null||n==null||r==null||i==null?null:{x:t,y:n,width:r,height:i,...typeof e.coordinateSpace=="string"?{coordinateSpace:e.coordinateSpace}:{}}}function be(e,t,n){if(!e)return null;if(e.coordinateSpace==="screen")return e;const r=L(e,t,n);if(!r)return e;const i=n.bounds;if(!i?.width||!i?.height)return{...e,x:r.x-e.width/2,y:r.y-e.height/2,coordinateSpace:"screen"};const o=i.width/t.width,s=i.height/t.height,a=r.x,c=r.y;return{x:a-e.width*o/2,y:c-e.height*s/2,width:e.width*o,height:e.height*s,coordinateSpace:"screen"}}function Rt(e,t,n){if(!e)return null;if(e.coordinateSpace!=="screen")return{...e,coordinateSpace:"screenshotPixel"};const r=n.bounds;if(!r?.width||!r?.height)return null;const i=t.width/r.width,o=t.height/r.height;return{x:(e.x-r.x)*i,y:(e.y-r.y)*o,width:e.width*i,height:e.height*o,coordinateSpace:"screenshotPixel"}}function b(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}function I(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function h(e){return typeof e=="string"?e.trim():""}function g(e){return h(e)||null}function _(e){const t=Number(e);return Number.isFinite(t)?t:null}async function Bn(e,t){await ye(e,t)}async function Ft(e,t){await ge(e,t)}async function ge(e,t){const n=await e.request("permissions.check",{},t);m(n,"permissions.check");const r=n.result??{},i=await At(e,r,t);if(i==="screen-recording")throw new Error("permission_screen_recording_missing");if(i==="accessibility")throw new Error("permission_accessibility_missing");if(i==="input-monitoring")throw new Error("permission_input_monitoring_missing");if(r.automation===!1)throw new Error("permission_automation_missing");return r}async function ye(e,t,n={}){const r=await ge(e,t),i=String(r.platform??"").toLowerCase();if(r.wechatRunning===!1)throw new Error("wechat_not_running");if(Kt(r))throw new Error("windows_visible_desktop_unavailable");if(r.dpiMappingAvailable===!1||r.displayTopologySupported===!1)throw new Error("dpi_mapping_failed");if(r.wechatMainWindowResponsive===!1)throw new Error("wechat_window_unresponsive");if(r.wechatWindowAvailable===!1&&i!=="win32"&&n.allowUnavailableWindow!==!0)throw new Error("wechat_window_unavailable");return r}async function At(e,t,n){const r=Dt(t);if(!r||process.platform!=="darwin")return r;const i=Lt(r);try{await e.request(i,{},n)}catch{}return r}function Dt(e){return e.screenRecording===!1?"screen-recording":e.accessibility===!1?"accessibility":e.inputMonitoring===!1?"input-monitoring":null}function Lt(e){return e==="screen-recording"?"permissions.requestScreenRecording":e==="accessibility"?"permissions.requestAccessibility":"permissions.requestInputMonitoring"}async function Ot(e,t,n={}){const i=(n.foreground??"required")!=="background",o=await ye(e,t,{allowUnavailableWindow:i});if(String(o.platform??"").toLowerCase()==="win32"){const c=await e.request("windows.ensureReady",{activate:i,allowRecovery:!1,allowLaunch:!1},t);m(c,"windows.ensureReady");const l=c.result;if(!l?.windowId)throw new Error("helper_invalid_response: windows.ensureReady missing WeChat window data");return l}const s=await e.request("windows.ensureReady",{restore:i,focus:i},t);m(s,"windows.ensureReady");const a=s.result;if(!a?.windowId)throw new Error("helper_invalid_response: windows.ensureReady missing WeChat window data");return a}function Kt(e){return e.windowsVisibleDesktopAvailable===!1||e.rdpVisibleDesktopAvailable===!1||e.desktopSessionVisible===!1||e.screenLocked===!0||e.rdpDisconnected===!0}function xe(e){const t=e.filter(q),n=t.filter(i=>i.visible!==!1&&i.minimized!==!0);return(n.length?n:t)[0]??null}async function En(e,t){let n=await e.request("windows.list",{},t);m(n,"windows.list");let r=n.result?.windows??[],i=xe(r);if(!i)throw new Error("wechat_window_not_found");const o=await e.request("windows.focus",{windowId:i.windowId},t);return m(o,"windows.focus"),await ve(e,i,t)}async function pe(e,t,n){if(!t.windowId)throw new Error("wechat_window_not_found");const r=await e.request("windows.focus",{windowId:t.windowId},n);return m(r,"windows.focus"),t}async function $t(e,t){const n=await e.request("windows.list",{},t);m(n,"windows.list");const r=n.result?.windows??[],i=xe(r);if(!i)throw new Error("wechat_window_not_found");const o=await e.request("windows.focus",{windowId:i.windowId},t);return m(o,"windows.focus"),await ve(e,i,t)}async function ve(e,t,n){let r;try{r=await e.request("windows.list",{},n)}catch{return t}if(!r.ok)return t;const i=(r.result?.windows??[]).filter(q);return i.find(o=>o.windowId===t.windowId)??i[0]??t}async function $(e){const{capture:t,ocr:n}=e.observation??await k(e),r=B({capture:t,ocr:n,window:e.window});if(r)return{opened:!1,reason:r};const i=qe({visibleItems:Jt(n,t,e.allowOcrTitleRows!==!1),targets:[{bindingId:e.binding.bindingId,conversationDisplayName:e.binding.conversationDisplayName}]})[0];if(!i)return{opened:!1,reason:"conversation_not_visible"};const o=fn(i.item.bbox,t,e.window);if(!o)return{opened:!1,reason:"conversation_bbox_missing"};const s=await e.helper.request("mouse.click",{x:o.x,y:o.y,coordinateSpace:"screen",windowId:e.window.windowId},e.traceId);return m(s,"mouse.click"),{opened:!0,reason:i.reason}}async function z(e){let t={opened:!1,reason:"conversation_not_opened"};for(let r=0;r<He;r+=1){if(t=await zt(e),t.opened||t.reason!=="conversation_title_not_confirmed")return t;await S(e,"open-retry",`retry:${r}`)}const n=await Ce(e);return n.opened?{opened:!0,reason:`${n.reason}:final-visible-recovery`}:t}async function zt(e){const t=await k(e),n=B({capture:t.capture,ocr:t.ocr,window:e.window});if(n)return{opened:!1,reason:n};let r=!1;if(D(t.ocr,t.capture,e.binding.conversationDisplayName))return e.settleToBottom===!0&&await T({helper:e.helper,window:e.window,traceId:e.traceId,observation:t}),{opened:!0,reason:"current-conversation-title-confirmed"};if(!r){const u=await $({...e,observation:t});if(u.opened){if(await S(e,"open-click-settle","visible-initial"),(await M(e)).ok)return{opened:!0,reason:u.reason};const f=await Ce(e);if(f.opened)return f}}const i=await Yt(e,t.capture);if(!i.ok)return{opened:!1,reason:i.reason};const o=await e.helper.request("wechat.searchConversation",{conversationName:e.binding.conversationDisplayName,windowId:e.window.windowId,waitMs:e.searchDelayMs??F(e,"search-input-settle","search-input"),searchPoint:i.point},e.traceId);m(o,"wechat.searchConversation");const s=await Vt(e);if(s.opened){await S(e,"open-click-settle","search-result-click");const u=await M(e);return u.ok?(e.settleToBottom===!0&&await T({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:s.reason}):{opened:!1,reason:u.reason}}let a=s.observation;const c=y(e.binding.conversationDisplayName)&&s.reason==="search_result_not_visible";if(!y(e.binding.conversationDisplayName)){await Xt(e.helper,e.traceId),await S(e,"open-click-settle","search-return");const u=await M(e);if(u.ok)return e.settleToBottom===!0&&await T({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:u.reason};const d=await e.helper.request("wechat.searchConversation",{conversationName:e.binding.conversationDisplayName,windowId:e.window.windowId,waitMs:e.searchDelayMs??F(e,"search-input-settle","search-input-retry-after-return"),searchPoint:i.point},e.traceId);m(d,"wechat.searchConversation"),a=void 0}const l=await $({...e,...a?{observation:a}:{},allowOcrTitleRows:!c});if(!l.opened)return l;await S(e,"open-click-settle","visible-final");const w=await M(e);return w.ok?(e.settleToBottom===!0&&await T({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:l.reason}):{opened:!1,reason:w.reason}}async function Ce(e){const t=await $(e);if(!t.opened)return t;await S(e,"open-click-settle","visible-retry");const n=await M(e);return n.ok?(e.settleToBottom===!0&&await T({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:`${t.reason}:retry-visible-title-confirmed`}):{opened:!1,reason:n.reason}}async function Vt(e){let t;const n=e.searchDelayMs===0?0:F(e,"search-result-probe","search-result-probe"),r=y(e.binding.conversationDisplayName)?Ye:1;for(let i=0;i<r;i+=1){i>0&&n>0&&await j(n);const o=await qt(e);t=o;const{capture:s,ocr:a}=o,c=B({capture:s,ocr:a,window:e.window});if(c)return{opened:!1,reason:c,observation:o};const l=en(a,s,e.binding.conversationDisplayName);if(!l){if(tn(a)||nn(a,e.binding.conversationDisplayName))break;continue}const w=L(l.bbox,s,jt(s,e.window));if(!w)return{opened:!1,reason:"search_result_bbox_missing",observation:o};const u=await e.helper.request("mouse.click",{x:w.x,y:w.y,coordinateSpace:"screen",windowId:e.window.windowId},e.traceId);return m(u,"mouse.click"),{opened:!0,reason:l.reason,observation:o}}return{opened:!1,reason:"search_result_not_visible",observation:t}}async function qt(e){if(e.window.bounds&&y(e.binding.conversationDisplayName)){const t=await e.helper.request("screen.capture",{bounds:e.window.bounds},e.traceId);if(t.ok&&t.result?.dataBase64&&t.result.width&&t.result.height){const n={...t.result,windowId:e.window.windowId,bounds:t.result.bounds??e.window.bounds},r=await _e(e.helper,n,e.traceId);return{capture:n,ocr:r}}t.errorCode&&t.errorCode!=="helper_unknown_command"&&t.errorCode!=="helper_command_unsupported"&&m(t,"screen.capture")}return k(e)}function jt(e,t){return e.bounds?{...t,bounds:e.bounds}:t}async function k(e){let{capture:t,ocr:n}=await V(e.helper,e.window.windowId,e.traceId,e.window.bounds);return await Gt({helper:e.helper,window:e.window,capture:t,ocr:n,traceId:e.traceId})&&(await J("preview-dismiss-settle",`${e.traceId||""}:${e.window.windowId}:preview-dismiss`),{capture:t,ocr:n}=await V(e.helper,e.window.windowId,e.traceId,e.window.bounds)),{capture:t,ocr:n}}async function Gt(e){const t=Ht(e.ocr,e.capture,e.window);if(!t)return!1;for(const n of[async()=>e.helper.request("keyboard.shortcut",{key:"escape",modifiers:[]},e.traceId),async()=>e.helper.request("mouse.click",{x:t.x,y:t.y,coordinateSpace:"screen",windowId:e.window.windowId},e.traceId)])if((await n()).ok)return!0;return!1}function Ht(e,t,n){const r=e.blocks??[],i=r.find(c=>{const l=String(c.text||""),w=c.bbox;if(!w)return!1;const u=Number(w.x),d=Number(w.y);return Number.isFinite(u)&&Number.isFinite(d)&&d>t.height*.06&&d<t.height*.22&&u>t.width*.3&&/查看原视频|查看原图|原视频|原图|视频|图片|图像/.test(l)});if(!i)return null;const o=i.bbox,s=Number(o?.y);if(!Number.isFinite(s))return null;const a=r.filter(c=>{const l=String(c.text||"").trim(),w=c.bbox;if(!w||l!=="\xD7")return!1;const u=Number(w.x),d=Number(w.y);return Number.isFinite(u)&&Number.isFinite(d)&&u>t.width*.55&&u<t.width*.9&&Math.abs(d-s)<t.height*.08}).sort((c,l)=>Number(l.bbox?.x??0)-Number(c.bbox?.x??0))[0];return L(a?.bbox,t,n)}async function Yt(e,t){if(!e.runtime||!e.api?.classifyWindow)return{ok:!1,reason:"wechat_search_input_detector_unavailable"};try{return{ok:!0,point:await Qe({runtime:e.runtime,binding:e.binding,helper:e.helper,api:e.api,workDir:e.workDir,traceId:e.traceId,window:e.window,...t.dataBase64?{screenshot:t}:{}})}}catch(n){return{ok:!1,reason:n instanceof Error?n.message:"wechat_search_input_not_found"}}}async function R(e,t,n,r){const i=await e.request("windows.capture",{windowId:t,scope:"full-window",...r?{bounds:r}:{}},n);m(i,"windows.capture");const o=i.result;if(!o?.dataBase64||!o.mimeType||!o.width||!o.height)throw new Error("helper_invalid_response: windows.capture missing screenshot data");return o}async function Ie(e){return e.capture.dataBase64?e.capture:R(e.helper,e.window.windowId,e.traceId,e.window.bounds)}async function V(e,t,n,r){const i=await e.request("windows.captureAndOcr",{windowId:t,scope:"full-window",...r?{bounds:r}:{}},n);if(i.ok&&i.result?.capture&&i.result?.ocr)return{capture:i.result.capture,ocr:i.result.ocr};!i.ok&&i.errorCode&&i.errorCode!=="helper_unknown_command"&&i.errorCode!=="helper_command_unsupported"&&m(i,"windows.captureAndOcr");const o=await R(e,t,n,r),s=await _e(e,o,n);return{capture:o,ocr:s}}async function _e(e,t,n){const r=await e.request("ocr.recognize",{mimeType:t.mimeType,dataBase64:t.dataBase64,width:t.width,height:t.height},n);return m(r,"ocr.recognize"),r.result??{}}function m(e,t){if(!e.ok)throw new Error(`${e.errorCode||"helper_command_failed"}: ${e.errorSummary||t}`)}function q(e){const t=String(e.appName||"").toLowerCase();return t==="wechat"||t==="weixin"||t==="\u5FAE\u4FE1"}function j(e){return new Promise(t=>setTimeout(t,e))}function F(e,t,n){return e.searchDelayMs===0?0:Ve(t,Ut(e,n))}async function S(e,t,n){const r=F(e,t,n);r>0&&await j(r)}function Ut(e,t){return`${e.traceId||""}:${e.binding.conversationDisplayName}:${t}`}async function Xt(e,t){const n=await e.request("keyboard.shortcut",{key:"return",modifiers:[]},t);m(n,"keyboard.shortcut")}async function T(e){if(!e.window.bounds)return;const t=e.observation,n=mn(t?.ocr,t?.capture,e.window);if(n){const r=await e.helper.request("mouse.click",{x:n.x,y:n.y,coordinateSpace:"screen",windowId:e.window.windowId},e.traceId);m(r,"mouse.click"),await J("jump-latest-settle",`${e.traceId||""}:${e.window.windowId}:jump-latest`)}}async function M(e){const{capture:t,ocr:n}=await V(e.helper,e.window.windowId,e.traceId,e.window.bounds),r=B({capture:t,ocr:n,window:e.window});return r?{ok:!1,reason:r}:D(n,t,e.binding.conversationDisplayName)?{ok:!0,reason:"target-conversation-title-confirmed"}:{ok:!1,reason:"conversation_title_not_confirmed"}}function B(e){const n=(e.ocr.blocks??[]).map(r=>on(r.text)).filter(Boolean).join("");return/(扫码登录|仅传输文件|重新登录|为了安全|安全验证|登录微信|微信登录|该账号已登录|帐号已登录|账号已登录|当前账号已登录|当前帐号已登录|已登录|进入微信|進入微信)/u.test(n)?Ue:null}function Jt(e,t,n=!0){const r=n?Zt(e.blocks??[],t):[];return r.length?r:e.visibleConversationFingerprints??[]}function Zt(e,t){const n=e.map(i=>ke(i)).filter(i=>!!i).filter(i=>i.text.length>=2).filter(i=>i.x>t.width*.08&&i.x<t.width*.38).filter(i=>i.y>t.height*.06&&i.y<t.height*.96).filter(i=>!Se(i.text)),r=new Set;return n.filter(i=>!Qt(i,n,t)).sort((i,o)=>i.y-o.y||i.x-o.x).map(i=>{const o=i.text.trim();return{title:o,bbox:{x:Math.max(0,i.x-24),y:Math.max(0,i.y-Math.max(24,i.height)),width:Math.max(120,t.width*.3),height:Math.max(48,i.height+40),coordinateSpace:"screenshotPixel"},fingerprint:o.toLowerCase()}}).filter(i=>{const o=E(i.title);return!o||r.has(o)?!1:(r.add(o),!0)})}function ke(e){const t=e.bbox,n=String(e.text||"").normalize("NFKC").trim(),r=Number(t?.x),i=Number(t?.y),o=Number(t?.width),s=Number(t?.height);return!n||![r,i,o,s].every(Number.isFinite)?null:{text:n,x:r,y:i,width:o,height:s}}function Qt(e,t,n){const r=Math.max(36,Math.min(64,n.height*.058));return t.some(i=>i!==e&&i.y<e.y&&e.y-i.y<=r&&Math.abs(i.x-e.x)<=16&&i.height>=e.height*.85)}function Se(e){const t=p(e);return/^(搜索|微信|通讯录|收藏|设置|search)$/iu.test(t)||A(t)||rn(t)||/^包含[::]?/u.test(t)||/搜索网络结果/u.test(t)}function en(e,t,n){const r=(e.blocks??[]).map(a=>ke(a)).filter(a=>!!a),i=r.filter(a=>A(p(a.text))).sort((a,c)=>a.y-c.y||a.x-c.x),o=["\u7FA4\u804A","GroupChats","\u8054\u7CFB\u4EBA","Contacts"];for(const a of o){const c=i.findIndex(d=>p(d.text)===a);if(c<0)continue;const l=i[c],w=i[c+1]?.y??Number.POSITIVE_INFINITY,u=r.filter(d=>d.y>l.y&&d.y<w).filter(d=>d.x<t.width*.7).filter(d=>d.text.length>=2).filter(d=>!A(p(d.text))).filter(d=>!Ne(d,r)).filter(d=>H(d.text,n)).sort((d,f)=>d.y-f.y||d.x-f.x)[0];if(u)return{bbox:Me(u,t),reason:"search-result-title"}}const s=r.filter(a=>a.y>t.height*.12&&a.y<t.height*.82).filter(a=>a.x>t.width*.08&&a.x<t.width*.45).filter(a=>a.text.length>=2).filter(a=>!Se(a.text)).filter(a=>!Ne(a,r)).filter(a=>H(a.text,n)).sort((a,c)=>a.y-c.y||a.x-c.x)[0];return y(n)?null:s?{bbox:Me(s,t),reason:"search-result-title"}:null}function Me(e,t){return{x:Math.max(0,e.x-24),y:Math.max(0,e.y-12),width:Math.max(e.width+t.width*.18,160),height:Math.max(e.height+28,44),coordinateSpace:"screenshotPixel"}}function A(e){return/^(群聊|联系人|聊天记录|公众号|小程序|GroupChats|Contacts|ChatHistory|OfficialAccounts|MiniPrograms)$/u.test(e)}function tn(e){return(e.blocks??[]).some(t=>A(p(t.text)))}function nn(e,t){return(e.visibleConversationFingerprints??[]).some(n=>H(n.title,t))}function p(e){return String(e||"").normalize("NFKC").replace(/[^\p{L}\p{N}_-]/gu,"").trim()}function Ne(e,t){return/^包含[::]?/u.test(e.text.trim())?!0:t.some(n=>n!==e&&Math.abs(n.y-e.y)<=Math.max(18,e.height)&&n.x<e.x&&/^包含[::]?/u.test(n.text.trim()))}function rn(e){const t=String(e||"").trim();return!!(/^(星期[一二三四五六日天]|周[一二三四五六日天])\s*\d{1,2}:\d{2}$/u.test(t)||/^\d{1,2}:\d{2}(?::\d{2})?$/u.test(t)||/^\d{4}[-/.年]\d{1,2}(?:[-/.月]\d{1,2}日?)?$/u.test(t)||/^\d{1,2}[-/.月]\d{1,2}日?$/u.test(t))}function on(e){return String(e||"").normalize("NFKC").replace(/\s+/g,"").trim()}function D(e,t,n){const r=y(n)?E(n):O(n);return r?(e.blocks??[]).some(i=>{const o=y(n)?E(i.text):O(i.text);if(!o||!Ee(o,r))return!1;const s=i.bbox;if(!s)return!1;const a=Number(s.x),c=Number(s.y);if(!Number.isFinite(a)||!Number.isFinite(c))return!1;const l=a>t.width*.32&&a<t.width*.78&&c<t.height*.16,w=a<t.width*.18&&c<t.height*.12;return l||w}):!1}function G(e){if(D(e.ocr,e.capture,e.binding.conversationDisplayName))return!1;const t=(e.ocr.blocks??[]).map(a=>String(a.text||"").normalize("NFKC").replace(/\s+/g," ").trim()).filter(Boolean),n=t.join(`
5
+ `),i=[{pattern:/UTF-?8/iu,source:n},{pattern:/Unix\s*\(LF\)/iu,source:n},{pattern:/纯文本/u,source:n},{pattern:/\d+\s*个字符/u,source:n},{pattern:/\bH1\b/u,source:n},{pattern:/^查看$/u,source:t}].reduce((a,c)=>{const l=Array.isArray(c.source)?c.source.some(w=>c.pattern.test(w)):c.pattern.test(c.source);return a+(l?1:0)},0);if(i>=2)return!0;const o=(e.ocr.blocks??[]).filter(a=>Number(a.bbox?.y)<e.capture.height*.14).map(a=>String(a.text||"")).join(`
6
+ `);return/\.(txt|md|rtf|docx?|pdf)\b/i.test(o)&&i>=1||/\.(png|jpe?g|gif|webp|bmp|heic|mp4|mov|m4v|avi|mkv|webm)\b/i.test(o)&&an(e.capture,e.ocr)?!0:((e.ocr.visibleConversationFingerprints??[]).some(a=>{const c=y(e.binding.conversationDisplayName)?E(a.title):O(a.title),l=y(e.binding.conversationDisplayName)?E(e.binding.conversationDisplayName):O(e.binding.conversationDisplayName);return!!(c&&l&&Ee(c,l))}),!1)}function an(e,t){const n=(t.blocks??[]).map(o=>String(o.text||"").normalize("NFKC").trim()).filter(Boolean),r=n.join(`
7
+ `);if(/\b\d+\s*x\s*\d+\b/i.test(r)&&/\b\d+(\.\d+)?\s*(B|KB|MB|GB)\b/i.test(r)||/\b100%\b/.test(r)&&n.length<=20)return!0;const i=n.filter(o=>!/^(□|×|\+|…|\.\.\.|100%|\d+|发送)$/u.test(o));return e.width>600&&e.height>500&&i.length<=6}function Te(e){const t=e.anchors.map(o=>({...o,normalized:x(o.expectedEchoAnchor)})).filter(o=>o.normalized);if(!t.length)return[];const n=[],r=new Set,i=sn(e.ocr,e.screenshot).sort((o,s)=>x(s.text).length-x(o.text).length);for(const o of i){const s=x(o.text);if(!s)continue;const a=t.find(l=>cn(l.normalized,s));if(!a)continue;const c=`local-outbound-echo:${e.binding.bindingId}:${a.idempotencyKey||a.replyId||a.normalized}`;r.has(c)||(r.add(c),n.push({stableMessageKey:c,senderRole:"self",kind:"text",normalizedText:s,anchorText:s,textExcerpt:o.text.trim(),bbox:o.bbox??null,observedAt:new Date().toISOString(),deliveryStatus:"suppressed"}))}return n}function sn(e,t){const n=(e.blocks??[]).filter(i=>hn(i,t)&&x(i.text)).sort((i,o)=>v(i.bbox,"y")-v(o.bbox,"y")),r=n.map(i=>({text:String(i.text||"").trim(),bbox:i.bbox}));for(let i=0;i<n.length;i+=1){const o=[n[i]];for(let s=i+1;s<n.length&&o.length<5;s+=1){const a=n[s];if(!dn(o[o.length-1],a,t))break;o.push(a),r.push({text:ln(o.map(c=>String(c.text||"").trim())),bbox:wn(o.map(c=>c.bbox))})}}return r}function cn(e,t){if(!e||!t)return!1;if(Z(e,t)>=.88)return!0;const n=e.replace(/\s+/g,""),r=t.replace(/\s+/g,"");return r.length>=8&&n.includes(r)}function dn(e,t,n){const r=v(e.bbox,"x"),i=v(e.bbox,"y"),o=v(e.bbox,"height"),s=v(t.bbox,"x"),a=v(t.bbox,"y");if(![r,i,o,s,a].every(Number.isFinite)||r<n.width*.5||s<n.width*.5)return!1;const c=a-(i+o);return c>=-8&&c<=Math.max(64,n.height*.04)&&Math.abs(s-r)<=120}function ln(e){return e.reduce((t,n)=>t?n?`${t}${un(t,n)?" ":""}${n}`:t:n,"")}function un(e,t){return/[A-Za-z0-9]$/u.test(e)&&/^[A-Za-z0-9]/u.test(t)}function wn(e){const t=e.filter(c=>!!c);if(!t.length)return;const n=t.map(c=>Number(c.x)).filter(Number.isFinite),r=t.map(c=>Number(c.y)).filter(Number.isFinite),i=t.map(c=>Number(c.x)+Number(c.width)).filter(Number.isFinite),o=t.map(c=>Number(c.y)+Number(c.height)).filter(Number.isFinite);if(!n.length||!r.length||!i.length||!o.length)return t[0];const s=Math.min(...n),a=Math.min(...r);return{x:s,y:a,width:Math.max(...i)-s,height:Math.max(...o)-a,coordinateSpace:t.find(c=>c.coordinateSpace)?.coordinateSpace}}function v(e,t){const n=Number(e?.[t]);return Number.isFinite(n)?n:Number.NaN}function Be(e,t){if(!t.length)return e;const n=[...e];for(const r of t){const i=x(r.anchorText||r.normalizedText||r.textExcerpt);n.some(s=>{if(s.senderRole!=="self")return!1;const a=x(s.anchorText||s.normalizedText||s.textExcerpt);return a&&i&&Z(a,i)>=.9})||n.push(r)}return n}function hn(e,t){const n=e.bbox;if(!n)return!1;const r=Number(n.x),i=Number(n.y);return!Number.isFinite(r)||!Number.isFinite(i)?!1:r>t.width*.34&&r<t.width*.96&&i>t.height*.12&&i<t.height*.86}function fn(e,t,n){if(!e||typeof e!="object")return null;const r=e,i=Number(r.x),o=Number(r.y),s=Number(r.width),a=Number(r.height);if(![i,o,s,a].every(Number.isFinite))return null;const c=typeof r.coordinateSpace=="string"?r.coordinateSpace:void 0,l=t.width&&n.bounds?.width?t.width/n.bounds.width:1,w=t.height&&n.bounds?.height?t.height/n.bounds.height:l,u={x:i+Math.min(s*.35,110*l),y:o+Math.min(a-8*w,Math.max(18*w,a/2))};if(c==="screen"||!n.bounds)return u;const d=Math.max(0,(t.width-n.bounds.width*l)/2),f=Math.max(0,(t.height-n.bounds.height*w)/2);return{x:n.bounds.x+(u.x-d)/l,y:n.bounds.y+(u.y-f)/w}}function L(e,t,n){if(!e||typeof e!="object")return null;const r=e,i=Number(r.x),o=Number(r.y),s=Number(r.width),a=Number(r.height);if(![i,o,s,a].every(Number.isFinite))return null;const c=typeof r.coordinateSpace=="string"?r.coordinateSpace:void 0,l={x:i+s/2,y:o+a/2};if(c==="screen"||!n.bounds)return l;const w=t.width&&n.bounds.width?t.width/n.bounds.width:1,u=t.height&&n.bounds.height?t.height/n.bounds.height:w,d=Math.max(0,(t.width-n.bounds.width*w)/2),f=Math.max(0,(t.height-n.bounds.height*u)/2);return{x:n.bounds.x+(l.x-d)/w,y:n.bounds.y+(l.y-f)/u}}function mn(e,t,n){if(!e?.blocks?.length||!t)return null;const r=e.blocks.find(i=>{const o=String(i.text||"").trim(),s=i.bbox;if(!s||!o)return!1;const a=Number(s.x),c=Number(s.y);return!Number.isFinite(a)||!Number.isFinite(c)||!/跳转到最新消息|回到最新消息|jump\s+to\s+latest/i.test(o)?!1:a>t.width*.55&&c>t.height*.45&&c<t.height*.92});return L(r?.bbox,t,n)}function Ee(e,t){return y(t)?We(e,t):e===t||e.includes(t)||t.includes(e)}function H(e,t){if(y(t))return We(e,t);const n=Pe(p(e)),r=Pe(p(t));return!n||!r?!1:n===r?!0:n.replace(/\d+$/u,"")===r}function Pe(e){return e.toLowerCase().replace(/[l1]/g,"i")}function O(e){return String(e||"").normalize("NFKC").replace(/[((]\d+[))]/g,"").replace(/\s+/g,"").replace(/[l1]/gi,"i").toLowerCase().trim()}function E(e){return String(e||"").normalize("NFKC").replace(/[((]\d+[))]/g,"").replace(/\s+/g,"").trim()}function y(e){const t=String(e||"").trim();return/^[A-Z0-9_-]{1,6}$/.test(t)&&/[A-Z]/.test(t)}function We(e,t){const n=String(e||"").normalize("NFKC").replace(/[((]\d+[))]/g,"").replace(/\s+/g,"").trim(),r=String(t||"").normalize("NFKC").replace(/[((]\d+[))]/g,"").replace(/\s+/g,"").trim();return!!(n&&r&&(n===r||n.replace(/\d+$/u,"")===r))}export{N as WeChatChannelUserActivityAbort,V as captureAndRecognizeWeChatWindow,R as captureWeChatWindow,Nn as detectWeChatMessageInputPointByServerVision,Qe as detectWeChatSearchInputPointByServerVision,Ft as ensureHelperPermissionPreflight,Bn as ensureHelperPreflight,Ot as ensureWeChatWindowReady,$t as focusExistingWeChatWindow,pe as focusKnownWeChatWindow,En as focusWeChatWindow,Mn as observeWeChatChannelBindingViaHelper,z as openConversationBySearch,$ as openConversationInVisibleList,_e as recognizeWeChatScreenshot,Ze as selectWeChatWindowForBinding};
@@ -1 +1 @@
1
- import M from"node:crypto";const o={"open-retry":{minMs:320,maxMs:560},"open-click-settle":{minMs:760,maxMs:1180},"search-input-settle":{minMs:620,maxMs:980},"search-result-probe":{minMs:360,maxMs:640},"preview-dismiss-settle":{minMs:260,maxMs:480},"jump-latest-settle":{minMs:620,maxMs:920},"send-focus-stabilize":{minMs:90,maxMs:190},"send-focus-retry":{minMs:280,maxMs:540},"send-post-paste":{minMs:420,maxMs:760},"send-after-submit":{minMs:820,maxMs:1280},"observe-retry":{minMs:1100,maxMs:2600}};function r(e,a){const t=o[e],s=Math.max(0,Math.floor(t.minMs)),m=Math.max(s,Math.floor(t.maxMs))-s+1;if(m<=1)return s;const i=M.createHash("sha256").update(`${e}:${a||""}`).digest().readUInt32BE(0);return s+i%m}async function u(e,a,t){const s=typeof t=="number"&&Number.isFinite(t)?Math.max(0,Math.floor(t)):r(e,a);return s>0&&await new Promise(n=>setTimeout(n,s)),s}export{u as waitForWeChatChannelPacing,r as weChatChannelPacingDelayMs};
1
+ import i from"node:crypto";const o={"open-retry":{minMs:320,maxMs:560},"open-click-settle":{minMs:760,maxMs:1180},"search-input-settle":{minMs:620,maxMs:980},"search-result-probe":{minMs:360,maxMs:640},"preview-dismiss-settle":{minMs:260,maxMs:480},"jump-latest-settle":{minMs:620,maxMs:920},"send-focus-stabilize":{minMs:90,maxMs:190},"send-focus-retry":{minMs:280,maxMs:540},"send-post-paste":{minMs:420,maxMs:1040},"send-after-submit":{minMs:820,maxMs:1680},"observe-retry":{minMs:1100,maxMs:2600}};function M(t,e){const s=o[t],n=Math.max(0,Math.floor(s.minMs)),m=Math.max(n,Math.floor(s.maxMs))-n+1;if(m<=1)return n;const r=i.createHash("sha256").update(`${t}:${e||""}`).digest().readUInt32BE(0);return n+r%m}function c(t){if(t<=0)return t;const e=Math.round(t*.35);if(e<=0)return t;const s=i.randomInt(-e,e+1);return Math.max(0,t+s)}async function f(t,e,s){const n=typeof s=="number"&&Number.isFinite(s)?Math.max(0,Math.floor(s)):c(M(t,e));return n>0&&await new Promise(a=>setTimeout(a,n)),n}export{f as waitForWeChatChannelPacing,M as weChatChannelPacingDelayMs};
@@ -4,6 +4,7 @@ import { type WeChatChannelHelperClientOptions, type WeChatChannelHelperRequestT
4
4
  import type { WeChatChannelHelperTransport } from './observer.js';
5
5
  import { type WeChatChannelObserveApi } from './observer.js';
6
6
  import { WeChatChannelScheduler, type WeChatChannelSchedulerTickOptions, type WeChatChannelSchedulerTickResult } from './scheduler.js';
7
+ import { type WeChatAutomationLane } from './automation-lane.js';
7
8
  export type WeChatChannelProductRunnerOptions = {
8
9
  runtime: WeChatChannelRuntime;
9
10
  workDir: string;
@@ -16,6 +17,7 @@ export type WeChatChannelProductRunnerOptions = {
16
17
  helperRequestLogger?: (event: WeChatChannelHelperRequestTraceEvent) => void;
17
18
  ledgerPath?: string;
18
19
  outboundLedgerPath?: string;
20
+ lane?: WeChatAutomationLane;
19
21
  onInboundMessages?: (binding: WeChatChannelBindingConfig, messages: WeChatChannelObservedMessage[]) => Promise<void> | void;
20
22
  };
21
23
  export type WeChatChannelRunnerApi = Pick<WeChatChannelApiClient, 'upsertRuntime' | 'ingest' | 'reportRunStatus'> & Partial<Pick<WeChatChannelApiClient, 'reportOutboundStatus'>> & WeChatChannelObserveApi;
@@ -27,6 +29,7 @@ export declare class WeChatChannelProductRunner {
27
29
  stop?: () => Promise<void>;
28
30
  };
29
31
  private helperStarted;
32
+ private readonly lane;
30
33
  constructor(options: WeChatChannelProductRunnerOptions);
31
34
  start(): Promise<void>;
32
35
  tick(options?: WeChatChannelSchedulerTickOptions): Promise<WeChatChannelSchedulerTickResult | void>;
@@ -1 +1 @@
1
- import u from"node:path";import h from"node:fs";import{createWeChatChannelApiClient as m}from"./client.js";import{WeChatChannelHelperClient as f}from"./helper-client.js";import{resolveWeChatChannelHelperAsset as k,WECHAT_CHANNEL_HELPER_VERSION as W}from"./helper-assets.js";import{requiredWindowsWeChatChannelHelperCapabilitiesForProfile as c}from"./helper-protocol.js";import{captureWeChatWindow as S,detectWeChatMessageInputPointByServerVision as I,focusKnownWeChatWindow as g,observeWeChatChannelBindingViaHelper as P,openConversationBySearch as O,recognizeWeChatScreenshot as A,selectWeChatWindowForBinding as w}from"./observer.js";import{WeChatChannelScheduler as D}from"./scheduler.js";import{loadWeChatChannelLedger as F}from"./ledger.js";import{buildWeChatConversationFingerprint as M,matchWeChatConversationFingerprints as T}from"./fingerprint.js";import{WeChatChannelOutboundSender as x}from"./outbound-sender.js";import{decideWeChatChannelActivityGate as L,nextWeChatChannelActivityRetryAt as _,normalizeWeChatChannelActivitySnapshot as q}from"./human-coordination.js";class H{options;scheduler;helper;helperStarted=!1;constructor(e){this.options=e;const n=e.api??m();this.helper=e.helper??E(e),this.scheduler=new D({runtime:e.runtime,workDir:e.workDir,api:n,ledgerPath:e.ledgerPath,outboundLedgerPath:e.outboundLedgerPath,onInboundMessages:e.onInboundMessages,activityGate:{canObserve:async(i,a)=>{if(e.runtime.foregroundPolicy==="work")return{ok:!0};const o=await this.helper.request("activity.snapshot",{});if(!o.ok)return{ok:!1,reasonCode:"user_activity_unknown",nextAttemptAt:new Date(a.getTime()+5e3)};const t=L({snapshot:q(o.result),stage:"observe"});return t.ok?{ok:!0}:{ok:!1,reasonCode:t.reasonCode,nextAttemptAt:_(a,t.waitMs)}}},shouldSkipObserve:(i,a)=>this.shouldSkipObserveForUnchangedFingerprint(i,a.foregroundMode),outboundSender:new x({helper:this.helper,platform:e.runtime.policy.platform,activityGatePolicy:e.runtime.foregroundPolicy==="work"?{mouseMovedThresholdMs:0,mouseClickThresholdMs:0,scrollWheelThresholdMs:0,keyDownThresholdMs:0}:void 0,takeoverCheck:e.runtime.foregroundPolicy!=="work",openConversation:async i=>{const a={bindingId:`outbound:${i}`,sessionId:"outbound",conversationDisplayName:i,enabled:!0,allowReply:!0,downloadMedia:!0},o=await w({runtime:e.runtime,binding:a,helper:this.helper,api:n,workDir:e.workDir}),t=await g(this.helper,o),s=await O({helper:this.helper,window:t,settleToBottom:!1,binding:a,runtime:e.runtime,api:n,workDir:e.workDir});if(!s.opened)return{...s,windowId:t.windowId,inputPoint:null};const l=await I({runtime:e.runtime,binding:a,helper:this.helper,api:n,workDir:e.workDir,window:t});return{...s,windowId:t.windowId,inputPoint:l}}}),observeBinding:(i,a)=>P({runtime:e.runtime,binding:i,helper:this.helper,api:n,workDir:e.workDir,localLedgerTailAnchors:R(e.ledgerPath??B(e.workDir,e.runtime.runtimeId),e.runtime.runtimeId,i.bindingId),localOutboundEchoAnchors:a?.localOutboundEchoAnchors,foregroundMode:a?.foregroundMode,onObservationEvidence:({ocr:o})=>{N({statePath:b(e.workDir,e.runtime.runtimeId),runtimeId:e.runtime.runtimeId,binding:i,ocr:o})}})})}async start(){await this.ensureHelperStarted(),await this.scheduler.start()}async tick(e){return await this.ensureHelperStarted(),this.scheduler.tick(e)}async stop(){await this.scheduler.stop(),this.helperStarted&&this.helper.stop&&await this.helper.stop(),this.helperStarted=!1}async ensureHelperStarted(){this.helperStarted||(this.helper.start&&await this.helper.start(),this.helperStarted=!0)}async shouldSkipObserveForUnchangedFingerprint(e,n="required"){if(this.options.runtime.policy.platform!=="darwin"&&this.options.runtime.policy.platform!=="win32")return{skip:!1};const i=b(this.options.workDir,this.options.runtime.runtimeId),a=p(i,this.options.runtime.runtimeId).bindings[e.bindingId];if(!a?.fingerprint)return{skip:!1};try{const o=this.options.api??m(),t=await w({runtime:this.options.runtime,binding:e,helper:this.helper,api:o,workDir:this.options.workDir},{foregroundMode:n}),s=n==="background"?t:await g(this.helper,t),l=await S(this.helper,s.windowId,void 0,s.bounds),y=await A(this.helper,l),d=v({binding:e,ocr:y,prior:a});return!d?.fingerprint||d.fingerprint!==a.fingerprint?{skip:!1}:{skip:!0,reasonCode:"conversation_fingerprint_unchanged",fingerprint:d.fingerprint}}catch{return{skip:!1}}}}function ne(r){return new H(r)}function R(r,e,n,i=3){return(F(r,e).bindings[n]?.recent??[]).slice(-i).map(t=>({stableMessageKey:t.stableMessageKey,senderRole:t.senderRole,kind:t.kind,anchorText:t.anchorText??t.normalizedText??t.textExcerpt??null,anchorMetadata:t.anchorMetadata??null}))}function E(r){if(r.helperClientOptions)return new f({...r.helperClientOptions,requiredCapabilities:r.helperClientOptions.requiredCapabilities??C(r.runtime),guardUnsafeWindowsCommands:r.helperClientOptions.guardUnsafeWindowsCommands??r.runtime.policy.platform==="win32",skipUserActivityGuard:r.helperClientOptions.skipUserActivityGuard??r.runtime.foregroundPolicy==="work",cleanupWindowsOverlays:r.helperClientOptions.cleanupWindowsOverlays??r.runtime.policy.platform==="win32",requestLogger:r.helperClientOptions.requestLogger??r.helperRequestLogger});const e=k();if(!e.ok)throw new Error(`${e.reasonCode}: ${e.message}`);return new f({helperPath:e.helperPath,expectedHelperVersion:e.version||W,requiredCapabilities:C(r.runtime),guardUnsafeWindowsCommands:r.runtime.policy.platform==="win32",skipUserActivityGuard:r.runtime.foregroundPolicy==="work",cleanupWindowsOverlays:r.runtime.policy.platform==="win32",requestLogger:r.helperRequestLogger})}function C(r){if(r.policy.platform==="win32")return r.bindings.some(e=>e.enabled!==!1&&e.allowReply!==!1)?c("send"):r.bindings.some(e=>e.enabled!==!1&&e.downloadMedia!==!1)?c("download"):c("observe")}function B(r,e){return u.join(r,"wechat-channel",`${e}.ledger.json`)}function b(r,e){return u.join(r,"wechat-channel",`${G(e)}.conversation-fingerprints.json`)}function p(r,e){try{const n=JSON.parse(h.readFileSync(r,"utf8"));if(n?.version===1&&n.runtimeId===e&&n.bindings&&typeof n.bindings=="object")return n}catch{}return{version:1,runtimeId:e,bindings:{}}}function U(r,e){h.mkdirSync(u.dirname(r),{recursive:!0}),h.writeFileSync(r,JSON.stringify(e,null,2))}function N(r){const e=v({binding:r.binding,ocr:r.ocr,prior:p(r.statePath,r.runtimeId).bindings[r.binding.bindingId]});if(!e?.fingerprint)return;const n=p(r.statePath,r.runtimeId);n.bindings[r.binding.bindingId]={bindingId:r.binding.bindingId,fingerprint:e.fingerprint,title:e.item.title??null,preview:e.item.preview??null,timeText:e.item.timeText??null,unreadText:e.item.unreadText??null,observedAt:new Date().toISOString()},U(r.statePath,n)}function v(r){const e=r.ocr.visibleConversationFingerprints??[],n=T({visibleItems:e,targets:[{bindingId:r.binding.bindingId,conversationDisplayName:r.binding.conversationDisplayName,lastConversationFingerprint:r.prior?.fingerprint,lastPreview:r.prior?.preview??void 0}]})[0];if(!n)return null;const i=n.item.fingerprint||M(n.item);return i?{item:n.item,fingerprint:i}:null}function G(r){return String(r||"").trim().replace(/[^a-zA-Z0-9._-]+/g,"_").replace(/^_+|_+$/g,"")||"wechat-channel-runtime"}export{H as WeChatChannelProductRunner,R as buildLocalLedgerTailAnchors,ne as createWeChatChannelProductRunner};
1
+ import u from"node:path";import h from"node:fs";import{createWeChatChannelApiClient as m}from"./client.js";import{WeChatChannelHelperClient as f}from"./helper-client.js";import{resolveWeChatChannelHelperAsset as k,WECHAT_CHANNEL_HELPER_VERSION as W}from"./helper-assets.js";import{requiredWindowsWeChatChannelHelperCapabilitiesForProfile as c}from"./helper-protocol.js";import{captureWeChatWindow as S,detectWeChatMessageInputPointByServerVision as I,focusKnownWeChatWindow as g,observeWeChatChannelBindingViaHelper as P,openConversationBySearch as A,recognizeWeChatScreenshot as O,selectWeChatWindowForBinding as w}from"./observer.js";import{WeChatChannelScheduler as D}from"./scheduler.js";import{loadWeChatChannelLedger as F}from"./ledger.js";import{buildWeChatConversationFingerprint as M,matchWeChatConversationFingerprints as T}from"./fingerprint.js";import{WeChatChannelOutboundSender as x}from"./outbound-sender.js";import{SerialWeChatAutomationLane as L}from"./automation-lane.js";import{decideWeChatChannelActivityGate as _,nextWeChatChannelActivityRetryAt as q,normalizeWeChatChannelActivitySnapshot as H}from"./human-coordination.js";class R{options;scheduler;helper;helperStarted=!1;lane;constructor(e){this.options=e;const n=e.api??m();this.helper=e.helper??B(e),this.lane=e.lane??new L,this.scheduler=new D({runtime:e.runtime,workDir:e.workDir,api:n,ledgerPath:e.ledgerPath,outboundLedgerPath:e.outboundLedgerPath,onInboundMessages:e.onInboundMessages,activityGate:{canObserve:async(i,a)=>{if(e.runtime.foregroundPolicy==="work")return{ok:!0};const o=await this.helper.request("activity.snapshot",{});if(!o.ok)return{ok:!1,reasonCode:"user_activity_unknown",nextAttemptAt:new Date(a.getTime()+5e3)};const t=_({snapshot:H(o.result),stage:"observe"});return t.ok?{ok:!0}:{ok:!1,reasonCode:t.reasonCode,nextAttemptAt:q(a,t.waitMs)}}},shouldSkipObserve:(i,a)=>this.shouldSkipObserveForUnchangedFingerprint(i,a.foregroundMode),outboundSender:new x({helper:this.helper,platform:e.runtime.policy.platform,activityGatePolicy:e.runtime.foregroundPolicy==="work"?{mouseMovedThresholdMs:0,mouseClickThresholdMs:0,scrollWheelThresholdMs:0,keyDownThresholdMs:0}:void 0,takeoverCheck:e.runtime.foregroundPolicy!=="work",openConversation:async i=>{const a={bindingId:`outbound:${i}`,sessionId:"outbound",conversationDisplayName:i,enabled:!0,allowReply:!0,downloadMedia:!0},o=await w({runtime:e.runtime,binding:a,helper:this.helper,api:n,workDir:e.workDir}),t=await g(this.helper,o),s=await A({helper:this.helper,window:t,settleToBottom:!1,binding:a,runtime:e.runtime,api:n,workDir:e.workDir});if(!s.opened)return{...s,windowId:t.windowId,inputPoint:null};const l=await I({runtime:e.runtime,binding:a,helper:this.helper,api:n,workDir:e.workDir,window:t});return{...s,windowId:t.windowId,inputPoint:l}}}),observeBinding:(i,a)=>P({runtime:e.runtime,binding:i,helper:this.helper,api:n,workDir:e.workDir,localLedgerTailAnchors:E(e.ledgerPath??U(e.workDir,e.runtime.runtimeId),e.runtime.runtimeId,i.bindingId),localOutboundEchoAnchors:a?.localOutboundEchoAnchors,foregroundMode:a?.foregroundMode,onObservationEvidence:({ocr:o})=>{G({statePath:b(e.workDir,e.runtime.runtimeId),runtimeId:e.runtime.runtimeId,binding:i,ocr:o})}})})}async start(){return this.lane.run("wechat.runner.start",async()=>{await this.ensureHelperStarted(),await this.scheduler.start()})}async tick(e){return this.lane.run("wechat.runner.tick",async()=>(await this.ensureHelperStarted(),this.scheduler.tick(e)))}async stop(){return this.lane.run("wechat.runner.stop",async()=>{await this.scheduler.stop(),this.helperStarted&&this.helper.stop&&await this.helper.stop(),this.helperStarted=!1})}async ensureHelperStarted(){this.helperStarted||(this.helper.start&&await this.helper.start(),this.helperStarted=!0)}async shouldSkipObserveForUnchangedFingerprint(e,n="required"){if(this.options.runtime.policy.platform!=="darwin"&&this.options.runtime.policy.platform!=="win32")return{skip:!1};const i=b(this.options.workDir,this.options.runtime.runtimeId),a=p(i,this.options.runtime.runtimeId).bindings[e.bindingId];if(!a?.fingerprint)return{skip:!1};try{const o=this.options.api??m(),t=await w({runtime:this.options.runtime,binding:e,helper:this.helper,api:o,workDir:this.options.workDir},{foregroundMode:n}),s=n==="background"?t:await g(this.helper,t),l=await S(this.helper,s.windowId,void 0,s.bounds),v=await O(this.helper,l),d=y({binding:e,ocr:v,prior:a});return!d?.fingerprint||d.fingerprint!==a.fingerprint?{skip:!1}:{skip:!0,reasonCode:"conversation_fingerprint_unchanged",fingerprint:d.fingerprint}}catch{return{skip:!1}}}}function ie(r){return new R(r)}function E(r,e,n,i=3){return(F(r,e).bindings[n]?.recent??[]).slice(-i).map(t=>({stableMessageKey:t.stableMessageKey,senderRole:t.senderRole,kind:t.kind,anchorText:t.anchorText??t.normalizedText??t.textExcerpt??null,anchorMetadata:t.anchorMetadata??null}))}function B(r){if(r.helperClientOptions)return new f({...r.helperClientOptions,requiredCapabilities:r.helperClientOptions.requiredCapabilities??C(r.runtime),guardUnsafeWindowsCommands:r.helperClientOptions.guardUnsafeWindowsCommands??r.runtime.policy.platform==="win32",skipUserActivityGuard:r.helperClientOptions.skipUserActivityGuard??r.runtime.foregroundPolicy==="work",cleanupWindowsOverlays:r.helperClientOptions.cleanupWindowsOverlays??r.runtime.policy.platform==="win32",requestLogger:r.helperClientOptions.requestLogger??r.helperRequestLogger});const e=k();if(!e.ok)throw new Error(`${e.reasonCode}: ${e.message}`);return new f({helperPath:e.helperPath,expectedHelperVersion:e.version||W,requiredCapabilities:C(r.runtime),guardUnsafeWindowsCommands:r.runtime.policy.platform==="win32",skipUserActivityGuard:r.runtime.foregroundPolicy==="work",cleanupWindowsOverlays:r.runtime.policy.platform==="win32",requestLogger:r.helperRequestLogger})}function C(r){if(r.policy.platform==="win32")return r.bindings.some(e=>e.enabled!==!1&&e.allowReply!==!1)?c("send"):r.bindings.some(e=>e.enabled!==!1&&e.downloadMedia!==!1)?c("download"):c("observe")}function U(r,e){return u.join(r,"wechat-channel",`${e}.ledger.json`)}function b(r,e){return u.join(r,"wechat-channel",`${$(e)}.conversation-fingerprints.json`)}function p(r,e){try{const n=JSON.parse(h.readFileSync(r,"utf8"));if(n?.version===1&&n.runtimeId===e&&n.bindings&&typeof n.bindings=="object")return n}catch{}return{version:1,runtimeId:e,bindings:{}}}function N(r,e){h.mkdirSync(u.dirname(r),{recursive:!0}),h.writeFileSync(r,JSON.stringify(e,null,2))}function G(r){const e=y({binding:r.binding,ocr:r.ocr,prior:p(r.statePath,r.runtimeId).bindings[r.binding.bindingId]});if(!e?.fingerprint)return;const n=p(r.statePath,r.runtimeId);n.bindings[r.binding.bindingId]={bindingId:r.binding.bindingId,fingerprint:e.fingerprint,title:e.item.title??null,preview:e.item.preview??null,timeText:e.item.timeText??null,unreadText:e.item.unreadText??null,observedAt:new Date().toISOString()},N(r.statePath,n)}function y(r){const e=r.ocr.visibleConversationFingerprints??[],n=T({visibleItems:e,targets:[{bindingId:r.binding.bindingId,conversationDisplayName:r.binding.conversationDisplayName,lastConversationFingerprint:r.prior?.fingerprint,lastPreview:r.prior?.preview??void 0}]})[0];if(!n)return null;const i=n.item.fingerprint||M(n.item);return i?{item:n.item,fingerprint:i}:null}function $(r){return String(r||"").trim().replace(/[^a-zA-Z0-9._-]+/g,"_").replace(/^_+|_+$/g,"")||"wechat-channel-runtime"}export{R as WeChatChannelProductRunner,E as buildLocalLedgerTailAnchors,ie as createWeChatChannelProductRunner};
@@ -1,9 +1,9 @@
1
- import w from"node:crypto";import m from"node:fs";import f from"node:path";import{loadConfig as W}from"../../config/index.js";import{createWeChatChannelProductRunner as O}from"../wechat-channel/runner.js";import{createWeChatChannelApiClient as k}from"../wechat-channel/client.js";import{createWeChatChannelRuntime as $}from"../wechat-channel/runtime.js";import{cancelWeChatOutboundRecord as D,enqueueWeChatOutboundReply as T,loadWeChatChannelOutboundLedger as h,saveWeChatChannelOutboundLedger as I}from"../wechat-channel/outbound-ledger.js";import{loadWeChatChannelLedger as S}from"../wechat-channel/ledger.js";const K="wechat-channel";function se(e){const t=$({runtimeId:e.config.id,machineId:J(),pollIntervalMs:e.secret.pollIntervalMs,foregroundPolicy:j(e.secret.forceForeground),bindings:H(e.config,e.secret)}),n=x(e.config.workDir,t.runtimeId),a=u(e.config.workDir,t.runtimeId),r=L(e.config.workDir,t.runtimeId),o=[];m.mkdirSync(f.dirname(r),{recursive:!0}),m.writeFileSync(r,"");const s=e.createRunner??O,c=z(),d=s({runtime:t,workDir:e.config.workDir,api:c,ledgerPath:n,outboundLedgerPath:a,helperRequestLogger:_(r),onInboundMessages:(C,A)=>{const M=t.bindings.find(p=>p.bindingId===C.bindingId)??C;for(const p of A){const y=R(e.config,M,p);if(!y)continue;const N=e.onMessage?.({...y,managerSessionId:e.config.managerSessionId})??y;o.push(N)}}});return{runtime:t,runner:d,emitted:o,ledgerPath:n,outboundLedgerPath:a,helperTracePath:r}}function z(){let e=null;const t=()=>(e??=k(),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 j(e){return e===!1?"polite":"work"}function ce(e){const t=e.config.id,n=P(e.config.id,e.conversationName),a=S(x(e.config.workDir,t),t),r=u(e.config.workDir,t),o=h(r,t),s=e.reply.attachment?[U(e.reply.attachment)]:[],c=T({ledger:o,replyId:e.reply.messageId||e.reply.idempotencyKey||l("wechat-channel-reply",`${Date.now()}:${e.reply.text}`),idempotencyKey:e.reply.idempotencyKey||l("wechat-channel-send",`${e.config.id}
1
+ import w from"node:crypto";import m from"node:fs";import f from"node:path";import{loadConfig as W}from"../../config/index.js";import{createWeChatChannelProductRunner as O}from"../wechat-channel/runner.js";import{createWeChatChannelApiClient as k}from"../wechat-channel/client.js";import{createWeChatAutomationLane as $}from"../wechat-channel/automation-lane.js";import{createWeChatChannelRuntime as D}from"../wechat-channel/runtime.js";import{cancelWeChatOutboundRecord as T,enqueueWeChatOutboundReply as K,loadWeChatChannelOutboundLedger as h,saveWeChatChannelOutboundLedger as I}from"../wechat-channel/outbound-ledger.js";import{loadWeChatChannelLedger as S}from"../wechat-channel/ledger.js";const z="wechat-channel";function le(e){const t=D({runtimeId:e.config.id,machineId:Z(),pollIntervalMs:e.secret.pollIntervalMs,foregroundPolicy:j(e.secret.forceForeground),bindings:q(e.config,e.secret)}),n=x(e.config.workDir,t.runtimeId),a=u(e.config.workDir,t.runtimeId),r=_(e.config.workDir,t.runtimeId),o=[];m.mkdirSync(f.dirname(r),{recursive:!0}),m.writeFileSync(r,"");const s=e.createRunner??O,c=L(),d=s({runtime:t,workDir:e.config.workDir,api:c,ledgerPath:n,outboundLedgerPath:a,lane:$(),helperRequestLogger:F(r),onInboundMessages:(C,R)=>{const M=t.bindings.find(p=>p.bindingId===C.bindingId)??C;for(const p of R){const y=A(e.config,M,p);if(!y)continue;const N=e.onMessage?.({...y,managerSessionId:e.config.managerSessionId})??y;o.push(N)}}});return{runtime:t,runner:d,emitted:o,ledgerPath:n,outboundLedgerPath:a,helperTracePath:r}}function L(){let e=null;const t=()=>(e??=k(),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 j(e){return e===!1?"polite":"work"}function de(e){const t=e.config.id,n=P(e.config.id,e.conversationName),a=S(x(e.config.workDir,t),t),r=u(e.config.workDir,t),o=h(r,t),s=e.reply.attachment?[G(e.reply.attachment)]:[],c=K({ledger:o,replyId:e.reply.messageId||e.reply.idempotencyKey||l("wechat-channel-reply",`${Date.now()}:${e.reply.text}`),idempotencyKey:e.reply.idempotencyKey||l("wechat-channel-send",`${e.config.id}
2
2
  ${e.reply.conversationId}
3
3
  ${e.reply.text}
4
4
  ${s.join(`
5
- `)}`),bindingId:n,runtimeId:t,sessionId:e.config.sessionId||e.config.managerSessionId,conversationName:e.conversationName,replyBaseRevision:a.bindings[n]?.revision??0,text:e.reply.text,attachmentLocalRefs:s});return I(r,o),c}function le(e){return h(u(e.config.workDir,e.config.id),e.config.id).records.filter(n=>["queued","sending","sent_unconfirmed"].includes(n.sendStatus)).length}function de(e){return h(u(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 ue(e){const t=Q(e.limit,20,1,100),n=S(e.product.ledgerPath,e.product.runtime.runtimeId),a=[];for(const r of e.product.runtime.bindings){const o=n.bindings[r.bindingId]?.recent??[];for(const s of o.slice(-t)){const c=R(e.config,r,s);c&&a.push(c)}}return a}function me(e){const t=u(e.config.workDir,e.config.id),n=h(t,e.config.id),a=String(e.idempotencyKey??"").trim(),r=String(e.replyId??"").trim();if(!a&&!r)return{cancelled:!1};const o=n.records.find(c=>a&&c.idempotencyKey===a||r&&c.replyId===r);if(!o)return{cancelled:!1};const s=o.sendStatus;return D(o,e.reason||"user_cancelled"),o.sendStatus!==s?(I(t,n),{cancelled:!0,status:o.sendStatus,record:o}):{cancelled:!1,status:o.sendStatus,record:o}}function fe(e){return e===K}function E(e){return l("wechat-conversation",i(e))}function P(e,t){return l("wechat-channel-binding",`${e}
6
- ${i(t)}`)}function x(e,t){return f.join(e,"wechat-channel",`${b(t)}.ledger.json`)}function u(e,t){return f.join(e,"wechat-channel",`${b(t)}.outbound-ledger.json`)}function L(e,t){return f.join(e,"wechat-channel",`${b(t)}.helper-trace.jsonl`)}function b(e){return String(e||"").trim().replace(/[^a-zA-Z0-9._-]+/g,"_").replace(/^_+|_+$/g,"")||"wechat-channel-runtime"}function _(e){return t=>{m.appendFileSync(e,`${JSON.stringify(F(t))}
7
- `)}}function F(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=g(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=g(e.result)),t}function g(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:w.createHash("sha256").update(e).digest("hex").slice(0,16),preview:e.slice(0,120)}:e;if(Array.isArray(e)){const o=e.slice(0,10).map(s=>g(s,t,n+1));return e.length>o.length?{type:"array",length:e.length,items:o}:o}if(typeof e!="object")return String(e);if(n>=5)return{type:"object",truncated:!0};const a=e,r={};for(const[o,s]of Object.entries(a))r[o]=g(s,o,n+1);return r}function H(e,t){return G(t).map(n=>({bindingId:P(e.id,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,n){const a=i(n.normalizedText||n.anchorText||n.textExcerpt||""),r=q(n);if(!a&&r.length===0)return null;const o=i(n.senderName||"");return{type:"external.message",channelId:e.id,channelType:"wechat-rpa",conversationId:E(t.conversationDisplayName),conversationName:t.conversationDisplayName,messageId:l("wechat-message",`${t.bindingId}
5
+ `)}`),bindingId:n,runtimeId:t,sessionId:e.config.sessionId||e.config.managerSessionId,conversationName:e.conversationName,replyBaseRevision:a.bindings[n]?.revision??0,text:e.reply.text,attachmentLocalRefs:s});return I(r,o),c}function ue(e){return h(u(e.config.workDir,e.config.id),e.config.id).records.filter(n=>["queued","sending","sent_unconfirmed"].includes(n.sendStatus)).length}function me(e){return h(u(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 fe(e){const t=X(e.limit,20,1,100),n=S(e.product.ledgerPath,e.product.runtime.runtimeId),a=[];for(const r of e.product.runtime.bindings){const o=n.bindings[r.bindingId]?.recent??[];for(const s of o.slice(-t)){const c=A(e.config,r,s);c&&a.push(c)}}return a}function he(e){const t=u(e.config.workDir,e.config.id),n=h(t,e.config.id),a=String(e.idempotencyKey??"").trim(),r=String(e.replyId??"").trim();if(!a&&!r)return{cancelled:!1};const o=n.records.find(c=>a&&c.idempotencyKey===a||r&&c.replyId===r);if(!o)return{cancelled:!1};const s=o.sendStatus;return T(o,e.reason||"user_cancelled"),o.sendStatus!==s?(I(t,n),{cancelled:!0,status:o.sendStatus,record:o}):{cancelled:!1,status:o.sendStatus,record:o}}function ge(e){return e===z}function E(e){return l("wechat-conversation",i(e))}function P(e,t){return l("wechat-channel-binding",`${e}
6
+ ${i(t)}`)}function x(e,t){return f.join(e,"wechat-channel",`${b(t)}.ledger.json`)}function u(e,t){return f.join(e,"wechat-channel",`${b(t)}.outbound-ledger.json`)}function _(e,t){return f.join(e,"wechat-channel",`${b(t)}.helper-trace.jsonl`)}function b(e){return String(e||"").trim().replace(/[^a-zA-Z0-9._-]+/g,"_").replace(/^_+|_+$/g,"")||"wechat-channel-runtime"}function F(e){return t=>{m.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=g(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=g(e.result)),t}function g(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:w.createHash("sha256").update(e).digest("hex").slice(0,16),preview:e.slice(0,120)}:e;if(Array.isArray(e)){const o=e.slice(0,10).map(s=>g(s,t,n+1));return e.length>o.length?{type:"array",length:e.length,items:o}:o}if(typeof e!="object")return String(e);if(n>=5)return{type:"object",truncated:!0};const a=e,r={};for(const[o,s]of Object.entries(a))r[o]=g(s,o,n+1);return r}function q(e,t){return J(t).map(n=>({bindingId:P(e.id,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,n){const a=i(n.normalizedText||n.anchorText||n.textExcerpt||""),r=B(n);if(!a&&r.length===0)return null;const o=i(n.senderName||"");return{type:"external.message",channelId:e.id,channelType:"wechat-rpa",conversationId:E(t.conversationDisplayName),conversationName:t.conversationDisplayName,messageId:l("wechat-message",`${t.bindingId}
8
8
  ${n.stableMessageKey}`),sender:{id:l("wechat-sender",`${t.bindingId}
9
- ${o||n.senderRole}`),name:o||null},text:a,attachments:r,receivedAt:Z(n.observedAt),isMentioned:!1,replyTarget:"",rawRef:n.stableMessageKey}}function q(e){const t=e.mediaMetadata;return B(t).map(a=>{const r=a,o=typeof r.localPath=="string"?i(r.localPath):"",s=typeof r.thumbnailPath=="string"?i(r.thumbnailPath):"",c=typeof r.url=="string"?i(r.url):"",d=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))}:{},...c?{url:c}:{},...typeof r.mimeType=="string"?{mimeType:i(r.mimeType)}:{},...typeof r.extension=="string"?{extension:i(r.extension)}:{},...Number.isFinite(d)&&d>0?{size:d}:{},...o?{localPath:o}:{},...s?{thumbnailPath:s}:{},...typeof r.hash=="string"||typeof r.sha256=="string"?{hash:i(String(r.hash||r.sha256))}:{},availability:V(r.availability,{localPath:o,url:c}),...typeof r.providerError=="string"?{providerError:i(r.providerError)}:{},...typeof r.sourceAction=="string"?{sourceAction:i(r.sourceAction)}:{},...v(r.materializationKind)?{materializationKind:r.materializationKind}:{},...typeof r.isOriginal=="boolean"?{isOriginal:r.isOriginal}:{},...typeof r.mimeKindMatches=="boolean"?{mimeKindMatches:r.mimeKindMatches}:{}}})}function B(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 V(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 v(e){return e==="original-file"||e==="clipboard-image"||e==="preview-crop"||e==="metadata"}function U(e){const t=i(e.localPath||"");if(!t)throw new Error("WeChat channel product send requires a local attachment path on this machine");if(!m.statSync(t).isFile())throw new Error(`WeChat channel product attachment is not a file: ${t}`);return t}function G(e){return Array.isArray(e.groups)?e.groups.map(t=>i(t?.name||"")).filter(Boolean):[]}function J(){return process.env.SHENNIAN_MACHINE_ID?.trim()||W().machineId||"local"}function Z(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 Q(e,t,n,a){const r=Number(e);return Number.isFinite(r)?Math.max(n,Math.min(a,Math.floor(r))):t}function l(e,t){return`${e}:${w.createHash("sha256").update(t).digest("hex").slice(0,24)}`}export{K as WECHAT_RPA_PRODUCT_SOURCE,me as cancelWeChatRpaProductOutboundReply,le as countPendingWeChatRpaProductOutbound,se as createWeChatRpaProductSourceConnection,ce as enqueueWeChatRpaProductOutboundReply,fe as isWeChatRpaProductSource,de as listPendingWeChatRpaProductOutbound,ue as listWeChatRpaProductRecentMessages,R as observedMessageToExternalEvent,b as safeWeChatChannelRuntimePathSegment,P as weChatChannelBindingId,E as weChatChannelConversationId,L as weChatChannelHelperTracePath,x as weChatChannelLedgerPath,u as weChatChannelOutboundLedgerPath};
9
+ ${o||n.senderRole}`),name:o||null},text:a,attachments:r,receivedAt:Q(n.observedAt),isMentioned:!1,replyTarget:"",rawRef:n.stableMessageKey}}function B(e){const t=e.mediaMetadata;return V(t).map(a=>{const r=a,o=typeof r.localPath=="string"?i(r.localPath):"",s=typeof r.thumbnailPath=="string"?i(r.thumbnailPath):"",c=typeof r.url=="string"?i(r.url):"",d=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))}:{},...c?{url:c}:{},...typeof r.mimeType=="string"?{mimeType:i(r.mimeType)}:{},...typeof r.extension=="string"?{extension:i(r.extension)}:{},...Number.isFinite(d)&&d>0?{size:d}:{},...o?{localPath:o}:{},...s?{thumbnailPath:s}:{},...typeof r.hash=="string"||typeof r.sha256=="string"?{hash:i(String(r.hash||r.sha256))}:{},availability:v(r.availability,{localPath:o,url:c}),...typeof r.providerError=="string"?{providerError:i(r.providerError)}:{},...typeof r.sourceAction=="string"?{sourceAction:i(r.sourceAction)}:{},...U(r.materializationKind)?{materializationKind:r.materializationKind}:{},...typeof r.isOriginal=="boolean"?{isOriginal:r.isOriginal}:{},...typeof r.mimeKindMatches=="boolean"?{mimeKindMatches:r.mimeKindMatches}:{}}})}function V(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 v(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 U(e){return e==="original-file"||e==="clipboard-image"||e==="preview-crop"||e==="metadata"}function G(e){const t=i(e.localPath||"");if(!t)throw new Error("WeChat channel product send requires a local attachment path on this machine");if(!m.statSync(t).isFile())throw new Error(`WeChat channel product attachment is not a file: ${t}`);return t}function J(e){return Array.isArray(e.groups)?e.groups.map(t=>i(t?.name||"")).filter(Boolean):[]}function Z(){return process.env.SHENNIAN_MACHINE_ID?.trim()||W().machineId||"local"}function Q(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 X(e,t,n,a){const r=Number(e);return Number.isFinite(r)?Math.max(n,Math.min(a,Math.floor(r))):t}function l(e,t){return`${e}:${w.createHash("sha256").update(t).digest("hex").slice(0,24)}`}export{z as WECHAT_RPA_PRODUCT_SOURCE,he as cancelWeChatRpaProductOutboundReply,ue as countPendingWeChatRpaProductOutbound,le as createWeChatRpaProductSourceConnection,de as enqueueWeChatRpaProductOutboundReply,ge as isWeChatRpaProductSource,me as listPendingWeChatRpaProductOutbound,fe as listWeChatRpaProductRecentMessages,A as observedMessageToExternalEvent,b as safeWeChatChannelRuntimePathSegment,P as weChatChannelBindingId,E as weChatChannelConversationId,_ as weChatChannelHelperTracePath,x as weChatChannelLedgerPath,u as weChatChannelOutboundLedgerPath};
@@ -0,0 +1,3 @@
1
+ export declare function isShennianRunServiceCommand(command: string): boolean;
2
+ export declare function parseWindowsDaemonProcessJson(raw: string, excludePid?: number): number[];
3
+ export declare function findRunningWindowsDaemonProcessIds(excludePid?: number): number[];
@@ -0,0 +1 @@
1
+ import{execFileSync as d}from"node:child_process";function m(s){const n=s.replace(/\\/g,"/").trim();return l(n)||u(n,"shennian")||u(n,"npx")}function a(s,n=process.pid){let e;try{e=JSON.parse(p(s))}catch{return[]}return(Array.isArray(e)?e:e?[e]:[]).map(r=>{if(!r||typeof r!="object")return null;const i=r,t=Number(i.ProcessId),c=typeof i.CommandLine=="string"?i.CommandLine:"";return!Number.isInteger(t)||t<=0||t===n?null:m(c)?t:null}).filter(r=>typeof r=="number")}function p(s){const n=s.trim(),e=[...n].map((t,c)=>t==="["||t==="{"?c:-1).find(t=>t>=0)??-1;if(e<0)return n;const r=n[e]==="["?"]":"}",i=n.lastIndexOf(r);return i>=e?n.slice(e,i+1):n.slice(e)}function x(s=process.pid){try{const n=["$ProgressPreference = 'SilentlyContinue'","Get-CimInstance Win32_Process | Where-Object { $_.CommandLine -match 'run-service' } | Select-Object ProcessId,CommandLine | ConvertTo-Json -Compress"].join("; "),e=d("powershell.exe",["-NoProfile","-NonInteractive","-Command",n],{encoding:"utf8",stdio:["ignore","pipe","ignore"],timeout:1e3,windowsHide:!0});return a(e,s)}catch{return[]}}function l(s){return/^"[^"]*\/node(?:\.exe)?"\s+"?[^"]*(?:\/node_modules\/shennian\/|\/dist\/bin\/shennian\.js|\/bin\/shennian)"?\s+run-service(?:\s|$)/i.test(s)||/^(?:\S*\/)?node(?:\.exe)?\s+\S*(?:\/node_modules\/shennian\/|\/dist\/bin\/shennian\.js|\/bin\/shennian)\s+run-service(?:\s|$)/i.test(s)}function u(s,n){const e=n==="shennian"?`${n}(?:\\.cmd|\\.exe)?`:`${n}(?:\\.cmd|\\.exe)?`,o=n==="npx"?"\\s+(?:--yes\\s+)?shennian\\s+run-service(?:\\s|$)":"\\s+run-service(?:\\s|$)";return new RegExp(`^"[^"]*\\/${e}"${o}`,"i").test(s)||new RegExp(`^(?:\\S*\\/)?${e}${o}`,"i").test(s)}export{x as findRunningWindowsDaemonProcessIds,m as isShennianRunServiceCommand,a as parseWindowsDaemonProcessJson};
@@ -1,6 +1,7 @@
1
1
  import { spawn } from 'node:child_process';
2
2
  import type { Command } from 'commander';
3
3
  export { buildWindowsLauncherCommand, buildWindowsScheduledTaskXml, buildWindowsStartupVbs, } from './daemon-windows.js';
4
+ export { isShennianRunServiceCommand } from './daemon-process.js';
4
5
  export declare function isSafeSnapshotEnvKey(key: string): boolean;
5
6
  type Platform = 'darwin' | 'linux' | 'win32';
6
7
  type ServiceLaunchMode = 'direct' | 'global-shim' | 'npx';
@@ -51,7 +52,6 @@ export declare function clearDaemonPidIfOwner(pid: number, instanceId: string):
51
52
  export declare function writeDaemonLauncher(pid: number, launcher?: DaemonLauncher, instanceId?: string): void;
52
53
  export declare function clearDaemonLauncher(instanceId?: string): void;
53
54
  export declare function recordStartedDaemon(childPid: number | undefined): void;
54
- export declare function isShennianRunServiceCommand(command: string): boolean;
55
55
  export declare function findRunningDaemonProcessIds(excludePid?: number): number[];
56
56
  export declare function getDaemonStatus(opts?: {
57
57
  cleanupStale?: boolean;
@@ -1,7 +1,7 @@
1
- import s from"chalk";import i from"node:fs";import d from"node:path";import a from"node:os";import{execSync as c,spawn as se}from"node:child_process";import{randomUUID as ce}from"node:crypto";import{fileURLToPath as ae}from"node:url";import{getShennianDir as ue,loadConfig as C,resolveShennianPath as h,saveConfig as de}from"../config/index.js";import{buildAugmentedPath as le}from"../env-path.js";import{buildWindowsLauncherCommand as pe,buildWindowsScheduledTaskXml as me,buildWindowsStartupVbs as fe,escapeXml as N,isWindowsCmdScript as Se,quoteCmdArg as j,quoteSystemdArg as he}from"./daemon-windows.js";import{buildWindowsLauncherCommand as dn,buildWindowsScheduledTaskXml as ln,buildWindowsStartupVbs as pn}from"./daemon-windows.js";const ge=d.dirname(ae(import.meta.url)),y=ue(),p=h("daemon.pid"),u=h("daemon.log"),b=h("remote-access.disabled"),I=h("daemon-launcher.json"),F=d.resolve(ge,"../../bin/shennian.js"),W=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"]);function ye(e){return B.has(e)}function l(){const e=a.platform();return e==="darwin"||e==="linux"||e==="win32"?e:"linux"}function L(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 we(e){return i.existsSync(e.scriptPath)&&!L(e.scriptPath)?{command:e.nodeExec,args:[e.scriptPath,"run-service"],mode:"direct"}:e.shennianCommandPath&&!L(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 ve(e){const n=e.trim();if(!n)return null;if(n.startsWith("{"))try{const r=JSON.parse(n),o=Number(r.pid);return!Number.isInteger(o)||o<=0?null:{pid:o,...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 R(){try{return ve(i.readFileSync(p,"utf-8"))}catch{return null}}function Ee(){return R()?.pid??null}function w(e){try{return process.kill(e,0),!0}catch{return!1}}function ke(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 Pe(){return i.existsSync(b)}function Ae(){try{i.unlinkSync(b)}catch{}}function V(){return"global-cli"}function Ie(){return`${process.pid}-${Date.now()}-${ce()}`}function De(e,n,t={}){i.mkdirSync(y,{recursive:!0}),i.writeFileSync(p,JSON.stringify({pid:e,instanceId:n,...t.version?{version:t.version}:{},launcher:t.launcher??V(),...t.adopted?{adopted:!0}:{},startedAt:new Date().toISOString()},null,2))}function sn(e,n){const t=R();if(!(t?.pid!==e||t.instanceId!==n))try{i.unlinkSync(p)}catch{}}function J(e,n=V(),t){i.mkdirSync(y,{recursive:!0}),i.writeFileSync(I,JSON.stringify({pid:e,launcher:n,...t?{instanceId:t}:{},updatedAt:new Date().toISOString()},null,2))}function O(e){if(e)try{if(JSON.parse(i.readFileSync(I,"utf-8")).instanceId!==e)return}catch{return}try{i.unlinkSync(I)}catch{}}function Ne(e){e&&J(e)}function be(e,n){if(!e||!n)return"unknown";try{const t=JSON.parse(i.readFileSync(I,"utf-8"));if(t.pid!==e)return"unknown";if(t.launcher==="desktop-managed"||t.launcher==="global-cli")return t.launcher}catch{}return $(e)}function $(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 Le(e){const n=e.replace(/\\/g,"/").trim();return/^(?:\S*\/)?node\s+\S*(?:\/node_modules\/shennian\/|\/dist\/bin\/shennian\.js|\/bin\/shennian)\s+run-service(?:\s|$)/.test(n)||/^(?:\S*\/)?shennian(?:\.cmd)?\s+run-service(?:\s|$)/.test(n)||/^(?:\S*\/)?npx(?:\.cmd)?\s+(?:--yes\s+)?shennian\s+run-service(?:\s|$)/.test(n)}function G(e=process.pid){if(l()==="win32")return[];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 o=Number(r[1]),S=r[2];return!Number.isInteger(o)||o===e?null:Le(S)?o:null}).filter(t=>typeof t=="number")}catch{return[]}}function f(e={}){let n=R(),t=n?.pid??null,r=t!==null&&w(t),o=t!==null&&!r,S=n?.adopted===!0;const v=C();if(o&&e.cleanupStale){try{i.unlinkSync(p)}catch{}n=null,t=null,r=!1,o=!1}if(!r){const g=G()[0];if(g&&(t=g,r=!0,o=!1,S=!0,e.cleanupStale)){const M=Ie();De(g,M,{launcher:$(g),adopted:!0}),J(g,$(g)),n={pid:g,instanceId:M,adopted:!0}}}return{running:r,pid:t,stale:o,remoteAccessDisabled:Pe(),launcher:be(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 K(e){const n=e?.trim();if(!n)return;const t=C();t.serverUrl=n,de(t)}function X(e){return e?.trim()||process.env.SHENNIAN_DESKTOP_SERVER_URL?.trim()||void 0}const x="com.shennian.agent",m=d.join(a.homedir(),"Library/LaunchAgents",`${x}.plist`),Re=d.join(a.homedir(),"AppData","Roaming","Microsoft","Windows","Start Menu","Programs","Startup"),T=h("autostart.cmd"),H=h("autostart.vbs"),_=h("autostart.xml"),Oe=d.join(Re,"ShennianAgent.vbs");function q(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(o=>o.trim()).find(Boolean)??null}catch{return null}}function $e(){const e=l()==="win32"?"npx.cmd":"npx",n=d.join(d.dirname(W),e);return i.existsSync(n)?n:q("npx")}function k(){return we({nodeExec:W,scriptPath:F,shennianCommandPath:q("shennian"),npxPath:$e()})}function Y(){try{c(`schtasks /delete /tn "${P}" /f`,{stdio:"pipe",windowsHide:!0})}catch{}}function z(){try{i.unlinkSync(Oe)}catch{}}function xe(){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 Te(e){if(l()==="win32"&&Se(e.command)){const n=[e.command,...e.args].map(j).join(" ");return{command:process.env.ComSpec||"cmd.exe",args:["/d","/s","/c",`"${n}"`],windowsVerbatimArguments:!0}}return{command:e.command,args:e.args}}function He(e,n,t=process.env){return{detached:!0,stdio:["ignore",n,n],env:t,windowsHide:!0,...e.windowsVerbatimArguments?{windowsVerbatimArguments:!0}:{}}}function _e(){const e=k();i.writeFileSync(T,pe(e,u)),i.writeFileSync(H,fe(T)),z(),Y();const n=me({userId:xe(),command:d.join(process.env.SystemRoot||"C:\\Windows","System32","wscript.exe"),arguments:[j(H)]});i.writeFileSync(_,n,"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 e={};for(const n of B)process.env[n]&&(e[n]=process.env[n]);if(e.HOME??=a.homedir(),e.PATH=le({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 Ue(){const e=Q(),n=k(),t=Object.entries(e).map(([o,S])=>` <key>${N(o)}</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 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 C,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"]);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=C();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 M=bn();Ln(g,M,{launcher:T(g),adopted:!0}),J(g,T(g)),e={pid:g,instanceId:M,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=C();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 Mn(){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 Cn(){const n=Q(),e=k(),t=Object.entries(n).map(([i,S])=>` <key>${N(i)}</key>
3
3
  <string>${N(S)}</string>`).join(`
4
- `),r=[n.command,...n.args].map(o=>` <string>${N(o)}</string>`).join(`
4
+ `),r=[e.command,...e.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">
@@ -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 Me(){const e=Q(),n=k(),t=Object.entries(e).map(([o,S])=>`Environment=${o}=${S}`).join(`
31
- `),r=[n.command,...n.args].map(he).join(" ");return`[Unit]
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]
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 Ce(e){c(e,{stdio:"pipe",windowsHide:!0})}function je(){const e=process.getuid?.();if(typeof e=="number")return e;const n=a.userInfo();return typeof n.uid=="number"?n.uid:n.username}function Z(e=je()){return`gui/${e}/${x}`}function Fe(e=Ce){try{return e(`launchctl unload "${m}" 2>/dev/null; launchctl load -w "${m}"`),e(`launchctl kickstart -k "${Z()}"`),!0}catch{try{return e(`launchctl load -w "${m}"`),e(`launchctl kickstart -k "${Z()}"`),!0}catch{return!1}}}function We(){i.mkdirSync(y,{recursive:!0});const e={};for(const[n,t]of Object.entries(process.env))t!==void 0&&ye(n)&&(e[n]=t);i.writeFileSync(h("env.json"),JSON.stringify(e,null,2))}function ee(e={}){i.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}ne(n);const t=i.openSync(u,"a"),r=Te(k()),o=se(r.command,r.args,He(r,t));o.unref(),i.closeSync(t),Ne(o.pid),e.quiet||(console.log(s.green(`\u2713 Background service started (PID ${o.pid})`)),console.log(s.gray(` Logs: ${u}`)))}function ne(e=f({cleanupStale:!0})){if(!(e.pid===null||!e.running||!e.adopted))try{process.kill(e.pid,"SIGTERM"),ke(e.pid);try{i.unlinkSync(p)}catch{}}catch{}}function te(){i.mkdirSync(y,{recursive:!0});const e=l();switch(k().mode==="direct"&&L(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 i.mkdirSync(t,{recursive:!0}),i.writeFileSync(m,Ue()),Fe()}case"linux":{const t=d.dirname(D);i.mkdirSync(t,{recursive:!0}),i.writeFileSync(D,Me());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 _e()}}function Be(){const n=f({cleanupStale:!0}).pid;if(n===null)return{};if(!w(n)){try{i.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 Ve(e=5e3){const n=Be();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{i.unlinkSync(p)}catch{}O();for(const r of G())if(r!==n.stoppedPid)try{process.kill(r,"SIGTERM"),await A(r,2e3)}catch{}return n}function Je(e={}){K(X(e.api)),Ae(),ne(),te()||ee({quiet:e.json}),e.json?E(f()):console.log(s.green("\u2713 Background service started"))}async function Ge(e={}){switch(i.mkdirSync(y,{recursive:!0}),i.writeFileSync(b,new Date().toISOString()),l()){case"darwin":{if(i.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 Ve(),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 re(e){We(),Je(e)}async function U(e={}){await Ge(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 Ke(e={}){K(X(e.api));const n=Ee();if(n!==null&&w(n))try{process.kill(n,"SIGTERM"),await A(n)||(process.kill(n,"SIGKILL"),await A(n,2e3));try{i.unlinkSync(p)}catch{}O(),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{i.unlinkSync(p)}catch{}O(),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"&&te()){e.json&&E(f());return}ee({quiet:e.json}),e.json&&E(f())}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 oe(e){if(!i.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=i.readFileSync(u,"utf-8").split(`
45
- `).slice(-e.lines);console.log(n.join(`
46
- `))}}async function Xe(){switch(await U(),l()){case"darwin":{if(i.existsSync(m)){try{c(`launchctl unload -w "${m}"`,{stdio:"pipe",windowsHide:!0})}catch{}i.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(i.existsSync(D)){i.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{i.unlinkSync(T)}catch{}try{i.unlinkSync(H)}catch{}try{i.unlinkSync(_)}catch{}z(),console.log(s.green("\u2713 Auto-start uninstalled (Task Scheduler)"));break}}}function cn(e){e.command("start").description("Start the background service").option("--json","Print machine-readable status JSON").option("--api <url>","Server URL override").action(r=>re(r)),e.command("stop").description("Stop the background service").option("--json","Print machine-readable status JSON").action(r=>U(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=>oe({lines:parseInt(r.lines,10)}));const n=e.command("daemon",{hidden:!0}).description("Deprecated: use top-level start/stop/status/logs"),t=(r,o)=>{o||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),re(r)}),n.command("stop").description("Stop the background service").option("--json","Print machine-readable status JSON").action(async r=>{t("stop",r.json),await U(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),Ke(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"),oe({lines:parseInt(r.lines,10)})}),n.command("uninstall").description("Uninstall auto-start service").action(Xe)}export{He as buildDaemonSpawnOptions,dn as buildWindowsLauncherCommand,ln as buildWindowsScheduledTaskXml,pn as buildWindowsStartupVbs,Q as captureEnvForService,O as clearDaemonLauncher,sn as clearDaemonPidIfOwner,Ae as clearRemoteAccessDisabled,Ie as createDaemonInstanceId,G as findRunningDaemonProcessIds,V as getCurrentProcessDaemonLauncher,f as getDaemonStatus,Z as getLaunchdServiceTarget,te as installService,L as isEphemeralCliPath,Pe as isRemoteAccessDisabled,ye as isSafeSnapshotEnvKey,Le as isShennianRunServiceCommand,Ne as recordStartedDaemon,cn as registerDaemonCommand,Fe as reloadLaunchdAgent,we as resolveServiceLaunchSpec,We as saveEnvSnapshot,ee as startDaemonProcess,J as writeDaemonLauncher,De as writeDaemonPid};
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,Cn()),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 Mn()}}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};
@@ -1 +1 @@
1
- import f from"node:fs";import y from"node:os";import i from"node:path";import{spawnSync as A}from"node:child_process";import v from"chalk";import{getDefaultWeChatHelperRuntimeRoot as D,readWeChatChannelHelperRuntimePackageManifest as w,resolveWeChatChannelHelperAsset as C,SHENNIAN_HELPER_RUNTIME_DIR_ENV as E,WECHAT_CHANNEL_HELPER_DIR_ENV as O}from"../channels/wechat-channel/helper-assets.js";import{downloadOfficialHelperRuntimePackage as $}from"./helper-runtime-official-download.js";import{compareVersions as x,getCurrentVersion as H}from"../upgrade/engine.js";import{SerialWeChatAutomationLane as U}from"../channels/wechat-channel/automation-lane.js";const u="helper-runtime-package.json";function ce(e){const r=e.command("runtime").description("Install, repair, and inspect Shennian local runtimes");r.command("status").description("Print local Shennian runtime status").option("--json","Output JSON",!1).option("--platform <platform>","Platform override: darwin or win32").action(o=>{const n=V({platform:o.platform});P(n,!!o.json),n.ok||(process.exitCode=1)}),r.command("install").description("Install a Shennian runtime component").argument("<component>","Runtime component, currently only: helper").option("--source <path>","Helper runtime package/source directory").option("--channel <channel>","Official Helper runtime channel: stable or next").option("--manifest-url <url>","Official Helper runtime manifest URL override").option("--no-official-download","Do not fall back to Shennian official Helper runtime download").option("--json","Output JSON",!1).option("--platform <platform>","Platform override: darwin or win32").action(async(o,n)=>{const a=o==="helper"?await b({source:n.source,platform:n.platform,officialDownload:n.officialDownload===!1?!1:{channel:n.channel,manifestUrl:n.manifestUrl}}):M(o);P(a,!!n.json),a.ok||(process.exitCode=1)}),r.command("repair").description("Repair a Shennian runtime component").argument("[component]","Runtime component, currently only: helper","helper").option("--source <path>","Helper runtime package/source directory").option("--channel <channel>","Official Helper runtime channel: stable or next").option("--manifest-url <url>","Official Helper runtime manifest URL override").option("--no-official-download","Do not fall back to Shennian official Helper runtime download").option("--json","Output JSON",!1).option("--platform <platform>","Platform override: darwin or win32").action(async(o,n)=>{const a=o==="helper"?await b({source:n.source,platform:n.platform,force:!0,officialDownload:n.officialDownload===!1?!1:{channel:n.channel,manifestUrl:n.manifestUrl}}):M(o);P(a,!!n.json),a.ok||(process.exitCode=1)})}function V(e={}){const r=k(e.platform);if(!r)return{ok:!1,installed:!1,repairable:!1,reasonCode:"unsupported_platform",message:"Shennian Helper runtime is only available on macOS and Windows.",repairSuggestion:l("unsupported_platform")};const o=e.env??process.env,n=e.homedir,a=C({platform:r,env:o,homedir:n,includeInstalledDesktop:!1}),t=D({platform:r,env:o,homedir:n});if(!a.ok)return{ok:!1,platform:r,targetPlatform:r,installed:!1,repairable:K(a.reasonCode),reasonCode:a.reasonCode,message:a.message,runtimeRoot:t,repairSuggestion:l(a.reasonCode),checkedSources:_({platform:r,env:o,homedir:n})};const s=w({platform:r,helperDir:a.helperDir});if(!s.ok)return{ok:!1,platform:r,targetPlatform:r,installed:!1,repairable:!0,reasonCode:s.reasonCode,message:s.message,helperDir:a.helperDir,helperPath:a.helperPath,helperVersion:a.version,runtimeVersion:a.version,protocolVersion:a.manifest.protocolVersion,runtimeRoot:t,repairSuggestion:l(s.reasonCode),checkedSources:_({platform:r,env:o,homedir:n})};const c=R({platform:r,packageManifest:s.manifest,currentCliVersion:H(),helperVersion:a.version,protocolVersion:a.manifest.protocolVersion});return c.ok?{ok:!0,platform:r,targetPlatform:r,installed:!0,repairable:!1,message:`Shennian Helper runtime is installed: ${a.helperDir}`,helperDir:a.helperDir,helperPath:a.helperPath,helperVersion:a.version,runtimeVersion:a.version,protocolVersion:a.manifest.protocolVersion,minCliVersion:s.manifest.minCliVersion,sha256:s.manifest.sha256,packageManifestPath:s.manifestPath,packageKind:s.manifest.packageKind,installTarget:s.manifest.installTarget,signature:s.manifest.signature,runtimeRoot:t}:{ok:!1,platform:r,targetPlatform:r,installed:!0,repairable:c.repairable,reasonCode:c.reasonCode,message:c.message,helperDir:a.helperDir,helperPath:a.helperPath,helperVersion:a.version,runtimeVersion:a.version,protocolVersion:a.manifest.protocolVersion,minCliVersion:s.manifest.minCliVersion,sha256:s.manifest.sha256,packageManifestPath:s.manifestPath,packageKind:s.manifest.packageKind,installTarget:s.manifest.installTarget,signature:s.manifest.signature,runtimeRoot:t,repairSuggestion:l(c.reasonCode,s.manifest.minCliVersion),checkedSources:_({platform:r,env:o,homedir:n})}}function j(e={}){const r=k(e.platform);if(!r)return{ok:!1,reasonCode:"unsupported_platform",message:"Shennian Helper runtime is only available on macOS and Windows.",repairable:!1,repairSuggestion:l("unsupported_platform")};const o=e.env??process.env,n=F(e.io),a=e.homedir??n.homedir(),t=V({platform:r,env:o,homedir:a});if(t.ok&&!e.force&&t.helperDir&&t.helperPath&&t.helperVersion&&t.protocolVersion)return{ok:!0,action:"already_installed",platform:r,targetPlatform:r,installed:!0,repairable:!1,targetDir:q(r,t.helperDir),helperDir:t.helperDir,helperPath:t.helperPath,helperVersion:t.helperVersion,runtimeVersion:t.helperVersion,protocolVersion:t.protocolVersion,minCliVersion:t.minCliVersion,sha256:t.sha256,packageManifestPath:t.packageManifestPath,packageKind:t.packageKind,installTarget:t.installTarget,signature:t.signature,message:`Shennian Helper runtime is already installed: ${t.helperDir}`};const s=e.source?[i.resolve(e.source)]:_({platform:r,env:o,homedir:a}),c=s.map(S=>z({candidate:S,platform:r,io:n})).find(S=>S!=null);if(!c)return{ok:!1,platform:r,targetPlatform:r,installed:!1,repairable:!0,reasonCode:"helper_runtime_source_missing",message:"No installable Shennian Helper runtime package was found. Open Shennian Desktop, pass --source, or download the official helper package.",repairSuggestion:l("helper_runtime_source_missing"),checkedSources:s};const h=w({platform:r,manifestPath:c.packageManifestPath});if(!h.ok)return{ok:!1,platform:r,targetPlatform:r,installed:!1,repairable:!0,reasonCode:h.reasonCode,message:h.message,repairSuggestion:l(h.reasonCode),checkedSources:s};const g=R({platform:r,packageManifest:h.manifest,currentCliVersion:H()});if(!g.ok)return{ok:!1,platform:r,targetPlatform:r,installed:!1,repairable:g.repairable,reasonCode:g.reasonCode,message:g.message,repairSuggestion:l(g.reasonCode,h.manifest.minCliVersion),checkedSources:s};const d=J({platform:r,env:o,homedir:a,packageKind:c.packageKind});if(!d)return{ok:!1,platform:r,targetPlatform:r,installed:!1,repairable:!1,reasonCode:"helper_runtime_target_unavailable",message:"Unable to determine Helper runtime install target for this platform.",repairSuggestion:l("helper_runtime_target_unavailable"),checkedSources:s};X({platform:r,sourceDir:c.sourceDir,targetDir:d,io:n}),Y({sourceManifestPath:c.packageManifestPath,targetManifestPath:c.packageManifestPathInTarget(d),io:n});const m=C({platform:r,baseDir:c.manifestDirInTarget(d),verifyIntegrity:!0});if(!m.ok)return{ok:!1,platform:r,targetPlatform:r,installed:!1,repairable:!0,reasonCode:m.reasonCode,message:`Installed Helper runtime could not be verified: ${m.message}`,repairSuggestion:l(m.reasonCode),checkedSources:s};Z(r,m.helperPath,n);const p=w({platform:r,helperDir:m.helperDir});return p.ok?{ok:!0,action:"installed",platform:r,targetPlatform:r,installed:!0,repairable:!1,targetDir:d,helperDir:m.helperDir,helperPath:m.helperPath,helperVersion:m.version,runtimeVersion:m.version,protocolVersion:m.manifest.protocolVersion,minCliVersion:p.manifest.minCliVersion,sha256:p.manifest.sha256,packageManifestPath:p.manifestPath,packageKind:p.manifest.packageKind,installTarget:p.manifest.installTarget,signature:p.manifest.signature,message:`Installed Shennian Helper runtime to ${d}`}:{ok:!1,platform:r,targetPlatform:r,installed:!1,repairable:!0,reasonCode:p.reasonCode,message:`Installed Helper runtime package manifest could not be verified: ${p.message}`,repairSuggestion:l(p.reasonCode),checkedSources:s}}async function b(e={}){const r=new U({lockPath:T(e),owner:"helper-runtime-install"});try{return await r.run("helper-runtime-install",()=>N(e))}catch(o){if(L(o)){const n=k(e.platform)??void 0;return{ok:!1,platform:n,targetPlatform:n,installed:!1,repairable:!0,reasonCode:"wechat_automation_busy",message:o instanceof Error?o.message:String(o),repairSuggestion:"Wait for the current WeChat automation run to finish, then retry Helper install or repair."}}throw o}}function T(e){const r=e.env?.SHENNIAN_HOME?.trim()||process.env.SHENNIAN_HOME?.trim(),o=r?i.resolve(r):i.join(e.homedir??y.homedir(),".shennian");return i.join(o,"runtime","wechat-automation.lock")}function L(e){return e instanceof Error?e.message.includes("wechat_automation_busy"):String(e).includes("wechat_automation_busy")}async function N(e={}){const r=j(e);if(r.ok||e.source||e.officialDownload===!1||!W(r.reasonCode))return r.ok?{...r,source:r.source??"local"}:r;const o=k(e.platform);if(!o)return r;const n=await $({...e.officialDownload,platform:o,env:e.env??process.env});if(!n.ok)return{ok:!1,platform:o,targetPlatform:o,installed:!1,repairable:!0,reasonCode:n.reasonCode,message:n.message,repairSuggestion:l(n.reasonCode),checkedSources:r.checkedSources,officialDownload:{manifestUrl:n.manifestUrl,artifactUrl:n.artifactUrl}};try{const a=j({...e,source:n.sourceDir,platform:o});return a.ok?{...a,source:"official-download",officialDownload:{channel:n.channel,manifestUrl:n.manifestUrl,artifactUrl:n.artifactUrl,artifactSha256:n.artifactSha256,artifactSizeBytes:n.artifactSizeBytes,arch:n.arch}}:{...a,reasonCode:a.reasonCode==="helper_runtime_source_missing"?"helper_runtime_artifact_invalid":a.reasonCode,message:a.reasonCode==="helper_runtime_source_missing"?"Official Helper runtime artifact did not contain an installable runtime package.":a.message,repairSuggestion:l(a.reasonCode==="helper_runtime_source_missing"?"helper_runtime_artifact_invalid":a.reasonCode),officialDownload:{manifestUrl:n.manifestUrl,artifactUrl:n.artifactUrl}}}finally{n.cleanup()}}function W(e){return e==="helper_runtime_source_missing"||e==="helper_runtime_package_manifest_missing"||e==="helper_runtime_package_manifest_invalid"}function k(e){const r=e??process.platform;return r==="darwin"||r==="win32"?r:null}function R(e){return e.packageManifest.platform!==e.platform?{ok:!1,reasonCode:"helper_runtime_platform_mismatch",message:`Helper runtime package platform ${e.packageManifest.platform} does not match ${e.platform}.`,repairable:!0}:e.helperVersion&&e.packageManifest.helperVersion!==e.helperVersion?{ok:!1,reasonCode:"helper_runtime_version_mismatch",message:`Helper runtime package version ${e.packageManifest.helperVersion} does not match installed helper ${e.helperVersion}.`,repairable:!0}:e.protocolVersion&&e.packageManifest.protocolVersion!==e.protocolVersion?{ok:!1,reasonCode:"helper_runtime_protocol_mismatch",message:`Helper runtime package protocol ${e.packageManifest.protocolVersion} does not match installed helper protocol ${e.protocolVersion}.`,repairable:!0}:x(e.currentCliVersion,e.packageManifest.minCliVersion)!=="none"?{ok:!1,reasonCode:"helper_runtime_cli_too_old",message:`Shennian CLI ${e.currentCliVersion} is older than required ${e.packageManifest.minCliVersion}.`,repairable:!1}:{ok:!0}}function K(e){return e!=="unsupported_platform"&&e!=="helper_runtime_cli_too_old"}function l(e,r){if(e==="unsupported_platform")return"Use Shennian Helper runtime on macOS or Windows.";if(e==="unsupported_runtime_component")return"Use `shennian runtime install helper` or `shennian runtime repair helper`.";if(e==="helper_runtime_cli_too_old")return`Upgrade Shennian CLI${r?` to ${r} or newer`:""}, then retry.`;if(e==="helper_runtime_target_unavailable")return"Set the current user home/LOCALAPPDATA correctly, then retry.";if(e==="helper_runtime_required")return"Open Shennian Desktop or use the \u4F7F\u7528\u5FAE\u4FE1 page to install Helper.";if(e==="helper_runtime_source_missing")return"Open Shennian Desktop, retry from the \u4F7F\u7528\u5FAE\u4FE1 page, or pass `--source <path>`.";if(e==="helper_runtime_manifest_unavailable")return"Check network access to Shennian official downloads, then retry from the \u4F7F\u7528\u5FAE\u4FE1 page.";if(e==="helper_runtime_artifact_unavailable")return"Upgrade Shennian CLI or wait for this platform Helper runtime to be published.";if(e==="helper_runtime_download_failed")return"Check network access to Shennian official downloads, then retry.";if(e==="helper_runtime_artifact_integrity_mismatch")return"Retry the official Helper runtime download; the downloaded package did not match the published hash.";if(e==="helper_runtime_artifact_invalid")return"Retry after Shennian republishes a valid Helper runtime package for this platform.";if(e==="helper_runtime_package_manifest_missing"||e==="helper_runtime_package_manifest_invalid")return"Reinstall or repair the official Shennian Helper runtime package.";if(e==="helper_runtime_platform_mismatch")return"Install the Helper runtime package for this platform.";if(e==="helper_runtime_version_mismatch"||e==="helper_runtime_protocol_mismatch")return"Run `shennian runtime repair helper` with the matching official Helper runtime package.";if(e==="manifest_missing"||e==="helper_missing"||e==="integrity_mismatch"||e==="helper_not_executable")return"Run `shennian runtime repair helper` or reinstall Shennian Desktop."}function M(e){return{ok:!1,repairable:!1,reasonCode:"unsupported_runtime_component",message:`Unsupported runtime component: ${e}. Supported component: helper.`,repairSuggestion:l("unsupported_runtime_component")}}function F(e){return{existsSync:e?.existsSync??f.existsSync,readFileSync:e?.readFileSync??f.readFileSync,mkdirSync:e?.mkdirSync??f.mkdirSync,rmSync:e?.rmSync??f.rmSync,cpSync:e?.cpSync??f.cpSync,renameSync:e?.renameSync??f.renameSync,chmodSync:e?.chmodSync??f.chmodSync,homedir:e?.homedir??y.homedir,stopWindowsHelperProcesses:e?.stopWindowsHelperProcesses??Q}}function z(e){const r=i.resolve(e.candidate);return e.io.existsSync(r)?e.platform==="darwin"?B(r,e.io):G(r,e.io):null}function B(e,r){const o=e.endsWith(".app")?e:i.join(e,"Shennian Helper.app");if(r.existsSync(i.join(o,"Contents","Resources","wechat-channel","macos","manifest.json")))return{sourceDir:o,packageKind:"macos-app",packageManifestPath:I(r,[i.join(o,"Contents","Resources","helper-runtime-package.json"),i.join(e,u)]),packageManifestPathInTarget:a=>i.join(a,"Contents","Resources",u),manifestDirInTarget:a=>i.join(a,"Contents","Resources","wechat-channel","macos")};const n=r.existsSync(i.join(e,"manifest.json"))?e:i.join(e,"wechat-channel","macos");return r.existsSync(i.join(n,"manifest.json"))?{sourceDir:n,packageKind:"asset-dir",packageManifestPath:i.join(n,"helper-runtime-package.json"),packageManifestPathInTarget:a=>i.join(a,u),manifestDirInTarget:a=>a}:null}function G(e,r){const o=i.basename(e).toLowerCase()==="shennian helper"?e:i.join(e,"Shennian Helper");if(r.existsSync(i.join(o,"resources","wechat-channel","windows","manifest.json")))return{sourceDir:o,packageKind:"windows-runtime",packageManifestPath:I(r,[i.join(o,"resources",u),i.join(e,u)]),packageManifestPathInTarget:a=>i.join(a,"resources",u),manifestDirInTarget:a=>i.join(a,"resources","wechat-channel","windows")};const n=r.existsSync(i.join(e,"manifest.json"))?e:i.join(e,"wechat-channel","windows");return r.existsSync(i.join(n,"manifest.json"))?{sourceDir:n,packageKind:"asset-dir",packageManifestPath:i.join(n,"helper-runtime-package.json"),packageManifestPathInTarget:a=>i.join(a,u),manifestDirInTarget:a=>a}:null}function J(e){const r=D(e);if(!r)return null;if(e.platform==="darwin")return e.packageKind==="macos-app"?i.join(r,"Shennian Helper.app"):i.join(r,"wechat-channel","macos");if(e.packageKind==="windows-runtime"){const o=e.env.LOCALAPPDATA||(e.homedir?i.join(e.homedir,"AppData","Local"):"");return o?i.join(o,"Programs","Shennian Helper"):null}return i.join(r,"wechat-channel","windows")}function q(e,r){const o=i.resolve(r);if(e==="darwin"){const a=`${i.sep}Contents${i.sep}Resources${i.sep}wechat-channel${i.sep}macos`;return o.endsWith(a)?o.slice(0,-a.length):o}const n=`${i.sep}resources${i.sep}wechat-channel${i.sep}windows`;return o.endsWith(n)?o.slice(0,-n.length):o}function X(e){const r=i.resolve(e.sourceDir),o=i.resolve(e.targetDir);if(r===o)return;const n=`${o}.tmp-${process.pid}-${Date.now()}`;e.io.rmSync(n,{recursive:!0,force:!0}),e.io.mkdirSync(i.dirname(o),{recursive:!0}),e.io.cpSync(r,n,{recursive:!0}),e.platform==="win32"&&e.io.stopWindowsHelperProcesses(),e.io.rmSync(o,{recursive:!0,force:!0}),e.io.renameSync(n,o)}function Q(){if(process.platform!=="win32")return;const e=A("taskkill",["/IM","shennian-wechat-channel-helper.exe","/T","/F"],{stdio:"ignore",windowsHide:!0});if(e.status!==0&&e.status!==128)throw new Error(`taskkill shennian-wechat-channel-helper.exe exited ${e.status}`)}function Y(e){const r=i.resolve(e.sourceManifestPath),o=i.resolve(e.targetManifestPath);!e.io.existsSync(r)||r===o||(e.io.mkdirSync(i.dirname(o),{recursive:!0}),e.io.cpSync(r,o))}function I(e,r){return r.find(o=>e.existsSync(o))??r[0]}function Z(e,r,o){if(e==="darwin")try{const n=f.statSync(r);(n.mode&73)===0&&o.chmodSync(r,n.mode|493)}catch{}}function _(e){const r=e.homedir||(e.platform==="win32"?e.env.USERPROFILE:e.env.HOME),o=[],n=a=>{if(!a)return;const t=i.resolve(a);o.includes(t)||o.push(t)};if(n(e.env.SHENNIAN_HELPER_PACKAGE_DIR),n(e.env[O]),n(e.env[E]),e.platform==="darwin")r&&(n(i.join(r,"Applications","Shennian.app","Contents","Resources")),n(i.join(r,"Applications","Shennian Helper.app"))),n(i.join("/Applications","Shennian.app","Contents","Resources")),n(i.join("/Applications","Shennian Helper.app"));else{const a=e.env.LOCALAPPDATA||(r?i.join(r,"AppData","Local"):""),t=e.env.ProgramFiles||e.env.PROGRAMFILES,s=e.env["ProgramFiles(x86)"]||e.env["PROGRAMFILES(X86)"];a&&(n(i.join(a,"Programs","Shennian","resources")),n(i.join(a,"Programs","Shennian Helper"))),t&&(n(i.join(t,"Shennian","resources")),n(i.join(t,"Shennian Helper"))),s&&(n(i.join(s,"Shennian","resources")),n(i.join(s,"Shennian Helper")))}return n(i.resolve(process.cwd(),"packages/helper-runtime/dist",e.platform==="darwin"?"macos":"windows")),n(i.resolve(process.cwd(),"packages/helper-runtime/wechat-channel",e.platform==="darwin"?"macos":"windows")),o}function P(e,r){if(r){console.log(JSON.stringify(e,null,2));return}if(e.ok){console.log(v.green(`\u2713 ${e.message}`)),"helperVersion"in e&&console.log(`version=${e.helperVersion} protocol=${e.protocolVersion}`),"helperPath"in e&&console.log(e.helperPath);return}console.error(v.red(`\u2717 ${e.reasonCode}`)),console.error(e.message)}export{V as getHelperRuntimeStatus,j as installHelperRuntime,b as installHelperRuntimeWithOfficialDownload,ce as registerRuntimeCommand};
1
+ import f from"node:fs";import y from"node:os";import t from"node:path";import{spawnSync as A}from"node:child_process";import v from"chalk";import{getDefaultWeChatHelperRuntimeRoot as D,readWeChatChannelHelperRuntimePackageManifest as w,resolveWeChatChannelHelperAsset as C,SHENNIAN_HELPER_RUNTIME_DIR_ENV as E,WECHAT_CHANNEL_HELPER_DIR_ENV as O}from"../channels/wechat-channel/helper-assets.js";import{downloadOfficialHelperRuntimePackage as $}from"./helper-runtime-official-download.js";import{compareVersions as x,getCurrentVersion as H}from"../upgrade/engine.js";import{SerialWeChatAutomationLane as U}from"../channels/wechat-channel/automation-lane.js";const u="helper-runtime-package.json";function ce(e){const r=e.command("runtime").description("Install, repair, and inspect Shennian local runtimes");r.command("status").description("Print local Shennian runtime status").option("--json","Output JSON",!1).option("--platform <platform>","Platform override: darwin or win32").action(o=>{const n=V({platform:o.platform});P(n,!!o.json),n.ok||(process.exitCode=1)}),r.command("install").description("Install a Shennian runtime component").argument("<component>","Runtime component, currently only: helper").option("--source <path>","Helper runtime package/source directory").option("--channel <channel>","Official Helper runtime channel: stable or next").option("--manifest-url <url>","Official Helper runtime manifest URL override").option("--no-official-download","Do not fall back to Shennian official Helper runtime download").option("--json","Output JSON",!1).option("--platform <platform>","Platform override: darwin or win32").action(async(o,n)=>{const a=o==="helper"?await b({source:n.source,platform:n.platform,officialDownload:n.officialDownload===!1?!1:{channel:n.channel,manifestUrl:n.manifestUrl}}):M(o);P(a,!!n.json),a.ok||(process.exitCode=1)}),r.command("repair").description("Repair a Shennian runtime component").argument("[component]","Runtime component, currently only: helper","helper").option("--source <path>","Helper runtime package/source directory").option("--channel <channel>","Official Helper runtime channel: stable or next").option("--manifest-url <url>","Official Helper runtime manifest URL override").option("--no-official-download","Do not fall back to Shennian official Helper runtime download").option("--json","Output JSON",!1).option("--platform <platform>","Platform override: darwin or win32").action(async(o,n)=>{const a=o==="helper"?await b({source:n.source,platform:n.platform,force:!0,officialDownload:n.officialDownload===!1?!1:{channel:n.channel,manifestUrl:n.manifestUrl}}):M(o);P(a,!!n.json),a.ok||(process.exitCode=1)})}function V(e={}){const r=k(e.platform);if(!r)return{ok:!1,installed:!1,repairable:!1,reasonCode:"unsupported_platform",message:"Shennian Helper runtime is only available on macOS and Windows.",repairSuggestion:l("unsupported_platform")};const o=e.env??process.env,n=e.homedir,a=C({platform:r,env:o,homedir:n,includeInstalledDesktop:!1}),i=D({platform:r,env:o,homedir:n});if(!a.ok)return{ok:!1,platform:r,targetPlatform:r,installed:!1,repairable:K(a.reasonCode),reasonCode:a.reasonCode,message:a.message,runtimeRoot:i,repairSuggestion:l(a.reasonCode),checkedSources:_({platform:r,env:o,homedir:n})};const s=w({platform:r,helperDir:a.helperDir});if(!s.ok)return{ok:!1,platform:r,targetPlatform:r,installed:!1,repairable:!0,reasonCode:s.reasonCode,message:s.message,helperDir:a.helperDir,helperPath:a.helperPath,helperVersion:a.version,runtimeVersion:a.version,protocolVersion:a.manifest.protocolVersion,runtimeRoot:i,repairSuggestion:l(s.reasonCode),checkedSources:_({platform:r,env:o,homedir:n})};const c=R({platform:r,packageManifest:s.manifest,currentCliVersion:H(),helperVersion:a.version,protocolVersion:a.manifest.protocolVersion});return c.ok?{ok:!0,platform:r,targetPlatform:r,installed:!0,repairable:!1,message:`Shennian Helper runtime is installed: ${a.helperDir}`,helperDir:a.helperDir,helperPath:a.helperPath,helperVersion:a.version,runtimeVersion:a.version,protocolVersion:a.manifest.protocolVersion,minCliVersion:s.manifest.minCliVersion,sha256:s.manifest.sha256,packageManifestPath:s.manifestPath,packageKind:s.manifest.packageKind,installTarget:s.manifest.installTarget,signature:s.manifest.signature,runtimeRoot:i}:{ok:!1,platform:r,targetPlatform:r,installed:!0,repairable:c.repairable,reasonCode:c.reasonCode,message:c.message,helperDir:a.helperDir,helperPath:a.helperPath,helperVersion:a.version,runtimeVersion:a.version,protocolVersion:a.manifest.protocolVersion,minCliVersion:s.manifest.minCliVersion,sha256:s.manifest.sha256,packageManifestPath:s.manifestPath,packageKind:s.manifest.packageKind,installTarget:s.manifest.installTarget,signature:s.manifest.signature,runtimeRoot:i,repairSuggestion:l(c.reasonCode,s.manifest.minCliVersion),checkedSources:_({platform:r,env:o,homedir:n})}}function j(e={}){const r=k(e.platform);if(!r)return{ok:!1,reasonCode:"unsupported_platform",message:"Shennian Helper runtime is only available on macOS and Windows.",repairable:!1,repairSuggestion:l("unsupported_platform")};const o=e.env??process.env,n=F(e.io),a=e.homedir??n.homedir(),i=V({platform:r,env:o,homedir:a});if(i.ok&&!e.force&&i.helperDir&&i.helperPath&&i.helperVersion&&i.protocolVersion)return{ok:!0,action:"already_installed",platform:r,targetPlatform:r,installed:!0,repairable:!1,targetDir:q(r,i.helperDir),helperDir:i.helperDir,helperPath:i.helperPath,helperVersion:i.helperVersion,runtimeVersion:i.helperVersion,protocolVersion:i.protocolVersion,minCliVersion:i.minCliVersion,sha256:i.sha256,packageManifestPath:i.packageManifestPath,packageKind:i.packageKind,installTarget:i.installTarget,signature:i.signature,message:`Shennian Helper runtime is already installed: ${i.helperDir}`};const s=e.source?[t.resolve(e.source)]:_({platform:r,env:o,homedir:a}),c=s.map(S=>z({candidate:S,platform:r,io:n})).find(S=>S!=null);if(!c)return{ok:!1,platform:r,targetPlatform:r,installed:!1,repairable:!0,reasonCode:"helper_runtime_source_missing",message:"No installable Shennian Helper runtime package was found. Open Shennian Desktop, pass --source, or download the official helper package.",repairSuggestion:l("helper_runtime_source_missing"),checkedSources:s};const h=w({platform:r,manifestPath:c.packageManifestPath});if(!h.ok)return{ok:!1,platform:r,targetPlatform:r,installed:!1,repairable:!0,reasonCode:h.reasonCode,message:h.message,repairSuggestion:l(h.reasonCode),checkedSources:s};const g=R({platform:r,packageManifest:h.manifest,currentCliVersion:H()});if(!g.ok)return{ok:!1,platform:r,targetPlatform:r,installed:!1,repairable:g.repairable,reasonCode:g.reasonCode,message:g.message,repairSuggestion:l(g.reasonCode,h.manifest.minCliVersion),checkedSources:s};const d=J({platform:r,env:o,homedir:a,packageKind:c.packageKind});if(!d)return{ok:!1,platform:r,targetPlatform:r,installed:!1,repairable:!1,reasonCode:"helper_runtime_target_unavailable",message:"Unable to determine Helper runtime install target for this platform.",repairSuggestion:l("helper_runtime_target_unavailable"),checkedSources:s};X({platform:r,sourceDir:c.sourceDir,targetDir:d,io:n}),Y({sourceManifestPath:c.packageManifestPath,targetManifestPath:c.packageManifestPathInTarget(d),io:n});const m=C({platform:r,baseDir:c.manifestDirInTarget(d),verifyIntegrity:!0});if(!m.ok)return{ok:!1,platform:r,targetPlatform:r,installed:!1,repairable:!0,reasonCode:m.reasonCode,message:`Installed Helper runtime could not be verified: ${m.message}`,repairSuggestion:l(m.reasonCode),checkedSources:s};Z(r,m.helperPath,n);const p=w({platform:r,helperDir:m.helperDir});return p.ok?{ok:!0,action:"installed",platform:r,targetPlatform:r,installed:!0,repairable:!1,targetDir:d,helperDir:m.helperDir,helperPath:m.helperPath,helperVersion:m.version,runtimeVersion:m.version,protocolVersion:m.manifest.protocolVersion,minCliVersion:p.manifest.minCliVersion,sha256:p.manifest.sha256,packageManifestPath:p.manifestPath,packageKind:p.manifest.packageKind,installTarget:p.manifest.installTarget,signature:p.manifest.signature,message:`Installed Shennian Helper runtime to ${d}`}:{ok:!1,platform:r,targetPlatform:r,installed:!1,repairable:!0,reasonCode:p.reasonCode,message:`Installed Helper runtime package manifest could not be verified: ${p.message}`,repairSuggestion:l(p.reasonCode),checkedSources:s}}async function b(e={}){const r=new U({lockPath:T(e),owner:"helper-runtime-install",defaultWaitMs:0});try{return await r.run("helper-runtime-install",()=>N(e))}catch(o){if(L(o)){const n=k(e.platform)??void 0;return{ok:!1,platform:n,targetPlatform:n,installed:!1,repairable:!0,reasonCode:"wechat_automation_busy",message:o instanceof Error?o.message:String(o),repairSuggestion:"Wait for the current WeChat automation run to finish, then retry Helper install or repair."}}throw o}}function T(e){const r=e.env?.SHENNIAN_HOME?.trim()||process.env.SHENNIAN_HOME?.trim(),o=r?t.resolve(r):t.join(e.homedir??y.homedir(),".shennian");return t.join(o,"runtime","wechat-automation.lock")}function L(e){return e instanceof Error?e.message.includes("wechat_automation_busy"):String(e).includes("wechat_automation_busy")}async function N(e={}){const r=j(e);if(r.ok||e.source||e.officialDownload===!1||!W(r.reasonCode))return r.ok?{...r,source:r.source??"local"}:r;const o=k(e.platform);if(!o)return r;const n=await $({...e.officialDownload,platform:o,env:e.env??process.env});if(!n.ok)return{ok:!1,platform:o,targetPlatform:o,installed:!1,repairable:!0,reasonCode:n.reasonCode,message:n.message,repairSuggestion:l(n.reasonCode),checkedSources:r.checkedSources,officialDownload:{manifestUrl:n.manifestUrl,artifactUrl:n.artifactUrl}};try{const a=j({...e,source:n.sourceDir,platform:o});return a.ok?{...a,source:"official-download",officialDownload:{channel:n.channel,manifestUrl:n.manifestUrl,artifactUrl:n.artifactUrl,artifactSha256:n.artifactSha256,artifactSizeBytes:n.artifactSizeBytes,arch:n.arch}}:{...a,reasonCode:a.reasonCode==="helper_runtime_source_missing"?"helper_runtime_artifact_invalid":a.reasonCode,message:a.reasonCode==="helper_runtime_source_missing"?"Official Helper runtime artifact did not contain an installable runtime package.":a.message,repairSuggestion:l(a.reasonCode==="helper_runtime_source_missing"?"helper_runtime_artifact_invalid":a.reasonCode),officialDownload:{manifestUrl:n.manifestUrl,artifactUrl:n.artifactUrl}}}finally{n.cleanup()}}function W(e){return e==="helper_runtime_source_missing"||e==="helper_runtime_package_manifest_missing"||e==="helper_runtime_package_manifest_invalid"}function k(e){const r=e??process.platform;return r==="darwin"||r==="win32"?r:null}function R(e){return e.packageManifest.platform!==e.platform?{ok:!1,reasonCode:"helper_runtime_platform_mismatch",message:`Helper runtime package platform ${e.packageManifest.platform} does not match ${e.platform}.`,repairable:!0}:e.helperVersion&&e.packageManifest.helperVersion!==e.helperVersion?{ok:!1,reasonCode:"helper_runtime_version_mismatch",message:`Helper runtime package version ${e.packageManifest.helperVersion} does not match installed helper ${e.helperVersion}.`,repairable:!0}:e.protocolVersion&&e.packageManifest.protocolVersion!==e.protocolVersion?{ok:!1,reasonCode:"helper_runtime_protocol_mismatch",message:`Helper runtime package protocol ${e.packageManifest.protocolVersion} does not match installed helper protocol ${e.protocolVersion}.`,repairable:!0}:x(e.currentCliVersion,e.packageManifest.minCliVersion)!=="none"?{ok:!1,reasonCode:"helper_runtime_cli_too_old",message:`Shennian CLI ${e.currentCliVersion} is older than required ${e.packageManifest.minCliVersion}.`,repairable:!1}:{ok:!0}}function K(e){return e!=="unsupported_platform"&&e!=="helper_runtime_cli_too_old"}function l(e,r){if(e==="unsupported_platform")return"Use Shennian Helper runtime on macOS or Windows.";if(e==="unsupported_runtime_component")return"Use `shennian runtime install helper` or `shennian runtime repair helper`.";if(e==="helper_runtime_cli_too_old")return`Upgrade Shennian CLI${r?` to ${r} or newer`:""}, then retry.`;if(e==="helper_runtime_target_unavailable")return"Set the current user home/LOCALAPPDATA correctly, then retry.";if(e==="helper_runtime_required")return"Open Shennian Desktop or use the \u4F7F\u7528\u5FAE\u4FE1 page to install Helper.";if(e==="helper_runtime_source_missing")return"Open Shennian Desktop, retry from the \u4F7F\u7528\u5FAE\u4FE1 page, or pass `--source <path>`.";if(e==="helper_runtime_manifest_unavailable")return"Check network access to Shennian official downloads, then retry from the \u4F7F\u7528\u5FAE\u4FE1 page.";if(e==="helper_runtime_artifact_unavailable")return"Upgrade Shennian CLI or wait for this platform Helper runtime to be published.";if(e==="helper_runtime_download_failed")return"Check network access to Shennian official downloads, then retry.";if(e==="helper_runtime_artifact_integrity_mismatch")return"Retry the official Helper runtime download; the downloaded package did not match the published hash.";if(e==="helper_runtime_artifact_invalid")return"Retry after Shennian republishes a valid Helper runtime package for this platform.";if(e==="helper_runtime_package_manifest_missing"||e==="helper_runtime_package_manifest_invalid")return"Reinstall or repair the official Shennian Helper runtime package.";if(e==="helper_runtime_platform_mismatch")return"Install the Helper runtime package for this platform.";if(e==="helper_runtime_version_mismatch"||e==="helper_runtime_protocol_mismatch")return"Run `shennian runtime repair helper` with the matching official Helper runtime package.";if(e==="manifest_missing"||e==="helper_missing"||e==="integrity_mismatch"||e==="helper_not_executable")return"Run `shennian runtime repair helper` or reinstall Shennian Desktop."}function M(e){return{ok:!1,repairable:!1,reasonCode:"unsupported_runtime_component",message:`Unsupported runtime component: ${e}. Supported component: helper.`,repairSuggestion:l("unsupported_runtime_component")}}function F(e){return{existsSync:e?.existsSync??f.existsSync,readFileSync:e?.readFileSync??f.readFileSync,mkdirSync:e?.mkdirSync??f.mkdirSync,rmSync:e?.rmSync??f.rmSync,cpSync:e?.cpSync??f.cpSync,renameSync:e?.renameSync??f.renameSync,chmodSync:e?.chmodSync??f.chmodSync,homedir:e?.homedir??y.homedir,stopWindowsHelperProcesses:e?.stopWindowsHelperProcesses??Q}}function z(e){const r=t.resolve(e.candidate);return e.io.existsSync(r)?e.platform==="darwin"?B(r,e.io):G(r,e.io):null}function B(e,r){const o=e.endsWith(".app")?e:t.join(e,"Shennian Helper.app");if(r.existsSync(t.join(o,"Contents","Resources","wechat-channel","macos","manifest.json")))return{sourceDir:o,packageKind:"macos-app",packageManifestPath:I(r,[t.join(o,"Contents","Resources","helper-runtime-package.json"),t.join(e,u)]),packageManifestPathInTarget:a=>t.join(a,"Contents","Resources",u),manifestDirInTarget:a=>t.join(a,"Contents","Resources","wechat-channel","macos")};const n=r.existsSync(t.join(e,"manifest.json"))?e:t.join(e,"wechat-channel","macos");return r.existsSync(t.join(n,"manifest.json"))?{sourceDir:n,packageKind:"asset-dir",packageManifestPath:t.join(n,"helper-runtime-package.json"),packageManifestPathInTarget:a=>t.join(a,u),manifestDirInTarget:a=>a}:null}function G(e,r){const o=t.basename(e).toLowerCase()==="shennian helper"?e:t.join(e,"Shennian Helper");if(r.existsSync(t.join(o,"resources","wechat-channel","windows","manifest.json")))return{sourceDir:o,packageKind:"windows-runtime",packageManifestPath:I(r,[t.join(o,"resources",u),t.join(e,u)]),packageManifestPathInTarget:a=>t.join(a,"resources",u),manifestDirInTarget:a=>t.join(a,"resources","wechat-channel","windows")};const n=r.existsSync(t.join(e,"manifest.json"))?e:t.join(e,"wechat-channel","windows");return r.existsSync(t.join(n,"manifest.json"))?{sourceDir:n,packageKind:"asset-dir",packageManifestPath:t.join(n,"helper-runtime-package.json"),packageManifestPathInTarget:a=>t.join(a,u),manifestDirInTarget:a=>a}:null}function J(e){const r=D(e);if(!r)return null;if(e.platform==="darwin")return e.packageKind==="macos-app"?t.join(r,"Shennian Helper.app"):t.join(r,"wechat-channel","macos");if(e.packageKind==="windows-runtime"){const o=e.env.LOCALAPPDATA||(e.homedir?t.join(e.homedir,"AppData","Local"):"");return o?t.join(o,"Programs","Shennian Helper"):null}return t.join(r,"wechat-channel","windows")}function q(e,r){const o=t.resolve(r);if(e==="darwin"){const a=`${t.sep}Contents${t.sep}Resources${t.sep}wechat-channel${t.sep}macos`;return o.endsWith(a)?o.slice(0,-a.length):o}const n=`${t.sep}resources${t.sep}wechat-channel${t.sep}windows`;return o.endsWith(n)?o.slice(0,-n.length):o}function X(e){const r=t.resolve(e.sourceDir),o=t.resolve(e.targetDir);if(r===o)return;const n=`${o}.tmp-${process.pid}-${Date.now()}`;e.io.rmSync(n,{recursive:!0,force:!0}),e.io.mkdirSync(t.dirname(o),{recursive:!0}),e.io.cpSync(r,n,{recursive:!0}),e.platform==="win32"&&e.io.stopWindowsHelperProcesses(),e.io.rmSync(o,{recursive:!0,force:!0}),e.io.renameSync(n,o)}function Q(){if(process.platform!=="win32")return;const e=A("taskkill",["/IM","shennian-wechat-channel-helper.exe","/T","/F"],{stdio:"ignore",windowsHide:!0});if(e.status!==0&&e.status!==128)throw new Error(`taskkill shennian-wechat-channel-helper.exe exited ${e.status}`)}function Y(e){const r=t.resolve(e.sourceManifestPath),o=t.resolve(e.targetManifestPath);!e.io.existsSync(r)||r===o||(e.io.mkdirSync(t.dirname(o),{recursive:!0}),e.io.cpSync(r,o))}function I(e,r){return r.find(o=>e.existsSync(o))??r[0]}function Z(e,r,o){if(e==="darwin")try{const n=f.statSync(r);(n.mode&73)===0&&o.chmodSync(r,n.mode|493)}catch{}}function _(e){const r=e.homedir||(e.platform==="win32"?e.env.USERPROFILE:e.env.HOME),o=[],n=a=>{if(!a)return;const i=t.resolve(a);o.includes(i)||o.push(i)};if(n(e.env.SHENNIAN_HELPER_PACKAGE_DIR),n(e.env[O]),n(e.env[E]),e.platform==="darwin")r&&(n(t.join(r,"Applications","Shennian.app","Contents","Resources")),n(t.join(r,"Applications","Shennian Helper.app"))),n(t.join("/Applications","Shennian.app","Contents","Resources")),n(t.join("/Applications","Shennian Helper.app"));else{const a=e.env.LOCALAPPDATA||(r?t.join(r,"AppData","Local"):""),i=e.env.ProgramFiles||e.env.PROGRAMFILES,s=e.env["ProgramFiles(x86)"]||e.env["PROGRAMFILES(X86)"];a&&(n(t.join(a,"Programs","Shennian","resources")),n(t.join(a,"Programs","Shennian Helper"))),i&&(n(t.join(i,"Shennian","resources")),n(t.join(i,"Shennian Helper"))),s&&(n(t.join(s,"Shennian","resources")),n(t.join(s,"Shennian Helper")))}return n(t.resolve(process.cwd(),"packages/helper-runtime/dist",e.platform==="darwin"?"macos":"windows")),n(t.resolve(process.cwd(),"packages/helper-runtime/wechat-channel",e.platform==="darwin"?"macos":"windows")),o}function P(e,r){if(r){console.log(JSON.stringify(e,null,2));return}if(e.ok){console.log(v.green(`\u2713 ${e.message}`)),"helperVersion"in e&&console.log(`version=${e.helperVersion} protocol=${e.protocolVersion}`),"helperPath"in e&&console.log(e.helperPath);return}console.error(v.red(`\u2717 ${e.reasonCode}`)),console.error(e.message)}export{V as getHelperRuntimeStatus,j as installHelperRuntime,b as installHelperRuntimeWithOfficialDownload,ce as registerRuntimeCommand};
@@ -1 +1 @@
1
- import{readExternalAttachment as w}from"../external-attachments.js";import{runWeChatDoctor as g}from"./doctor.js";import{runWeChatReadLatest as C,runWeChatSend as k}from"./operations.js";import{printReadLatestResult as v,printToolError as m,printToolResult as y}from"./output.js";import{clampPositiveInteger as u}from"./utils.js";const c=5*6e4,j=10*6e4;function _(e){const o=e.command("wechat").description("Use WeChat through Shennian local runtime");o.option("--conversation <name>","WeChat conversation/group name"),o.command("doctor").description("Check local Shennian, pairing, entitlement, credits, and WeChat runtime readiness").option("--json","Output JSON",!1).action(async r=>{const t=await g();if(r.json)console.log(JSON.stringify(t,null,2));else for(const a of t.checks){const n=a.ok?"\u2713":a.status==="warning"?"!":"\u2717";console.log(`${n} ${a.id}: ${a.message}`)}t.ok||(process.exitCode=1)}),o.command("write").aliases(["send"]).description("Write a message to a local WeChat conversation through Shennian").argument("[text...]","Message text").option("--conversation <name>","WeChat conversation/group name").option("--text <text>","Message text").option("--file <path>","File attachment path").option("--image <path>","Image attachment path").option("--video <path>","Video attachment path").option("--session-id <id>","Bind to an existing Shennian chat/session; omit for direct local WeChat tool mode").option("--work-dir <path>","Local tool work dir; defaults under ~/.shennian/wechat-cli").option("--idempotency-key <key>","Idempotency key").option("--dry-run","Validate the request and output the planned write without sending",!1).option("--trace-id <id>","Trace id included in JSON output").option("--timeout <ms>",`Manager IPC request timeout in milliseconds. Default: ${c}`).option("--transport <mode>","Execution transport: auto, daemon, or direct","auto").option("--format <format>","Output format: json or text","text").option("--json","Output JSON",!1).action(async(r,t,a)=>{let n;try{n=l(a,t.conversation);const i=t.text??r.join(" "),s=x(t),f=await k({conversation:n,text:i,attachment:s,sessionId:t.sessionId,workDir:t.workDir,idempotencyKey:t.idempotencyKey,dryRun:t.dryRun,traceId:t.traceId,timeoutMs:p(t.timeout),transport:h(t.transport)});y(f,t.json||t.format==="json")}catch(i){m("write",n||t.conversation,i,t.json||t.format==="json")}}),o.command("read").aliases(["read-latest","read_latest","latest"]).description("Read recent messages from a local WeChat conversation through Shennian").argument("[count]","Number of latest messages","10").option("--conversation <name>","WeChat conversation/group name").option("--limit <n>","Number of latest messages").option("--format <format>","Output format: markdown or json","markdown").option("--download <mode>","Attachment download mode: auto or never","auto").option("--trace-id <id>","Trace id included in JSON output").option("--timeout <ms>",`Manager IPC request timeout in milliseconds. Default: ${c}`).option("--session-id <id>","Bind to an existing Shennian chat/session; omit for direct local WeChat tool mode").option("--work-dir <path>","Local tool work dir; defaults under ~/.shennian/wechat-cli").option("--transport <mode>","Execution transport: auto, daemon, or direct","auto").option("--json","Output JSON",!1).action(async(r,t,a)=>{let n,i="markdown";try{n=l(a,t.conversation),i=S(t.json?"json":t.format);const s=await C({conversation:n,limit:u(t.limit??r,10),download:W(t.download),traceId:t.traceId,timeoutMs:p(t.timeout),sessionId:t.sessionId,workDir:t.workDir,transport:h(t.transport)});v(s,i)}catch(s){m("read",n||t.conversation,s,t.json||i==="json")}})}function l(e,o){const d=e.parent?.opts()??{},r=o||d.conversation||"";if(!r.trim())throw new Error("--conversation is required");return r}function x(e){const o=[e.file?{kind:"file",path:e.file}:null,e.image?{kind:"image",path:e.image}:null,e.video?{kind:"video",path:e.video}:null].filter(d=>!!d);if(o.length>1)throw new Error("Pass only one of --file, --image, or --video");if(o.length!==0)return w(o[0].path,o[0].kind)}function S(e){const o=String(e||"markdown").trim().toLowerCase();if(o==="json")return"json";if(o==="markdown"||o==="md"||o==="text")return"markdown";throw new Error(`Unsupported --format: ${e}. Use markdown or json.`)}function W(e){const o=String(e||"auto").trim().toLowerCase();if(o==="auto"||o==="never")return o;throw new Error(`Unsupported --download: ${e}. Use auto or never.`)}function h(e){const o=String(e||"auto").trim().toLowerCase();if(o==="auto"||o==="daemon"||o==="direct")return o;throw new Error(`Unsupported --transport: ${e}. Use auto, daemon, or direct.`)}function p(e){return e?u(e,j):c}export{_ as registerWeChatCommand,p as resolveWeChatCommandTimeout};
1
+ import{readExternalAttachment as g}from"../external-attachments.js";import{runWeChatDoctor as C}from"./doctor.js";import{runWeChatReadLatest as k,runWeChatSend as v}from"./operations.js";import{printReadLatestResult as y,printToolError as c,printToolResult as j}from"./output.js";import{clampPositiveInteger as u}from"./utils.js";const m=5*6e4,l=10*6e4;function _(e){const o=e.command("wechat").description("Use WeChat through Shennian local runtime");o.option("--conversation <name>","WeChat conversation/group name"),o.command("doctor").description("Check local Shennian, pairing, entitlement, credits, and WeChat runtime readiness").option("--json","Output JSON",!1).action(async r=>{const t=await C();if(r.json)console.log(JSON.stringify(t,null,2));else for(const a of t.checks){const n=a.ok?"\u2713":a.status==="warning"?"!":"\u2717";console.log(`${n} ${a.id}: ${a.message}`)}t.ok||(process.exitCode=1)}),o.command("write").aliases(["send"]).description("Write a message to a local WeChat conversation through Shennian").argument("[text...]","Message text").option("--conversation <name>","WeChat conversation/group name").option("--text <text>","Message text").option("--file <path>","File attachment path").option("--image <path>","Image attachment path").option("--video <path>","Video attachment path").option("--session-id <id>","Bind to an existing Shennian chat/session; omit for direct local WeChat tool mode").option("--work-dir <path>","Local tool work dir; defaults under ~/.shennian/wechat-cli").option("--idempotency-key <key>","Idempotency key").option("--dry-run","Validate the request and output the planned write without sending",!1).option("--trace-id <id>","Trace id included in JSON output").option("--timeout <ms>",`Manager IPC request timeout in milliseconds. Default: ${m}`).option("--transport <mode>","Execution transport: auto, daemon, or direct","auto").option("--format <format>","Output format: json or text","text").option("--json","Output JSON",!1).action(async(r,t,a)=>{let n;try{n=h(a,t.conversation);const i=t.text??r.join(" "),s=x(t),w=await v({conversation:n,text:i,attachment:s,sessionId:t.sessionId,workDir:t.workDir,idempotencyKey:t.idempotencyKey,dryRun:t.dryRun,traceId:t.traceId,timeoutMs:f(t.timeout),transport:p(t.transport)});j(w,t.json||t.format==="json")}catch(i){c("write",n||t.conversation,i,t.json||t.format==="json")}}),o.command("read").aliases(["read-latest","read_latest","latest"]).description("Read recent messages from a local WeChat conversation through Shennian").argument("[count]","Number of latest messages","10").option("--conversation <name>","WeChat conversation/group name").option("--limit <n>","Number of latest messages").option("--format <format>","Output format: markdown or json","markdown").option("--download <mode>","Attachment download mode: auto or never","auto").option("--trace-id <id>","Trace id included in JSON output").option("--timeout <ms>",`Manager IPC request timeout in milliseconds. Default: ${m}`).option("--session-id <id>","Bind to an existing Shennian chat/session; omit for direct local WeChat tool mode").option("--work-dir <path>","Local tool work dir; defaults under ~/.shennian/wechat-cli").option("--transport <mode>","Execution transport: auto, daemon, or direct","auto").option("--json","Output JSON",!1).action(async(r,t,a)=>{let n,i="markdown";try{n=h(a,t.conversation),i=S(t.json?"json":t.format);const s=await k({conversation:n,limit:u(t.limit??r,10),download:W(t.download),traceId:t.traceId,timeoutMs:f(t.timeout),sessionId:t.sessionId,workDir:t.workDir,transport:p(t.transport)});y(s,i)}catch(s){c("read",n||t.conversation,s,t.json||i==="json")}})}function h(e,o){const d=e.parent?.opts()??{},r=o||d.conversation||"";if(!r.trim())throw new Error("--conversation is required");return r}function x(e){const o=[e.file?{kind:"file",path:e.file}:null,e.image?{kind:"image",path:e.image}:null,e.video?{kind:"video",path:e.video}:null].filter(d=>!!d);if(o.length>1)throw new Error("Pass only one of --file, --image, or --video");if(o.length!==0)return g(o[0].path,o[0].kind)}function S(e){const o=String(e||"markdown").trim().toLowerCase();if(o==="json")return"json";if(o==="markdown"||o==="md"||o==="text")return"markdown";throw new Error(`Unsupported --format: ${e}. Use markdown or json.`)}function W(e){const o=String(e||"auto").trim().toLowerCase();if(o==="auto"||o==="never")return o;throw new Error(`Unsupported --download: ${e}. Use auto or never.`)}function p(e){const o=String(e||"auto").trim().toLowerCase();if(o==="auto"||o==="daemon"||o==="direct")return o;throw new Error(`Unsupported --transport: ${e}. Use auto, daemon, or direct.`)}function f(e){return e?Math.min(u(e,l),l):m}export{_ as registerWeChatCommand,f as resolveWeChatCommandTimeout};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shennian",
3
- "version": "0.2.113",
3
+ "version": "0.2.115",
4
4
  "description": "Shennian — AI Agent Control Plane CLI",
5
5
  "type": "module",
6
6
  "bin": {