shennian 0.2.100 → 0.2.101

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 (43) hide show
  1. package/dist/publish-build-manifest.json +70 -25
  2. package/dist/src/channels/runtime.d.ts +5 -1
  3. package/dist/src/channels/runtime.js +3 -3
  4. package/dist/src/channels/wechat-channel/helper-assets.d.ts +59 -1
  5. package/dist/src/channels/wechat-channel/helper-assets.js +1 -1
  6. package/dist/src/channels/wechat-channel/helper-client.js +2 -2
  7. package/dist/src/channels/wechat-channel/helper-protocol.d.ts +1 -1
  8. package/dist/src/channels/wechat-channel/observer.d.ts +32 -2
  9. package/dist/src/channels/wechat-channel/observer.js +6 -6
  10. package/dist/src/channels/wechat-channel/preflight.d.ts +7 -0
  11. package/dist/src/channels/wechat-channel/preflight.js +1 -1
  12. package/dist/src/channels/wechat-channel/runner.d.ts +0 -1
  13. package/dist/src/channels/wechat-channel/runner.js +1 -1
  14. package/dist/src/channels/wechat-channel/scheduler.d.ts +4 -3
  15. package/dist/src/channels/wechat-channel/scheduler.js +1 -1
  16. package/dist/src/channels/wechat-rpa.d.ts +5 -1
  17. package/dist/src/channels/wechat-rpa.js +4 -4
  18. package/dist/src/commands/runtime.d.ts +89 -0
  19. package/dist/src/commands/runtime.js +1 -0
  20. package/dist/src/commands/wechat/command.d.ts +2 -0
  21. package/dist/src/commands/wechat/command.js +1 -0
  22. package/dist/src/commands/wechat/direct.d.ts +14 -0
  23. package/dist/src/commands/wechat/direct.js +2 -0
  24. package/dist/src/commands/wechat/doctor.d.ts +2 -0
  25. package/dist/src/commands/wechat/doctor.js +1 -0
  26. package/dist/src/commands/wechat/ipc.d.ts +9 -0
  27. package/dist/src/commands/wechat/ipc.js +1 -0
  28. package/dist/src/commands/wechat/operations.d.ts +3 -0
  29. package/dist/src/commands/wechat/operations.js +4 -0
  30. package/dist/src/commands/wechat/output.d.ts +7 -0
  31. package/dist/src/commands/wechat/output.js +4 -0
  32. package/dist/src/commands/wechat/types.d.ts +141 -0
  33. package/dist/src/commands/wechat/types.js +1 -0
  34. package/dist/src/commands/wechat/utils.d.ts +14 -0
  35. package/dist/src/commands/wechat/utils.js +1 -0
  36. package/dist/src/commands/wechat.d.ts +6 -127
  37. package/dist/src/commands/wechat.js +1 -7
  38. package/dist/src/devtools/wechat-channel-action-smoke.d.ts +2 -0
  39. package/dist/src/devtools/wechat-channel-action-smoke.js +8 -8
  40. package/dist/src/index.js +2 -2
  41. package/dist/src/manager/runtime.d.ts +3 -1
  42. package/dist/src/manager/runtime.js +5 -5
  43. package/package.json +1 -1
@@ -2,7 +2,7 @@
2
2
  "schemaVersion": 1,
3
3
  "jsMinified": true,
4
4
  "minifier": "esbuild",
5
- "fileCount": 123,
5
+ "fileCount": 132,
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": 29792,
165
- "afterBytes": 16367
164
+ "beforeBytes": 29890,
165
+ "afterBytes": 16425
166
166
  },
167
167
  {
168
168
  "file": "src/channels/secret-registry.js",
@@ -216,13 +216,13 @@
216
216
  },
217
217
  {
218
218
  "file": "src/channels/wechat-channel/helper-assets.js",
219
- "beforeBytes": 11316,
220
- "afterBytes": 5547
219
+ "beforeBytes": 17196,
220
+ "afterBytes": 9141
221
221
  },
222
222
  {
223
223
  "file": "src/channels/wechat-channel/helper-client.js",
224
- "beforeBytes": 18296,
225
- "afterBytes": 9771
224
+ "beforeBytes": 18878,
225
+ "afterBytes": 10104
226
226
  },
227
227
  {
228
228
  "file": "src/channels/wechat-channel/helper-protocol.js",
@@ -266,8 +266,8 @@
266
266
  },
267
267
  {
268
268
  "file": "src/channels/wechat-channel/observer.js",
269
- "beforeBytes": 82424,
270
- "afterBytes": 38164
269
+ "beforeBytes": 91541,
270
+ "afterBytes": 42250
271
271
  },
272
272
  {
273
273
  "file": "src/channels/wechat-channel/outbound-ledger.js",
@@ -286,13 +286,13 @@
286
286
  },
287
287
  {
288
288
  "file": "src/channels/wechat-channel/preflight.js",
289
- "beforeBytes": 4564,
290
- "afterBytes": 3477
289
+ "beforeBytes": 5271,
290
+ "afterBytes": 4135
291
291
  },
292
292
  {
293
293
  "file": "src/channels/wechat-channel/runner.js",
294
- "beforeBytes": 12915,
295
- "afterBytes": 6617
294
+ "beforeBytes": 12876,
295
+ "afterBytes": 6560
296
296
  },
297
297
  {
298
298
  "file": "src/channels/wechat-channel/runtime.js",
@@ -301,8 +301,8 @@
301
301
  },
302
302
  {
303
303
  "file": "src/channels/wechat-channel/scheduler.js",
304
- "beforeBytes": 19486,
305
- "afterBytes": 8892
304
+ "beforeBytes": 22012,
305
+ "afterBytes": 10014
306
306
  },
307
307
  {
308
308
  "file": "src/channels/wechat-channel/vector-store.js",
@@ -331,8 +331,8 @@
331
331
  },
332
332
  {
333
333
  "file": "src/channels/wechat-rpa.js",
334
- "beforeBytes": 53881,
335
- "afterBytes": 28288
334
+ "beforeBytes": 54000,
335
+ "afterBytes": 28362
336
336
  },
337
337
  {
338
338
  "file": "src/channels/wecom.js",
@@ -379,6 +379,11 @@
379
379
  "beforeBytes": 11239,
380
380
  "afterBytes": 6479
381
381
  },
382
+ {
383
+ "file": "src/commands/runtime.js",
384
+ "beforeBytes": 26921,
385
+ "afterBytes": 14905
386
+ },
382
387
  {
383
388
  "file": "src/commands/tools.js",
384
389
  "beforeBytes": 1572,
@@ -389,10 +394,50 @@
389
394
  "beforeBytes": 8537,
390
395
  "afterBytes": 4347
391
396
  },
397
+ {
398
+ "file": "src/commands/wechat/command.js",
399
+ "beforeBytes": 7590,
400
+ "afterBytes": 4681
401
+ },
402
+ {
403
+ "file": "src/commands/wechat/direct.js",
404
+ "beforeBytes": 8133,
405
+ "afterBytes": 4093
406
+ },
407
+ {
408
+ "file": "src/commands/wechat/doctor.js",
409
+ "beforeBytes": 3331,
410
+ "afterBytes": 2115
411
+ },
412
+ {
413
+ "file": "src/commands/wechat/ipc.js",
414
+ "beforeBytes": 5364,
415
+ "afterBytes": 3122
416
+ },
417
+ {
418
+ "file": "src/commands/wechat/operations.js",
419
+ "beforeBytes": 12663,
420
+ "afterBytes": 6403
421
+ },
422
+ {
423
+ "file": "src/commands/wechat/output.js",
424
+ "beforeBytes": 11355,
425
+ "afterBytes": 7084
426
+ },
427
+ {
428
+ "file": "src/commands/wechat/types.js",
429
+ "beforeBytes": 881,
430
+ "afterBytes": 490
431
+ },
432
+ {
433
+ "file": "src/commands/wechat/utils.js",
434
+ "beforeBytes": 2474,
435
+ "afterBytes": 1460
436
+ },
392
437
  {
393
438
  "file": "src/commands/wechat.js",
394
- "beforeBytes": 38003,
395
- "afterBytes": 20530
439
+ "beforeBytes": 400,
440
+ "afterBytes": 259
396
441
  },
397
442
  {
398
443
  "file": "src/config/index.js",
@@ -406,8 +451,8 @@
406
451
  },
407
452
  {
408
453
  "file": "src/devtools/wechat-channel-action-smoke.js",
409
- "beforeBytes": 68351,
410
- "afterBytes": 36066
454
+ "beforeBytes": 68492,
455
+ "afterBytes": 36144
411
456
  },
412
457
  {
413
458
  "file": "src/devtools/wechat-channel-download-report.js",
@@ -451,8 +496,8 @@
451
496
  },
452
497
  {
453
498
  "file": "src/index.js",
454
- "beforeBytes": 16718,
455
- "afterBytes": 8393
499
+ "beforeBytes": 16815,
500
+ "afterBytes": 8463
456
501
  },
457
502
  {
458
503
  "file": "src/log-reporter.js",
@@ -471,8 +516,8 @@
471
516
  },
472
517
  {
473
518
  "file": "src/manager/runtime.js",
474
- "beforeBytes": 57340,
475
- "afterBytes": 29445
519
+ "beforeBytes": 57490,
520
+ "afterBytes": 29545
476
521
  },
477
522
  {
478
523
  "file": "src/native-fusion/config.js",
@@ -1,5 +1,9 @@
1
1
  import type { ExternalChannelConfig, ExternalChannelView, ExternalMessageEvent, ExternalReply } from './base.js';
2
2
  import type { ExternalChannelSessionStatus } from '@shennian/wire';
3
+ import type { WeChatRpaProductRunnerFactory } from './wechat-rpa/product-channel.js';
4
+ export type ChannelRuntimeOptions = {
5
+ createWeChatRpaProductRunner?: WeChatRpaProductRunnerFactory;
6
+ };
3
7
  export declare class ChannelRuntime {
4
8
  private onExternalMessage;
5
9
  private createReplyTarget;
@@ -13,7 +17,7 @@ export declare class ChannelRuntime {
13
17
  channelId: string;
14
18
  conversationId: string;
15
19
  messageId?: string | null;
16
- }) => string);
20
+ }) => string, options?: ChannelRuntimeOptions);
17
21
  start(): Promise<void>;
18
22
  stop(): Promise<void>;
19
23
  ingest(event: ExternalMessageEvent & {
@@ -1,5 +1,5 @@
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}
1
+ import M from"node:crypto";import g from"node:fs";import u from"node:path";import{ChannelConfigRegistry as v}from"./registry.js";import{ChannelSecretRegistry as N}from"./secret-registry.js";import{WeComChannelAdapter as D}from"./wecom.js";import{WeChatRpaChannelAdapter as B}from"./wechat-rpa.js";import{ExternalWebSocketChannelAdapter as x}from"./websocket.js";import{splitExternalReplyText as E}from"./reply-split.js";class J{onExternalMessage;createReplyTarget;configs=new v;secrets=new N;adapters=new Map;completedReplyKeys=new Map;recentMessages=new Map;constructor(e,t,n={}){this.onExternalMessage=e,this.createReplyTarget=t;const o=new D(a=>this.ingest({...a,type:"external.message"}));this.adapters.set(o.type,o);const r=new x(a=>this.ingest({...a,type:"external.message"}));this.adapters.set(r.type,r);const i=new B(a=>this.ingest(a),{createProductRunner:n.createWeChatRpaProductRunner});this.adapters.set(i.type,i)}async start(){for(const e of this.configs.list().filter(t=>t.enabled))await this.adapters.get(e.type)?.connect(e).catch(()=>{})}async stop(){for(const e of this.configs.list())await this.adapters.get(e.type)?.disconnect(e).catch(()=>{})}ingest(e){const t=this.findRecentMessageDuplicate(e);if(t)return t;const n=this.configs.get(e.channelId),o=e.managerSessionId??n?.sessionId??n?.managerSessionId;if(!o)throw new Error(`No session bound for channel ${e.channelId}`);const r=e.replyTarget||this.createReplyTarget({managerSessionId:o,channelId:e.channelId,conversationId:e.conversationId,messageId:e.messageId}),i={...e,replyTarget:r};return this.onExternalMessage(o,i),this.recordRecentMessage(i),i}async reply(e){const t=this.configs.get(e.channelId);if(!t)return{ok:!1,error:`Unknown channel: ${e.channelId}`};if((t.sessionId??t.managerSessionId)!==e.managerSessionId)return{ok:!1,error:"Channel is not bound to this session"};const n=this.adapters.get(t.type);if(!n)return{ok:!1,error:`Unsupported channel type: ${t.type}`};try{const o=K(t.type,e);if(!o.length)return{ok:!1,error:"Reply text or attachment is required"};let r=!1;for(const i of o){const a=i.idempotencyKey;if(a&&this.isReplyCompleted(t,e.conversationId,a))continue;const c=await n.send(t,{...e,...i});if(c?.status==="queued"){r=!0;continue}if(c?.status==="manual-review")return{ok:!1,error:c.reason||"Reply requires manual review before retry"};a&&this.markReplyCompleted(t,e.conversationId,a)}return r?{ok:!0,pending:!0}:{ok:!0}}catch(o){return{ok:!1,error:o instanceof Error?o.message:String(o)}}}isReplyCompleted(e,t,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 i=this.secrets.get(r.secretRef),a=this.adapters.get(r.type)?.runtimeStatus?.(r)??{};return{id:r.id,type:r.type,name:r.name,sessionId:r.sessionId??r.managerSessionId,managerSessionId:r.managerSessionId,workDir:r.workDir,agentType:r.agentType,agentSessionId:r.agentSessionId,modelId:r.modelId,enabled:r.enabled,wsUrl:i?.wsUrl??"",token:n.includeSecret?i?.token??"":"",tokenConfigured:!!i?.token,canReply:!!i?.canReply,systemPrompt:typeof i?.systemPrompt=="string"?i.systemPrompt:"",...k(i),...a}}getChannelById(e,t={}){const n=this.configs.get(e);if(!n)return null;const o=this.secrets.get(n.secretRef),r=this.adapters.get(n.type)?.runtimeStatus?.(n)??{};return{id:n.id,type:n.type,name:n.name,sessionId:n.sessionId??n.managerSessionId,managerSessionId:n.managerSessionId,workDir:n.workDir,agentType:n.agentType,agentSessionId:n.agentSessionId,modelId:n.modelId,enabled:n.enabled,wsUrl:o?.wsUrl??"",token:t.includeSecret?o?.token??"":"",tokenConfigured:!!o?.token,canReply:!!o?.canReply,systemPrompt:typeof o?.systemPrompt=="string"?o.systemPrompt:"",...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(i=>i.enabled&&i.type==="wechat-rpa"&&(i.sessionId??i.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}`},i=this.secrets.get(r.secretRef),a=e.wsUrl?.trim()||i?.wsUrl||"",c=e.token?.trim()||i?.token||"",p=e.canReply??i?.canReply??!1,m=e.systemPrompt??(typeof i?.systemPrompt=="string"?i.systemPrompt:"");if(r.enabled&&(!a||!c))throw new Error("WebSocket \u5730\u5740\u548C Token \u5FC5\u586B");const f=n.filter(l=>l.id!==r.id).map(l=>(l.sessionId??l.managerSessionId)===o&&l.type===e.type?{...l,enabled:!1}:l);f.push(r),this.configs.replaceAll(f),(a||c)&&this.secrets.upsert(r.secretRef,{type:"websocket",wsUrl:a,token:c,canReply:p,systemPrompt:m});const d=this.adapters.get(r.type);for(const l of 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 i={id:e.id,type:"wechat-rpa",name:e.name?.trim()||t?.name||"\u672C\u673A\u5FAE\u4FE1 RPA",sessionId:o,managerSessionId:o,workDir:e.workDir,agentType:e.agentType||t?.agentType,agentSessionId:e.agentSessionId??t?.agentSessionId??null,modelId:e.modelId??t?.modelId??null,enabled:e.enabled,secretRef:t?.secretRef||`channel:${e.id}`},a=this.secrets.get(i.secretRef),c=e.source||(a?.source==="wechat-channel"||a?.source==="macos-probe"||a?.source==="fixture-jsonl"||a?.source==="macos-flow"||a?.source==="windows-visual-flow"||a?.source==="wechat-rpa-lab"?a.source:W());if(e.enabled&&c==="windows-visual-flow")throw new Error("\u65E7 Windows visual flow \u5DF2\u5F52\u6863\uFF1BmacOS \u548C Windows \u6B63\u5F0F\u901A\u9053\u90FD\u4F7F\u7528 wechat-channel");const p=e.privacyConsentAccepted??!!a?.privacyConsentAccepted;if(e.enabled&&!p)throw new Error("\u542F\u7528\u524D\u9700\u8981\u786E\u8BA4\u5FAE\u4FE1\u901A\u9053\u6570\u636E\u4E0E\u9690\u79C1\u6388\u6743");const m=n.filter(d=>d.id!==i.id).map(d=>(d.sessionId??d.managerSessionId)===o&&d.type==="wechat-rpa"?{...d,enabled:!1}:d);m.push(i),this.configs.replaceAll(m),this.secrets.upsert(i.secretRef,{type:"wechat-rpa",source:c,groups:r,pollIntervalMs:R(e.pollIntervalMs,a?.pollIntervalMs),recentLimit:R(e.recentLimit,a?.recentLimit),idleSeconds:R(e.idleSeconds,a?.idleSeconds),forceForeground:e.forceForeground??!!a?.forceForeground,noRestore:e.noRestore??(a?.noRestore===void 0?!0:!!a.noRestore),downloadAttachments:e.downloadAttachments??(a?.downloadAttachments===void 0?!0:!!a.downloadAttachments),downloadAttachmentsDir:e.downloadAttachmentsDir?.trim()||b(a?.downloadAttachmentsDir),selfNickname:e.selfNickname?.trim()||b(a?.selfNickname),privacyConsentAccepted:p,flowScriptPath:e.flowScriptPath?.trim()||b(a?.flowScriptPath),canReply:e.canReply??a?.canReply??!1,systemPrompt:e.systemPrompt??(typeof a?.systemPrompt=="string"?a.systemPrompt:"")});const f=this.adapters.get(i.type);for(const d of n)(d.sessionId??d.managerSessionId)===o&&d.type==="wechat-rpa"&&d.enabled&&await this.adapters.get(d.type)?.disconnect(d).catch(()=>{});return i.enabled&&f?.connect(i).catch(()=>{}),this.getManagerChannel(o,"wechat-rpa",{includeSecret:!0})}}function y(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 W(){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 K(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
2
  ${t}
3
- ${n}`}function S(s){return typeof s=="string"?s.trim():""}function A(s,e,t){return P.createHash("sha256").update(`${s}
3
+ ${n}`}function S(s){return typeof s=="string"?s.trim():""}function A(s,e,t){return M.createHash("sha256").update(`${s}
4
4
  ${e}
5
- ${t}`).digest("hex").slice(0,32)}function 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};
5
+ ${t}`).digest("hex").slice(0,32)}function P(s){return u.join(s,".shennian","external-reply-idempotency.json")}function T(s){try{const e=JSON.parse(g.readFileSync(P(s),"utf8")),t=Array.isArray(e.completed)?e.completed:[];return new Set(t.map(n=>typeof n=="string"?n:n&&typeof n=="object"&&typeof n.key=="string"?n.key:"").filter(Boolean))}catch{return new Set}}function $(s,e){const t=P(s);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,K as planExternalReplySends};
@@ -1,6 +1,7 @@
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
3
  export declare const SHENNIAN_HELPER_RUNTIME_DIR_ENV = "SHENNIAN_HELPER_RUNTIME_DIR";
4
+ export declare const WECHAT_CHANNEL_HELPER_RUNTIME_REQUIRED_REASON = "helper_runtime_required";
4
5
  export type WeChatChannelHelperAssetManifest = {
5
6
  schemaVersion: 1;
6
7
  helperVersion: string;
@@ -23,6 +24,36 @@ export type WeChatChannelHelperAssetManifest = {
23
24
  selfContained?: boolean;
24
25
  }>;
25
26
  };
27
+ export type WeChatChannelHelperRuntimePackageManifest = {
28
+ schemaVersion: 1;
29
+ packageKind: 'shennian-helper-runtime';
30
+ platform: 'darwin' | 'win32';
31
+ helperVersion: string;
32
+ protocolVersion: number;
33
+ minCliVersion: string;
34
+ sha256: {
35
+ runtimeManifest: string;
36
+ entrypoint: string | null;
37
+ };
38
+ installTarget: {
39
+ kind: 'app-bundle' | 'directory';
40
+ defaultPath: string;
41
+ };
42
+ payload: {
43
+ kind: 'macos-helper-app' | 'windows-helper-runtime';
44
+ runtimeManifest: string;
45
+ entrypoint: string;
46
+ bundleId?: string;
47
+ target?: string;
48
+ selfContained?: boolean;
49
+ };
50
+ signature: {
51
+ requiredForRelease: boolean;
52
+ signed: boolean;
53
+ notarized: boolean;
54
+ evidence?: Record<string, unknown>;
55
+ };
56
+ };
26
57
  export type WeChatChannelHelperAssetResolution = {
27
58
  ok: true;
28
59
  helperPath: string;
@@ -32,8 +63,18 @@ export type WeChatChannelHelperAssetResolution = {
32
63
  warning?: string;
33
64
  } | {
34
65
  ok: false;
35
- reasonCode: 'unsupported_platform' | 'desktop_app_required' | 'manifest_missing' | 'helper_missing' | 'integrity_mismatch' | 'helper_not_executable';
66
+ reasonCode: 'unsupported_platform' | 'helper_runtime_required' | 'manifest_missing' | 'helper_missing' | 'integrity_mismatch' | 'helper_not_executable';
67
+ message: string;
68
+ };
69
+ export type WeChatChannelHelperRuntimePackageResolution = {
70
+ ok: true;
71
+ manifestPath: string;
72
+ manifest: WeChatChannelHelperRuntimePackageManifest;
73
+ } | {
74
+ ok: false;
75
+ reasonCode: 'unsupported_platform' | 'helper_runtime_package_manifest_missing' | 'helper_runtime_package_manifest_invalid';
36
76
  message: string;
77
+ manifestPath?: string;
37
78
  };
38
79
  export declare function resolveWeChatChannelHelperAsset(input?: {
39
80
  platform?: NodeJS.Platform | string;
@@ -43,3 +84,20 @@ export declare function resolveWeChatChannelHelperAsset(input?: {
43
84
  homedir?: string;
44
85
  includeInstalledDesktop?: boolean;
45
86
  }): WeChatChannelHelperAssetResolution;
87
+ export declare function getDefaultWeChatHelperRuntimeRoot(input?: {
88
+ platform?: NodeJS.Platform | string;
89
+ env?: NodeJS.ProcessEnv;
90
+ homedir?: string;
91
+ }): string | null;
92
+ export declare function helperRuntimePackageManifestCandidates(helperDir: string): string[];
93
+ export declare function readWeChatChannelHelperRuntimePackageManifest(input: {
94
+ platform?: NodeJS.Platform | string;
95
+ helperDir?: string;
96
+ manifestPath?: string;
97
+ }): WeChatChannelHelperRuntimePackageResolution;
98
+ export declare function validateWeChatChannelHelperRuntimePackageManifest(manifest: unknown, platform: NodeJS.Platform | string): {
99
+ ok: true;
100
+ } | {
101
+ ok: false;
102
+ message: string;
103
+ };
@@ -1 +1 @@
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};
1
+ import c from"node:fs";import s from"node:path";import E from"node:crypto";const V="0.1.9",S="SHENNIAN_WECHAT_CHANNEL_HELPER_DIR",g="SHENNIAN_HELPER_RUNTIME_DIR",C="helper_runtime_required",u=["en","v"].join("");function $(n={}){const r=n.platform??process.platform;if(r!=="darwin"&&r!=="win32")return{ok:!1,reasonCode:"unsupported_platform",message:"WeChat channel helper is only available on macOS and Windows"};const e=n.baseDir?[s.resolve(n.baseDir)]:A({platform:r,env:n[u]??R(),homedir:n.homedir,includeInstalledDesktop:n.includeInstalledDesktop});let a=null;for(const t of e){const i=_({platform:r,baseDir:t,verifyIntegrity:n.verifyIntegrity});if(i.ok||(n.baseDir||c.existsSync(t))&&(a??=i,n.baseDir))return i}return a??{ok:!1,reasonCode:C,message:x(r,e)}}function _(n){const{platform:r,baseDir:e}=n,a=s.join(e,"manifest.json"),t=F(a);if(!t)return{ok:!1,reasonCode:"manifest_missing",message:`WeChat channel helper manifest is missing: ${a}`};const i=t.platforms[r],o=i?s.join(e,i.executable):"";if(!i||!o||!c.existsSync(o))return{ok:!1,reasonCode:"helper_missing",message:`WeChat channel helper executable is missing: ${o||e}`};if(N({platform:r,baseDir:e,verifyIntegrity:n.verifyIntegrity})&&i.sha256&&E.createHash("sha256").update(c.readFileSync(o)).digest("hex")!==i.sha256)return{ok:!1,reasonCode:"integrity_mismatch",message:`WeChat channel helper integrity mismatch: ${o}`};const f=H(r,o);return f.ok?{ok:!0,helperPath:o,helperDir:e,version:t.helperVersion,manifest:t,warning:i.signed&&i.notarized?void 0:"helper_not_signed_or_notarized"}:{ok:!1,reasonCode:"helper_not_executable",message:f.message}}function H(n,r){if(n!=="darwin")return{ok:!0};try{const e=c.statSync(r);return(e.mode&73)!==0?{ok:!0}:(c.chmodSync(r,e.mode|73),{ok:!0})}catch(e){return{ok:!1,message:`WeChat channel helper is not executable and could not be repaired: ${r}: ${e instanceof Error?e.message:String(e)}`}}}function A(n){const{platform:r,env:e}=n,a=r==="win32"?"windows":"macos",t=[],i=l=>{if(!l)return;const p=s.resolve(l);t.includes(p)||t.push(p)},o=e[S]?.trim();o&&i(j(o,a));const f=e[g]?.trim();if(f)for(const l of P(f,a))i(l);if(b({platform:r,platformDir:a,env:e,homedir:n.homedir,push:i}),n.includeInstalledDesktop===!1)return t;if(r==="darwin"){const l=n.homedir||e.HOME;l&&i(s.join(l,"Applications","Shennian.app","Contents","Resources","wechat-channel",a)),i(s.join("/Applications","Shennian.app","Contents","Resources","wechat-channel",a))}else if(r==="win32"){const l=n.homedir||e.USERPROFILE,p=e.LOCALAPPDATA||(l?s.join(l,"AppData","Local"):""),m=e.ProgramFiles||e.PROGRAMFILES,h=e["ProgramFiles(x86)"]||e["PROGRAMFILES(X86)"];for(const d of[p?s.join(p,"Programs","Shennian"):"",p?s.join(p,"Programs","shennian"):"",m?s.join(m,"Shennian"):"",h?s.join(h,"Shennian"):""])d&&i(s.join(d,"resources","wechat-channel",a))}return t}function b(n){const{platform:r,platformDir:e,env:a,push:t}=n;if(r==="darwin"){const i=n.homedir||a.HOME,o=k({platform:r,env:a,homedir:n.homedir});o&&(t(s.join(o,"Shennian Helper.app","Contents","Resources","wechat-channel",e)),t(s.join(o,"wechat-channel",e))),i&&t(s.join(i,"Applications","Shennian Helper.app","Contents","Resources","wechat-channel",e)),t(s.join("/Applications","Shennian Helper.app","Contents","Resources","wechat-channel",e)),t(s.join("/Library","Application Support","Shennian","Helper","wechat-channel",e))}else if(r==="win32"){const i=n.homedir||a.USERPROFILE,o=a.LOCALAPPDATA||(i?s.join(i,"AppData","Local"):""),f=a.ProgramFiles||a.PROGRAMFILES,l=a["ProgramFiles(x86)"]||a["PROGRAMFILES(X86)"];o&&(t(s.join(o,"Programs","Shennian Helper","resources","wechat-channel",e)),t(s.join(o,"Shennian","Helper","wechat-channel",e))),f&&t(s.join(f,"Shennian Helper","resources","wechat-channel",e)),l&&t(s.join(l,"Shennian Helper","resources","wechat-channel",e))}}function j(n,r){return c.existsSync(s.join(n,"manifest.json"))?n:s.join(n,r)}function P(n,r){if(c.existsSync(s.join(n,"manifest.json")))return[n];const e=s.join(n,"Shennian Helper.app","Contents","Resources","wechat-channel",r),a=s.join(n,"wechat-channel",r),t=s.join(n,r);return[e,a,t]}function k(n){const r=n[u],e=r[g]?.trim();if(e)return s.resolve(e);const a=n.homedir||(n.platform==="win32"?r.USERPROFILE:r.HOME);if(n.platform==="darwin")return a?s.join(a,"Library","Application Support","Shennian","Helper"):null;if(n.platform==="win32"){const t=r.LOCALAPPDATA||(a?s.join(a,"AppData","Local"):"");return t?s.join(t,"Shennian","Helper"):null}return null}function q(n={}){return k({platform:n.platform??process.platform,env:n.env??R(),homedir:n.homedir})}function D(n){const r=s.resolve(n);return O([s.join(r,"helper-runtime-package.json"),s.join(s.dirname(r),"..","helper-runtime-package.json")])}function U(n){const r=n.platform??process.platform;if(r!=="darwin"&&r!=="win32")return{ok:!1,reasonCode:"unsupported_platform",message:"WeChat channel helper runtime package is only available on macOS and Windows"};const e=n.manifestPath?[s.resolve(n.manifestPath)]:n.helperDir?D(n.helperDir):[],a=e.find(t=>c.existsSync(t));if(!a)return{ok:!1,reasonCode:"helper_runtime_package_manifest_missing",message:`Helper runtime package manifest is missing. Checked: ${e.join(", ")||"(none)"}`,manifestPath:e[0]};try{const t=JSON.parse(w(a)),i=I(t,r);return i.ok?{ok:!0,manifestPath:a,manifest:t}:{ok:!1,reasonCode:"helper_runtime_package_manifest_invalid",message:`${i.message}: ${a}`,manifestPath:a}}catch(t){return{ok:!1,reasonCode:"helper_runtime_package_manifest_invalid",message:`Helper runtime package manifest is invalid JSON: ${t instanceof Error?t.message:String(t)}`,manifestPath:a}}}function I(n,r){if(!n||typeof n!="object"||Array.isArray(n))return{ok:!1,message:"manifest must be an object"};const e=n;return e.schemaVersion!==1?{ok:!1,message:"schemaVersion must be 1"}:e.packageKind!=="shennian-helper-runtime"?{ok:!1,message:"packageKind must be shennian-helper-runtime"}:e.platform!==r?{ok:!1,message:`platform must be ${r}`}:e.helperVersion?!Number.isInteger(e.protocolVersion)||Number(e.protocolVersion)<=0?{ok:!1,message:"protocolVersion must be a positive integer"}:L(e.minCliVersion)?!e.sha256||typeof e.sha256.runtimeManifest!="string"?{ok:!1,message:"sha256.runtimeManifest is required"}:y(e.sha256.runtimeManifest)?e.sha256.entrypoint!==null&&typeof e.sha256.entrypoint!="string"?{ok:!1,message:"sha256.entrypoint must be a string or null"}:typeof e.sha256.entrypoint=="string"&&!y(e.sha256.entrypoint)?{ok:!1,message:"sha256.entrypoint must be a sha256 digest or null"}:!e.installTarget?.kind||!e.installTarget.defaultPath?{ok:!1,message:"installTarget.kind and installTarget.defaultPath are required"}:!e.payload?.kind||!e.payload.runtimeManifest||!e.payload.entrypoint?{ok:!1,message:"payload.kind, payload.runtimeManifest, and payload.entrypoint are required"}:r==="darwin"&&e.installTarget.kind!=="app-bundle"?{ok:!1,message:"darwin installTarget.kind must be app-bundle"}:r==="darwin"&&e.payload.kind!=="macos-helper-app"?{ok:!1,message:"darwin payload.kind must be macos-helper-app"}:r==="win32"&&e.installTarget.kind!=="directory"?{ok:!1,message:"win32 installTarget.kind must be directory"}:r==="win32"&&e.payload.kind!=="windows-helper-runtime"?{ok:!1,message:"win32 payload.kind must be windows-helper-runtime"}:!e.signature||typeof e.signature.requiredForRelease!="boolean"||typeof e.signature.signed!="boolean"||typeof e.signature.notarized!="boolean"?{ok:!1,message:"signature.requiredForRelease, signature.signed, and signature.notarized are required"}:{ok:!0}:{ok:!1,message:"sha256.runtimeManifest must be a sha256 digest"}:{ok:!1,message:"minCliVersion must be a semver string"}:{ok:!1,message:"helperVersion is required"}}function L(n){return typeof n=="string"&&/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/.test(n)}function y(n){return/^[a-f0-9]{64}$/i.test(n)}function N(n){return n.verifyIntegrity===!0?!0:n.verifyIntegrity===!1?!1:!v(n.platform,n.baseDir)}function v(n,r){const e=r.replace(/[\\/]+/g,"/");return n==="darwin"?/\/Shennian\.app\/Contents\/Resources\/wechat-channel\/macos$/i.test(e):n==="win32"?/\/resources\/wechat-channel\/windows$/i.test(e):!1}function x(n,r){const e=n==="win32"?"Windows":"macOS",a=r.length>0?` Checked helper directories: ${r.join(", ")}`:"";return`WeChat RPA requires Shennian Helper runtime on ${e}; install or open Shennian Desktop, or run shennian runtime install helper, then retry.${a}`}function R(){return globalThis.process?.[u]??{}}function F(n){try{const r=JSON.parse(w(n));return r.schemaVersion!==1||typeof r.helperVersion!="string"||!r.platforms?null:r}catch{return null}}function w(n){return c.readFileSync(n,"utf8").replace(/^\uFEFF/,"")}function O(n){const r=new Set,e=[];for(const a of n){const t=s.resolve(a);r.has(t)||(r.add(t),e.push(t))}return e}export{g as SHENNIAN_HELPER_RUNTIME_DIR_ENV,S as WECHAT_CHANNEL_HELPER_DIR_ENV,C as WECHAT_CHANNEL_HELPER_RUNTIME_REQUIRED_REASON,V as WECHAT_CHANNEL_HELPER_VERSION,q as getDefaultWeChatHelperRuntimeRoot,D as helperRuntimePackageManifestCandidates,U as readWeChatChannelHelperRuntimePackageManifest,$ as resolveWeChatChannelHelperAsset,I as validateWeChatChannelHelperRuntimePackageManifest};
@@ -1,3 +1,3 @@
1
- import{spawn as f}from"node:child_process";import{randomUUID as w}from"node:crypto";import{createInterface as m}from"node:readline";import{decideWeChatChannelActivityGate as y,normalizeWeChatChannelActivitySnapshot as g}from"./human-coordination.js";import{createWeChatChannelHelperHello as C,extractWeChatChannelHelperWarmupSnapshot as S,timeoutForWeChatChannelHelperCommand as W,validateWeChatChannelHelperReady as b}from"./helper-protocol.js";class H{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=_(this.options.helperPath,this.options.args??[]),i=f(e.command,e.args,{cwd:this.options.cwd,stdio:["pipe","pipe","pipe"],windowsHide:!0});this.child=i,this.stderrTail="",this.lines=m({input:i.stdout}),this.lines.on("line",r=>this.handleLine(r)),i.stderr.on("data",r=>{this.captureStderr(r)}),i.stdin.on("error",r=>{this.rejectAllPending(r instanceof Error?r:new Error(String(r)))}),i.once("error",r=>{this.child===i&&(this.child=null,this.readyState=null,this.lines?.close(),this.lines=null),this.rejectAllPending(r instanceof Error?r:new Error(String(r)))}),i.once("exit",(r,a)=>{this.child===i&&(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",l),i.off("exit",n),a(new Error(`WeChat channel helper failed to start: ${u.message}`))},n=(u,h)=>{clearTimeout(o),i.off("error",c),this.lines?.off("line",l),a(new Error(this.formatHelperExitMessage(u,h)))},l=u=>{let h;try{h=JSON.parse(u)}catch{clearTimeout(o),i.off("error",c),this.lines?.off("line",l),a(new Error("WeChat channel helper sent invalid handshake JSON"));return}if(!R(h))return;clearTimeout(o),i.off("error",c),i.off("exit",n),this.lines?.off("line",l);const d=b(h,this.options.expectedHelperVersion,this.options.requiredCapabilities);d.ok?(this.readyState=h,this.captureWarmupSnapshot(h),r(h)):a(new Error(`${d.errorCode}: ${d.errorSummary}`))};i.once("error",c),i.once("exit",n),this.lines?.on("line",l)});try{i.stdin.write(`${JSON.stringify(C(this.options.expectedHelperVersion,this.options.requiredCapabilities))}
1
+ import{spawn as f}from"node:child_process";import{randomUUID as w}from"node:crypto";import{createInterface as m}from"node:readline";import{decideWeChatChannelActivityGate as y,normalizeWeChatChannelActivitySnapshot as g}from"./human-coordination.js";import{createWeChatChannelHelperHello as S,extractWeChatChannelHelperWarmupSnapshot as C,timeoutForWeChatChannelHelperCommand as W,validateWeChatChannelHelperReady as b}from"./helper-protocol.js";class H{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=T(this.options.helperPath,this.options.args??[]),i=f(e.command,e.args,{cwd:this.options.cwd,stdio:["pipe","pipe","pipe"],windowsHide:!0});this.child=i,this.stderrTail="",this.lines=m({input:i.stdout}),this.lines.on("line",r=>this.handleLine(r)),i.stderr.on("data",r=>{this.captureStderr(r)}),i.stdin.on("error",r=>{this.rejectAllPending(r instanceof Error?r:new Error(String(r)))}),i.once("error",r=>{this.child===i&&(this.child=null,this.readyState=null,this.lines?.close(),this.lines=null),this.rejectAllPending(r instanceof Error?r:new Error(String(r)))}),i.once("exit",(r,a)=>{this.child===i&&(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",l),i.off("exit",n),a(new Error(`WeChat channel helper failed to start: ${u.message}`))},n=(u,h)=>{clearTimeout(o),i.off("error",c),this.lines?.off("line",l),a(new Error(this.formatHelperExitMessage(u,h)))},l=u=>{let h;try{h=JSON.parse(u)}catch{clearTimeout(o),i.off("error",c),this.lines?.off("line",l),a(new Error("WeChat channel helper sent invalid handshake JSON"));return}if(!A(h))return;clearTimeout(o),i.off("error",c),i.off("exit",n),this.lines?.off("line",l);const d=b(h,this.options.expectedHelperVersion,this.options.requiredCapabilities);d.ok?(this.readyState=h,this.captureWarmupSnapshot(h),r(h)):a(new Error(`${d.errorCode}: ${d.errorSummary}`))};i.once("error",c),i.once("exit",n),this.lines?.on("line",l)});try{i.stdin.write(`${JSON.stringify(S(this.options.expectedHelperVersion,this.options.requiredCapabilities))}
2
2
  `)}catch(r){throw await this.stop().catch(()=>{}),r}return s}async request(e,i,s){const r=this.requestQueueTail.catch(()=>{}).then(()=>this.sendRequest(e,i,s));return this.requestQueueTail=r.then(()=>{},()=>{}),r}async sendRequest(e,i,s){await this.guardUnsafeWindowsCommand(e,s);try{return await this.sendRawRequest(e,i,s)}finally{await this.cleanupWindowsOverlaysAfterCommand(e,s).catch(()=>{})}}async sendRawRequest(e,i,s){if((!this.child||!this.readyState)&&await this.start(),!this.child)throw new Error("WeChat channel helper is not started");const r=w(),a=W(e),o=Date.now();this.emitRequestTrace({phase:"request",at:new Date(o).toISOString(),id:r,command:e,traceId:s,params:i,timeoutMs:a});const c=new Promise((n,l)=>{const u=setTimeout(()=>{this.pending.delete(r),l(new Error(`helper_command_timeout: ${e}`))},a);this.pending.set(r,{resolve:n,reject:l,timer:u})});try{this.child.stdin.write(`${JSON.stringify({id:r,command:e,params:i,traceId:s})}
3
- `)}catch(n){const l=this.pending.get(r);throw l&&clearTimeout(l.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,i){if(!this.options.guardUnsafeWindowsCommands||!A(e))return;const s=await this.sendRawRequest("permissions.check",{},i);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(v(r))throw new Error("wechat_login_required");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&&M(e)){const a=await this.sendRawRequest("activity.snapshot",{},i);if(!a.ok)throw new Error(`${a.errorCode??"user_activity_unknown"}: ${a.errorSummary??e}`);const o=y({snapshot:g(a.result),stage:"dangerous_action"});if(!o.ok)throw new Error(`user_active:${o.reasonCode}`)}await this.cleanupWindowsOverlays("before",e,i)}async cleanupWindowsOverlaysAfterCommand(e,i){!this.options.guardUnsafeWindowsCommands||!q(e)||await this.cleanupWindowsOverlays("after",e,i)}async cleanupWindowsOverlays(e,i,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:i,includeGeneratedMediaPreviews:!0,includeToolingTerminals:!1,waitMs:e==="after"?220:120},s);if(!r.ok)throw new Error(`${r.errorCode??"overlay_cleanup_failed"}: ${r.errorSummary??i}`)}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 i;try{i=JSON.parse(e)}catch{return}if(!k(i))return;const s=this.pending.get(i.id);s&&(clearTimeout(s.timer),this.pending.delete(i.id),s.resolve(i))}captureWarmupSnapshot(e){const i=S(e);i&&(this.warmupState=i)}captureStderr(e){const i=Buffer.isBuffer(e)?e.toString("utf8"):String(e??"");i&&(this.stderrTail=(this.stderrTail+i).slice(-2e3))}formatHelperExitMessage(e,i){const s=[e===null?null:`code=${e}`,i?`signal=${i}`: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[i,s]of this.pending)clearTimeout(s.timer),s.reject(e),this.pending.delete(i)}emitRequestTrace(e){try{this.options.requestLogger?.(e)}catch{}}}function v(t){if(Number(t.duplicateMainWindowCount??0)>0||(t.duplicateMainWindows??[]).some(p))return!1;const e=t.unsafeAuxiliaryWindows??[];return!(Number(t.unsafeAuxiliaryWindowCount??0)>0||e.some(T))&&!(t.duplicateInstanceBlocked===!0&&t.wechatWindowAvailable===!1)?!1:![...t.captureCandidates??[],...t.restoreCandidates??[],...t.hiddenRestoreCandidates??[]].some(E)||t.wechatWindowAvailable===!1}function p(t){const e=`${t.appName||""} ${t.title||""} ${t.className||""}`.toLowerCase();return e.includes("wechat")||e.includes("weixin")||e.includes("\u5FAE\u4FE1")||e.includes("qwindowicon")}function E(t){if(!p(t)||t.visible===!1||t.minimized===!0)return!1;const e=Number(t.bounds?.width),i=Number(t.bounds?.height);return!Number.isFinite(e)||!Number.isFinite(i)?!1:e>=760&&i>=520}function T(t){if(!p(t)||t.visible===!1||t.minimized===!0)return!1;const e=Number(t.bounds?.width),i=Number(t.bounds?.height);if(!Number.isFinite(e)||!Number.isFinite(i))return!1;const s=e>=360&&e<=720&&i>=420&&i<=840&&i>e,r=e>=420&&e<=680&&i>=260&&i<=460;return s||r}function _(t,e){return{command:t,args:e}}function R(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 k(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 A(t){return t==="windows.focus"||t==="windows.capture"||t==="screen.capture"||t==="ocr.recognize"||t==="image.cropHash"||t==="wechat.searchConversation"||t==="wechat.focusMessageInput"||t.startsWith("mouse.")||t.startsWith("keyboard.")||t.startsWith("clipboard.")||t==="menu.pickItem"||t==="savePanel.saveToPath"}function q(t){return t.startsWith("mouse.")||t.startsWith("keyboard.")||t.startsWith("clipboard.")||t==="menu.pickItem"||t==="savePanel.saveToPath"}function M(t){return t==="windows.focus"||t==="wechat.searchConversation"||t==="wechat.focusMessageInput"||t.startsWith("mouse.")||t.startsWith("keyboard.")||t.startsWith("clipboard.set")||t==="clipboard.restore"||t==="menu.pickItem"||t==="savePanel.saveToPath"}export{H as WeChatChannelHelperClient};
3
+ `)}catch(n){const l=this.pending.get(r);throw l&&clearTimeout(l.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,i){if(!this.options.guardUnsafeWindowsCommands||!M(e))return;const s=await this.sendRawRequest("permissions.check",{},i);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(v(r))throw new Error("wechat_login_required");if(r.duplicateInstanceBlocked===!0||Number(r.duplicateMainWindowCount??0)>0||Number(r.unsafeAuxiliaryWindowCount??0)>0)throw new Error("wechat_duplicate_instance");if(E(r))throw new Error("windows_visible_desktop_unavailable");if(r.dpiMappingAvailable===!1||r.displayTopologySupported===!1)throw new Error("dpi_mapping_failed");if(r.wechatWindowAvailable===!1)throw new Error("wechat_window_unavailable");if(r.wechatMainWindowResponsive===!1)throw new Error("wechat_window_unresponsive");if(!this.options.skipUserActivityGuard&&P(e)){const a=await this.sendRawRequest("activity.snapshot",{},i);if(!a.ok)throw new Error(`${a.errorCode??"user_activity_unknown"}: ${a.errorSummary??e}`);const o=y({snapshot:g(a.result),stage:"dangerous_action"});if(!o.ok)throw new Error(`user_active:${o.reasonCode}`)}await this.cleanupWindowsOverlays("before",e,i)}async cleanupWindowsOverlaysAfterCommand(e,i){!this.options.guardUnsafeWindowsCommands||!q(e)||await this.cleanupWindowsOverlays("after",e,i)}async cleanupWindowsOverlays(e,i,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:i,includeGeneratedMediaPreviews:!0,includeToolingTerminals:!1,waitMs:e==="after"?220:120},s);if(!r.ok)throw new Error(`${r.errorCode??"overlay_cleanup_failed"}: ${r.errorSummary??i}`)}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 i;try{i=JSON.parse(e)}catch{return}if(!R(i))return;const s=this.pending.get(i.id);s&&(clearTimeout(s.timer),this.pending.delete(i.id),s.resolve(i))}captureWarmupSnapshot(e){const i=C(e);i&&(this.warmupState=i)}captureStderr(e){const i=Buffer.isBuffer(e)?e.toString("utf8"):String(e??"");i&&(this.stderrTail=(this.stderrTail+i).slice(-2e3))}formatHelperExitMessage(e,i){const s=[e===null?null:`code=${e}`,i?`signal=${i}`: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[i,s]of this.pending)clearTimeout(s.timer),s.reject(e),this.pending.delete(i)}emitRequestTrace(e){try{this.options.requestLogger?.(e)}catch{}}}function v(t){if(Number(t.duplicateMainWindowCount??0)>0||(t.duplicateMainWindows??[]).some(p))return!1;const e=t.unsafeAuxiliaryWindows??[];return!(Number(t.unsafeAuxiliaryWindowCount??0)>0||e.some(k))&&!(t.duplicateInstanceBlocked===!0&&t.wechatWindowAvailable===!1)?!1:![...t.captureCandidates??[],...t.restoreCandidates??[],...t.hiddenRestoreCandidates??[]].some(_)||t.wechatWindowAvailable===!1}function E(t){return t.windowsVisibleDesktopAvailable===!1||t.rdpVisibleDesktopAvailable===!1||t.desktopSessionVisible===!1||t.screenLocked===!0||t.rdpDisconnected===!0}function p(t){const e=`${t.appName||""} ${t.title||""} ${t.className||""}`.toLowerCase();return e.includes("wechat")||e.includes("weixin")||e.includes("\u5FAE\u4FE1")||e.includes("qwindowicon")}function _(t){if(!p(t)||t.visible===!1||t.minimized===!0)return!1;const e=Number(t.bounds?.width),i=Number(t.bounds?.height);return!Number.isFinite(e)||!Number.isFinite(i)?!1:e>=760&&i>=520}function k(t){if(!p(t)||t.visible===!1||t.minimized===!0)return!1;const e=Number(t.bounds?.width),i=Number(t.bounds?.height);if(!Number.isFinite(e)||!Number.isFinite(i))return!1;const s=e>=360&&e<=720&&i>=420&&i<=840&&i>e,r=e>=420&&e<=680&&i>=260&&i<=460;return s||r}function T(t,e){return{command:t,args:e}}function A(t){if(!t||typeof t!="object")return!1;const e=t;return e.type==="ready"&&typeof e.helperVersion=="string"&&typeof e.protocolVersion=="number"&&Array.isArray(e.capabilities)&&typeof e.pid=="number"}function R(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 M(t){return t==="windows.focus"||t==="windows.capture"||t==="screen.capture"||t==="ocr.recognize"||t==="image.cropHash"||t==="wechat.searchConversation"||t==="wechat.focusMessageInput"||t.startsWith("mouse.")||t.startsWith("keyboard.")||t.startsWith("clipboard.")||t==="menu.pickItem"||t==="savePanel.saveToPath"}function q(t){return t.startsWith("mouse.")||t.startsWith("keyboard.")||t.startsWith("clipboard.")||t==="menu.pickItem"||t==="savePanel.saveToPath"}function P(t){return t==="windows.focus"||t==="wechat.searchConversation"||t==="wechat.focusMessageInput"||t.startsWith("mouse.")||t.startsWith("keyboard.")||t.startsWith("clipboard.set")||t==="clipboard.restore"||t==="menu.pickItem"||t==="savePanel.saveToPath"}export{H as WeChatChannelHelperClient};
@@ -107,7 +107,7 @@ export type WeChatChannelHelperResponse<T = unknown> = {
107
107
  warmState?: WeChatChannelHelperWarmState;
108
108
  warmup?: WeChatChannelHelperWarmupMetrics;
109
109
  };
110
- export type WeChatChannelHelperErrorCode = 'helper_protocol_mismatch' | 'helper_version_mismatch' | 'helper_capability_missing' | 'helper_command_timeout' | 'helper_process_exited' | 'helper_invalid_response' | 'permission_screen_recording_missing' | 'permission_accessibility_missing' | 'permission_automation_missing' | 'automation_event_tap_unavailable' | 'wechat_not_running' | 'wechat_window_unavailable' | 'wechat_window_not_found' | 'wechat_window_minimized' | 'wechat_window_unresponsive' | 'wechat_recovery_failed' | 'wechat_login_required' | 'wechat_duplicate_instance' | 'wechat_single_main_window_required' | 'capture_failed' | 'screenshot_blank' | 'ocr_failed' | 'title_not_confirmed' | 'user_active' | 'user_takeover' | 'clipboard_unavailable' | 'clipboard_restore_failed' | 'menu_item_not_found' | 'attachment_unavailable' | 'dpi_mapping_failed' | 'platform_unsupported' | 'platform_runtime_degraded' | 'overlay_cleanup_failed';
110
+ export type WeChatChannelHelperErrorCode = 'helper_protocol_mismatch' | 'helper_version_mismatch' | 'helper_capability_missing' | 'helper_command_timeout' | 'helper_process_exited' | 'helper_invalid_response' | 'permission_screen_recording_missing' | 'permission_accessibility_missing' | 'permission_automation_missing' | 'automation_event_tap_unavailable' | 'wechat_not_running' | 'wechat_window_unavailable' | 'wechat_window_not_found' | 'wechat_window_minimized' | 'wechat_window_unresponsive' | 'wechat_recovery_failed' | 'wechat_login_required' | 'wechat_duplicate_instance' | 'wechat_single_main_window_required' | 'windows_visible_desktop_unavailable' | 'capture_failed' | 'screenshot_blank' | 'ocr_failed' | 'title_not_confirmed' | 'user_active' | 'user_takeover' | 'clipboard_unavailable' | 'clipboard_restore_failed' | 'menu_item_not_found' | 'attachment_unavailable' | 'dpi_mapping_failed' | 'platform_unsupported' | 'platform_runtime_degraded' | 'overlay_cleanup_failed';
111
111
  export declare function createWeChatChannelHelperHello(expectedHelperVersion: string, capabilities?: WeChatChannelHelperCapability[]): WeChatChannelHelperHello;
112
112
  export declare function validateWeChatChannelHelperReady(ready: WeChatChannelHelperReady, expectedHelperVersion: string, requiredCapabilities?: WeChatChannelHelperCapability[]): {
113
113
  ok: true;
@@ -1,8 +1,9 @@
1
- import type { WeChatChannelHelperCommandName, WeChatChannelHelperResponse } from './helper-protocol.js';
1
+ import type { WeChatChannelHelperCommandName, WeChatChannelHelperResponse, WeChatChannelHumanActivityReasonCode } from './helper-protocol.js';
2
2
  import type { WeChatChannelRuntime, WeChatChannelBindingConfig } from './runtime.js';
3
3
  import type { WeChatChannelApiClient, WeChatChannelObservedMessage } from './client.js';
4
4
  import { type WeChatChannelLedger } from './ledger.js';
5
5
  import { type WeChatConversationListFingerprint } from './fingerprint.js';
6
+ import { type WeChatChannelActivityGatePolicy, type WeChatChannelActivityGateStage } from './human-coordination.js';
6
7
  export type WeChatChannelHelperTransport = {
7
8
  request<T = unknown>(command: WeChatChannelHelperCommandName, params?: Record<string, unknown>, traceId?: string): Promise<WeChatChannelHelperResponse<T>>;
8
9
  };
@@ -28,6 +29,7 @@ export type WeChatChannelScreenPoint = {
28
29
  y: number;
29
30
  coordinateSpace: 'screen';
30
31
  };
32
+ export type WeChatChannelForegroundMode = 'required' | 'background';
31
33
  export type WeChatChannelScreenshot = {
32
34
  mimeType: string;
33
35
  dataBase64?: string;
@@ -66,6 +68,30 @@ type WeChatChannelWindowObservation = {
66
68
  };
67
69
  export type WeChatChannelObservePipeline = 'server-observe' | 'edge-structure';
68
70
  export type WeChatChannelObserveApi = Pick<WeChatChannelApiClient, 'observe'> & Partial<Pick<WeChatChannelApiClient, 'structureWindow' | 'embedVisual'>>;
71
+ export type WeChatChannelObserveActivityGuardEvent = {
72
+ phase: 'snapshot' | 'lease-acquire' | 'lease-status' | 'lease-release';
73
+ at: string;
74
+ stage: WeChatChannelActivityGateStage;
75
+ action: string;
76
+ ok: boolean;
77
+ reasonCode?: WeChatChannelHumanActivityReasonCode | string;
78
+ waitMs?: number;
79
+ };
80
+ export type WeChatChannelObserveActivityGuardOptions = {
81
+ policy?: Partial<WeChatChannelActivityGatePolicy>;
82
+ useAutomationLease?: boolean;
83
+ onEvent?: (event: WeChatChannelObserveActivityGuardEvent) => void;
84
+ };
85
+ export declare class WeChatChannelUserActivityAbort extends Error {
86
+ readonly reasonCode: WeChatChannelHumanActivityReasonCode | string;
87
+ readonly stage: WeChatChannelActivityGateStage;
88
+ readonly action: string;
89
+ constructor(input: {
90
+ reasonCode: WeChatChannelHumanActivityReasonCode | string;
91
+ stage: WeChatChannelActivityGateStage;
92
+ action: string;
93
+ });
94
+ }
69
95
  export type HelperBackedObserveOptions = {
70
96
  runtime: WeChatChannelRuntime;
71
97
  binding: WeChatChannelBindingConfig;
@@ -80,7 +106,9 @@ export type HelperBackedObserveOptions = {
80
106
  baselineOnly?: boolean;
81
107
  localLedgerTailAnchors?: unknown[];
82
108
  localOutboundEchoAnchors?: WeChatChannelLocalOutboundEchoAnchor[];
109
+ foregroundMode?: WeChatChannelForegroundMode;
83
110
  mediaAnchorText?: string;
111
+ activityGuard?: WeChatChannelObserveActivityGuardOptions;
84
112
  onObservationEvidence?: (evidence: {
85
113
  window: WeChatChannelWindowInfo;
86
114
  capture: WeChatChannelScreenshot;
@@ -94,7 +122,9 @@ export type WeChatChannelLocalOutboundEchoAnchor = {
94
122
  };
95
123
  export declare function observeWeChatChannelBindingViaHelper(options: HelperBackedObserveOptions): Promise<WeChatChannelObservedMessage[]>;
96
124
  export declare function ensureHelperPreflight(helper: WeChatChannelHelperTransport, traceId?: string): Promise<void>;
97
- export declare function ensureWeChatWindowReady(helper: WeChatChannelHelperTransport, traceId?: string): Promise<WeChatChannelWindowInfo>;
125
+ export declare function ensureWeChatWindowReady(helper: WeChatChannelHelperTransport, traceId?: string, options?: {
126
+ foreground?: WeChatChannelForegroundMode;
127
+ }): Promise<WeChatChannelWindowInfo>;
98
128
  export declare function focusWeChatWindow(helper: WeChatChannelHelperTransport, traceId?: string): Promise<WeChatChannelWindowInfo>;
99
129
  export declare function focusKnownWeChatWindow(helper: WeChatChannelHelperTransport, window: WeChatChannelWindowInfo, traceId?: string): Promise<WeChatChannelWindowInfo>;
100
130
  export declare function focusExistingWeChatWindow(helper: WeChatChannelHelperTransport, traceId?: string): Promise<WeChatChannelWindowInfo>;