shennian 0.2.95 → 0.2.97

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/dist/publish-build-manifest.json +45 -35
  2. package/dist/src/channels/runtime.d.ts +1 -0
  3. package/dist/src/channels/runtime.js +4 -4
  4. package/dist/src/channels/wechat-channel/fingerprint.js +1 -1
  5. package/dist/src/channels/wechat-channel/helper-assets.d.ts +1 -0
  6. package/dist/src/channels/wechat-channel/helper-assets.js +1 -1
  7. package/dist/src/channels/wechat-channel/helper-client.d.ts +17 -0
  8. package/dist/src/channels/wechat-channel/helper-client.js +3 -3
  9. package/dist/src/channels/wechat-channel/helper-protocol.d.ts +1 -1
  10. package/dist/src/channels/wechat-channel/helper-protocol.js +1 -1
  11. package/dist/src/channels/wechat-channel/macos-helper-app-bridge.d.ts +1 -0
  12. package/dist/src/channels/wechat-channel/macos-helper-app-bridge.js +2 -0
  13. package/dist/src/channels/wechat-channel/observer.d.ts +23 -3
  14. package/dist/src/channels/wechat-channel/observer.js +6 -6
  15. package/dist/src/channels/wechat-channel/outbound-sender.d.ts +3 -1
  16. package/dist/src/channels/wechat-channel/outbound-sender.js +1 -1
  17. package/dist/src/channels/wechat-channel/preflight.js +1 -1
  18. package/dist/src/channels/wechat-channel/runner.js +1 -1
  19. package/dist/src/commands/daemon.d.ts +3 -0
  20. package/dist/src/commands/daemon.js +7 -7
  21. package/dist/src/commands/pair.js +8 -8
  22. package/dist/src/commands/wechat.d.ts +26 -2
  23. package/dist/src/commands/wechat.js +7 -3
  24. package/dist/src/devtools/wechat-channel-action-smoke.d.ts +18 -0
  25. package/dist/src/devtools/wechat-channel-action-smoke.js +8 -7
  26. package/dist/src/devtools/wechat-channel-markdown-report.d.ts +6 -0
  27. package/dist/src/devtools/wechat-channel-markdown-report.js +5 -0
  28. package/dist/src/devtools/wechat-channel-product-steps.d.ts +3 -2
  29. package/dist/src/devtools/wechat-channel-product-steps.js +8 -4
  30. package/dist/src/manager/registry.js +1 -1
  31. package/dist/src/native-fusion/parsers.js +4 -4
  32. package/dist/src/session/handlers/tool-detail.d.ts +1 -1
  33. package/dist/src/session/handlers/tool-detail.js +1 -1
  34. package/node_modules/@shennian/wire/dist/machine.d.ts +25 -0
  35. package/node_modules/@shennian/wire/dist/machine.d.ts.map +1 -1
  36. package/node_modules/@shennian/wire/dist/message-payload.d.ts.map +1 -1
  37. package/node_modules/@shennian/wire/dist/message-payload.js +10 -2
  38. package/node_modules/@shennian/wire/dist/message-payload.js.map +1 -1
  39. package/package.json +9 -10
@@ -2,7 +2,7 @@
2
2
  "schemaVersion": 1,
3
3
  "jsMinified": true,
4
4
  "minifier": "esbuild",
5
- "fileCount": 120,
5
+ "fileCount": 122,
6
6
  "files": [
7
7
  {
8
8
  "file": "bin/shennian.js",
@@ -161,8 +161,8 @@
161
161
  },
162
162
  {
163
163
  "file": "src/channels/runtime.js",
164
- "beforeBytes": 28772,
165
- "afterBytes": 16002
164
+ "beforeBytes": 29792,
165
+ "afterBytes": 16367
166
166
  },
167
167
  {
168
168
  "file": "src/channels/secret-registry.js",
@@ -211,23 +211,23 @@
211
211
  },
212
212
  {
213
213
  "file": "src/channels/wechat-channel/fingerprint.js",
214
- "beforeBytes": 4353,
215
- "afterBytes": 2143
214
+ "beforeBytes": 4351,
215
+ "afterBytes": 2141
216
216
  },
217
217
  {
218
218
  "file": "src/channels/wechat-channel/helper-assets.js",
219
- "beforeBytes": 7649,
220
- "afterBytes": 3652
219
+ "beforeBytes": 11316,
220
+ "afterBytes": 5547
221
221
  },
222
222
  {
223
223
  "file": "src/channels/wechat-channel/helper-client.js",
224
- "beforeBytes": 13794,
225
- "afterBytes": 7725
224
+ "beforeBytes": 15977,
225
+ "afterBytes": 8671
226
226
  },
227
227
  {
228
228
  "file": "src/channels/wechat-channel/helper-protocol.js",
229
- "beforeBytes": 7417,
230
- "afterBytes": 3575
229
+ "beforeBytes": 7794,
230
+ "afterBytes": 3686
231
231
  },
232
232
  {
233
233
  "file": "src/channels/wechat-channel/human-coordination.js",
@@ -244,6 +244,11 @@
244
244
  "beforeBytes": 12414,
245
245
  "afterBytes": 5534
246
246
  },
247
+ {
248
+ "file": "src/channels/wechat-channel/macos-helper-app-bridge.js",
249
+ "beforeBytes": 2249,
250
+ "afterBytes": 1288
251
+ },
247
252
  {
248
253
  "file": "src/channels/wechat-channel/media-cache-resolver.js",
249
254
  "beforeBytes": 9983,
@@ -261,8 +266,8 @@
261
266
  },
262
267
  {
263
268
  "file": "src/channels/wechat-channel/observer.js",
264
- "beforeBytes": 65719,
265
- "afterBytes": 31251
269
+ "beforeBytes": 80918,
270
+ "afterBytes": 37541
266
271
  },
267
272
  {
268
273
  "file": "src/channels/wechat-channel/outbound-ledger.js",
@@ -271,18 +276,18 @@
271
276
  },
272
277
  {
273
278
  "file": "src/channels/wechat-channel/outbound-sender.js",
274
- "beforeBytes": 15286,
275
- "afterBytes": 7800
279
+ "beforeBytes": 16842,
280
+ "afterBytes": 8541
276
281
  },
277
282
  {
278
283
  "file": "src/channels/wechat-channel/preflight.js",
279
- "beforeBytes": 4704,
280
- "afterBytes": 3601
284
+ "beforeBytes": 4564,
285
+ "afterBytes": 3477
281
286
  },
282
287
  {
283
288
  "file": "src/channels/wechat-channel/runner.js",
284
- "beforeBytes": 12586,
285
- "afterBytes": 6476
289
+ "beforeBytes": 12915,
290
+ "afterBytes": 6617
286
291
  },
287
292
  {
288
293
  "file": "src/channels/wechat-channel/runtime.js",
@@ -341,8 +346,8 @@
341
346
  },
342
347
  {
343
348
  "file": "src/commands/daemon.js",
344
- "beforeBytes": 37098,
345
- "afterBytes": 17650
349
+ "beforeBytes": 37382,
350
+ "afterBytes": 17983
346
351
  },
347
352
  {
348
353
  "file": "src/commands/external-attachments.js",
@@ -366,8 +371,8 @@
366
371
  },
367
372
  {
368
373
  "file": "src/commands/pair.js",
369
- "beforeBytes": 11499,
370
- "afterBytes": 6591
374
+ "beforeBytes": 11239,
375
+ "afterBytes": 6479
371
376
  },
372
377
  {
373
378
  "file": "src/commands/tools.js",
@@ -381,8 +386,8 @@
381
386
  },
382
387
  {
383
388
  "file": "src/commands/wechat.js",
384
- "beforeBytes": 22721,
385
- "afterBytes": 12734
389
+ "beforeBytes": 33293,
390
+ "afterBytes": 18694
386
391
  },
387
392
  {
388
393
  "file": "src/config/index.js",
@@ -396,18 +401,23 @@
396
401
  },
397
402
  {
398
403
  "file": "src/devtools/wechat-channel-action-smoke.js",
399
- "beforeBytes": 55411,
400
- "afterBytes": 29725
404
+ "beforeBytes": 67091,
405
+ "afterBytes": 35476
401
406
  },
402
407
  {
403
408
  "file": "src/devtools/wechat-channel-download-report.js",
404
409
  "beforeBytes": 40277,
405
410
  "afterBytes": 19499
406
411
  },
412
+ {
413
+ "file": "src/devtools/wechat-channel-markdown-report.js",
414
+ "beforeBytes": 3610,
415
+ "afterBytes": 1761
416
+ },
407
417
  {
408
418
  "file": "src/devtools/wechat-channel-product-steps.js",
409
- "beforeBytes": 8142,
410
- "afterBytes": 5745
419
+ "beforeBytes": 8961,
420
+ "afterBytes": 6307
411
421
  },
412
422
  {
413
423
  "file": "src/env-path.js",
@@ -451,8 +461,8 @@
451
461
  },
452
462
  {
453
463
  "file": "src/manager/registry.js",
454
- "beforeBytes": 11767,
455
- "afterBytes": 5801
464
+ "beforeBytes": 11834,
465
+ "afterBytes": 5843
456
466
  },
457
467
  {
458
468
  "file": "src/manager/runtime.js",
@@ -476,8 +486,8 @@
476
486
  },
477
487
  {
478
488
  "file": "src/native-fusion/parsers.js",
479
- "beforeBytes": 30394,
480
- "afterBytes": 13019
489
+ "beforeBytes": 30459,
490
+ "afterBytes": 13036
481
491
  },
482
492
  {
483
493
  "file": "src/native-fusion/service.js",
@@ -551,8 +561,8 @@
551
561
  },
552
562
  {
553
563
  "file": "src/session/handlers/tool-detail.js",
554
- "beforeBytes": 8659,
555
- "afterBytes": 4063
564
+ "beforeBytes": 8800,
565
+ "afterBytes": 4161
556
566
  },
557
567
  {
558
568
  "file": "src/session/manager.js",
@@ -69,6 +69,7 @@ export declare class ChannelRuntime {
69
69
  channel: ExternalChannelView;
70
70
  }>;
71
71
  private recordRecentMessage;
72
+ private findRecentMessageDuplicate;
72
73
  private getRecentMessages;
73
74
  upsertManagerChannel(input: {
74
75
  id: string;
@@ -1,5 +1,5 @@
1
- import A from"node:crypto";import g from"node:fs";import y from"node:path";import{ChannelConfigRegistry as v}from"./registry.js";import{ChannelSecretRegistry as P}from"./secret-registry.js";import{WeComChannelAdapter as M}from"./wecom.js";import{WeChatRpaChannelAdapter as N}from"./wechat-rpa.js";import{ExternalWebSocketChannelAdapter as B}from"./websocket.js";import{splitExternalReplyText as D}from"./reply-split.js";class G{onExternalMessage;createReplyTarget;configs=new v;secrets=new P;adapters=new Map;completedReplyKeys=new Map;recentMessages=new Map;constructor(e,t){this.onExternalMessage=e,this.createReplyTarget=t;const n=new M(r=>this.ingest({...r,type:"external.message"}));this.adapters.set(n.type,n);const o=new B(r=>this.ingest({...r,type:"external.message"}));this.adapters.set(o.type,o);const a=new N(r=>this.ingest(r));this.adapters.set(a.type,a)}async start(){for(const e of this.configs.list().filter(t=>t.enabled))await this.adapters.get(e.type)?.connect(e).catch(()=>{})}async stop(){for(const e of this.configs.list())await this.adapters.get(e.type)?.disconnect(e).catch(()=>{})}ingest(e){const t=this.configs.get(e.channelId),n=e.managerSessionId??t?.sessionId??t?.managerSessionId;if(!n)throw new Error(`No session bound for channel ${e.channelId}`);const o=e.replyTarget||this.createReplyTarget({managerSessionId:n,channelId:e.channelId,conversationId:e.conversationId,messageId:e.messageId}),a={...e,replyTarget:o};return this.onExternalMessage(n,a),this.recordRecentMessage(a),a}async reply(e){const t=this.configs.get(e.channelId);if(!t)return{ok:!1,error:`Unknown channel: ${e.channelId}`};if((t.sessionId??t.managerSessionId)!==e.managerSessionId)return{ok:!1,error:"Channel is not bound to this session"};const n=this.adapters.get(t.type);if(!n)return{ok:!1,error:`Unsupported channel type: ${t.type}`};try{const o=E(t.type,e);if(!o.length)return{ok:!1,error:"Reply text or attachment is required"};let a=!1;for(const r of o){const i=r.idempotencyKey;if(i&&this.isReplyCompleted(t,e.conversationId,i))continue;const c=await n.send(t,{...e,...r});if(c?.status==="queued"){a=!0;continue}if(c?.status==="manual-review")return{ok:!1,error:c.reason||"Reply requires manual review before retry"};i&&this.markReplyCompleted(t,e.conversationId,i)}return a?{ok:!0,pending:!0}:{ok:!0}}catch(o){return{ok:!1,error:o instanceof Error?o.message:String(o)}}}isReplyCompleted(e,t,n){return this.loadReplyCompletionSet(e).has(k(e.id,t,n))}markReplyCompleted(e,t,n){const o=this.loadReplyCompletionSet(e);o.add(k(e.id,t,n));try{K(e.workDir,o)}catch{}}loadReplyCompletionSet(e){const t=y.resolve(e.workDir),n=this.completedReplyKeys.get(t);if(n)return n;const o=F(e.workDir);return this.completedReplyKeys.set(t,o),o}async getDefaultReplyTarget(e){const t=this.configs.list().find(a=>(a.sessionId??a.managerSessionId)===e&&a.enabled);if(!t)throw new Error("No enabled external channel is bound to this session");const n=this.adapters.get(t.type);if(!n?.defaultConversation)throw new Error(`External channel ${t.type} has no default conversation`);const o=await n.defaultConversation(t);return{channelId:t.id,conversationId:o.conversationId}}getManagerChannel(e,t,n={}){const o=this.configs.list().filter(c=>(c.sessionId??c.managerSessionId)===e&&c.type===t),a=o.find(c=>c.enabled)??o.at(-1);if(!a)return null;const r=this.secrets.get(a.secretRef),i=this.adapters.get(a.type)?.runtimeStatus?.(a)??{};return{id:a.id,type:a.type,name:a.name,sessionId:a.sessionId??a.managerSessionId,managerSessionId:a.managerSessionId,workDir:a.workDir,agentType:a.agentType,agentSessionId:a.agentSessionId,modelId:a.modelId,enabled:a.enabled,wsUrl:r?.wsUrl??"",token:n.includeSecret?r?.token??"":"",tokenConfigured:!!r?.token,canReply:!!r?.canReply,systemPrompt:typeof r?.systemPrompt=="string"?r.systemPrompt:"",...R(r),...i}}getChannelById(e,t={}){const n=this.configs.get(e);if(!n)return null;const o=this.secrets.get(n.secretRef),a=this.adapters.get(n.type)?.runtimeStatus?.(n)??{};return{id:n.id,type:n.type,name:n.name,sessionId:n.sessionId??n.managerSessionId,managerSessionId:n.managerSessionId,workDir:n.workDir,agentType:n.agentType,agentSessionId:n.agentSessionId,modelId:n.modelId,enabled:n.enabled,wsUrl:o?.wsUrl??"",token:t.includeSecret?o?.token??"":"",tokenConfigured:!!o?.token,canReply:!!o?.canReply,systemPrompt:typeof o?.systemPrompt=="string"?o.systemPrompt:"",...R(o),...a}}getChannelStatusById(e){const t=this.configs.get(e);if(!t)return null;const n=this.secrets.get(t.secretRef),o=this.adapters.get(t.type)?.runtimeStatus?.(t)??{};return{configured:!0,connected:h(t,n),type:t.type,channelId:t.id,name:t.name,canReply:!!n?.canReply,systemPrompt:typeof n?.systemPrompt=="string"?n.systemPrompt:"",...u(n),...o}}getManagerChannelStatus(e){const t=this.configs.list().find(a=>(a.sessionId??a.managerSessionId)===e&&a.enabled);if(!t)return null;const n=this.secrets.get(t.secretRef),o=this.adapters.get(t.type)?.runtimeStatus?.(t)??{};return{configured:!0,connected:h(t,n),type:t.type,channelId:t.id,name:t.name,canReply:!!n?.canReply,systemPrompt:typeof n?.systemPrompt=="string"?n.systemPrompt:"",...u(n),...o}}listManagerChannelStatuses(){return this.configs.list().filter(e=>e.enabled).map(e=>({managerSessionId:e.sessionId??e.managerSessionId,status:this.getManagerChannelStatus(e.sessionId??e.managerSessionId)})).filter(e=>!!e.status)}listManagerExternalChannels(e){return this.configs.list().filter(t=>t.enabled&&(t.sessionId??t.managerSessionId)===e).map(t=>{const n=this.secrets.get(t.secretRef),o=this.adapters.get(t.type)?.runtimeStatus?.(t)??{};return{configured:!0,connected:h(t,n),type:t.type,channelId:t.id,name:t.name,canReply:!!n?.canReply,systemPrompt:typeof n?.systemPrompt=="string"?n.systemPrompt:"",...u(n),...o}})}async syncManagerWeChatRpaChannel(e){const t=this.configs.list().find(r=>r.enabled&&r.type==="wechat-rpa"&&(r.sessionId??r.managerSessionId)===e);if(!t)throw new Error("No enabled WeChat RPA channel is bound to this session");const n=this.adapters.get(t.type);if(!n?.syncNow)throw new Error("WeChat RPA channel does not support manual sync");const o=Date.now()-120*1e3,a=W(await n.syncNow(t)??[],this.getRecentMessages(t.id,o));return{channel:this.getManagerChannel(e,"wechat-rpa",{includeSecret:!0}),messages:a}}async cancelManagerWeChatRpaOutbound(e){const t=this.configs.list().find(a=>a.enabled&&a.type==="wechat-rpa"&&(a.sessionId??a.managerSessionId)===e.managerSessionId);if(!t)throw new Error("No enabled WeChat RPA channel is bound to this session");const n=this.adapters.get(t.type);if(!n?.cancelOutbound)throw new Error("WeChat RPA channel does not support outbound cancellation");const o=await n.cancelOutbound(t,{idempotencyKey:e.idempotencyKey,replyId:e.replyId,reason:e.reason});return{cancelled:o.cancelled,status:o.status,channel:this.getManagerChannel(e.managerSessionId,"wechat-rpa",{includeSecret:!0})}}recordRecentMessage(e){const t=this.recentMessages.get(e.channelId)??[];t.push(e),this.recentMessages.set(e.channelId,t.slice(-50))}getRecentMessages(e,t){return(this.recentMessages.get(e)??[]).filter(o=>{const a=Date.parse(o.receivedAt);return!Number.isFinite(a)||a>=t})}async upsertManagerChannel(e){const t=this.configs.get(e.id),n=this.configs.list(),o=e.sessionId||e.managerSessionId,a={id:e.id,type:e.type,name:e.name?.trim()||t?.name||"\u5916\u90E8\u6D88\u606F\u901A\u9053",sessionId:o,managerSessionId:o,workDir:e.workDir,agentType:e.agentType||t?.agentType,agentSessionId:e.agentSessionId??t?.agentSessionId??null,modelId:e.modelId??t?.modelId??null,enabled:e.enabled,secretRef:t?.secretRef||`channel:${e.id}`},r=this.secrets.get(a.secretRef),i=e.wsUrl?.trim()||r?.wsUrl||"",c=e.token?.trim()||r?.token||"",p=e.canReply??r?.canReply??!1,m=e.systemPrompt??(typeof r?.systemPrompt=="string"?r.systemPrompt:"");if(a.enabled&&(!i||!c))throw new Error("WebSocket \u5730\u5740\u548C Token \u5FC5\u586B");const f=n.filter(l=>l.id!==a.id).map(l=>(l.sessionId??l.managerSessionId)===o&&l.type===e.type?{...l,enabled:!1}:l);f.push(a),this.configs.replaceAll(f),(i||c)&&this.secrets.upsert(a.secretRef,{type:"websocket",wsUrl:i,token:c,canReply:p,systemPrompt:m});const d=this.adapters.get(a.type);for(const l of n)(l.sessionId??l.managerSessionId)===o&&l.type===e.type&&l.enabled&&await this.adapters.get(l.type)?.disconnect(l).catch(()=>{});return a.enabled&&d?.connect(a).catch(()=>{}),this.getManagerChannel(o,e.type,{includeSecret:!0})}async upsertManagerWeChatRpaChannel(e){const t=this.configs.get(e.id),n=this.configs.list(),o=e.sessionId||e.managerSessionId,a=w(e.groups);if(e.enabled&&!a.length)throw new Error("WeChat RPA \u81F3\u5C11\u9700\u8981\u914D\u7F6E\u4E00\u4E2A\u7FA4");if(e.enabled&&a.length>1)throw new Error("WeChat RPA \u6BCF\u4E2A\u5BF9\u8BDD\u53EA\u80FD\u7ED1\u5B9A\u4E00\u4E2A\u7FA4");const r={id:e.id,type:"wechat-rpa",name:e.name?.trim()||t?.name||"\u672C\u673A\u5FAE\u4FE1 RPA",sessionId:o,managerSessionId:o,workDir:e.workDir,agentType:e.agentType||t?.agentType,agentSessionId:e.agentSessionId??t?.agentSessionId??null,modelId:e.modelId??t?.modelId??null,enabled:e.enabled,secretRef:t?.secretRef||`channel:${e.id}`},i=this.secrets.get(r.secretRef),c=e.source||(i?.source==="wechat-channel"||i?.source==="macos-probe"||i?.source==="fixture-jsonl"||i?.source==="macos-flow"||i?.source==="windows-visual-flow"||i?.source==="wechat-rpa-lab"?i.source:x());if(e.enabled&&c==="windows-visual-flow")throw new Error("\u65E7 Windows visual flow \u5DF2\u5F52\u6863\uFF1BmacOS \u548C Windows \u6B63\u5F0F\u901A\u9053\u90FD\u4F7F\u7528 wechat-channel");const p=e.privacyConsentAccepted??!!i?.privacyConsentAccepted;if(e.enabled&&!p)throw new Error("\u542F\u7528\u524D\u9700\u8981\u786E\u8BA4\u5FAE\u4FE1\u901A\u9053\u6570\u636E\u4E0E\u9690\u79C1\u6388\u6743");const m=n.filter(d=>d.id!==r.id).map(d=>(d.sessionId??d.managerSessionId)===o&&d.type==="wechat-rpa"?{...d,enabled:!1}:d);m.push(r),this.configs.replaceAll(m),this.secrets.upsert(r.secretRef,{type:"wechat-rpa",source:c,groups:a,pollIntervalMs:I(e.pollIntervalMs,i?.pollIntervalMs),recentLimit:I(e.recentLimit,i?.recentLimit),idleSeconds:I(e.idleSeconds,i?.idleSeconds),forceForeground:e.forceForeground??!!i?.forceForeground,noRestore:e.noRestore??(i?.noRestore===void 0?!0:!!i.noRestore),downloadAttachments:e.downloadAttachments??(i?.downloadAttachments===void 0?!0:!!i.downloadAttachments),downloadAttachmentsDir:e.downloadAttachmentsDir?.trim()||S(i?.downloadAttachmentsDir),selfNickname:e.selfNickname?.trim()||S(i?.selfNickname),privacyConsentAccepted:p,flowScriptPath:e.flowScriptPath?.trim()||S(i?.flowScriptPath),canReply:e.canReply??i?.canReply??!1,systemPrompt:e.systemPrompt??(typeof i?.systemPrompt=="string"?i.systemPrompt:"")});const f=this.adapters.get(r.type);for(const d of n)(d.sessionId??d.managerSessionId)===o&&d.type==="wechat-rpa"&&d.enabled&&await this.adapters.get(d.type)?.disconnect(d).catch(()=>{});return r.enabled&&f?.connect(r).catch(()=>{}),this.getManagerChannel(o,"wechat-rpa",{includeSecret:!0})}}function h(s,e){return!e||e.type!==s.type?!1:s.type==="wechat-rpa"?!0:s.type==="websocket"?!!(e.wsUrl&&e.token):s.type==="wecom"?!!(e.token||e.botId||e.secret):!!e.token}function R(s){return!s||s.type!=="wechat-rpa"?{}:{wechatRpaSource:typeof s.source=="string"?s.source:"",wechatRpaGroups:w(Array.isArray(s.groups)?s.groups:[]),pollIntervalMs:Number.isFinite(s.pollIntervalMs)?Number(s.pollIntervalMs):void 0,recentLimit:Number.isFinite(s.recentLimit)?Number(s.recentLimit):void 0,idleSeconds:Number.isFinite(s.idleSeconds)?Number(s.idleSeconds):void 0,forceForeground:!!s.forceForeground,noRestore:s.noRestore===void 0?void 0:!!s.noRestore,downloadAttachments:s.downloadAttachments===void 0?!0:!!s.downloadAttachments,downloadAttachmentsDir:typeof s.downloadAttachmentsDir=="string"?s.downloadAttachmentsDir:"",selfNickname:typeof s.selfNickname=="string"?s.selfNickname:"",wechatRpaPrivacyConsentAccepted:!!s.privacyConsentAccepted,wechatRpaServerDecisionAvailable:!0,wechatRpaPreflightChecks:b(s)}}function u(s){return!s||s.type!=="wechat-rpa"?{}:{wechatRpaSource:typeof s.source=="string"?s.source:null,wechatRpaGroups:w(Array.isArray(s.groups)?s.groups:[]),pollIntervalMs:Number.isFinite(s.pollIntervalMs)?Number(s.pollIntervalMs):null,recentLimit:Number.isFinite(s.recentLimit)?Number(s.recentLimit):null,idleSeconds:Number.isFinite(s.idleSeconds)?Number(s.idleSeconds):null,forceForeground:!!s.forceForeground,noRestore:s.noRestore===void 0?null:!!s.noRestore,downloadAttachments:s.downloadAttachments===void 0?!0:!!s.downloadAttachments,downloadAttachmentsDir:typeof s.downloadAttachmentsDir=="string"?s.downloadAttachmentsDir:null,selfNickname:typeof s.selfNickname=="string"?s.selfNickname:null,wechatRpaPrivacyConsentAccepted:!!s.privacyConsentAccepted,wechatRpaServerDecisionAvailable:!0,wechatRpaPreflightChecks:b(s)}}function w(s){const e=new Set,t=[];for(const n of s){const o=String(n?.name||"").replace(/\s+/g," ").trim();!o||e.has(o)||(e.add(o),t.push({name:o}))}return t}function x(){return"wechat-channel"}function b(s){const e=[];if(s.lastHelperPermissions&&typeof s.lastHelperPermissions=="object"){const t=s.lastHelperPermissions,n=t.duplicateInstanceBlocked===!0||Number(t.duplicateMainWindowCount??0)>0||Number(t.unsafeAuxiliaryWindowCount??0)>0;e.push({code:"wechat_single_main_window_required",ok:!n,severity:"blocking",message:n?"\u8BF7\u53EA\u4FDD\u7559\u4E00\u4E2A\u5DF2\u767B\u5F55\u7684\u5FAE\u4FE1\u4E3B\u7A97\u53E3\uFF0C\u5173\u95ED\u91CD\u590D\u4E3B\u7A97\u53E3\u3001\u89C6\u9891\u9884\u89C8\u6216\u767B\u5F55\u63D0\u793A\u540E\u518D\u8BD5\u3002":"\u5F53\u524D\u53EA\u6709\u4E00\u4E2A\u53EF\u7528\u7684\u5FAE\u4FE1\u4E3B\u7A97\u53E3\u3002"})}return e.push({code:"platform_unsupported",ok:s.source!=="windows-visual-flow",severity:"blocking",message:s.source==="windows-visual-flow"?"\u65E7 Windows visual flow \u5DF2\u5F52\u6863\uFF0C\u8BF7\u6539\u7528 wechat-channel\u3002":"\u5F53\u524D\u914D\u7F6E\u4F7F\u7528\u8DE8\u5E73\u53F0\u5FAE\u4FE1\u901A\u9053\u3002"}),e.push({code:"privacy_consent_required",ok:!!s.privacyConsentAccepted,severity:"blocking",message:s.privacyConsentAccepted?"\u5DF2\u786E\u8BA4\u5FAE\u4FE1\u901A\u9053\u6570\u636E\u4E0E\u9690\u79C1\u6388\u6743\u3002":"\u542F\u7528\u524D\u9700\u8981\u786E\u8BA4\u5FAE\u4FE1\u901A\u9053\u6570\u636E\u4E0E\u9690\u79C1\u6388\u6743\u3002"}),e.push({code:"server_decision_unavailable",ok:!0,severity:"blocking",message:"\u670D\u52A1\u7AEF\u5224\u65AD\u80FD\u529B\u53EF\u7528\u3002"}),e}function E(s,e){const t=D(e.text);if(!t.length&&!e.attachment)return[];if(s==="wechat-rpa"&&e.attachment&&t.length<=1)return[{text:t[0]??"",attachment:e.attachment,idempotencyKey:e.idempotencyKey}];const n=t.map((o,a)=>({text:o,attachment:void 0,idempotencyKey:t.length>1&&e.idempotencyKey?`${e.idempotencyKey}:${a+1}`:e.idempotencyKey}));return e.attachment&&n.push({text:"",attachment:e.attachment,idempotencyKey:t.length&&e.idempotencyKey?`${e.idempotencyKey}:attachment`:e.idempotencyKey}),n}function W(...s){const e=new Set,t=[];for(const n of s.flat()){const o=`${n.channelId}
2
- ${n.conversationId}
3
- ${n.messageId}`;e.has(o)||(e.add(o),t.push(n))}return t}function k(s,e,t){return A.createHash("sha256").update(`${s}
1
+ import P from"node:crypto";import g from"node:fs";import u from"node:path";import{ChannelConfigRegistry as v}from"./registry.js";import{ChannelSecretRegistry as N}from"./secret-registry.js";import{WeComChannelAdapter as D}from"./wecom.js";import{WeChatRpaChannelAdapter as B}from"./wechat-rpa.js";import{ExternalWebSocketChannelAdapter as x}from"./websocket.js";import{splitExternalReplyText as E}from"./reply-split.js";class J{onExternalMessage;createReplyTarget;configs=new v;secrets=new N;adapters=new Map;completedReplyKeys=new Map;recentMessages=new Map;constructor(e,t){this.onExternalMessage=e,this.createReplyTarget=t;const n=new D(a=>this.ingest({...a,type:"external.message"}));this.adapters.set(n.type,n);const o=new x(a=>this.ingest({...a,type:"external.message"}));this.adapters.set(o.type,o);const r=new B(a=>this.ingest(a));this.adapters.set(r.type,r)}async start(){for(const e of this.configs.list().filter(t=>t.enabled))await this.adapters.get(e.type)?.connect(e).catch(()=>{})}async stop(){for(const e of this.configs.list())await this.adapters.get(e.type)?.disconnect(e).catch(()=>{})}ingest(e){const t=this.findRecentMessageDuplicate(e);if(t)return t;const n=this.configs.get(e.channelId),o=e.managerSessionId??n?.sessionId??n?.managerSessionId;if(!o)throw new Error(`No session bound for channel ${e.channelId}`);const r=e.replyTarget||this.createReplyTarget({managerSessionId:o,channelId:e.channelId,conversationId:e.conversationId,messageId:e.messageId}),a={...e,replyTarget:r};return this.onExternalMessage(o,a),this.recordRecentMessage(a),a}async reply(e){const t=this.configs.get(e.channelId);if(!t)return{ok:!1,error:`Unknown channel: ${e.channelId}`};if((t.sessionId??t.managerSessionId)!==e.managerSessionId)return{ok:!1,error:"Channel is not bound to this session"};const n=this.adapters.get(t.type);if(!n)return{ok:!1,error:`Unsupported channel type: ${t.type}`};try{const o=W(t.type,e);if(!o.length)return{ok:!1,error:"Reply text or attachment is required"};let r=!1;for(const a of o){const i=a.idempotencyKey;if(i&&this.isReplyCompleted(t,e.conversationId,i))continue;const c=await n.send(t,{...e,...a});if(c?.status==="queued"){r=!0;continue}if(c?.status==="manual-review")return{ok:!1,error:c.reason||"Reply requires manual review before retry"};i&&this.markReplyCompleted(t,e.conversationId,i)}return r?{ok:!0,pending:!0}:{ok:!0}}catch(o){return{ok:!1,error:o instanceof Error?o.message:String(o)}}}isReplyCompleted(e,t,n){return this.loadReplyCompletionSet(e).has(A(e.id,t,n))}markReplyCompleted(e,t,n){const o=this.loadReplyCompletionSet(e);o.add(A(e.id,t,n));try{$(e.workDir,o)}catch{}}loadReplyCompletionSet(e){const t=u.resolve(e.workDir),n=this.completedReplyKeys.get(t);if(n)return n;const o=T(e.workDir);return this.completedReplyKeys.set(t,o),o}async getDefaultReplyTarget(e){const t=this.configs.list().find(r=>(r.sessionId??r.managerSessionId)===e&&r.enabled);if(!t)throw new Error("No enabled external channel is bound to this session");const n=this.adapters.get(t.type);if(!n?.defaultConversation)throw new Error(`External channel ${t.type} has no default conversation`);const o=await n.defaultConversation(t);return{channelId:t.id,conversationId:o.conversationId}}getManagerChannel(e,t,n={}){const o=this.configs.list().filter(c=>(c.sessionId??c.managerSessionId)===e&&c.type===t),r=o.find(c=>c.enabled)??o.at(-1);if(!r)return null;const a=this.secrets.get(r.secretRef),i=this.adapters.get(r.type)?.runtimeStatus?.(r)??{};return{id:r.id,type:r.type,name:r.name,sessionId:r.sessionId??r.managerSessionId,managerSessionId:r.managerSessionId,workDir:r.workDir,agentType:r.agentType,agentSessionId:r.agentSessionId,modelId:r.modelId,enabled:r.enabled,wsUrl:a?.wsUrl??"",token:n.includeSecret?a?.token??"":"",tokenConfigured:!!a?.token,canReply:!!a?.canReply,systemPrompt:typeof a?.systemPrompt=="string"?a.systemPrompt:"",...k(a),...i}}getChannelById(e,t={}){const n=this.configs.get(e);if(!n)return null;const o=this.secrets.get(n.secretRef),r=this.adapters.get(n.type)?.runtimeStatus?.(n)??{};return{id:n.id,type:n.type,name:n.name,sessionId:n.sessionId??n.managerSessionId,managerSessionId:n.managerSessionId,workDir:n.workDir,agentType:n.agentType,agentSessionId:n.agentSessionId,modelId:n.modelId,enabled:n.enabled,wsUrl:o?.wsUrl??"",token:t.includeSecret?o?.token??"":"",tokenConfigured:!!o?.token,canReply:!!o?.canReply,systemPrompt:typeof o?.systemPrompt=="string"?o.systemPrompt:"",...k(o),...r}}getChannelStatusById(e){const t=this.configs.get(e);if(!t)return null;const n=this.secrets.get(t.secretRef),o=this.adapters.get(t.type)?.runtimeStatus?.(t)??{};return{configured:!0,connected:y(t,n),type:t.type,channelId:t.id,name:t.name,canReply:!!n?.canReply,systemPrompt:typeof n?.systemPrompt=="string"?n.systemPrompt:"",...h(n),...o}}getManagerChannelStatus(e){const t=this.configs.list().find(r=>(r.sessionId??r.managerSessionId)===e&&r.enabled);if(!t)return null;const n=this.secrets.get(t.secretRef),o=this.adapters.get(t.type)?.runtimeStatus?.(t)??{};return{configured:!0,connected:y(t,n),type:t.type,channelId:t.id,name:t.name,canReply:!!n?.canReply,systemPrompt:typeof n?.systemPrompt=="string"?n.systemPrompt:"",...h(n),...o}}listManagerChannelStatuses(){return this.configs.list().filter(e=>e.enabled).map(e=>({managerSessionId:e.sessionId??e.managerSessionId,status:this.getManagerChannelStatus(e.sessionId??e.managerSessionId)})).filter(e=>!!e.status)}listManagerExternalChannels(e){return this.configs.list().filter(t=>t.enabled&&(t.sessionId??t.managerSessionId)===e).map(t=>{const n=this.secrets.get(t.secretRef),o=this.adapters.get(t.type)?.runtimeStatus?.(t)??{};return{configured:!0,connected:y(t,n),type:t.type,channelId:t.id,name:t.name,canReply:!!n?.canReply,systemPrompt:typeof n?.systemPrompt=="string"?n.systemPrompt:"",...h(n),...o}})}async syncManagerWeChatRpaChannel(e){const t=this.configs.list().find(a=>a.enabled&&a.type==="wechat-rpa"&&(a.sessionId??a.managerSessionId)===e);if(!t)throw new Error("No enabled WeChat RPA channel is bound to this session");const n=this.adapters.get(t.type);if(!n?.syncNow)throw new Error("WeChat RPA channel does not support manual sync");const o=Date.now()-120*1e3,r=F(await n.syncNow(t)??[],this.getRecentMessages(t.id,o));return{channel:this.getManagerChannel(e,"wechat-rpa",{includeSecret:!0}),messages:r}}async cancelManagerWeChatRpaOutbound(e){const t=this.configs.list().find(r=>r.enabled&&r.type==="wechat-rpa"&&(r.sessionId??r.managerSessionId)===e.managerSessionId);if(!t)throw new Error("No enabled WeChat RPA channel is bound to this session");const n=this.adapters.get(t.type);if(!n?.cancelOutbound)throw new Error("WeChat RPA channel does not support outbound cancellation");const o=await n.cancelOutbound(t,{idempotencyKey:e.idempotencyKey,replyId:e.replyId,reason:e.reason});return{cancelled:o.cancelled,status:o.status,channel:this.getManagerChannel(e.managerSessionId,"wechat-rpa",{includeSecret:!0})}}recordRecentMessage(e){const t=this.recentMessages.get(e.channelId)??[];this.findRecentMessageDuplicate(e)||(t.push(e),this.recentMessages.set(e.channelId,t.slice(-50)))}findRecentMessageDuplicate(e){const t=I(e);return t?(this.recentMessages.get(e.channelId)??[]).find(o=>I(o)===t)??null:null}getRecentMessages(e,t){return(this.recentMessages.get(e)??[]).filter(o=>{const r=Date.parse(o.receivedAt);return!Number.isFinite(r)||r>=t})}async upsertManagerChannel(e){const t=this.configs.get(e.id),n=this.configs.list(),o=e.sessionId||e.managerSessionId,r={id:e.id,type:e.type,name:e.name?.trim()||t?.name||"\u5916\u90E8\u6D88\u606F\u901A\u9053",sessionId:o,managerSessionId:o,workDir:e.workDir,agentType:e.agentType||t?.agentType,agentSessionId:e.agentSessionId??t?.agentSessionId??null,modelId:e.modelId??t?.modelId??null,enabled:e.enabled,secretRef:t?.secretRef||`channel:${e.id}`},a=this.secrets.get(r.secretRef),i=e.wsUrl?.trim()||a?.wsUrl||"",c=e.token?.trim()||a?.token||"",p=e.canReply??a?.canReply??!1,m=e.systemPrompt??(typeof a?.systemPrompt=="string"?a.systemPrompt:"");if(r.enabled&&(!i||!c))throw new Error("WebSocket \u5730\u5740\u548C Token \u5FC5\u586B");const f=n.filter(l=>l.id!==r.id).map(l=>(l.sessionId??l.managerSessionId)===o&&l.type===e.type?{...l,enabled:!1}:l);f.push(r),this.configs.replaceAll(f),(i||c)&&this.secrets.upsert(r.secretRef,{type:"websocket",wsUrl:i,token:c,canReply:p,systemPrompt:m});const d=this.adapters.get(r.type);for(const l of n)(l.sessionId??l.managerSessionId)===o&&l.type===e.type&&l.enabled&&await this.adapters.get(l.type)?.disconnect(l).catch(()=>{});return r.enabled&&d?.connect(r).catch(()=>{}),this.getManagerChannel(o,e.type,{includeSecret:!0})}async upsertManagerWeChatRpaChannel(e){const t=this.configs.get(e.id),n=this.configs.list(),o=e.sessionId||e.managerSessionId,r=w(e.groups);if(e.enabled&&!r.length)throw new Error("WeChat RPA \u81F3\u5C11\u9700\u8981\u914D\u7F6E\u4E00\u4E2A\u7FA4");if(e.enabled&&r.length>1)throw new Error("WeChat RPA \u6BCF\u4E2A\u5BF9\u8BDD\u53EA\u80FD\u7ED1\u5B9A\u4E00\u4E2A\u7FA4");const a={id:e.id,type:"wechat-rpa",name:e.name?.trim()||t?.name||"\u672C\u673A\u5FAE\u4FE1 RPA",sessionId:o,managerSessionId:o,workDir:e.workDir,agentType:e.agentType||t?.agentType,agentSessionId:e.agentSessionId??t?.agentSessionId??null,modelId:e.modelId??t?.modelId??null,enabled:e.enabled,secretRef:t?.secretRef||`channel:${e.id}`},i=this.secrets.get(a.secretRef),c=e.source||(i?.source==="wechat-channel"||i?.source==="macos-probe"||i?.source==="fixture-jsonl"||i?.source==="macos-flow"||i?.source==="windows-visual-flow"||i?.source==="wechat-rpa-lab"?i.source:K());if(e.enabled&&c==="windows-visual-flow")throw new Error("\u65E7 Windows visual flow \u5DF2\u5F52\u6863\uFF1BmacOS \u548C Windows \u6B63\u5F0F\u901A\u9053\u90FD\u4F7F\u7528 wechat-channel");const p=e.privacyConsentAccepted??!!i?.privacyConsentAccepted;if(e.enabled&&!p)throw new Error("\u542F\u7528\u524D\u9700\u8981\u786E\u8BA4\u5FAE\u4FE1\u901A\u9053\u6570\u636E\u4E0E\u9690\u79C1\u6388\u6743");const m=n.filter(d=>d.id!==a.id).map(d=>(d.sessionId??d.managerSessionId)===o&&d.type==="wechat-rpa"?{...d,enabled:!1}:d);m.push(a),this.configs.replaceAll(m),this.secrets.upsert(a.secretRef,{type:"wechat-rpa",source:c,groups:r,pollIntervalMs:R(e.pollIntervalMs,i?.pollIntervalMs),recentLimit:R(e.recentLimit,i?.recentLimit),idleSeconds:R(e.idleSeconds,i?.idleSeconds),forceForeground:e.forceForeground??!!i?.forceForeground,noRestore:e.noRestore??(i?.noRestore===void 0?!0:!!i.noRestore),downloadAttachments:e.downloadAttachments??(i?.downloadAttachments===void 0?!0:!!i.downloadAttachments),downloadAttachmentsDir:e.downloadAttachmentsDir?.trim()||b(i?.downloadAttachmentsDir),selfNickname:e.selfNickname?.trim()||b(i?.selfNickname),privacyConsentAccepted:p,flowScriptPath:e.flowScriptPath?.trim()||b(i?.flowScriptPath),canReply:e.canReply??i?.canReply??!1,systemPrompt:e.systemPrompt??(typeof i?.systemPrompt=="string"?i.systemPrompt:"")});const f=this.adapters.get(a.type);for(const d of n)(d.sessionId??d.managerSessionId)===o&&d.type==="wechat-rpa"&&d.enabled&&await this.adapters.get(d.type)?.disconnect(d).catch(()=>{});return a.enabled&&f?.connect(a).catch(()=>{}),this.getManagerChannel(o,"wechat-rpa",{includeSecret:!0})}}function y(s,e){return!e||e.type!==s.type?!1:s.type==="wechat-rpa"?!0:s.type==="websocket"?!!(e.wsUrl&&e.token):s.type==="wecom"?!!(e.token||e.botId||e.secret):!!e.token}function k(s){return!s||s.type!=="wechat-rpa"?{}:{wechatRpaSource:typeof s.source=="string"?s.source:"",wechatRpaGroups:w(Array.isArray(s.groups)?s.groups:[]),pollIntervalMs:Number.isFinite(s.pollIntervalMs)?Number(s.pollIntervalMs):void 0,recentLimit:Number.isFinite(s.recentLimit)?Number(s.recentLimit):void 0,idleSeconds:Number.isFinite(s.idleSeconds)?Number(s.idleSeconds):void 0,forceForeground:!!s.forceForeground,noRestore:s.noRestore===void 0?void 0:!!s.noRestore,downloadAttachments:s.downloadAttachments===void 0?!0:!!s.downloadAttachments,downloadAttachmentsDir:typeof s.downloadAttachmentsDir=="string"?s.downloadAttachmentsDir:"",selfNickname:typeof s.selfNickname=="string"?s.selfNickname:"",wechatRpaPrivacyConsentAccepted:!!s.privacyConsentAccepted,wechatRpaServerDecisionAvailable:!0,wechatRpaPreflightChecks:C(s)}}function h(s){return!s||s.type!=="wechat-rpa"?{}:{wechatRpaSource:typeof s.source=="string"?s.source:null,wechatRpaGroups:w(Array.isArray(s.groups)?s.groups:[]),pollIntervalMs:Number.isFinite(s.pollIntervalMs)?Number(s.pollIntervalMs):null,recentLimit:Number.isFinite(s.recentLimit)?Number(s.recentLimit):null,idleSeconds:Number.isFinite(s.idleSeconds)?Number(s.idleSeconds):null,forceForeground:!!s.forceForeground,noRestore:s.noRestore===void 0?null:!!s.noRestore,downloadAttachments:s.downloadAttachments===void 0?!0:!!s.downloadAttachments,downloadAttachmentsDir:typeof s.downloadAttachmentsDir=="string"?s.downloadAttachmentsDir:null,selfNickname:typeof s.selfNickname=="string"?s.selfNickname:null,wechatRpaPrivacyConsentAccepted:!!s.privacyConsentAccepted,wechatRpaServerDecisionAvailable:!0,wechatRpaPreflightChecks:C(s)}}function w(s){const e=new Set,t=[];for(const n of s){const o=String(n?.name||"").replace(/\s+/g," ").trim();!o||e.has(o)||(e.add(o),t.push({name:o}))}return t}function K(){return"wechat-channel"}function C(s){const e=[];if(s.lastHelperPermissions&&typeof s.lastHelperPermissions=="object"){const t=s.lastHelperPermissions,n=t.duplicateInstanceBlocked===!0||Number(t.duplicateMainWindowCount??0)>0||Number(t.unsafeAuxiliaryWindowCount??0)>0;e.push({code:"wechat_single_main_window_required",ok:!n,severity:"blocking",message:n?"\u8BF7\u53EA\u4FDD\u7559\u4E00\u4E2A\u5DF2\u767B\u5F55\u7684\u5FAE\u4FE1\u4E3B\u7A97\u53E3\uFF0C\u5173\u95ED\u91CD\u590D\u4E3B\u7A97\u53E3\u3001\u89C6\u9891\u9884\u89C8\u6216\u767B\u5F55\u63D0\u793A\u540E\u518D\u8BD5\u3002":"\u5F53\u524D\u53EA\u6709\u4E00\u4E2A\u53EF\u7528\u7684\u5FAE\u4FE1\u4E3B\u7A97\u53E3\u3002"})}return e.push({code:"platform_unsupported",ok:s.source!=="windows-visual-flow",severity:"blocking",message:s.source==="windows-visual-flow"?"\u65E7 Windows visual flow \u5DF2\u5F52\u6863\uFF0C\u8BF7\u6539\u7528 wechat-channel\u3002":"\u5F53\u524D\u914D\u7F6E\u4F7F\u7528\u8DE8\u5E73\u53F0\u5FAE\u4FE1\u901A\u9053\u3002"}),e.push({code:"privacy_consent_required",ok:!!s.privacyConsentAccepted,severity:"blocking",message:s.privacyConsentAccepted?"\u5DF2\u786E\u8BA4\u5FAE\u4FE1\u901A\u9053\u6570\u636E\u4E0E\u9690\u79C1\u6388\u6743\u3002":"\u542F\u7528\u524D\u9700\u8981\u786E\u8BA4\u5FAE\u4FE1\u901A\u9053\u6570\u636E\u4E0E\u9690\u79C1\u6388\u6743\u3002"}),e.push({code:"server_decision_unavailable",ok:!0,severity:"blocking",message:"\u670D\u52A1\u7AEF\u5224\u65AD\u80FD\u529B\u53EF\u7528\u3002"}),e}function W(s,e){const t=E(e.text);if(!t.length&&!e.attachment)return[];if(s==="wechat-rpa"&&e.attachment&&t.length<=1)return[{text:t[0]??"",attachment:e.attachment,idempotencyKey:e.idempotencyKey}];const n=t.map((o,r)=>({text:o,attachment:void 0,idempotencyKey:t.length>1&&e.idempotencyKey?`${e.idempotencyKey}:${r+1}`:e.idempotencyKey}));return e.attachment&&n.push({text:"",attachment:e.attachment,idempotencyKey:t.length&&e.idempotencyKey?`${e.idempotencyKey}:attachment`:e.idempotencyKey}),n}function F(...s){const e=new Set,t=[];for(const n of s.flat()){const o=I(n);if(!o){t.push(n);continue}e.has(o)||(e.add(o),t.push(n))}return t}function I(s){const e=S(s.channelId),t=S(s.conversationId),n=S(s.messageId);return!e||!t||!n?null:`${e}
2
+ ${t}
3
+ ${n}`}function S(s){return typeof s=="string"?s.trim():""}function A(s,e,t){return P.createHash("sha256").update(`${s}
4
4
  ${e}
5
- ${t}`).digest("hex").slice(0,32)}function C(s){return y.join(s,".shennian","external-reply-idempotency.json")}function F(s){try{const e=JSON.parse(g.readFileSync(C(s),"utf8")),t=Array.isArray(e.completed)?e.completed:[];return new Set(t.map(n=>typeof n=="string"?n:n&&typeof n=="object"&&typeof n.key=="string"?n.key:"").filter(Boolean))}catch{return new Set}}function K(s,e){const t=C(s);g.mkdirSync(y.dirname(t),{recursive:!0});const n=Array.from(e).slice(-500);g.writeFileSync(t,JSON.stringify({updatedAt:new Date().toISOString(),completed:n.map(o=>({key:o}))},null,2)),e.clear();for(const o of n)e.add(o)}function I(s,e){const t=Number(s??e);return Number.isFinite(t)&&t>=0?t:void 0}function S(s){return typeof s=="string"&&s.trim()?s.trim():void 0}export{G as ChannelRuntime,E as planExternalReplySends};
5
+ ${t}`).digest("hex").slice(0,32)}function M(s){return u.join(s,".shennian","external-reply-idempotency.json")}function T(s){try{const e=JSON.parse(g.readFileSync(M(s),"utf8")),t=Array.isArray(e.completed)?e.completed:[];return new Set(t.map(n=>typeof n=="string"?n:n&&typeof n=="object"&&typeof n.key=="string"?n.key:"").filter(Boolean))}catch{return new Set}}function $(s,e){const t=M(s);g.mkdirSync(u.dirname(t),{recursive:!0});const n=Array.from(e).slice(-500);g.writeFileSync(t,JSON.stringify({updatedAt:new Date().toISOString(),completed:n.map(o=>({key:o}))},null,2)),e.clear();for(const o of n)e.add(o)}function R(s,e){const t=Number(s??e);return Number.isFinite(t)&&t>=0?t:void 0}function b(s){return typeof s=="string"&&s.trim()?s.trim():void 0}export{J as ChannelRuntime,W as planExternalReplySends};
@@ -1 +1 @@
1
- function l(n){return[n.title,n.preview,n.timeText,n.unreadText].map(e=>a(e)).filter(Boolean).join("|")}function h(n){const e=n.minScore??.72,t=n.visibleItems.map(o=>({...o,fingerprint:o.fingerprint||l(o)})),r=[];for(const o of n.targets){let i=null;for(const s of t){const c=u(o,s);(!i||c.score>i.score)&&(i=c)}if(i&&i.score>=e&&i.reason!=="none"){r.push(i);const s=t.findIndex(c=>c===i.item);s>=0&&t.splice(s,1)}}return r}function u(n,e){const t=e.fingerprint||l(e);if(n.lastConversationFingerprint&&t&&n.lastConversationFingerprint===t)return{bindingId:n.bindingId,item:e,score:1,reason:"fingerprint"};if(f(n.conversationDisplayName)&&d(e.title,n.conversationDisplayName))return{bindingId:n.bindingId,item:e,score:1,reason:"title"};if(f(n.conversationDisplayName)&&!d(e.title,n.conversationDisplayName))return{bindingId:n.bindingId,item:e,score:0,reason:"none"};const r=p(n.conversationDisplayName,e.title),o=n.lastPreview?p(n.lastPreview,e.preview):0;return r>=.8&&o>=.55?{bindingId:n.bindingId,item:e,score:r*.7+o*.3,reason:"title-preview"}:r>=.88?{bindingId:n.bindingId,item:e,score:r*.86,reason:"title"}:{bindingId:n.bindingId,item:e,score:r*.5+o*.2,reason:"none"}}function a(n){return typeof n=="string"?n.trim().replace(/\s+/g," ").toLowerCase():""}function f(n){const e=typeof n=="string"?n.trim():"";return/^[A-Z0-9_-]{1,6}$/.test(e)&&/[A-Z]/.test(e)}function d(n,e){const t=typeof n=="string"?n.trim().replace(/\s+/g," "):"",r=typeof e=="string"?e.trim().replace(/\s+/g," "):"";return!t||!r?!1:t===r?!0:g(t)===r}function g(n){return n.normalize("NFKC").replace(/\s*[\(][0-9]+[\)]\s*$/u,"").replace(/\s+/g," ").trim()}function p(n,e){const t=a(n),r=a(e);return!t||!r?0:t===r?1:1-v(t,r)/Math.max(t.length,r.length)}function v(n,e){const t=Array.from({length:e.length+1},(o,i)=>i),r=Array.from({length:e.length+1},()=>0);for(let o=1;o<=n.length;o+=1){r[0]=o;for(let i=1;i<=e.length;i+=1)r[i]=Math.min(t[i]+1,r[i-1]+1,t[i-1]+(n[o-1]===e[i-1]?0:1));for(let i=0;i<=e.length;i+=1)t[i]=r[i]}return t[e.length]}export{l as buildWeChatConversationFingerprint,h as matchWeChatConversationFingerprints,u as scoreConversationFingerprint};
1
+ function l(n){return[n.title,n.preview,n.timeText,n.unreadText].map(e=>a(e)).filter(Boolean).join("|")}function h(n){const e=n.minScore??.72,t=n.visibleItems.map(o=>({...o,fingerprint:o.fingerprint||l(o)})),r=[];for(const o of n.targets){let i=null;for(const s of t){const c=u(o,s);(!i||c.score>i.score)&&(i=c)}if(i&&i.score>=e&&i.reason!=="none"){r.push(i);const s=t.findIndex(c=>c===i.item);s>=0&&t.splice(s,1)}}return r}function u(n,e){const t=e.fingerprint||l(e);if(n.lastConversationFingerprint&&t&&n.lastConversationFingerprint===t)return{bindingId:n.bindingId,item:e,score:1,reason:"fingerprint"};if(f(n.conversationDisplayName)&&d(e.title,n.conversationDisplayName))return{bindingId:n.bindingId,item:e,score:1,reason:"title"};if(f(n.conversationDisplayName)&&!d(e.title,n.conversationDisplayName))return{bindingId:n.bindingId,item:e,score:0,reason:"none"};const r=p(n.conversationDisplayName,e.title),o=n.lastPreview?p(n.lastPreview,e.preview):0;return r>=.8&&o>=.55?{bindingId:n.bindingId,item:e,score:r*.7+o*.3,reason:"title-preview"}:r>=.88?{bindingId:n.bindingId,item:e,score:r*.86,reason:"title"}:{bindingId:n.bindingId,item:e,score:r*.5+o*.2,reason:"none"}}function a(n){return typeof n=="string"?n.trim().replace(/\s+/g," ").toLowerCase():""}function f(n){const e=typeof n=="string"?n.trim():"";return/^[A-Z0-9_-]{1,6}$/.test(e)&&/[A-Z]/.test(e)}function d(n,e){const t=typeof n=="string"?n.trim().replace(/\s+/g," "):"",r=typeof e=="string"?e.trim().replace(/\s+/g," "):"";return!t||!r?!1:t===r?!0:g(t)===r}function g(n){return n.normalize("NFKC").replace(/\s*[(][0-9]+[)]\s*$/u,"").replace(/\s+/g," ").trim()}function p(n,e){const t=a(n),r=a(e);return!t||!r?0:t===r?1:1-v(t,r)/Math.max(t.length,r.length)}function v(n,e){const t=Array.from({length:e.length+1},(o,i)=>i),r=Array.from({length:e.length+1},()=>0);for(let o=1;o<=n.length;o+=1){r[0]=o;for(let i=1;i<=e.length;i+=1)r[i]=Math.min(t[i]+1,r[i-1]+1,t[i-1]+(n[o-1]===e[i-1]?0:1));for(let i=0;i<=e.length;i+=1)t[i]=r[i]}return t[e.length]}export{l as buildWeChatConversationFingerprint,h as matchWeChatConversationFingerprints,u as scoreConversationFingerprint};
@@ -1,5 +1,6 @@
1
1
  export declare const WECHAT_CHANNEL_HELPER_VERSION = "0.1.9";
2
2
  export declare const WECHAT_CHANNEL_HELPER_DIR_ENV = "SHENNIAN_WECHAT_CHANNEL_HELPER_DIR";
3
+ export declare const SHENNIAN_HELPER_RUNTIME_DIR_ENV = "SHENNIAN_HELPER_RUNTIME_DIR";
3
4
  export type WeChatChannelHelperAssetManifest = {
4
5
  schemaVersion: 1;
5
6
  helperVersion: string;
@@ -1 +1 @@
1
- import f from"node:fs";import t from"node:path";import d from"node:crypto";const x="0.1.9",g="SHENNIAN_WECHAT_CHANNEL_HELPER_DIR",m=["en","v"].join("");function j(e={}){const n=e.platform??process.platform;if(n!=="darwin"&&n!=="win32")return{ok:!1,reasonCode:"unsupported_platform",message:"WeChat channel helper is only available on macOS and Windows"};const r=e.baseDir?[t.resolve(e.baseDir)]:C({platform:n,env:e[m]??w(),homedir:e.homedir,includeInstalledDesktop:e.includeInstalledDesktop});let o=null;for(const i of r){const s=y({platform:n,baseDir:i,verifyIntegrity:e.verifyIntegrity});if(s.ok||(e.baseDir||f.existsSync(i))&&(o??=s,e.baseDir))return s}return o??{ok:!1,reasonCode:"desktop_app_required",message:S(n,r)}}function y(e){const{platform:n,baseDir:r}=e,o=t.join(r,"manifest.json"),i=A(o);if(!i)return{ok:!1,reasonCode:"manifest_missing",message:`WeChat channel helper manifest is missing: ${o}`};const s=i.platforms[n],c=s?t.join(r,s.executable):"";if(!s||!c||!f.existsSync(c))return{ok:!1,reasonCode:"helper_missing",message:`WeChat channel helper executable is missing: ${c||r}`};if(D({platform:n,baseDir:r,verifyIntegrity:e.verifyIntegrity})&&s.sha256&&d.createHash("sha256").update(f.readFileSync(c)).digest("hex")!==s.sha256)return{ok:!1,reasonCode:"integrity_mismatch",message:`WeChat channel helper integrity mismatch: ${c}`};const a=E(n,c);return a.ok?{ok:!0,helperPath:c,helperDir:r,version:i.helperVersion,manifest:i,warning:s.signed&&s.notarized?void 0:"helper_not_signed_or_notarized"}:{ok:!1,reasonCode:"helper_not_executable",message:a.message}}function E(e,n){if(e!=="darwin")return{ok:!0};try{const r=f.statSync(n);return(r.mode&73)!==0?{ok:!0}:(f.chmodSync(n,r.mode|73),{ok:!0})}catch(r){return{ok:!1,message:`WeChat channel helper is not executable and could not be repaired: ${n}: ${r instanceof Error?r.message:String(r)}`}}}function C(e){const{platform:n,env:r}=e,o=n==="win32"?"windows":"macos",i=[],s=a=>{if(!a)return;const l=t.resolve(a);i.includes(l)||i.push(l)},c=r[g]?.trim();if(c&&s(k(c,o)),e.includeInstalledDesktop===!1)return i;if(n==="darwin"){const a=e.homedir||r.HOME;a&&s(t.join(a,"Applications","Shennian.app","Contents","Resources","wechat-channel",o)),s(t.join("/Applications","Shennian.app","Contents","Resources","wechat-channel",o))}else if(n==="win32"){const a=e.homedir||r.USERPROFILE,l=r.LOCALAPPDATA||(a?t.join(a,"AppData","Local"):""),h=r.ProgramFiles||r.PROGRAMFILES,u=r["ProgramFiles(x86)"]||r["PROGRAMFILES(X86)"];for(const p of[l?t.join(l,"Programs","Shennian"):"",l?t.join(l,"Programs","shennian"):"",h?t.join(h,"Shennian"):"",u?t.join(u,"Shennian"):""])p&&s(t.join(p,"resources","wechat-channel",o))}return i}function k(e,n){return f.existsSync(t.join(e,"manifest.json"))?e:t.join(e,n)}function D(e){return e.verifyIntegrity===!0?!0:e.verifyIntegrity===!1?!1:!_(e.platform,e.baseDir)}function _(e,n){const r=n.replace(/[\\/]+/g,"/");return e==="darwin"?/\/Shennian\.app\/Contents\/Resources\/wechat-channel\/macos$/i.test(r):e==="win32"?/\/resources\/wechat-channel\/windows$/i.test(r):!1}function S(e,n){const r=e==="win32"?"Windows":"macOS",o=n.length>0?` Checked helper directories: ${n.join(", ")}`:"";return`WeChat RPA requires Shennian Desktop on ${r}; install or open Shennian Desktop, then retry.${o}`}function w(){return globalThis.process?.[m]??{}}function A(e){try{const n=JSON.parse(R(e));return n.schemaVersion!==1||typeof n.helperVersion!="string"||!n.platforms?null:n}catch{return null}}function R(e){return f.readFileSync(e,"utf8").replace(/^\uFEFF/,"")}export{g as WECHAT_CHANNEL_HELPER_DIR_ENV,x as WECHAT_CHANNEL_HELPER_VERSION,j as resolveWeChatChannelHelperAsset};
1
+ import l from"node:fs";import o from"node:path";import g from"node:crypto";const b="0.1.9",R="SHENNIAN_WECHAT_CHANNEL_HELPER_DIR",S="SHENNIAN_HELPER_RUNTIME_DIR",f=["en","v"].join("");function O(e={}){const r=e.platform??process.platform;if(r!=="darwin"&&r!=="win32")return{ok:!1,reasonCode:"unsupported_platform",message:"WeChat channel helper is only available on macOS and Windows"};const n=e.baseDir?[o.resolve(e.baseDir)]:j({platform:r,env:e[f]??L(),homedir:e.homedir,includeInstalledDesktop:e.includeInstalledDesktop});let t=null;for(const s of n){const i=E({platform:r,baseDir:s,verifyIntegrity:e.verifyIntegrity});if(i.ok||(e.baseDir||l.existsSync(s))&&(t??=i,e.baseDir))return i}return t??{ok:!1,reasonCode:"desktop_app_required",message:I(r,n)}}function E(e){const{platform:r,baseDir:n}=e,t=o.join(n,"manifest.json"),s=P(t);if(!s)return{ok:!1,reasonCode:"manifest_missing",message:`WeChat channel helper manifest is missing: ${t}`};const i=s.platforms[r],a=i?o.join(n,i.executable):"";if(!i||!a||!l.existsSync(a))return{ok:!1,reasonCode:"helper_missing",message:`WeChat channel helper executable is missing: ${a||n}`};if(D({platform:r,baseDir:n,verifyIntegrity:e.verifyIntegrity})&&i.sha256&&g.createHash("sha256").update(l.readFileSync(a)).digest("hex")!==i.sha256)return{ok:!1,reasonCode:"integrity_mismatch",message:`WeChat channel helper integrity mismatch: ${a}`};const h=A(r,a);return h.ok?{ok:!0,helperPath:a,helperDir:n,version:s.helperVersion,manifest:s,warning:i.signed&&i.notarized?void 0:"helper_not_signed_or_notarized"}:{ok:!1,reasonCode:"helper_not_executable",message:h.message}}function A(e,r){if(e!=="darwin")return{ok:!0};try{const n=l.statSync(r);return(n.mode&73)!==0?{ok:!0}:(l.chmodSync(r,n.mode|73),{ok:!0})}catch(n){return{ok:!1,message:`WeChat channel helper is not executable and could not be repaired: ${r}: ${n instanceof Error?n.message:String(n)}`}}}function j(e){const{platform:r,env:n}=e,t=r==="win32"?"windows":"macos",s=[],i=c=>{if(!c)return;const p=o.resolve(c);s.includes(p)||s.push(p)},a=n[R]?.trim();a&&i(w(a,t));const h=n[S]?.trim();if(h&&i(y(h,t)),H({platform:r,platformDir:t,env:n,homedir:e.homedir,push:i}),e.includeInstalledDesktop===!1)return s;if(r==="darwin"){const c=e.homedir||n.HOME;c&&i(o.join(c,"Applications","Shennian.app","Contents","Resources","wechat-channel",t)),i(o.join("/Applications","Shennian.app","Contents","Resources","wechat-channel",t))}else if(r==="win32"){const c=e.homedir||n.USERPROFILE,p=n.LOCALAPPDATA||(c?o.join(c,"AppData","Local"):""),m=n.ProgramFiles||n.PROGRAMFILES,u=n["ProgramFiles(x86)"]||n["PROGRAMFILES(X86)"];for(const d of[p?o.join(p,"Programs","Shennian"):"",p?o.join(p,"Programs","shennian"):"",m?o.join(m,"Shennian"):"",u?o.join(u,"Shennian"):""])d&&i(o.join(d,"resources","wechat-channel",t))}return s}function H(e){const{platform:r,platformDir:n,env:t,push:s}=e;if(r==="darwin"){const i=e.homedir||t.HOME,a=C({platform:r,env:t,homedir:e.homedir});a&&(s(o.join(a,"wechat-channel",n)),s(o.join(a,"Shennian Helper.app","Contents","Resources","wechat-channel",n))),i&&s(o.join(i,"Applications","Shennian Helper.app","Contents","Resources","wechat-channel",n)),s(o.join("/Applications","Shennian Helper.app","Contents","Resources","wechat-channel",n)),s(o.join("/Library","Application Support","Shennian","Helper","wechat-channel",n))}else if(r==="win32"){const i=e.homedir||t.USERPROFILE,a=t.LOCALAPPDATA||(i?o.join(i,"AppData","Local"):""),h=t.ProgramFiles||t.PROGRAMFILES,c=t["ProgramFiles(x86)"]||t["PROGRAMFILES(X86)"];a&&(s(o.join(a,"Programs","Shennian Helper","resources","wechat-channel",n)),s(o.join(a,"Shennian","Helper","wechat-channel",n))),h&&s(o.join(h,"Shennian Helper","resources","wechat-channel",n)),c&&s(o.join(c,"Shennian Helper","resources","wechat-channel",n))}}function w(e,r){return l.existsSync(o.join(e,"manifest.json"))?e:o.join(e,r)}function y(e,r){if(l.existsSync(o.join(e,"manifest.json")))return e;const n=o.join(e,"wechat-channel",r);if(l.existsSync(o.join(n,"manifest.json")))return n;const t=o.join(e,"Shennian Helper.app","Contents","Resources","wechat-channel",r);if(l.existsSync(o.join(t,"manifest.json")))return t;const s=o.join(e,r);return l.existsSync(o.join(s,"manifest.json"))?s:n}function C(e){const r=e[f],n=r[S]?.trim();if(n)return o.resolve(n);const t=e.homedir||(e.platform==="win32"?r.USERPROFILE:r.HOME);if(e.platform==="darwin")return t?o.join(t,"Library","Application Support","Shennian","Helper"):null;if(e.platform==="win32"){const s=r.LOCALAPPDATA||(t?o.join(t,"AppData","Local"):"");return s?o.join(s,"Shennian","Helper"):null}return null}function D(e){return e.verifyIntegrity===!0?!0:e.verifyIntegrity===!1?!1:!_(e.platform,e.baseDir)}function _(e,r){const n=r.replace(/[\\/]+/g,"/");return e==="darwin"?/\/Shennian\.app\/Contents\/Resources\/wechat-channel\/macos$/i.test(n):e==="win32"?/\/resources\/wechat-channel\/windows$/i.test(n):!1}function I(e,r){const n=e==="win32"?"Windows":"macOS",t=r.length>0?` Checked helper directories: ${r.join(", ")}`:"";return`WeChat RPA requires Shennian Desktop or Shennian Helper on ${n}; install or open Shennian Desktop, then retry.${t}`}function L(){return globalThis.process?.[f]??{}}function P(e){try{const r=JSON.parse(k(e));return r.schemaVersion!==1||typeof r.helperVersion!="string"||!r.platforms?null:r}catch{return null}}function k(e){return l.readFileSync(e,"utf8").replace(/^\uFEFF/,"")}export{S as SHENNIAN_HELPER_RUNTIME_DIR_ENV,R as WECHAT_CHANNEL_HELPER_DIR_ENV,b as WECHAT_CHANNEL_HELPER_VERSION,O as resolveWeChatChannelHelperAsset};
@@ -8,6 +8,22 @@ export type WeChatChannelHelperClientOptions = {
8
8
  guardUnsafeWindowsCommands?: boolean;
9
9
  skipUserActivityGuard?: boolean;
10
10
  cleanupWindowsOverlays?: boolean;
11
+ requestLogger?: (event: WeChatChannelHelperRequestTraceEvent) => void;
12
+ };
13
+ export type WeChatChannelHelperRequestTraceEvent = {
14
+ phase: 'request' | 'response' | 'error';
15
+ at: string;
16
+ id: string;
17
+ command: WeChatChannelHelperCommandName;
18
+ traceId?: string;
19
+ timeoutMs?: number;
20
+ durationMs?: number;
21
+ params?: Record<string, unknown>;
22
+ ok?: boolean;
23
+ errorCode?: string;
24
+ errorSummary?: string;
25
+ latencyMs?: number;
26
+ result?: unknown;
11
27
  };
12
28
  export declare class WeChatChannelHelperClient {
13
29
  private options;
@@ -37,4 +53,5 @@ export declare class WeChatChannelHelperClient {
37
53
  private captureStderr;
38
54
  private formatHelperExitMessage;
39
55
  private rejectAllPending;
56
+ private emitRequestTrace;
40
57
  }
@@ -1,3 +1,3 @@
1
- import{spawn as p}from"node:child_process";import{randomUUID as d}from"node:crypto";import{createInterface as f}from"node:readline";import{decideWeChatChannelActivityGate as w,normalizeWeChatChannelActivitySnapshot as y}from"./human-coordination.js";import{createWeChatChannelHelperHello as m,extractWeChatChannelHelperWarmupSnapshot as g,timeoutForWeChatChannelHelperCommand as C,validateWeChatChannelHelperReady as W}from"./helper-protocol.js";class x{options;child=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.child&&this.readyState?this.readyState:this.startPromise?this.startPromise:(this.child&&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=p(this.options.helperPath,this.options.args??[],{cwd:this.options.cwd,stdio:["pipe","pipe","pipe"],windowsHide:!0});this.child=e,this.stderrTail="",this.lines=f({input:e.stdout}),this.lines.on("line",t=>this.handleLine(t)),e.stderr.on("data",t=>{this.captureStderr(t)}),e.once("error",t=>{this.child===e&&(this.child=null,this.readyState=null,this.lines?.close(),this.lines=null),this.rejectAllPending(t instanceof Error?t:new Error(String(t)))}),e.once("exit",(t,s)=>{this.child===e&&(this.child=null,this.readyState=null,this.lines?.close(),this.lines=null),this.rejectAllPending(new Error(this.formatHelperExitMessage(t,s)))});const r=new Promise((t,s)=>{const n=setTimeout(()=>s(new Error("WeChat channel helper handshake timed out")),2e3),o=l=>{clearTimeout(n),this.lines?.off("line",u),e.off("exit",h),s(new Error(`WeChat channel helper failed to start: ${l.message}`))},h=(l,a)=>{clearTimeout(n),e.off("error",o),this.lines?.off("line",u),s(new Error(this.formatHelperExitMessage(l,a)))},u=l=>{let a;try{a=JSON.parse(l)}catch{clearTimeout(n),e.off("error",o),this.lines?.off("line",u),s(new Error("WeChat channel helper sent invalid handshake JSON"));return}if(!S(a))return;clearTimeout(n),e.off("error",o),e.off("exit",h),this.lines?.off("line",u);const c=W(a,this.options.expectedHelperVersion,this.options.requiredCapabilities);c.ok?(this.readyState=a,this.captureWarmupSnapshot(a),t(a)):s(new Error(`${c.errorCode}: ${c.errorSummary}`))};e.once("error",o),e.once("exit",h),this.lines?.on("line",u)});try{e.stdin.write(`${JSON.stringify(m(this.options.expectedHelperVersion,this.options.requiredCapabilities))}
2
- `)}catch(t){throw await this.stop().catch(()=>{}),t}return r}async request(e,r,t){const s=this.requestQueueTail.catch(()=>{}).then(()=>this.sendRequest(e,r,t));return this.requestQueueTail=s.then(()=>{},()=>{}),s}async sendRequest(e,r,t){await this.guardUnsafeWindowsCommand(e,t);try{return await this.sendRawRequest(e,r,t)}finally{await this.cleanupWindowsOverlaysAfterCommand(e,t).catch(()=>{})}}async sendRawRequest(e,r,t){if((!this.child||!this.readyState)&&await this.start(),!this.child)throw new Error("WeChat channel helper is not started");const s=d(),n=C(e),o=new Promise((u,l)=>{const a=setTimeout(()=>{this.pending.delete(s),l(new Error(`helper_command_timeout: ${e}`))},n);this.pending.set(s,{resolve:u,reject:l,timer:a})});this.child.stdin.write(`${JSON.stringify({id:s,command:e,params:r,traceId:t})}
3
- `);const h=await o;return this.captureWarmupSnapshot(h),this.captureWarmupSnapshot(h.result),h}async guardUnsafeWindowsCommand(e,r){if(!this.options.guardUnsafeWindowsCommands||!E(e))return;const t=await this.sendRawRequest("permissions.check",{},r);if(!t.ok)throw new Error(`${t.errorCode??"permissions_check_failed"}: ${t.errorSummary??e}`);const s=t.result??{};if(s.screenRecording===!1)throw new Error("permission_screen_recording_missing");if(s.accessibility===!1)throw new Error("permission_accessibility_missing");if(s.automation===!1)throw new Error("permission_automation_missing");if(s.wechatRunning===!1)throw new Error("wechat_not_running");if(s.duplicateInstanceBlocked===!0||Number(s.duplicateMainWindowCount??0)>0||Number(s.unsafeAuxiliaryWindowCount??0)>0)throw new Error("wechat_duplicate_instance");if(s.wechatWindowAvailable===!1)throw new Error("wechat_window_unavailable");if(s.wechatMainWindowResponsive===!1)throw new Error("wechat_window_unresponsive");if(!this.options.skipUserActivityGuard&&b(e)){const n=await this.sendRawRequest("activity.snapshot",{},r);if(!n.ok)throw new Error(`${n.errorCode??"user_activity_unknown"}: ${n.errorSummary??e}`);const o=w({snapshot:y(n.result),stage:"dangerous_action"});if(!o.ok)throw new Error(`user_active:${o.reasonCode}`)}await this.cleanupWindowsOverlays("before",e,r)}async cleanupWindowsOverlaysAfterCommand(e,r){!this.options.guardUnsafeWindowsCommands||!_(e)||await this.cleanupWindowsOverlays("after",e,r)}async cleanupWindowsOverlays(e,r,t){if(this.options.cleanupWindowsOverlays===!1)return;if(!this.readyState?.capabilities.includes("overlayCleanup"))throw new Error("helper_capability_missing: overlayCleanup");const s=await this.sendRawRequest("windows.cleanupOverlays",{stage:e,command:r,includeGeneratedMediaPreviews:!0,includeToolingTerminals:!1,waitMs:e==="after"?220:120},t);if(!s.ok)throw new Error(`${s.errorCode??"overlay_cleanup_failed"}: ${s.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;this.child=null,this.readyState=null,this.lines?.close(),this.lines=null,this.rejectAllPending(new Error("WeChat channel helper stopped")),!(!e||e.killed)&&e.kill("SIGTERM")}handleLine(e){let r;try{r=JSON.parse(e)}catch{return}if(!v(r))return;const t=this.pending.get(r.id);t&&(clearTimeout(t.timer),this.pending.delete(r.id),t.resolve(r))}captureWarmupSnapshot(e){const r=g(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 t=[e===null?null:`code=${e}`,r?`signal=${r}`:null,this.stderrTail.trim()?`stderr=${this.stderrTail.trim()}`:null].filter(Boolean);return t.length?`WeChat channel helper process exited (${t.join(", ")})`:"WeChat channel helper process exited"}rejectAllPending(e){for(const[r,t]of this.pending)clearTimeout(t.timer),t.reject(e),this.pending.delete(r)}}function S(i){if(!i||typeof i!="object")return!1;const e=i;return e.type==="ready"&&typeof e.helperVersion=="string"&&typeof e.protocolVersion=="number"&&Array.isArray(e.capabilities)&&typeof e.pid=="number"}function v(i){if(!i||typeof i!="object")return!1;const e=i;return typeof e.id=="string"&&typeof e.ok=="boolean"&&typeof e.latencyMs=="number"}function E(i){return i==="windows.focus"||i==="windows.capture"||i==="screen.capture"||i==="ocr.recognize"||i==="image.cropHash"||i==="wechat.searchConversation"||i==="wechat.focusMessageInput"||i.startsWith("mouse.")||i.startsWith("keyboard.")||i.startsWith("clipboard.")||i==="menu.pickItem"||i==="savePanel.saveToPath"}function _(i){return i.startsWith("mouse.")||i.startsWith("keyboard.")||i.startsWith("clipboard.")||i==="menu.pickItem"||i==="savePanel.saveToPath"}function b(i){return i==="windows.focus"||i==="wechat.searchConversation"||i==="wechat.focusMessageInput"||i.startsWith("mouse.")||i.startsWith("keyboard.")||i.startsWith("clipboard.set")||i==="clipboard.restore"||i==="menu.pickItem"||i==="savePanel.saveToPath"}export{x as WeChatChannelHelperClient};
1
+ import{spawn as d}from"node:child_process";import{randomUUID as w}from"node:crypto";import{createInterface as f}from"node:readline";import{decideWeChatChannelActivityGate as y,normalizeWeChatChannelActivitySnapshot as m}from"./human-coordination.js";import{createWeChatChannelHelperHello as g,extractWeChatChannelHelperWarmupSnapshot as S,timeoutForWeChatChannelHelperCommand as C,validateWeChatChannelHelperReady as W}from"./helper-protocol.js";class ${options;child=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.child&&this.readyState?this.readyState:this.startPromise?this.startPromise:(this.child&&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=v(this.options.helperPath,this.options.args??[]),t=d(e.command,e.args,{cwd:this.options.cwd,stdio:["pipe","pipe","pipe"],windowsHide:!0});this.child=t,this.stderrTail="",this.lines=f({input:t.stdout}),this.lines.on("line",r=>this.handleLine(r)),t.stderr.on("data",r=>{this.captureStderr(r)}),t.stdin.on("error",r=>{this.rejectAllPending(r instanceof Error?r:new Error(String(r)))}),t.once("error",r=>{this.child===t&&(this.child=null,this.readyState=null,this.lines?.close(),this.lines=null),this.rejectAllPending(r instanceof Error?r:new Error(String(r)))}),t.once("exit",(r,a)=>{this.child===t&&(this.child=null,this.readyState=null,this.lines?.close(),this.lines=null),this.rejectAllPending(new Error(this.formatHelperExitMessage(r,a)))});const s=new Promise((r,a)=>{const o=setTimeout(()=>a(new Error("WeChat channel helper handshake timed out")),1e4),c=u=>{clearTimeout(o),this.lines?.off("line",h),t.off("exit",n),a(new Error(`WeChat channel helper failed to start: ${u.message}`))},n=(u,l)=>{clearTimeout(o),t.off("error",c),this.lines?.off("line",h),a(new Error(this.formatHelperExitMessage(u,l)))},h=u=>{let l;try{l=JSON.parse(u)}catch{clearTimeout(o),t.off("error",c),this.lines?.off("line",h),a(new Error("WeChat channel helper sent invalid handshake JSON"));return}if(!E(l))return;clearTimeout(o),t.off("error",c),t.off("exit",n),this.lines?.off("line",h);const p=W(l,this.options.expectedHelperVersion,this.options.requiredCapabilities);p.ok?(this.readyState=l,this.captureWarmupSnapshot(l),r(l)):a(new Error(`${p.errorCode}: ${p.errorSummary}`))};t.once("error",c),t.once("exit",n),this.lines?.on("line",h)});try{t.stdin.write(`${JSON.stringify(g(this.options.expectedHelperVersion,this.options.requiredCapabilities))}
2
+ `)}catch(r){throw await this.stop().catch(()=>{}),r}return s}async request(e,t,s){const r=this.requestQueueTail.catch(()=>{}).then(()=>this.sendRequest(e,t,s));return this.requestQueueTail=r.then(()=>{},()=>{}),r}async sendRequest(e,t,s){await this.guardUnsafeWindowsCommand(e,s);try{return await this.sendRawRequest(e,t,s)}finally{await this.cleanupWindowsOverlaysAfterCommand(e,s).catch(()=>{})}}async sendRawRequest(e,t,s){if((!this.child||!this.readyState)&&await this.start(),!this.child)throw new Error("WeChat channel helper is not started");const r=w(),a=C(e),o=Date.now();this.emitRequestTrace({phase:"request",at:new Date(o).toISOString(),id:r,command:e,traceId:s,params:t,timeoutMs:a});const c=new Promise((n,h)=>{const u=setTimeout(()=>{this.pending.delete(r),h(new Error(`helper_command_timeout: ${e}`))},a);this.pending.set(r,{resolve:n,reject:h,timer:u})});try{this.child.stdin.write(`${JSON.stringify({id:r,command:e,params:t,traceId:s})}
3
+ `)}catch(n){const h=this.pending.get(r);throw h&&clearTimeout(h.timer),this.pending.delete(r),this.emitRequestTrace({phase:"error",at:new Date().toISOString(),id:r,command:e,traceId:s,durationMs:Date.now()-o,errorSummary:n instanceof Error?n.message:String(n)}),n}try{const n=await c;return this.emitRequestTrace({phase:"response",at:new Date().toISOString(),id:r,command:e,traceId:s,durationMs:Date.now()-o,ok:n.ok,errorCode:n.errorCode,errorSummary:n.errorSummary,latencyMs:n.latencyMs,result:n.result}),this.captureWarmupSnapshot(n),this.captureWarmupSnapshot(n.result),n}catch(n){throw this.emitRequestTrace({phase:"error",at:new Date().toISOString(),id:r,command:e,traceId:s,durationMs:Date.now()-o,errorSummary:n instanceof Error?n.message:String(n)}),n}}async guardUnsafeWindowsCommand(e,t){if(!this.options.guardUnsafeWindowsCommands||!_(e))return;const s=await this.sendRawRequest("permissions.check",{},t);if(!s.ok)throw new Error(`${s.errorCode??"permissions_check_failed"}: ${s.errorSummary??e}`);const r=s.result??{};if(r.screenRecording===!1)throw new Error("permission_screen_recording_missing");if(r.automation===!1)throw new Error("permission_automation_missing");if(r.wechatRunning===!1)throw new Error("wechat_not_running");if(r.duplicateInstanceBlocked===!0||Number(r.duplicateMainWindowCount??0)>0||Number(r.unsafeAuxiliaryWindowCount??0)>0)throw new Error("wechat_duplicate_instance");if(r.wechatWindowAvailable===!1)throw new Error("wechat_window_unavailable");if(r.wechatMainWindowResponsive===!1)throw new Error("wechat_window_unresponsive");if(!this.options.skipUserActivityGuard&&R(e)){const a=await this.sendRawRequest("activity.snapshot",{},t);if(!a.ok)throw new Error(`${a.errorCode??"user_activity_unknown"}: ${a.errorSummary??e}`);const o=y({snapshot:m(a.result),stage:"dangerous_action"});if(!o.ok)throw new Error(`user_active:${o.reasonCode}`)}await this.cleanupWindowsOverlays("before",e,t)}async cleanupWindowsOverlaysAfterCommand(e,t){!this.options.guardUnsafeWindowsCommands||!b(e)||await this.cleanupWindowsOverlays("after",e,t)}async cleanupWindowsOverlays(e,t,s){if(this.options.cleanupWindowsOverlays===!1)return;if(!this.readyState?.capabilities.includes("overlayCleanup"))throw new Error("helper_capability_missing: overlayCleanup");const r=await this.sendRawRequest("windows.cleanupOverlays",{stage:e,command:t,includeGeneratedMediaPreviews:!0,includeToolingTerminals:!1,waitMs:e==="after"?220:120},s);if(!r.ok)throw new Error(`${r.errorCode??"overlay_cleanup_failed"}: ${r.errorSummary??t}`)}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;this.child=null,this.readyState=null,this.lines?.close(),this.lines=null,this.rejectAllPending(new Error("WeChat channel helper stopped")),!(!e||e.killed)&&e.kill("SIGTERM")}handleLine(e){let t;try{t=JSON.parse(e)}catch{return}if(!T(t))return;const s=this.pending.get(t.id);s&&(clearTimeout(s.timer),this.pending.delete(t.id),s.resolve(t))}captureWarmupSnapshot(e){const t=S(e);t&&(this.warmupState=t)}captureStderr(e){const t=Buffer.isBuffer(e)?e.toString("utf8"):String(e??"");t&&(this.stderrTail=(this.stderrTail+t).slice(-2e3))}formatHelperExitMessage(e,t){const s=[e===null?null:`code=${e}`,t?`signal=${t}`: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[t,s]of this.pending)clearTimeout(s.timer),s.reject(e),this.pending.delete(t)}emitRequestTrace(e){try{this.options.requestLogger?.(e)}catch{}}}function v(i,e){return{command:i,args:e}}function E(i){if(!i||typeof i!="object")return!1;const e=i;return e.type==="ready"&&typeof e.helperVersion=="string"&&typeof e.protocolVersion=="number"&&Array.isArray(e.capabilities)&&typeof e.pid=="number"}function T(i){if(!i||typeof i!="object")return!1;const e=i;return typeof e.id=="string"&&typeof e.ok=="boolean"&&typeof e.latencyMs=="number"}function _(i){return i==="windows.focus"||i==="windows.capture"||i==="screen.capture"||i==="ocr.recognize"||i==="image.cropHash"||i==="wechat.searchConversation"||i==="wechat.focusMessageInput"||i.startsWith("mouse.")||i.startsWith("keyboard.")||i.startsWith("clipboard.")||i==="menu.pickItem"||i==="savePanel.saveToPath"}function b(i){return i.startsWith("mouse.")||i.startsWith("keyboard.")||i.startsWith("clipboard.")||i==="menu.pickItem"||i==="savePanel.saveToPath"}function R(i){return i==="windows.focus"||i==="wechat.searchConversation"||i==="wechat.focusMessageInput"||i.startsWith("mouse.")||i.startsWith("keyboard.")||i.startsWith("clipboard.set")||i==="clipboard.restore"||i==="menu.pickItem"||i==="savePanel.saveToPath"}export{$ as WeChatChannelHelperClient};
@@ -89,7 +89,7 @@ export type WeChatChannelHumanActivitySnapshot = {
89
89
  capturesMousePath?: boolean;
90
90
  };
91
91
  };
92
- export type WeChatChannelHelperCommandName = 'health.check' | 'permissions.check' | 'activity.snapshot' | 'automation.lease.acquire' | 'automation.lease.release' | 'automation.lease.status' | 'automation.lease.simulateInterruption' | 'windows.ensureReady' | 'windows.list' | 'windows.focus' | 'windows.capture' | 'windows.cleanupOverlays' | 'windows.recoverWeChat' | 'screen.capture' | 'ocr.recognize' | 'mouse.click' | 'mouse.rightClick' | 'mouse.scroll' | 'keyboard.type' | 'keyboard.shortcut' | 'clipboard.snapshot' | 'clipboard.restore' | 'clipboard.setText' | 'clipboard.setFiles' | 'clipboard.setImage' | 'clipboard.readFileUrls' | 'clipboard.readAttachment' | 'menu.pickItem' | 'savePanel.saveToPath' | 'image.cropHash' | 'wechat.searchConversation' | 'wechat.focusMessageInput';
92
+ export type WeChatChannelHelperCommandName = 'health.check' | 'permissions.check' | 'permissions.requestScreenRecording' | 'permissions.requestAccessibility' | 'activity.snapshot' | 'automation.lease.acquire' | 'automation.lease.release' | 'automation.lease.status' | 'automation.lease.simulateInterruption' | 'windows.ensureReady' | 'windows.list' | 'windows.focus' | 'windows.capture' | 'windows.captureAndOcr' | 'windows.cleanupOverlays' | 'windows.recoverWeChat' | 'screen.capture' | 'ocr.recognize' | 'mouse.click' | 'mouse.rightClick' | 'mouse.scroll' | 'keyboard.type' | 'keyboard.shortcut' | 'clipboard.snapshot' | 'clipboard.restore' | 'clipboard.setText' | 'clipboard.setFiles' | 'clipboard.setImage' | 'clipboard.readFileUrls' | 'clipboard.readAttachment' | 'menu.pickItem' | 'savePanel.saveToPath' | 'image.cropHash' | 'wechat.searchConversation' | 'wechat.focusMessageInput';
93
93
  export type WeChatChannelHelperCommand = {
94
94
  id: string;
95
95
  command: WeChatChannelHelperCommandName;
@@ -1 +1 @@
1
- const H=1,o={healthCheck:2e3,permissionsCheck:1e4,activitySnapshot:1e3,automationLease:1e3,windowsEnsureReady:2e4,ocrRecognize:1e4,windowsCapture:5e3,mouseKeyboard:3e3,clipboard:5e3,menuPickItem:5e3,savePanel:1e4,wechatSearch:6e3},s=["screenCapture","visionOcr","windowList","windowFocus","mouseKeyboard","clipboard","imageCropHash","wechatSearch","humanActivity"],c=[...s,"contextMenu"],u=[...c,"automationLease"],p=u,l=[...s,"overlayCleanup","wechatRecovery"],C=[...l,"contextMenu"],E=[...C,"automationLease"];function A(e){return e==="observe"?[...s]:e==="download"?[...c]:[...u]}function w(e){return e==="observe"?[...l]:e==="download"?[...C]:[...E]}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(!h(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"?o.healthCheck:e==="permissions.check"?o.permissionsCheck:e==="activity.snapshot"?o.activitySnapshot:e.startsWith("automation.lease.")?o.automationLease:e==="windows.ensureReady"||e==="windows.cleanupOverlays"||e==="windows.recoverWeChat"?o.windowsEnsureReady:e==="ocr.recognize"?o.ocrRecognize:e==="windows.capture"||e==="screen.capture"?o.windowsCapture:e.startsWith("mouse.")||e.startsWith("keyboard.")?o.mouseKeyboard:e.startsWith("clipboard.")?o.clipboard:e==="menu.pickItem"?o.menuPickItem:e==="savePanel.saveToPath"?o.savePanel:e.startsWith("wechat.")?o.wechatSearch:o.healthCheck}function h(e){return e==="cold"||e==="warming"||e==="warm"||e==="failed"}function f(e){if(!e||typeof e!="object")return;const t=e,r={};return i(t,r,"startedAt"),i(t,r,"readyAt"),i(t,r,"warmupStartedAt"),i(t,r,"warmupCompletedAt"),a(t,r,"coldStartMs"),a(t,r,"warmupMs"),a(t,r,"firstOcrMs"),a(t,r,"warmOcrMs"),a(t,r,"lastOcrMs"),a(t,r,"ocrSampleCount"),i(t,r,"errorCode"),i(t,r,"errorSummary"),Object.keys(r).length>0?r:void 0}function i(e,t,r){typeof e[r]=="string"&&(t[r]=e[r])}function a(e,t,r){const n=e[r];typeof n=="number"&&Number.isFinite(n)&&n>=0&&(t[r]=n)}export{c as WECHAT_CHANNEL_DOWNLOAD_HELPER_CAPABILITIES,o as WECHAT_CHANNEL_HELPER_COMMAND_TIMEOUT_MS,H as WECHAT_CHANNEL_HELPER_PROTOCOL_VERSION,s as WECHAT_CHANNEL_OBSERVE_HELPER_CAPABILITIES,p as WECHAT_CHANNEL_REQUIRED_HELPER_CAPABILITIES,u as WECHAT_CHANNEL_SEND_HELPER_CAPABILITIES,C as WECHAT_CHANNEL_WINDOWS_DOWNLOAD_HELPER_CAPABILITIES,l as WECHAT_CHANNEL_WINDOWS_OBSERVE_HELPER_CAPABILITIES,E as WECHAT_CHANNEL_WINDOWS_SEND_HELPER_CAPABILITIES,L as createWeChatChannelHelperHello,d as extractWeChatChannelHelperWarmupSnapshot,A as requiredWeChatChannelHelperCapabilitiesForProfile,w as requiredWindowsWeChatChannelHelperCapabilitiesForProfile,W as timeoutForWeChatChannelHelperCommand,S as validateWeChatChannelHelperReady};
1
+ const H=1,i={healthCheck:2e3,permissionsCheck:1e4,activitySnapshot:1e3,automationLease:1e3,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"],c=[...a,"contextMenu"],u=[...c,"automationLease"],p=u,l=[...a,"overlayCleanup","wechatRecovery"],C=[...l,"contextMenu"],h=[...C,"automationLease"];function A(e){return e==="observe"?[...a]:e==="download"?[...c]:[...u]}function w(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 o=r.filter(_=>!e.capabilities.includes(_));return o.length?{ok:!1,errorCode:"helper_capability_missing",errorSummary:`Helper missing capabilities: ${o.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==="permissions.check"||e==="permissions.requestScreenRecording"||e==="permissions.requestAccessibility"?i.permissionsCheck:e==="activity.snapshot"?i.activitySnapshot:e.startsWith("automation.lease.")?i.automationLease: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 n(t,r,"startedAt"),n(t,r,"readyAt"),n(t,r,"warmupStartedAt"),n(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"),n(t,r,"errorCode"),n(t,r,"errorSummary"),Object.keys(r).length>0?r:void 0}function n(e,t,r){typeof e[r]=="string"&&(t[r]=e[r])}function s(e,t,r){const o=e[r];typeof o=="number"&&Number.isFinite(o)&&o>=0&&(t[r]=o)}export{c as WECHAT_CHANNEL_DOWNLOAD_HELPER_CAPABILITIES,i as WECHAT_CHANNEL_HELPER_COMMAND_TIMEOUT_MS,H as WECHAT_CHANNEL_HELPER_PROTOCOL_VERSION,a as WECHAT_CHANNEL_OBSERVE_HELPER_CAPABILITIES,p as WECHAT_CHANNEL_REQUIRED_HELPER_CAPABILITIES,u 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,A as requiredWeChatChannelHelperCapabilitiesForProfile,w as requiredWindowsWeChatChannelHelperCapabilitiesForProfile,W as timeoutForWeChatChannelHelperCommand,S as validateWeChatChannelHelperReady};
@@ -0,0 +1,2 @@
1
+ import o from"node:fs";import l from"node:os";import p from"node:path";import{spawn as E,spawnSync as R}from"node:child_process";const m=process.argv[2];m||n("missing Helper.app path");const c=o.mkdtempSync(p.join(l.tmpdir(),"shennian-helper-app-")),f=p.join(c,"input.fifo"),a=p.join(c,"output.fifo");try{g(f),g(a);const e=E("/usr/bin/open",["-n","-W",m,"--args","--stdio-fifo",f,a],{stdio:["ignore","ignore","pipe"]});e.stderr.on("data",t=>process.stderr.write(t)),e.on("exit",(t,d)=>{S(),d?process.kill(process.pid,d):process.exit(t??0)}),e.on("error",t=>n(t.message));const r=o[["create","Read","Stream"].join("")],i=o.createWriteStream("",{fd:o.openSync(f,"r+"),encoding:"utf8",autoClose:!0}),u=r("",{fd:o.openSync(a,"r+"),encoding:"utf8",autoClose:!0});i.on("error",s),u.on("error",s),process.stdin.on("error",s),process.stdout.on("error",s),process.stdin.pipe(i),u.pipe(process.stdout),process.stdin.on("end",()=>i.end())}catch(e){S(),n(e instanceof Error?e.message:String(e))}function g(e){const r=R("mkfifo",[e],{encoding:"utf8"});r.status!==0&&n(r.stderr||`mkfifo exited ${r.status}`)}function S(){o.rmSync(c,{recursive:!0,force:!0})}function s(e){e.code==="EPIPE"||e.code==="ERR_STREAM_PREMATURE_CLOSE"||n(e.message)}function n(e){process.stderr.write(`${e}
2
+ `),process.exit(1)}
@@ -20,13 +20,30 @@ export type WeChatChannelWindowInfo = {
20
20
  height: number;
21
21
  coordinateSpace?: string;
22
22
  };
23
+ zOrder?: number | null;
24
+ rootProcessId?: number | null;
25
+ };
26
+ export type WeChatChannelScreenPoint = {
27
+ x: number;
28
+ y: number;
29
+ coordinateSpace: 'screen';
23
30
  };
24
31
  export type WeChatChannelScreenshot = {
25
32
  mimeType: string;
26
- dataBase64: string;
33
+ dataBase64?: string;
27
34
  width: number;
28
35
  height: number;
29
36
  windowId?: string | null;
37
+ bounds?: {
38
+ x: number;
39
+ y: number;
40
+ width: number;
41
+ height: number;
42
+ coordinateSpace?: string;
43
+ };
44
+ };
45
+ export type WeChatChannelScreenshotWithData = WeChatChannelScreenshot & {
46
+ dataBase64: string;
30
47
  };
31
48
  export type WeChatChannelOcrBlock = {
32
49
  text?: string;
@@ -97,10 +114,13 @@ export declare function openConversationBySearch(input: {
97
114
  binding: WeChatChannelBindingConfig;
98
115
  traceId?: string;
99
116
  searchDelayMs?: number;
117
+ settleToBottom?: boolean;
100
118
  }): Promise<{
101
119
  opened: boolean;
102
120
  reason: string;
103
121
  }>;
104
- export declare function captureWeChatWindow(helper: WeChatChannelHelperTransport, windowId: string, traceId?: string, bounds?: WeChatChannelWindowInfo['bounds']): Promise<WeChatChannelScreenshot>;
105
- export declare function recognizeWeChatScreenshot(helper: WeChatChannelHelperTransport, screenshot: WeChatChannelScreenshot, traceId?: string): Promise<WeChatChannelOcrResult>;
122
+ export declare function fallbackWeChatMessageInputPointForWindow(window: WeChatChannelWindowInfo): WeChatChannelScreenPoint | null;
123
+ export declare function captureWeChatWindow(helper: WeChatChannelHelperTransport, windowId: string, traceId?: string, bounds?: WeChatChannelWindowInfo['bounds']): Promise<WeChatChannelScreenshotWithData>;
124
+ export declare function captureAndRecognizeWeChatWindow(helper: WeChatChannelHelperTransport, windowId: string, traceId?: string, bounds?: WeChatChannelWindowInfo['bounds']): Promise<WeChatChannelWindowObservation>;
125
+ export declare function recognizeWeChatScreenshot(helper: WeChatChannelHelperTransport, screenshot: WeChatChannelScreenshotWithData, traceId?: string): Promise<WeChatChannelOcrResult>;
106
126
  export {};