shennian 0.2.109 → 0.2.111
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.
- package/dist/publish-build-manifest.json +35 -40
- package/dist/src/channels/runtime.d.ts +1 -0
- package/dist/src/channels/runtime.js +2 -2
- package/dist/src/channels/wechat-channel/automation-lane.d.ts +12 -0
- package/dist/src/channels/wechat-channel/automation-lane.js +2 -1
- package/dist/src/channels/wechat-channel/client.d.ts +16 -0
- package/dist/src/channels/wechat-channel/helper-client.js +3 -3
- package/dist/src/channels/wechat-channel/helper-protocol.d.ts +1 -1
- package/dist/src/channels/wechat-channel/observer.d.ts +9 -1
- package/dist/src/channels/wechat-channel/observer.js +7 -6
- package/dist/src/channels/wechat-channel/outbound-ledger.js +2 -2
- package/dist/src/channels/wechat-channel/outbound-sender.d.ts +8 -0
- package/dist/src/channels/wechat-channel/outbound-sender.js +1 -1
- package/dist/src/channels/wechat-channel/runner.js +1 -1
- package/dist/src/channels/wechat-rpa.js +1 -1
- package/dist/src/commands/runtime.d.ts +1 -0
- package/dist/src/commands/runtime.js +1 -1
- package/dist/src/commands/wechat/command.d.ts +1 -0
- package/dist/src/commands/wechat/command.js +1 -1
- package/dist/src/commands/wechat.d.ts +1 -1
- package/dist/src/commands/wechat.js +1 -1
- package/dist/src/devtools/wechat-channel-action-smoke.d.ts +3 -1
- package/dist/src/devtools/wechat-channel-action-smoke.js +9 -9
- package/dist/src/index.js +2 -2
- package/dist/src/native-fusion/service.d.ts +0 -10
- package/dist/src/native-fusion/service.js +2 -2
- package/dist/src/session/handlers/chat.js +2 -2
- package/dist/src/session/handlers/session-refresh.js +1 -1
- package/dist/src/session/manager.d.ts +1 -1
- package/dist/src/session/manager.js +1 -1
- package/package.json +1 -1
- package/dist/src/agents/codex-control.d.ts +0 -35
- package/dist/src/agents/codex-control.js +0 -2
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"jsMinified": true,
|
|
4
4
|
"minifier": "esbuild",
|
|
5
|
-
"fileCount":
|
|
5
|
+
"fileCount": 133,
|
|
6
6
|
"files": [
|
|
7
7
|
{
|
|
8
8
|
"file": "bin/shennian.js",
|
|
@@ -29,11 +29,6 @@
|
|
|
29
29
|
"beforeBytes": 11638,
|
|
30
30
|
"afterBytes": 6020
|
|
31
31
|
},
|
|
32
|
-
{
|
|
33
|
-
"file": "src/agents/codex-control.js",
|
|
34
|
-
"beforeBytes": 6674,
|
|
35
|
-
"afterBytes": 3673
|
|
36
|
-
},
|
|
37
32
|
{
|
|
38
33
|
"file": "src/agents/codex-utils.js",
|
|
39
34
|
"beforeBytes": 7479,
|
|
@@ -161,8 +156,8 @@
|
|
|
161
156
|
},
|
|
162
157
|
{
|
|
163
158
|
"file": "src/channels/runtime.js",
|
|
164
|
-
"beforeBytes":
|
|
165
|
-
"afterBytes":
|
|
159
|
+
"beforeBytes": 30070,
|
|
160
|
+
"afterBytes": 16457
|
|
166
161
|
},
|
|
167
162
|
{
|
|
168
163
|
"file": "src/channels/secret-registry.js",
|
|
@@ -181,8 +176,8 @@
|
|
|
181
176
|
},
|
|
182
177
|
{
|
|
183
178
|
"file": "src/channels/wechat-channel/automation-lane.js",
|
|
184
|
-
"beforeBytes":
|
|
185
|
-
"afterBytes":
|
|
179
|
+
"beforeBytes": 4299,
|
|
180
|
+
"afterBytes": 2233
|
|
186
181
|
},
|
|
187
182
|
{
|
|
188
183
|
"file": "src/channels/wechat-channel/client.js",
|
|
@@ -226,8 +221,8 @@
|
|
|
226
221
|
},
|
|
227
222
|
{
|
|
228
223
|
"file": "src/channels/wechat-channel/helper-client.js",
|
|
229
|
-
"beforeBytes":
|
|
230
|
-
"afterBytes":
|
|
224
|
+
"beforeBytes": 31826,
|
|
225
|
+
"afterBytes": 16459
|
|
231
226
|
},
|
|
232
227
|
{
|
|
233
228
|
"file": "src/channels/wechat-channel/helper-protocol.js",
|
|
@@ -266,18 +261,18 @@
|
|
|
266
261
|
},
|
|
267
262
|
{
|
|
268
263
|
"file": "src/channels/wechat-channel/observer.js",
|
|
269
|
-
"beforeBytes":
|
|
270
|
-
"afterBytes":
|
|
264
|
+
"beforeBytes": 102627,
|
|
265
|
+
"afterBytes": 47836
|
|
271
266
|
},
|
|
272
267
|
{
|
|
273
268
|
"file": "src/channels/wechat-channel/outbound-ledger.js",
|
|
274
|
-
"beforeBytes":
|
|
275
|
-
"afterBytes":
|
|
269
|
+
"beforeBytes": 9833,
|
|
270
|
+
"afterBytes": 5572
|
|
276
271
|
},
|
|
277
272
|
{
|
|
278
273
|
"file": "src/channels/wechat-channel/outbound-sender.js",
|
|
279
|
-
"beforeBytes":
|
|
280
|
-
"afterBytes":
|
|
274
|
+
"beforeBytes": 21306,
|
|
275
|
+
"afterBytes": 10998
|
|
281
276
|
},
|
|
282
277
|
{
|
|
283
278
|
"file": "src/channels/wechat-channel/pacing.js",
|
|
@@ -291,8 +286,8 @@
|
|
|
291
286
|
},
|
|
292
287
|
{
|
|
293
288
|
"file": "src/channels/wechat-channel/runner.js",
|
|
294
|
-
"beforeBytes":
|
|
295
|
-
"afterBytes":
|
|
289
|
+
"beforeBytes": 14776,
|
|
290
|
+
"afterBytes": 7286
|
|
296
291
|
},
|
|
297
292
|
{
|
|
298
293
|
"file": "src/channels/wechat-channel/runtime.js",
|
|
@@ -331,8 +326,8 @@
|
|
|
331
326
|
},
|
|
332
327
|
{
|
|
333
328
|
"file": "src/channels/wechat-rpa.js",
|
|
334
|
-
"beforeBytes":
|
|
335
|
-
"afterBytes":
|
|
329
|
+
"beforeBytes": 55613,
|
|
330
|
+
"afterBytes": 29151
|
|
336
331
|
},
|
|
337
332
|
{
|
|
338
333
|
"file": "src/channels/wecom.js",
|
|
@@ -386,8 +381,8 @@
|
|
|
386
381
|
},
|
|
387
382
|
{
|
|
388
383
|
"file": "src/commands/runtime.js",
|
|
389
|
-
"beforeBytes":
|
|
390
|
-
"afterBytes":
|
|
384
|
+
"beforeBytes": 34179,
|
|
385
|
+
"afterBytes": 19155
|
|
391
386
|
},
|
|
392
387
|
{
|
|
393
388
|
"file": "src/commands/tools.js",
|
|
@@ -401,8 +396,8 @@
|
|
|
401
396
|
},
|
|
402
397
|
{
|
|
403
398
|
"file": "src/commands/wechat/command.js",
|
|
404
|
-
"beforeBytes":
|
|
405
|
-
"afterBytes":
|
|
399
|
+
"beforeBytes": 7941,
|
|
400
|
+
"afterBytes": 4755
|
|
406
401
|
},
|
|
407
402
|
{
|
|
408
403
|
"file": "src/commands/wechat/direct.js",
|
|
@@ -441,8 +436,8 @@
|
|
|
441
436
|
},
|
|
442
437
|
{
|
|
443
438
|
"file": "src/commands/wechat.js",
|
|
444
|
-
"beforeBytes":
|
|
445
|
-
"afterBytes":
|
|
439
|
+
"beforeBytes": 429,
|
|
440
|
+
"afterBytes": 325
|
|
446
441
|
},
|
|
447
442
|
{
|
|
448
443
|
"file": "src/config/index.js",
|
|
@@ -456,8 +451,8 @@
|
|
|
456
451
|
},
|
|
457
452
|
{
|
|
458
453
|
"file": "src/devtools/wechat-channel-action-smoke.js",
|
|
459
|
-
"beforeBytes":
|
|
460
|
-
"afterBytes":
|
|
454
|
+
"beforeBytes": 74169,
|
|
455
|
+
"afterBytes": 38722
|
|
461
456
|
},
|
|
462
457
|
{
|
|
463
458
|
"file": "src/devtools/wechat-channel-download-report.js",
|
|
@@ -501,8 +496,8 @@
|
|
|
501
496
|
},
|
|
502
497
|
{
|
|
503
498
|
"file": "src/index.js",
|
|
504
|
-
"beforeBytes":
|
|
505
|
-
"afterBytes":
|
|
499
|
+
"beforeBytes": 17034,
|
|
500
|
+
"afterBytes": 8527
|
|
506
501
|
},
|
|
507
502
|
{
|
|
508
503
|
"file": "src/log-reporter.js",
|
|
@@ -546,8 +541,8 @@
|
|
|
546
541
|
},
|
|
547
542
|
{
|
|
548
543
|
"file": "src/native-fusion/service.js",
|
|
549
|
-
"beforeBytes":
|
|
550
|
-
"afterBytes":
|
|
544
|
+
"beforeBytes": 7549,
|
|
545
|
+
"afterBytes": 3456
|
|
551
546
|
},
|
|
552
547
|
{
|
|
553
548
|
"file": "src/native-fusion/state.js",
|
|
@@ -586,8 +581,8 @@
|
|
|
586
581
|
},
|
|
587
582
|
{
|
|
588
583
|
"file": "src/session/handlers/chat.js",
|
|
589
|
-
"beforeBytes":
|
|
590
|
-
"afterBytes":
|
|
584
|
+
"beforeBytes": 30739,
|
|
585
|
+
"afterBytes": 13936
|
|
591
586
|
},
|
|
592
587
|
{
|
|
593
588
|
"file": "src/session/handlers/control.js",
|
|
@@ -601,8 +596,8 @@
|
|
|
601
596
|
},
|
|
602
597
|
{
|
|
603
598
|
"file": "src/session/handlers/session-refresh.js",
|
|
604
|
-
"beforeBytes":
|
|
605
|
-
"afterBytes":
|
|
599
|
+
"beforeBytes": 963,
|
|
600
|
+
"afterBytes": 451
|
|
606
601
|
},
|
|
607
602
|
{
|
|
608
603
|
"file": "src/session/handlers/skills.js",
|
|
@@ -626,8 +621,8 @@
|
|
|
626
621
|
},
|
|
627
622
|
{
|
|
628
623
|
"file": "src/session/manager.js",
|
|
629
|
-
"beforeBytes":
|
|
630
|
-
"afterBytes":
|
|
624
|
+
"beforeBytes": 15312,
|
|
625
|
+
"afterBytes": 7880
|
|
631
626
|
},
|
|
632
627
|
{
|
|
633
628
|
"file": "src/session/projection.js",
|
|
@@ -63,6 +63,7 @@ export declare class ChannelRuntime {
|
|
|
63
63
|
syncManagerWeChatRpaChannel(managerSessionId: string): Promise<{
|
|
64
64
|
channel: ExternalChannelView;
|
|
65
65
|
messages: ExternalMessageEvent[];
|
|
66
|
+
recentMessages: ExternalMessageEvent[];
|
|
66
67
|
}>;
|
|
67
68
|
cancelManagerWeChatRpaOutbound(input: {
|
|
68
69
|
managerSessionId: string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import v from"node:crypto";import g from"node:fs";import u from"node:path";import{ChannelConfigRegistry as M}from"./registry.js";import{ChannelSecretRegistry as N}from"./secret-registry.js";import{WeComChannelAdapter as D}from"./wecom.js";import{WeChatRpaChannelAdapter as B}from"./wechat-rpa.js";import{ExternalWebSocketChannelAdapter as x}from"./websocket.js";import{splitExternalReplyText as F}from"./reply-split.js";class J{onExternalMessage;createReplyTarget;configs=new M;secrets=new N;adapters=new Map;completedReplyKeys=new Map;recentMessages=new Map;constructor(e,t,s={}){this.onExternalMessage=e,this.createReplyTarget=t;const o=new D(a=>this.ingest({...a,type:"external.message"}));this.adapters.set(o.type,o);const r=new x(a=>this.ingest({...a,type:"external.message"}));this.adapters.set(r.type,r);const i=new B(a=>this.ingest(a),{createProductRunner:s.createWeChatRpaProductRunner,automationLane:s.weChatAutomationLane});this.adapters.set(i.type,i)}async start(){for(const e of this.configs.list().filter(t=>t.enabled))await this.adapters.get(e.type)?.connect(e).catch(()=>{})}async stop(){for(const e of this.configs.list())await this.adapters.get(e.type)?.disconnect(e).catch(()=>{})}ingest(e){const t=this.findRecentMessageDuplicate(e);if(t)return t;const s=this.configs.get(e.channelId),o=e.managerSessionId??s?.sessionId??s?.managerSessionId;if(!o)throw new Error(`No session bound for channel ${e.channelId}`);const r=e.replyTarget||this.createReplyTarget({managerSessionId:o,channelId:e.channelId,conversationId:e.conversationId,messageId:e.messageId}),i={...e,replyTarget:r};return this.onExternalMessage(o,i),this.recordRecentMessage(i),i}async reply(e){const t=this.configs.get(e.channelId);if(!t)return{ok:!1,error:`Unknown channel: ${e.channelId}`};if((t.sessionId??t.managerSessionId)!==e.managerSessionId)return{ok:!1,error:"Channel is not bound to this session"};const s=this.adapters.get(t.type);if(!s)return{ok:!1,error:`Unsupported channel type: ${t.type}`};try{const o=W(t.type,e);if(!o.length)return{ok:!1,error:"Reply text or attachment is required"};let r=!1;for(const i of o){const a=i.idempotencyKey;if(a&&this.isReplyCompleted(t,e.conversationId,a))continue;const c=await s.send(t,{...e,...i});if(c?.status==="queued"){r=!0;continue}if(c?.status==="manual-review")return{ok:!1,error:c.reason||"Reply requires manual review before retry"};a&&this.markReplyCompleted(t,e.conversationId,a)}return r?{ok:!0,pending:!0}:{ok:!0}}catch(o){return{ok:!1,error:o instanceof Error?o.message:String(o)}}}isReplyCompleted(e,t,s){return this.loadReplyCompletionSet(e).has(A(e.id,t,s))}markReplyCompleted(e,t,s){const o=this.loadReplyCompletionSet(e);o.add(A(e.id,t,s));try{$(e.workDir,o)}catch{}}loadReplyCompletionSet(e){const t=u.resolve(e.workDir),s=this.completedReplyKeys.get(t);if(s)return s;const o=T(e.workDir);return this.completedReplyKeys.set(t,o),o}async getDefaultReplyTarget(e){const t=this.configs.list().find(r=>(r.sessionId??r.managerSessionId)===e&&r.enabled);if(!t)throw new Error("No enabled external channel is bound to this session");const s=this.adapters.get(t.type);if(!s?.defaultConversation)throw new Error(`External channel ${t.type} has no default conversation`);const o=await s.defaultConversation(t);return{channelId:t.id,conversationId:o.conversationId}}getManagerChannel(e,t,s={}){const o=this.configs.list().filter(c=>(c.sessionId??c.managerSessionId)===e&&c.type===t),r=o.find(c=>c.enabled)??o.at(-1);if(!r)return null;const i=this.secrets.get(r.secretRef),a=this.adapters.get(r.type)?.runtimeStatus?.(r)??{};return{id:r.id,type:r.type,name:r.name,sessionId:r.sessionId??r.managerSessionId,managerSessionId:r.managerSessionId,workDir:r.workDir,agentType:r.agentType,agentSessionId:r.agentSessionId,modelId:r.modelId,enabled:r.enabled,wsUrl:i?.wsUrl??"",token:s.includeSecret?i?.token??"":"",tokenConfigured:!!i?.token,canReply:!!i?.canReply,systemPrompt:typeof i?.systemPrompt=="string"?i.systemPrompt:"",...k(i),...a}}getChannelById(e,t={}){const s=this.configs.get(e);if(!s)return null;const o=this.secrets.get(s.secretRef),r=this.adapters.get(s.type)?.runtimeStatus?.(s)??{};return{id:s.id,type:s.type,name:s.name,sessionId:s.sessionId??s.managerSessionId,managerSessionId:s.managerSessionId,workDir:s.workDir,agentType:s.agentType,agentSessionId:s.agentSessionId,modelId:s.modelId,enabled:s.enabled,wsUrl:o?.wsUrl??"",token:t.includeSecret?o?.token??"":"",tokenConfigured:!!o?.token,canReply:!!o?.canReply,systemPrompt:typeof o?.systemPrompt=="string"?o.systemPrompt:"",...k(o),...r}}getChannelStatusById(e){const t=this.configs.get(e);if(!t)return null;const s=this.secrets.get(t.secretRef),o=this.adapters.get(t.type)?.runtimeStatus?.(t)??{};return{configured:!0,connected:y(t,s),type:t.type,channelId:t.id,name:t.name,canReply:!!s?.canReply,systemPrompt:typeof s?.systemPrompt=="string"?s.systemPrompt:"",...h(s),...o}}getManagerChannelStatus(e){const t=this.configs.list().find(r=>(r.sessionId??r.managerSessionId)===e&&r.enabled);if(!t)return null;const s=this.secrets.get(t.secretRef),o=this.adapters.get(t.type)?.runtimeStatus?.(t)??{};return{configured:!0,connected:y(t,s),type:t.type,channelId:t.id,name:t.name,canReply:!!s?.canReply,systemPrompt:typeof s?.systemPrompt=="string"?s.systemPrompt:"",...h(s),...o}}listManagerChannelStatuses(){return this.configs.list().filter(e=>e.enabled).map(e=>({managerSessionId:e.sessionId??e.managerSessionId,status:this.getManagerChannelStatus(e.sessionId??e.managerSessionId)})).filter(e=>!!e.status)}listManagerExternalChannels(e){return this.configs.list().filter(t=>t.enabled&&(t.sessionId??t.managerSessionId)===e).map(t=>{const s=this.secrets.get(t.secretRef),o=this.adapters.get(t.type)?.runtimeStatus?.(t)??{};return{configured:!0,connected:y(t,s),type:t.type,channelId:t.id,name:t.name,canReply:!!s?.canReply,systemPrompt:typeof s?.systemPrompt=="string"?s.systemPrompt:"",...h(s),...o}})}async syncManagerWeChatRpaChannel(e){const t=this.configs.list().find(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 s=this.adapters.get(t.type);if(!s?.syncNow)throw new Error("WeChat RPA channel does not support manual sync");const o=Date.now()-120*1e3,r=K(await s.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 s=this.adapters.get(t.type);if(!s?.cancelOutbound)throw new Error("WeChat RPA channel does not support outbound cancellation");const o=await s.cancelOutbound(t,{idempotencyKey:e.idempotencyKey,replyId:e.replyId,reason:e.reason});return{cancelled:o.cancelled,status:o.status,channel:this.getManagerChannel(e.managerSessionId,"wechat-rpa",{includeSecret:!0})}}recordRecentMessage(e){const t=this.recentMessages.get(e.channelId)??[];this.findRecentMessageDuplicate(e)||(t.push(e),this.recentMessages.set(e.channelId,t.slice(-50)))}findRecentMessageDuplicate(e){const t=I(e);return t?(this.recentMessages.get(e.channelId)??[]).find(o=>I(o)===t)??null:null}getRecentMessages(e,t){return(this.recentMessages.get(e)??[]).filter(o=>{const r=Date.parse(o.receivedAt);return!Number.isFinite(r)||r>=t})}async upsertManagerChannel(e){const t=this.configs.get(e.id),s=this.configs.list(),o=e.sessionId||e.managerSessionId,r={id:e.id,type:e.type,name:e.name?.trim()||t?.name||"\u5916\u90E8\u6D88\u606F\u901A\u9053",sessionId:o,managerSessionId:o,workDir:e.workDir,agentType:e.agentType||t?.agentType,agentSessionId:e.agentSessionId??t?.agentSessionId??null,modelId:e.modelId??t?.modelId??null,enabled:e.enabled,secretRef:t?.secretRef||`channel:${e.id}`},i=this.secrets.get(r.secretRef),a=e.wsUrl?.trim()||i?.wsUrl||"",c=e.token?.trim()||i?.token||"",p=e.canReply??i?.canReply??!1,m=e.systemPrompt??(typeof i?.systemPrompt=="string"?i.systemPrompt:"");if(r.enabled&&(!a||!c))throw new Error("WebSocket \u5730\u5740\u548C Token \u5FC5\u586B");const f=s.filter(l=>l.id!==r.id).map(l=>(l.sessionId??l.managerSessionId)===o&&l.type===e.type?{...l,enabled:!1}:l);f.push(r),this.configs.replaceAll(f),(a||c)&&this.secrets.upsert(r.secretRef,{type:"websocket",wsUrl:a,token:c,canReply:p,systemPrompt:m});const d=this.adapters.get(r.type);for(const l of s)(l.sessionId??l.managerSessionId)===o&&l.type===e.type&&l.enabled&&await this.adapters.get(l.type)?.disconnect(l).catch(()=>{});return r.enabled&&d?.connect(r).catch(()=>{}),this.getManagerChannel(o,e.type,{includeSecret:!0})}async upsertManagerWeChatRpaChannel(e){const t=this.configs.get(e.id),s=this.configs.list(),o=e.sessionId||e.managerSessionId,r=w(e.groups);if(e.enabled&&!r.length)throw new Error("WeChat RPA \u81F3\u5C11\u9700\u8981\u914D\u7F6E\u4E00\u4E2A\u7FA4");if(e.enabled&&r.length>1)throw new Error("WeChat RPA \u6BCF\u4E2A\u5BF9\u8BDD\u53EA\u80FD\u7ED1\u5B9A\u4E00\u4E2A\u7FA4");const i={id:e.id,type:"wechat-rpa",name:e.name?.trim()||t?.name||"\u672C\u673A\u5FAE\u4FE1 RPA",sessionId:o,managerSessionId:o,workDir:e.workDir,agentType:e.agentType||t?.agentType,agentSessionId:e.agentSessionId??t?.agentSessionId??null,modelId:e.modelId??t?.modelId??null,enabled:e.enabled,secretRef:t?.secretRef||`channel:${e.id}`},a=this.secrets.get(i.secretRef),c=e.source||(a?.source==="wechat-channel"||a?.source==="macos-probe"||a?.source==="fixture-jsonl"||a?.source==="macos-flow"||a?.source==="windows-visual-flow"||a?.source==="wechat-rpa-lab"?a.source:E());if(e.enabled&&c==="windows-visual-flow")throw new Error("\u65E7 Windows visual flow \u5DF2\u5F52\u6863\uFF1BmacOS \u548C Windows \u6B63\u5F0F\u901A\u9053\u90FD\u4F7F\u7528 wechat-channel");const p=e.privacyConsentAccepted??!!a?.privacyConsentAccepted;if(e.enabled&&!p)throw new Error("\u542F\u7528\u524D\u9700\u8981\u786E\u8BA4\u5FAE\u4FE1\u901A\u9053\u6570\u636E\u4E0E\u9690\u79C1\u6388\u6743");const m=s.filter(d=>d.id!==i.id).map(d=>(d.sessionId??d.managerSessionId)===o&&d.type==="wechat-rpa"?{...d,enabled:!1}:d);m.push(i),this.configs.replaceAll(m),this.secrets.upsert(i.secretRef,{type:"wechat-rpa",source:c,groups:r,pollIntervalMs:R(e.pollIntervalMs,a?.pollIntervalMs),recentLimit:R(e.recentLimit,a?.recentLimit),idleSeconds:R(e.idleSeconds,a?.idleSeconds),forceForeground:e.forceForeground??(a?.forceForeground===void 0?!0:!!a.forceForeground),noRestore:e.noRestore??(a?.noRestore===void 0?!0:!!a.noRestore),downloadAttachments:e.downloadAttachments??(a?.downloadAttachments===void 0?!0:!!a.downloadAttachments),downloadAttachmentsDir:e.downloadAttachmentsDir?.trim()||b(a?.downloadAttachmentsDir),selfNickname:e.selfNickname?.trim()||b(a?.selfNickname),selfTriggerMarker:e.selfTriggerMarker?.trim()||void 0,deferInitialPoll:e.deferInitialPoll===!0?!0:void 0,privacyConsentAccepted:p,flowScriptPath:e.flowScriptPath?.trim()||b(a?.flowScriptPath),canReply:e.canReply??a?.canReply??!1,systemPrompt:e.systemPrompt??(typeof a?.systemPrompt=="string"?a.systemPrompt:"")});const f=this.adapters.get(i.type);for(const d of s)(d.sessionId??d.managerSessionId)===o&&d.type==="wechat-rpa"&&d.enabled&&await this.adapters.get(d.type)?.disconnect(d).catch(()=>{});return i.enabled&&f?.connect(i).catch(()=>{}),this.getManagerChannel(o,"wechat-rpa",{includeSecret:!0})}}function y(n,e){return!e||e.type!==n.type?!1:n.type==="wechat-rpa"?!0:n.type==="websocket"?!!(e.wsUrl&&e.token):n.type==="wecom"?!!(e.token||e.botId||e.secret):!!e.token}function k(n){return!n||n.type!=="wechat-rpa"?{}:{wechatRpaSource:typeof n.source=="string"?n.source:"",wechatRpaGroups:w(Array.isArray(n.groups)?n.groups:[]),pollIntervalMs:Number.isFinite(n.pollIntervalMs)?Number(n.pollIntervalMs):void 0,recentLimit:Number.isFinite(n.recentLimit)?Number(n.recentLimit):void 0,idleSeconds:Number.isFinite(n.idleSeconds)?Number(n.idleSeconds):void 0,forceForeground:n.forceForeground===void 0?!0:!!n.forceForeground,noRestore:n.noRestore===void 0?void 0:!!n.noRestore,downloadAttachments:n.downloadAttachments===void 0?!0:!!n.downloadAttachments,downloadAttachmentsDir:typeof n.downloadAttachmentsDir=="string"?n.downloadAttachmentsDir:"",selfNickname:typeof n.selfNickname=="string"?n.selfNickname:"",wechatRpaPrivacyConsentAccepted:!!n.privacyConsentAccepted,wechatRpaServerDecisionAvailable:!0,wechatRpaPreflightChecks:C(n)}}function h(n){return!n||n.type!=="wechat-rpa"?{}:{wechatRpaSource:typeof n.source=="string"?n.source:null,wechatRpaGroups:w(Array.isArray(n.groups)?n.groups:[]),pollIntervalMs:Number.isFinite(n.pollIntervalMs)?Number(n.pollIntervalMs):null,recentLimit:Number.isFinite(n.recentLimit)?Number(n.recentLimit):null,idleSeconds:Number.isFinite(n.idleSeconds)?Number(n.idleSeconds):null,forceForeground:n.forceForeground===void 0?!0:!!n.forceForeground,noRestore:n.noRestore===void 0?null:!!n.noRestore,downloadAttachments:n.downloadAttachments===void 0?!0:!!n.downloadAttachments,downloadAttachmentsDir:typeof n.downloadAttachmentsDir=="string"?n.downloadAttachmentsDir:null,selfNickname:typeof n.selfNickname=="string"?n.selfNickname:null,wechatRpaPrivacyConsentAccepted:!!n.privacyConsentAccepted,wechatRpaServerDecisionAvailable:!0,wechatRpaPreflightChecks:C(n)}}function w(n){const e=new Set,t=[];for(const s of n){const o=String(s?.name||"").replace(/\s+/g," ").trim();!o||e.has(o)||(e.add(o),t.push({name:o}))}return t}function E(){return"wechat-channel"}function C(n){const e=[];if(n.lastHelperPermissions&&typeof n.lastHelperPermissions=="object"){const t=n.lastHelperPermissions;e.push({code:"wechat_window_unavailable",ok:t.wechatWindowAvailable!==!1,severity:"blocking",message:t.wechatWindowAvailable===!1?"\u5FAE\u4FE1\u7A97\u53E3\u4E0D\u53EF\u7528\uFF0C\u8BF7\u6253\u5F00\u5E76\u767B\u5F55\u5FAE\u4FE1\u540E\u518D\u8BD5\u3002":"\u5FAE\u4FE1\u7A97\u53E3\u53EF\u7528\u3002"})}return e.push({code:"platform_unsupported",ok:n.source!=="windows-visual-flow",severity:"blocking",message:n.source==="windows-visual-flow"?"\u65E7 Windows visual flow \u5DF2\u5F52\u6863\uFF0C\u8BF7\u6539\u7528 wechat-channel\u3002":"\u5F53\u524D\u914D\u7F6E\u4F7F\u7528\u8DE8\u5E73\u53F0\u5FAE\u4FE1\u901A\u9053\u3002"}),e.push({code:"privacy_consent_required",ok:!!n.privacyConsentAccepted,severity:"blocking",message:n.privacyConsentAccepted?"\u5DF2\u786E\u8BA4\u5FAE\u4FE1\u901A\u9053\u6570\u636E\u4E0E\u9690\u79C1\u6388\u6743\u3002":"\u542F\u7528\u524D\u9700\u8981\u786E\u8BA4\u5FAE\u4FE1\u901A\u9053\u6570\u636E\u4E0E\u9690\u79C1\u6388\u6743\u3002"}),e.push({code:"server_decision_unavailable",ok:!0,severity:"blocking",message:"\u670D\u52A1\u7AEF\u5224\u65AD\u80FD\u529B\u53EF\u7528\u3002"}),e}function W(n,e){const t=F(e.text);if(!t.length&&!e.attachment)return[];if(n==="wechat-rpa"&&e.attachment&&t.length<=1)return[{text:t[0]??"",attachment:e.attachment,idempotencyKey:e.idempotencyKey}];const s=t.map((o,r)=>({text:o,attachment:void 0,idempotencyKey:t.length>1&&e.idempotencyKey?`${e.idempotencyKey}:${r+1}`:e.idempotencyKey}));return e.attachment&&s.push({text:"",attachment:e.attachment,idempotencyKey:t.length&&e.idempotencyKey?`${e.idempotencyKey}:attachment`:e.idempotencyKey}),s}function K(...n){const e=new Set,t=[];for(const s of n.flat()){const o=I(s);if(!o){t.push(s);continue}e.has(o)||(e.add(o),t.push(s))}return t}function I(n){const e=S(n.channelId),t=S(n.conversationId),s=S(n.messageId);return!e||!t||!s?null:`${e}
|
|
1
|
+
import M from"node:crypto";import g from"node:fs";import u from"node:path";import{ChannelConfigRegistry as v}from"./registry.js";import{ChannelSecretRegistry as N}from"./secret-registry.js";import{WeComChannelAdapter as D}from"./wecom.js";import{WeChatRpaChannelAdapter as B}from"./wechat-rpa.js";import{ExternalWebSocketChannelAdapter as x}from"./websocket.js";import{splitExternalReplyText as F}from"./reply-split.js";class J{onExternalMessage;createReplyTarget;configs=new v;secrets=new N;adapters=new Map;completedReplyKeys=new Map;recentMessages=new Map;constructor(e,t,s={}){this.onExternalMessage=e,this.createReplyTarget=t;const o=new D(a=>this.ingest({...a,type:"external.message"}));this.adapters.set(o.type,o);const r=new x(a=>this.ingest({...a,type:"external.message"}));this.adapters.set(r.type,r);const i=new B(a=>this.ingest(a),{createProductRunner:s.createWeChatRpaProductRunner,automationLane:s.weChatAutomationLane});this.adapters.set(i.type,i)}async start(){for(const e of this.configs.list().filter(t=>t.enabled))await this.adapters.get(e.type)?.connect(e).catch(()=>{})}async stop(){for(const e of this.configs.list())await this.adapters.get(e.type)?.disconnect(e).catch(()=>{})}ingest(e){const t=this.findRecentMessageDuplicate(e);if(t)return t;const s=this.configs.get(e.channelId),o=e.managerSessionId??s?.sessionId??s?.managerSessionId;if(!o)throw new Error(`No session bound for channel ${e.channelId}`);const r=e.replyTarget||this.createReplyTarget({managerSessionId:o,channelId:e.channelId,conversationId:e.conversationId,messageId:e.messageId}),i={...e,replyTarget:r};return this.onExternalMessage(o,i),this.recordRecentMessage(i),i}async reply(e){const t=this.configs.get(e.channelId);if(!t)return{ok:!1,error:`Unknown channel: ${e.channelId}`};if((t.sessionId??t.managerSessionId)!==e.managerSessionId)return{ok:!1,error:"Channel is not bound to this session"};const s=this.adapters.get(t.type);if(!s)return{ok:!1,error:`Unsupported channel type: ${t.type}`};try{const o=W(t.type,e);if(!o.length)return{ok:!1,error:"Reply text or attachment is required"};let r=!1;for(const i of o){const a=i.idempotencyKey;if(a&&this.isReplyCompleted(t,e.conversationId,a))continue;const c=await s.send(t,{...e,...i});if(c?.status==="queued"){r=!0;continue}if(c?.status==="manual-review")return{ok:!1,error:c.reason||"Reply requires manual review before retry"};a&&this.markReplyCompleted(t,e.conversationId,a)}return r?{ok:!0,pending:!0}:{ok:!0}}catch(o){return{ok:!1,error:o instanceof Error?o.message:String(o)}}}isReplyCompleted(e,t,s){return this.loadReplyCompletionSet(e).has(A(e.id,t,s))}markReplyCompleted(e,t,s){const o=this.loadReplyCompletionSet(e);o.add(A(e.id,t,s));try{$(e.workDir,o)}catch{}}loadReplyCompletionSet(e){const t=u.resolve(e.workDir),s=this.completedReplyKeys.get(t);if(s)return s;const o=T(e.workDir);return this.completedReplyKeys.set(t,o),o}async getDefaultReplyTarget(e){const t=this.configs.list().find(r=>(r.sessionId??r.managerSessionId)===e&&r.enabled);if(!t)throw new Error("No enabled external channel is bound to this session");const s=this.adapters.get(t.type);if(!s?.defaultConversation)throw new Error(`External channel ${t.type} has no default conversation`);const o=await s.defaultConversation(t);return{channelId:t.id,conversationId:o.conversationId}}getManagerChannel(e,t,s={}){const o=this.configs.list().filter(c=>(c.sessionId??c.managerSessionId)===e&&c.type===t),r=o.find(c=>c.enabled)??o.at(-1);if(!r)return null;const i=this.secrets.get(r.secretRef),a=this.adapters.get(r.type)?.runtimeStatus?.(r)??{};return{id:r.id,type:r.type,name:r.name,sessionId:r.sessionId??r.managerSessionId,managerSessionId:r.managerSessionId,workDir:r.workDir,agentType:r.agentType,agentSessionId:r.agentSessionId,modelId:r.modelId,enabled:r.enabled,wsUrl:i?.wsUrl??"",token:s.includeSecret?i?.token??"":"",tokenConfigured:!!i?.token,canReply:!!i?.canReply,systemPrompt:typeof i?.systemPrompt=="string"?i.systemPrompt:"",...k(i),...a}}getChannelById(e,t={}){const s=this.configs.get(e);if(!s)return null;const o=this.secrets.get(s.secretRef),r=this.adapters.get(s.type)?.runtimeStatus?.(s)??{};return{id:s.id,type:s.type,name:s.name,sessionId:s.sessionId??s.managerSessionId,managerSessionId:s.managerSessionId,workDir:s.workDir,agentType:s.agentType,agentSessionId:s.agentSessionId,modelId:s.modelId,enabled:s.enabled,wsUrl:o?.wsUrl??"",token:t.includeSecret?o?.token??"":"",tokenConfigured:!!o?.token,canReply:!!o?.canReply,systemPrompt:typeof o?.systemPrompt=="string"?o.systemPrompt:"",...k(o),...r}}getChannelStatusById(e){const t=this.configs.get(e);if(!t)return null;const s=this.secrets.get(t.secretRef),o=this.adapters.get(t.type)?.runtimeStatus?.(t)??{};return{configured:!0,connected:y(t,s),type:t.type,channelId:t.id,name:t.name,canReply:!!s?.canReply,systemPrompt:typeof s?.systemPrompt=="string"?s.systemPrompt:"",...h(s),...o}}getManagerChannelStatus(e){const t=this.configs.list().find(r=>(r.sessionId??r.managerSessionId)===e&&r.enabled);if(!t)return null;const s=this.secrets.get(t.secretRef),o=this.adapters.get(t.type)?.runtimeStatus?.(t)??{};return{configured:!0,connected:y(t,s),type:t.type,channelId:t.id,name:t.name,canReply:!!s?.canReply,systemPrompt:typeof s?.systemPrompt=="string"?s.systemPrompt:"",...h(s),...o}}listManagerChannelStatuses(){return this.configs.list().filter(e=>e.enabled).map(e=>({managerSessionId:e.sessionId??e.managerSessionId,status:this.getManagerChannelStatus(e.sessionId??e.managerSessionId)})).filter(e=>!!e.status)}listManagerExternalChannels(e){return this.configs.list().filter(t=>t.enabled&&(t.sessionId??t.managerSessionId)===e).map(t=>{const s=this.secrets.get(t.secretRef),o=this.adapters.get(t.type)?.runtimeStatus?.(t)??{};return{configured:!0,connected:y(t,s),type:t.type,channelId:t.id,name:t.name,canReply:!!s?.canReply,systemPrompt:typeof s?.systemPrompt=="string"?s.systemPrompt:"",...h(s),...o}})}async syncManagerWeChatRpaChannel(e){const t=this.configs.list().find(a=>a.enabled&&a.type==="wechat-rpa"&&(a.sessionId??a.managerSessionId)===e);if(!t)throw new Error("No enabled WeChat RPA channel is bound to this session");const s=this.adapters.get(t.type);if(!s?.syncNow)throw new Error("WeChat RPA channel does not support manual sync");const o=Date.now()-120*1e3,r=await s.syncNow(t)??[],i=K(r,this.getRecentMessages(t.id,o));return{channel:this.getManagerChannel(e,"wechat-rpa",{includeSecret:!0}),messages:r,recentMessages:i}}async cancelManagerWeChatRpaOutbound(e){const t=this.configs.list().find(r=>r.enabled&&r.type==="wechat-rpa"&&(r.sessionId??r.managerSessionId)===e.managerSessionId);if(!t)throw new Error("No enabled WeChat RPA channel is bound to this session");const s=this.adapters.get(t.type);if(!s?.cancelOutbound)throw new Error("WeChat RPA channel does not support outbound cancellation");const o=await s.cancelOutbound(t,{idempotencyKey:e.idempotencyKey,replyId:e.replyId,reason:e.reason});return{cancelled:o.cancelled,status:o.status,channel:this.getManagerChannel(e.managerSessionId,"wechat-rpa",{includeSecret:!0})}}recordRecentMessage(e){const t=this.recentMessages.get(e.channelId)??[];this.findRecentMessageDuplicate(e)||(t.push(e),this.recentMessages.set(e.channelId,t.slice(-50)))}findRecentMessageDuplicate(e){const t=I(e);return t?(this.recentMessages.get(e.channelId)??[]).find(o=>I(o)===t)??null:null}getRecentMessages(e,t){return(this.recentMessages.get(e)??[]).filter(o=>{const r=Date.parse(o.receivedAt);return!Number.isFinite(r)||r>=t})}async upsertManagerChannel(e){const t=this.configs.get(e.id),s=this.configs.list(),o=e.sessionId||e.managerSessionId,r={id:e.id,type:e.type,name:e.name?.trim()||t?.name||"\u5916\u90E8\u6D88\u606F\u901A\u9053",sessionId:o,managerSessionId:o,workDir:e.workDir,agentType:e.agentType||t?.agentType,agentSessionId:e.agentSessionId??t?.agentSessionId??null,modelId:e.modelId??t?.modelId??null,enabled:e.enabled,secretRef:t?.secretRef||`channel:${e.id}`},i=this.secrets.get(r.secretRef),a=e.wsUrl?.trim()||i?.wsUrl||"",c=e.token?.trim()||i?.token||"",p=e.canReply??i?.canReply??!1,m=e.systemPrompt??(typeof i?.systemPrompt=="string"?i.systemPrompt:"");if(r.enabled&&(!a||!c))throw new Error("WebSocket \u5730\u5740\u548C Token \u5FC5\u586B");const f=s.filter(l=>l.id!==r.id).map(l=>(l.sessionId??l.managerSessionId)===o&&l.type===e.type?{...l,enabled:!1}:l);f.push(r),this.configs.replaceAll(f),(a||c)&&this.secrets.upsert(r.secretRef,{type:"websocket",wsUrl:a,token:c,canReply:p,systemPrompt:m});const d=this.adapters.get(r.type);for(const l of s)(l.sessionId??l.managerSessionId)===o&&l.type===e.type&&l.enabled&&await this.adapters.get(l.type)?.disconnect(l).catch(()=>{});return r.enabled&&d?.connect(r).catch(()=>{}),this.getManagerChannel(o,e.type,{includeSecret:!0})}async upsertManagerWeChatRpaChannel(e){const t=this.configs.get(e.id),s=this.configs.list(),o=e.sessionId||e.managerSessionId,r=w(e.groups);if(e.enabled&&!r.length)throw new Error("WeChat RPA \u81F3\u5C11\u9700\u8981\u914D\u7F6E\u4E00\u4E2A\u7FA4");if(e.enabled&&r.length>1)throw new Error("WeChat RPA \u6BCF\u4E2A\u5BF9\u8BDD\u53EA\u80FD\u7ED1\u5B9A\u4E00\u4E2A\u7FA4");const i={id:e.id,type:"wechat-rpa",name:e.name?.trim()||t?.name||"\u672C\u673A\u5FAE\u4FE1 RPA",sessionId:o,managerSessionId:o,workDir:e.workDir,agentType:e.agentType||t?.agentType,agentSessionId:e.agentSessionId??t?.agentSessionId??null,modelId:e.modelId??t?.modelId??null,enabled:e.enabled,secretRef:t?.secretRef||`channel:${e.id}`},a=this.secrets.get(i.secretRef),c=e.source||(a?.source==="wechat-channel"||a?.source==="macos-probe"||a?.source==="fixture-jsonl"||a?.source==="macos-flow"||a?.source==="windows-visual-flow"||a?.source==="wechat-rpa-lab"?a.source:E());if(e.enabled&&c==="windows-visual-flow")throw new Error("\u65E7 Windows visual flow \u5DF2\u5F52\u6863\uFF1BmacOS \u548C Windows \u6B63\u5F0F\u901A\u9053\u90FD\u4F7F\u7528 wechat-channel");const p=e.privacyConsentAccepted??!!a?.privacyConsentAccepted;if(e.enabled&&!p)throw new Error("\u542F\u7528\u524D\u9700\u8981\u786E\u8BA4\u5FAE\u4FE1\u901A\u9053\u6570\u636E\u4E0E\u9690\u79C1\u6388\u6743");const m=s.filter(d=>d.id!==i.id).map(d=>(d.sessionId??d.managerSessionId)===o&&d.type==="wechat-rpa"?{...d,enabled:!1}:d);m.push(i),this.configs.replaceAll(m),this.secrets.upsert(i.secretRef,{type:"wechat-rpa",source:c,groups:r,pollIntervalMs:R(e.pollIntervalMs,a?.pollIntervalMs),recentLimit:R(e.recentLimit,a?.recentLimit),idleSeconds:R(e.idleSeconds,a?.idleSeconds),forceForeground:e.forceForeground??(a?.forceForeground===void 0?!0:!!a.forceForeground),noRestore:e.noRestore??(a?.noRestore===void 0?!0:!!a.noRestore),downloadAttachments:e.downloadAttachments??(a?.downloadAttachments===void 0?!0:!!a.downloadAttachments),downloadAttachmentsDir:e.downloadAttachmentsDir?.trim()||b(a?.downloadAttachmentsDir),selfNickname:e.selfNickname?.trim()||b(a?.selfNickname),selfTriggerMarker:e.selfTriggerMarker?.trim()||void 0,deferInitialPoll:e.deferInitialPoll===!0?!0:void 0,privacyConsentAccepted:p,flowScriptPath:e.flowScriptPath?.trim()||b(a?.flowScriptPath),canReply:e.canReply??a?.canReply??!1,systemPrompt:e.systemPrompt??(typeof a?.systemPrompt=="string"?a.systemPrompt:"")});const f=this.adapters.get(i.type);for(const d of s)(d.sessionId??d.managerSessionId)===o&&d.type==="wechat-rpa"&&d.enabled&&await this.adapters.get(d.type)?.disconnect(d).catch(()=>{});return i.enabled&&f?.connect(i).catch(()=>{}),this.getManagerChannel(o,"wechat-rpa",{includeSecret:!0})}}function y(n,e){return!e||e.type!==n.type?!1:n.type==="wechat-rpa"?!0:n.type==="websocket"?!!(e.wsUrl&&e.token):n.type==="wecom"?!!(e.token||e.botId||e.secret):!!e.token}function k(n){return!n||n.type!=="wechat-rpa"?{}:{wechatRpaSource:typeof n.source=="string"?n.source:"",wechatRpaGroups:w(Array.isArray(n.groups)?n.groups:[]),pollIntervalMs:Number.isFinite(n.pollIntervalMs)?Number(n.pollIntervalMs):void 0,recentLimit:Number.isFinite(n.recentLimit)?Number(n.recentLimit):void 0,idleSeconds:Number.isFinite(n.idleSeconds)?Number(n.idleSeconds):void 0,forceForeground:n.forceForeground===void 0?!0:!!n.forceForeground,noRestore:n.noRestore===void 0?void 0:!!n.noRestore,downloadAttachments:n.downloadAttachments===void 0?!0:!!n.downloadAttachments,downloadAttachmentsDir:typeof n.downloadAttachmentsDir=="string"?n.downloadAttachmentsDir:"",selfNickname:typeof n.selfNickname=="string"?n.selfNickname:"",wechatRpaPrivacyConsentAccepted:!!n.privacyConsentAccepted,wechatRpaServerDecisionAvailable:!0,wechatRpaPreflightChecks:C(n)}}function h(n){return!n||n.type!=="wechat-rpa"?{}:{wechatRpaSource:typeof n.source=="string"?n.source:null,wechatRpaGroups:w(Array.isArray(n.groups)?n.groups:[]),pollIntervalMs:Number.isFinite(n.pollIntervalMs)?Number(n.pollIntervalMs):null,recentLimit:Number.isFinite(n.recentLimit)?Number(n.recentLimit):null,idleSeconds:Number.isFinite(n.idleSeconds)?Number(n.idleSeconds):null,forceForeground:n.forceForeground===void 0?!0:!!n.forceForeground,noRestore:n.noRestore===void 0?null:!!n.noRestore,downloadAttachments:n.downloadAttachments===void 0?!0:!!n.downloadAttachments,downloadAttachmentsDir:typeof n.downloadAttachmentsDir=="string"?n.downloadAttachmentsDir:null,selfNickname:typeof n.selfNickname=="string"?n.selfNickname:null,wechatRpaPrivacyConsentAccepted:!!n.privacyConsentAccepted,wechatRpaServerDecisionAvailable:!0,wechatRpaPreflightChecks:C(n)}}function w(n){const e=new Set,t=[];for(const s of n){const o=String(s?.name||"").replace(/\s+/g," ").trim();!o||e.has(o)||(e.add(o),t.push({name:o}))}return t}function E(){return"wechat-channel"}function C(n){const e=[];if(n.lastHelperPermissions&&typeof n.lastHelperPermissions=="object"){const t=n.lastHelperPermissions;e.push({code:"wechat_window_unavailable",ok:t.wechatWindowAvailable!==!1,severity:"blocking",message:t.wechatWindowAvailable===!1?"\u5FAE\u4FE1\u7A97\u53E3\u4E0D\u53EF\u7528\uFF0C\u8BF7\u6253\u5F00\u5E76\u767B\u5F55\u5FAE\u4FE1\u540E\u518D\u8BD5\u3002":"\u5FAE\u4FE1\u7A97\u53E3\u53EF\u7528\u3002"})}return e.push({code:"platform_unsupported",ok:n.source!=="windows-visual-flow",severity:"blocking",message:n.source==="windows-visual-flow"?"\u65E7 Windows visual flow \u5DF2\u5F52\u6863\uFF0C\u8BF7\u6539\u7528 wechat-channel\u3002":"\u5F53\u524D\u914D\u7F6E\u4F7F\u7528\u8DE8\u5E73\u53F0\u5FAE\u4FE1\u901A\u9053\u3002"}),e.push({code:"privacy_consent_required",ok:!!n.privacyConsentAccepted,severity:"blocking",message:n.privacyConsentAccepted?"\u5DF2\u786E\u8BA4\u5FAE\u4FE1\u901A\u9053\u6570\u636E\u4E0E\u9690\u79C1\u6388\u6743\u3002":"\u542F\u7528\u524D\u9700\u8981\u786E\u8BA4\u5FAE\u4FE1\u901A\u9053\u6570\u636E\u4E0E\u9690\u79C1\u6388\u6743\u3002"}),e.push({code:"server_decision_unavailable",ok:!0,severity:"blocking",message:"\u670D\u52A1\u7AEF\u5224\u65AD\u80FD\u529B\u53EF\u7528\u3002"}),e}function W(n,e){const t=F(e.text);if(!t.length&&!e.attachment)return[];if(n==="wechat-rpa"&&e.attachment&&t.length<=1)return[{text:t[0]??"",attachment:e.attachment,idempotencyKey:e.idempotencyKey}];const s=t.map((o,r)=>({text:o,attachment:void 0,idempotencyKey:t.length>1&&e.idempotencyKey?`${e.idempotencyKey}:${r+1}`:e.idempotencyKey}));return e.attachment&&s.push({text:"",attachment:e.attachment,idempotencyKey:t.length&&e.idempotencyKey?`${e.idempotencyKey}:attachment`:e.idempotencyKey}),s}function K(...n){const e=new Set,t=[];for(const s of n.flat()){const o=I(s);if(!o){t.push(s);continue}e.has(o)||(e.add(o),t.push(s))}return t}function I(n){const e=S(n.channelId),t=S(n.conversationId),s=S(n.messageId);return!e||!t||!s?null:`${e}
|
|
2
2
|
${t}
|
|
3
|
-
${s}`}function S(n){return typeof n=="string"?n.trim():""}function A(n,e,t){return
|
|
3
|
+
${s}`}function S(n){return typeof n=="string"?n.trim():""}function A(n,e,t){return M.createHash("sha256").update(`${n}
|
|
4
4
|
${e}
|
|
5
5
|
${t}`).digest("hex").slice(0,32)}function P(n){return u.join(n,".shennian","external-reply-idempotency.json")}function T(n){try{const e=JSON.parse(g.readFileSync(P(n),"utf8")),t=Array.isArray(e.completed)?e.completed:[];return new Set(t.map(s=>typeof s=="string"?s:s&&typeof s=="object"&&typeof s.key=="string"?s.key:"").filter(Boolean))}catch{return new Set}}function $(n,e){const t=P(n);g.mkdirSync(u.dirname(t),{recursive:!0});const s=Array.from(e).slice(-500);g.writeFileSync(t,JSON.stringify({updatedAt:new Date().toISOString(),completed:s.map(o=>({key:o}))},null,2)),e.clear();for(const o of s)e.add(o)}function R(n,e){const t=Number(n??e);return Number.isFinite(t)&&t>=0?t:void 0}function b(n){return typeof n=="string"&&n.trim()?n.trim():void 0}export{J as ChannelRuntime,W as planExternalReplySends};
|
|
@@ -6,13 +6,25 @@ export type WeChatAutomationLaneSnapshot = {
|
|
|
6
6
|
active: boolean;
|
|
7
7
|
queued: number;
|
|
8
8
|
lastLabel: string | null;
|
|
9
|
+
lockPath?: string;
|
|
10
|
+
};
|
|
11
|
+
export type WeChatAutomationLaneOptions = {
|
|
12
|
+
lockPath?: string;
|
|
13
|
+
staleMs?: number;
|
|
14
|
+
owner?: string;
|
|
9
15
|
};
|
|
10
16
|
export declare class SerialWeChatAutomationLane implements WeChatAutomationLane {
|
|
11
17
|
private tail;
|
|
12
18
|
private active;
|
|
13
19
|
private queued;
|
|
14
20
|
private lastLabel;
|
|
21
|
+
private readonly lockPath?;
|
|
22
|
+
private readonly staleMs;
|
|
23
|
+
private readonly owner;
|
|
24
|
+
constructor(options?: WeChatAutomationLaneOptions);
|
|
15
25
|
run<T>(label: string, operation: () => Promise<T>): Promise<T>;
|
|
16
26
|
snapshot(): WeChatAutomationLaneSnapshot;
|
|
27
|
+
private acquireLock;
|
|
17
28
|
}
|
|
18
29
|
export declare function createWeChatAutomationLane(): WeChatAutomationLane;
|
|
30
|
+
export declare function defaultWeChatAutomationLockPath(): string;
|
|
@@ -1 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
import r from"node:fs";import l from"node:os";import s from"node:path";import{resolveShennianPath as u}from"../../config/index.js";const h=900*1e3;class f{tail=Promise.resolve();active=!1;queued=0;lastLabel=null;lockPath;staleMs;owner;constructor(t={}){this.lockPath=t.lockPath,this.staleMs=t.staleMs??h,this.owner=t.owner??`${process.platform}:${process.pid}`}async run(t,a){this.queued+=1;const i=this.tail;let o;this.tail=new Promise(c=>{o=c}),await i.catch(()=>{}),this.queued-=1,this.active=!0,this.lastLabel=t;let n=null;try{return n=this.acquireLock(t),await a()}finally{n?.release(),this.active=!1,o()}}snapshot(){return{active:this.active,queued:this.queued,lastLabel:this.lastLabel,lockPath:this.lockPath}}acquireLock(t){if(!this.lockPath)return null;r.mkdirSync(s.dirname(this.lockPath),{recursive:!0});try{r.mkdirSync(this.lockPath)}catch(a){if(!w(a))throw a;const i=d(this.lockPath);if(p(this.lockPath,i,this.staleMs))r.rmSync(this.lockPath,{recursive:!0,force:!0}),r.mkdirSync(this.lockPath);else throw new Error(P(this.lockPath,i))}return k(this.lockPath,{pid:process.pid,label:t,owner:this.owner,hostname:l.hostname(),startedAt:new Date().toISOString()}),{release:()=>{r.rmSync(this.lockPath,{recursive:!0,force:!0})}}}}function b(){return new f({lockPath:m()})}function m(){return u("runtime","wechat-automation.lock")}function k(e,t){r.writeFileSync(s.join(e,"owner.json"),`${JSON.stringify(t,null,2)}
|
|
2
|
+
`,"utf8")}function d(e){try{const t=JSON.parse(r.readFileSync(s.join(e,"owner.json"),"utf8"));return t&&typeof t=="object"?t:null}catch{return null}}function p(e,t,a){if(t?.pid&&y(t.pid))return!1;try{return Date.now()-r.statSync(e).mtimeMs>a}catch{return!0}}function y(e){if(!Number.isInteger(e)||e<=0)return!1;try{return process.kill(e,0),!0}catch{return!1}}function P(e,t){return`wechat_automation_busy: another WeChat automation run is active (${[`lock=${e}`,t?.pid?`pid=${t.pid}`:"",t?.label?`label=${t.label}`:"",t?.startedAt?`startedAt=${t.startedAt}`:"",t?.owner?`owner=${t.owner}`:""].filter(Boolean).join(" ")})`}function w(e){return typeof e=="object"&&e!==null&&"code"in e&&e.code==="EEXIST"}export{f as SerialWeChatAutomationLane,b as createWeChatAutomationLane,m as defaultWeChatAutomationLockPath};
|
|
@@ -84,6 +84,22 @@ export type WeChatChannelClassifyWindowResponse = {
|
|
|
84
84
|
conversationTitle?: string | null;
|
|
85
85
|
isTargetConversation?: boolean;
|
|
86
86
|
confidence?: number;
|
|
87
|
+
layout?: {
|
|
88
|
+
messageInputRect?: {
|
|
89
|
+
x?: number;
|
|
90
|
+
y?: number;
|
|
91
|
+
width?: number;
|
|
92
|
+
height?: number;
|
|
93
|
+
coordinateSpace?: string;
|
|
94
|
+
} | null;
|
|
95
|
+
searchInputRect?: {
|
|
96
|
+
x?: number;
|
|
97
|
+
y?: number;
|
|
98
|
+
width?: number;
|
|
99
|
+
height?: number;
|
|
100
|
+
coordinateSpace?: string;
|
|
101
|
+
} | null;
|
|
102
|
+
} | null;
|
|
87
103
|
reasonCode?: string;
|
|
88
104
|
warnings?: string[];
|
|
89
105
|
usageRefId?: string;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{spawn as w}from"node:child_process";import{randomUUID as R}from"node:crypto";import f from"node:fs";import v from"node:net";import M from"node:os";import d from"node:path";import{createInterface as g}from"node:readline";import{decideWeChatChannelActivityGate as x,normalizeWeChatChannelActivitySnapshot as A}from"./human-coordination.js";import{createWeChatChannelHelperHello as S,extractWeChatChannelHelperWarmupSnapshot as H,timeoutForWeChatChannelHelperCommand as $,validateWeChatChannelHelperReady as k}from"./helper-protocol.js";const q=["en","v"].join(""),F="shennian-wechat-channel-helper.exe",m=1500;class ue{options;child=null;socket=null;lines=null;readyState=null;warmupState=null;stderrTail="";startPromise=null;requestQueueTail=Promise.resolve();pending=new Map;constructor(e){this.options=e}async start(){return this.hasTransport()&&this.readyState?this.readyState:this.startPromise?this.startPromise:(this.hasTransport()&&await this.stop(),this.startPromise=this.startFresh().catch(async e=>{throw await this.stop().catch(()=>{}),e}).finally(()=>{this.startPromise=null}),this.startPromise)}async startFresh(){const e=U(this.options.helperPath,this.options.args??[]);return e.transport==="macos-socket"?this.startDarwinAppSocketFresh(e):this.startStdioFresh(e)}async startStdioFresh(e){const r=w(e.command,e.args,{cwd:this.options.cwd,stdio:["pipe","pipe","pipe"],windowsHide:!0});this.child=r,this.stderrTail="",this.lines=g({input:r.stdout}),this.lines.on("line",i=>this.handleLine(i)),r.stderr.on("data",i=>{this.captureStderr(i)}),r.stdin.on("error",i=>{this.rejectAllPending(i instanceof Error?i:new Error(String(i)))}),r.once("error",i=>{this.child===r&&(this.child=null,this.readyState=null,this.lines?.close(),this.lines=null),this.rejectAllPending(i instanceof Error?i:new Error(String(i)))}),r.once("exit",(i,n)=>{this.child===r&&(this.child=null,this.readyState=null,this.lines?.close(),this.lines=null),this.rejectAllPending(new Error(this.formatHelperExitMessage(i,n)))});const s=new Promise((i,n)=>{const a=setTimeout(()=>n(new Error("WeChat channel helper handshake timed out")),1e4),l=u=>{clearTimeout(a),this.lines?.off("line",c),r.off("exit",o),n(new Error(`WeChat channel helper failed to start: ${u.message}`))},o=(u,h)=>{clearTimeout(a),r.off("error",l),this.lines?.off("line",c),n(new Error(this.formatHelperExitMessage(u,h)))},c=u=>{let h;try{h=JSON.parse(u)}catch{clearTimeout(a),r.off("error",l),this.lines?.off("line",c),n(new Error("WeChat channel helper sent invalid handshake JSON"));return}if(!
|
|
2
|
-
`)}catch(i){throw await this.stop().catch(()=>{}),i}return s}async startDarwinAppSocketFresh(e){const r=await this.connectDarwinHelperSocket(e);this.socket=r,this.stderrTail="",this.lines=g({input:r}),this.lines.on("line",i=>this.handleLine(i)),r.on("error",i=>{this.socket===r&&(this.socket=null,this.readyState=null,this.lines?.close(),this.lines=null),this.rejectAllPending(i instanceof Error?i:new Error(String(i)))}),r.once("close",()=>{this.socket===r&&(this.socket=null,this.readyState=null,this.lines?.close(),this.lines=null),this.rejectAllPending(new Error("WeChat channel helper socket closed"))});const s=new Promise((i,n)=>{const a=setTimeout(()=>n(new Error("WeChat channel helper socket handshake timed out")),1e4),l=u=>{clearTimeout(a),this.lines?.off("line",c),r.off("close",o),n(new Error(`WeChat channel helper socket failed: ${u.message}`))},o=()=>{clearTimeout(a),r.off("error",l),this.lines?.off("line",c),n(new Error("WeChat channel helper socket closed before handshake"))},c=u=>{let h;try{h=JSON.parse(u)}catch{clearTimeout(a),r.off("error",l),r.off("close",o),this.lines?.off("line",c),n(new Error("WeChat channel helper sent invalid socket handshake JSON"));return}if(!
|
|
3
|
-
`;if(this.socket){this.socket.write(r);return}if(this.child){this.child.stdin.write(r);return}throw new Error("WeChat channel helper is not started")}handleLine(e){let r;try{r=JSON.parse(e)}catch{return}if(!X(r))return;const s=this.pending.get(r.id);s&&(clearTimeout(s.timer),this.pending.delete(r.id),s.resolve(r))}captureWarmupSnapshot(e){const r=H(e);r&&(this.warmupState=r)}captureStderr(e){const r=Buffer.isBuffer(e)?e.toString("utf8"):String(e??"");r&&(this.stderrTail=(this.stderrTail+r).slice(-2e3))}formatHelperExitMessage(e,r){const s=[e===null?null:`code=${e}`,r?`signal=${r}`:null,this.stderrTail.trim()?`stderr=${this.stderrTail.trim()}`:null].filter(Boolean);return s.length?`WeChat channel helper process exited (${s.join(", ")})`:"WeChat channel helper process exited"}rejectAllPending(e){for(const[r,s]of this.pending)clearTimeout(s.timer),s.reject(e),this.pending.delete(r)}emitRequestTrace(e){try{this.options.requestLogger?.(e)}catch{}}async stopAfterCommandTimeout(e){await this.stop().catch(()=>{}),process.platform==="win32"&&await D().catch(()=>{}),this.emitRequestTrace({phase:"error",at:new Date().toISOString(),id:`timeout-cleanup:${e}`,command:e,errorSummary:"helper process stopped after command timeout"})}async connectDarwinHelperSocket(e){try{return await _(e.runtimeFile)}catch{f.rmSync(e.runtimeFile,{force:!0})}await W(e,{},this.options.openHelperAppForTest);const r=await T(e.runtimeFile,this.options.macosRuntimeInitialReadyTimeoutMs??5e3);if(r.ok)return r.socket;f.rmSync(e.runtimeFile,{force:!0}),await W(e,{forceNewInstance:!0},this.options.openHelperAppForTest);const s=await T(e.runtimeFile,this.options.macosRuntimeRelaunchReadyTimeoutMs??1e4);if(s.ok)return s.socket;throw new Error(`WeChat channel Helper.app socket did not become ready: ${s.error.message}`)}}async function D(){process.platform==="win32"&&await C(["taskkill","/IM",F,"/T","/F"],[0,128])}function O(t){return t.wechatWindowAvailable!==!1?!1:![...t.captureCandidates??[],...t.restoreCandidates??[],...t.hiddenRestoreCandidates??[]].some(L)}function N(t){return t.windowsVisibleDesktopAvailable===!1||t.rdpVisibleDesktopAvailable===!1||t.desktopSessionVisible===!1||t.screenLocked===!0||t.rdpDisconnected===!0}function I(t){const e=String(t.appName||"").toLowerCase();return e==="wechat"||e==="weixin"}function L(t){return!(!I(t)||t.visible===!1||t.minimized===!0)}function U(t,e,r=process.platform){if(r==="darwin"){const s=G(t);if(s&&e.length===0){const i=V();return{transport:"macos-socket",command:"/usr/bin/open",args:["-g",s,"--args","--socket-runtime",i],appPath:s,runtimeDir:i,runtimeFile:d.join(i,"runtime.json")}}}return{transport:"legacy-stdio",command:t,args:e}}function G(t){const e=d.resolve(t),r=`${d.sep}Contents${d.sep}MacOS${d.sep}`,s=e.lastIndexOf(r);if(s<0)return null;const i=e.slice(0,s);return i.endsWith(".app")?i:null}function V(){const t=j().SHENNIAN_HELPER_RUNTIME_DIR?.trim();return t?d.resolve(t):d.join(M.homedir(),"Library","Application Support","Shennian","Helper")}async function J(t){if(E(t))return;if(process.platform==="win32"&&t.pid){t.kill("SIGTERM"),await C(["taskkill","/PID",String(t.pid),"/T","/F"],[0,128]).catch(()=>{}),await y(t,m);return}t.kill("SIGTERM"),!await y(t,m)&&t.pid&&
|
|
1
|
+
import{spawn as w}from"node:child_process";import{randomUUID as R}from"node:crypto";import f from"node:fs";import v from"node:net";import M from"node:os";import d from"node:path";import{createInterface as g}from"node:readline";import{decideWeChatChannelActivityGate as x,normalizeWeChatChannelActivitySnapshot as A}from"./human-coordination.js";import{createWeChatChannelHelperHello as S,extractWeChatChannelHelperWarmupSnapshot as H,timeoutForWeChatChannelHelperCommand as $,validateWeChatChannelHelperReady as k}from"./helper-protocol.js";const q=["en","v"].join(""),F="shennian-wechat-channel-helper.exe",m=1500;class ue{options;child=null;socket=null;lines=null;readyState=null;warmupState=null;stderrTail="";startPromise=null;requestQueueTail=Promise.resolve();pending=new Map;constructor(e){this.options=e}async start(){return this.hasTransport()&&this.readyState?this.readyState:this.startPromise?this.startPromise:(this.hasTransport()&&await this.stop(),this.startPromise=this.startFresh().catch(async e=>{throw await this.stop().catch(()=>{}),e}).finally(()=>{this.startPromise=null}),this.startPromise)}async startFresh(){const e=U(this.options.helperPath,this.options.args??[]);return e.transport==="macos-socket"?this.startDarwinAppSocketFresh(e):this.startStdioFresh(e)}async startStdioFresh(e){const r=w(e.command,e.args,{cwd:this.options.cwd,stdio:["pipe","pipe","pipe"],windowsHide:!0});this.child=r,this.stderrTail="",this.lines=g({input:r.stdout}),this.lines.on("line",i=>this.handleLine(i)),r.stderr.on("data",i=>{this.captureStderr(i)}),r.stdin.on("error",i=>{this.rejectAllPending(i instanceof Error?i:new Error(String(i)))}),r.once("error",i=>{this.child===r&&(this.child=null,this.readyState=null,this.lines?.close(),this.lines=null),this.rejectAllPending(i instanceof Error?i:new Error(String(i)))}),r.once("exit",(i,n)=>{this.child===r&&(this.child=null,this.readyState=null,this.lines?.close(),this.lines=null),this.rejectAllPending(new Error(this.formatHelperExitMessage(i,n)))});const s=new Promise((i,n)=>{const a=setTimeout(()=>n(new Error("WeChat channel helper handshake timed out")),1e4),l=u=>{clearTimeout(a),this.lines?.off("line",c),r.off("exit",o),n(new Error(`WeChat channel helper failed to start: ${u.message}`))},o=(u,h)=>{clearTimeout(a),r.off("error",l),this.lines?.off("line",c),n(new Error(this.formatHelperExitMessage(u,h)))},c=u=>{let h;try{h=JSON.parse(u)}catch{clearTimeout(a),r.off("error",l),this.lines?.off("line",c),n(new Error("WeChat channel helper sent invalid handshake JSON"));return}if(!P(h))return;clearTimeout(a),r.off("error",l),r.off("exit",o),this.lines?.off("line",c);const p=k(h,this.options.expectedHelperVersion,this.options.requiredCapabilities);p.ok?(this.readyState=h,this.captureWarmupSnapshot(h),i(h)):n(new Error(`${p.errorCode}: ${p.errorSummary}`))};r.once("error",l),r.once("exit",o),this.lines?.on("line",c)});try{r.stdin.write(`${JSON.stringify(S(this.options.expectedHelperVersion,this.options.requiredCapabilities))}
|
|
2
|
+
`)}catch(i){throw await this.stop().catch(()=>{}),i}return s}async startDarwinAppSocketFresh(e){const r=await this.connectDarwinHelperSocket(e);this.socket=r,this.stderrTail="",this.lines=g({input:r}),this.lines.on("line",i=>this.handleLine(i)),r.on("error",i=>{this.socket===r&&(this.socket=null,this.readyState=null,this.lines?.close(),this.lines=null),this.rejectAllPending(i instanceof Error?i:new Error(String(i)))}),r.once("close",()=>{this.socket===r&&(this.socket=null,this.readyState=null,this.lines?.close(),this.lines=null),this.rejectAllPending(new Error("WeChat channel helper socket closed"))});const s=new Promise((i,n)=>{const a=setTimeout(()=>n(new Error("WeChat channel helper socket handshake timed out")),1e4),l=u=>{clearTimeout(a),this.lines?.off("line",c),r.off("close",o),n(new Error(`WeChat channel helper socket failed: ${u.message}`))},o=()=>{clearTimeout(a),r.off("error",l),this.lines?.off("line",c),n(new Error("WeChat channel helper socket closed before handshake"))},c=u=>{let h;try{h=JSON.parse(u)}catch{clearTimeout(a),r.off("error",l),r.off("close",o),this.lines?.off("line",c),n(new Error("WeChat channel helper sent invalid socket handshake JSON"));return}if(!P(h))return;clearTimeout(a),r.off("error",l),r.off("close",o),this.lines?.off("line",c);const p=k(h,this.options.expectedHelperVersion,this.options.requiredCapabilities);p.ok?(this.readyState=h,this.captureWarmupSnapshot(h),i(h)):n(new Error(`${p.errorCode}: ${p.errorSummary}`))};r.once("error",l),r.once("close",o),this.lines?.on("line",c)});try{this.writeFrame(S(this.options.expectedHelperVersion,this.options.requiredCapabilities))}catch(i){throw await this.stop().catch(()=>{}),i}return s}async request(e,r,s){const i=this.requestQueueTail.catch(()=>{}).then(()=>this.sendRequest(e,r,s));return this.requestQueueTail=i.then(()=>{},()=>{}),i}async sendRequest(e,r,s){await this.guardUnsafeWindowsCommand(e,s);try{return await this.sendRawRequest(e,r,s)}finally{await this.cleanupWindowsOverlaysAfterCommand(e,s).catch(()=>{})}}async sendRawRequest(e,r,s){if((!this.hasTransport()||!this.readyState)&&await this.start(),!this.hasTransport())throw new Error("WeChat channel helper is not started");const i=R(),n=$(e),a=Date.now();this.emitRequestTrace({phase:"request",at:new Date(a).toISOString(),id:i,command:e,traceId:s,params:r,timeoutMs:n});const l=new Promise((o,c)=>{const u=setTimeout(()=>{this.pending.get(i)&&(this.pending.delete(i),this.stopAfterCommandTimeout(e).finally(()=>{c(new Error(`helper_command_timeout: ${e}`))}))},n);this.pending.set(i,{resolve:o,reject:c,timer:u})});try{this.writeFrame({id:i,command:e,params:r,traceId:s})}catch(o){const c=this.pending.get(i);throw c&&clearTimeout(c.timer),this.pending.delete(i),this.emitRequestTrace({phase:"error",at:new Date().toISOString(),id:i,command:e,traceId:s,durationMs:Date.now()-a,errorSummary:o instanceof Error?o.message:String(o)}),o}try{const o=await l;return this.emitRequestTrace({phase:"response",at:new Date().toISOString(),id:i,command:e,traceId:s,durationMs:Date.now()-a,ok:o.ok,errorCode:o.errorCode,errorSummary:o.errorSummary,latencyMs:o.latencyMs,result:o.result}),this.captureWarmupSnapshot(o),this.captureWarmupSnapshot(o.result),o}catch(o){throw this.emitRequestTrace({phase:"error",at:new Date().toISOString(),id:i,command:e,traceId:s,durationMs:Date.now()-a,errorSummary:o instanceof Error?o.message:String(o)}),o}}async guardUnsafeWindowsCommand(e,r){if(!this.options.guardUnsafeWindowsCommands||!Z(e))return;const s=await this.sendRawRequest("permissions.check",{},r);if(!s.ok)throw new Error(`${s.errorCode??"permissions_check_failed"}: ${s.errorSummary??e}`);const i=s.result??{},n=await this.requestFirstMissingMacPermissionPrompt(i,r);if(n==="screen-recording")throw new Error("permission_screen_recording_missing");if(n==="accessibility")throw new Error("permission_accessibility_missing");if(n==="input-monitoring")throw new Error("permission_input_monitoring_missing");if(i.automation===!1)throw new Error("permission_automation_missing");if(i.wechatRunning===!1)throw new Error("wechat_not_running");if(O(i))throw new Error("wechat_login_required");if(N(i))throw new Error("windows_visible_desktop_unavailable");if(i.dpiMappingAvailable===!1||i.displayTopologySupported===!1)throw new Error("dpi_mapping_failed");if(i.wechatWindowAvailable===!1)throw new Error("wechat_window_unavailable");if(i.wechatMainWindowResponsive===!1)throw new Error("wechat_window_unresponsive");if(!this.options.skipUserActivityGuard&&te(e)){const a=await this.sendRawRequest("activity.snapshot",{},r);if(!a.ok)throw new Error(`${a.errorCode??"user_activity_unknown"}: ${a.errorSummary??e}`);const l=x({snapshot:A(a.result),stage:"dangerous_action"});if(!l.ok)throw new Error(`user_active:${l.reasonCode}`)}await this.cleanupWindowsOverlays("before",e,r)}async requestFirstMissingMacPermissionPrompt(e,r){const s=K(e);if(!s||process.platform!=="darwin")return s;const i=Y(s);try{await this.sendRawRequest(i,{},r)}catch{}return s}async cleanupWindowsOverlaysAfterCommand(e,r){!this.options.guardUnsafeWindowsCommands||!ee(e)||await this.cleanupWindowsOverlays("after",e,r)}async cleanupWindowsOverlays(e,r,s){if(this.options.cleanupWindowsOverlays===!1)return;if(!this.readyState?.capabilities.includes("overlayCleanup"))throw new Error("helper_capability_missing: overlayCleanup");const i=await this.sendRawRequest("windows.cleanupOverlays",{stage:e,command:r,includeGeneratedMediaPreviews:!0,includeToolingTerminals:!1,waitMs:e==="after"?220:120},s);if(!i.ok)throw new Error(`${i.errorCode??"overlay_cleanup_failed"}: ${i.errorSummary??r}`)}async healthCheck(e){return this.request("health.check",{},e)}getReadyState(){return this.readyState?{...this.readyState,capabilities:[...this.readyState.capabilities]}:null}getWarmupState(){return this.warmupState?{warmState:this.warmupState.warmState,metrics:this.warmupState.metrics?{...this.warmupState.metrics}:void 0}:null}async stop(){const e=this.child,r=this.socket;this.child=null,this.socket=null,this.readyState=null,this.lines?.close(),this.lines=null,this.rejectAllPending(new Error("WeChat channel helper stopped")),r&&!r.destroyed&&r.destroy(),e&&await J(e)}hasTransport(){return!!this.child||!!this.socket}writeFrame(e){const r=`${JSON.stringify(e)}
|
|
3
|
+
`;if(this.socket){this.socket.write(r);return}if(this.child){this.child.stdin.write(r);return}throw new Error("WeChat channel helper is not started")}handleLine(e){let r;try{r=JSON.parse(e)}catch{return}if(!X(r))return;const s=this.pending.get(r.id);s&&(clearTimeout(s.timer),this.pending.delete(r.id),s.resolve(r))}captureWarmupSnapshot(e){const r=H(e);r&&(this.warmupState=r)}captureStderr(e){const r=Buffer.isBuffer(e)?e.toString("utf8"):String(e??"");r&&(this.stderrTail=(this.stderrTail+r).slice(-2e3))}formatHelperExitMessage(e,r){const s=[e===null?null:`code=${e}`,r?`signal=${r}`:null,this.stderrTail.trim()?`stderr=${this.stderrTail.trim()}`:null].filter(Boolean);return s.length?`WeChat channel helper process exited (${s.join(", ")})`:"WeChat channel helper process exited"}rejectAllPending(e){for(const[r,s]of this.pending)clearTimeout(s.timer),s.reject(e),this.pending.delete(r)}emitRequestTrace(e){try{this.options.requestLogger?.(e)}catch{}}async stopAfterCommandTimeout(e){await this.stop().catch(()=>{}),process.platform==="win32"&&await D().catch(()=>{}),this.emitRequestTrace({phase:"error",at:new Date().toISOString(),id:`timeout-cleanup:${e}`,command:e,errorSummary:"helper process stopped after command timeout"})}async connectDarwinHelperSocket(e){try{return await _(e.runtimeFile)}catch{f.rmSync(e.runtimeFile,{force:!0})}await W(e,{},this.options.openHelperAppForTest);const r=await T(e.runtimeFile,this.options.macosRuntimeInitialReadyTimeoutMs??5e3);if(r.ok)return r.socket;f.rmSync(e.runtimeFile,{force:!0}),await W(e,{forceNewInstance:!0},this.options.openHelperAppForTest);const s=await T(e.runtimeFile,this.options.macosRuntimeRelaunchReadyTimeoutMs??1e4);if(s.ok)return s.socket;throw new Error(`WeChat channel Helper.app socket did not become ready: ${s.error.message}`)}}async function D(){process.platform==="win32"&&await C(["taskkill","/IM",F,"/T","/F"],[0,128])}function O(t){return t.wechatWindowAvailable!==!1?!1:![...t.captureCandidates??[],...t.restoreCandidates??[],...t.hiddenRestoreCandidates??[]].some(L)}function N(t){return t.windowsVisibleDesktopAvailable===!1||t.rdpVisibleDesktopAvailable===!1||t.desktopSessionVisible===!1||t.screenLocked===!0||t.rdpDisconnected===!0}function I(t){const e=String(t.appName||"").toLowerCase();return e==="wechat"||e==="weixin"}function L(t){return!(!I(t)||t.visible===!1||t.minimized===!0)}function U(t,e,r=process.platform){if(r==="darwin"){const s=G(t);if(s&&e.length===0){const i=V();return{transport:"macos-socket",command:"/usr/bin/open",args:["-g",s,"--args","--socket-runtime",i],appPath:s,runtimeDir:i,runtimeFile:d.join(i,"runtime.json")}}}return{transport:"legacy-stdio",command:t,args:e}}function G(t){const e=d.resolve(t),r=`${d.sep}Contents${d.sep}MacOS${d.sep}`,s=e.lastIndexOf(r);if(s<0)return null;const i=e.slice(0,s);return i.endsWith(".app")?i:null}function V(){const t=j().SHENNIAN_HELPER_RUNTIME_DIR?.trim();return t?d.resolve(t):d.join(M.homedir(),"Library","Application Support","Shennian","Helper")}async function J(t){if(E(t))return;if(process.platform==="win32"&&t.pid){t.kill("SIGTERM"),await C(["taskkill","/PID",String(t.pid),"/T","/F"],[0,128]).catch(()=>{}),await y(t,m);return}t.kill("SIGTERM"),!await y(t,m)&&t.pid&&b(t.pid)&&(t.kill("SIGKILL"),await y(t,m))}function E(t){return t.exitCode!==null||t.signalCode!==null}function y(t,e){return E(t)?Promise.resolve(!0):new Promise(r=>{const s=setTimeout(()=>{t.off("exit",i),r(!1)},e),i=()=>{clearTimeout(s),r(!0)};t.once("exit",i)})}function C(t,e){return new Promise((r,s)=>{const[i,...n]=t,a=w(i,n,{stdio:"ignore",windowsHide:!0});a.once("error",s),a.once("exit",l=>{l!==null&&e.includes(l)?r():s(new Error(`${i} exited ${l}`))})})}function j(){return globalThis.process?.[q]??{}}function z(t){let e;try{e=JSON.parse(f.readFileSync(t,"utf8"))}catch(i){throw new Error(`runtime_file_unreadable: ${i instanceof Error?i.message:String(i)}`)}if(!e||typeof e!="object")throw new Error("runtime_file_invalid");const r=e.socketPath;if(typeof r!="string"||!r.trim())throw new Error("runtime_socket_missing");const s=e.pid;return{socketPath:r,...typeof s=="number"&&Number.isInteger(s)&&s>0?{pid:s}:{}}}async function _(t){const{socketPath:e,pid:r}=z(t);if(r&&!b(r))throw new Error(`runtime_process_stale: ${r}`);return new Promise((s,i)=>{const n=v.createConnection(e),a=setTimeout(()=>{n.destroy(),i(new Error(`runtime_socket_connect_timeout: ${e}`))},1e3);n.once("connect",()=>{clearTimeout(a),n.off("error",l),s(n)});const l=o=>{clearTimeout(a),n.destroy(),i(o)};n.once("error",l)})}async function T(t,e){const r=Date.now()+e;let s=null;for(;Date.now()<r;)try{return{ok:!0,socket:await _(t)}}catch(i){s=i instanceof Error?i:new Error(String(i));const n=r-Date.now();n>0&&await Q(Math.min(150,n))}return{ok:!1,error:s??new Error("runtime file missing")}}async function W(t,e={},r=B){f.mkdirSync(t.runtimeDir,{recursive:!0,mode:448});const s=e.forceNewInstance?["-n",...t.args]:t.args;try{await r(t.command,s)}catch(i){if(!(i instanceof Error?i.message:String(i)).includes("-1712"))throw i;await r(t.command,["-n",...t.args])}}function b(t){try{return process.kill(t,0),!0}catch{return!1}}async function B(t,e){await new Promise((r,s)=>{const i=w(t,e,{stdio:["ignore","ignore","pipe"],windowsHide:!0});let n="";i.stderr.on("data",a=>{n=(n+String(a)).slice(-2e3)}),i.once("error",s),i.once("exit",(a,l)=>{a===0?r():s(new Error(`open Helper.app failed (${l?`signal=${l}`:`code=${a}`}${n.trim()?`, stderr=${n.trim()}`:""})`))})})}function Q(t){return new Promise(e=>setTimeout(e,t))}function K(t){return t.screenRecording===!1?"screen-recording":t.accessibility===!1?"accessibility":t.inputMonitoring===!1?"input-monitoring":null}function Y(t){return t==="screen-recording"?"permissions.requestScreenRecording":t==="accessibility"?"permissions.requestAccessibility":"permissions.requestInputMonitoring"}function P(t){if(!t||typeof t!="object")return!1;const e=t;return e.type==="ready"&&typeof e.helperVersion=="string"&&typeof e.protocolVersion=="number"&&Array.isArray(e.capabilities)&&typeof e.pid=="number"}function X(t){if(!t||typeof t!="object")return!1;const e=t;return typeof e.id=="string"&&typeof e.ok=="boolean"&&typeof e.latencyMs=="number"}function Z(t){return t==="windows.focus"||t==="windows.capture"||t==="screen.capture"||t==="ocr.recognize"||t==="image.cropHash"||t==="wechat.searchConversation"||t==="wechat.focusMessageInput"||t==="wechat.pasteAndSubmit"||t==="keyboard.primeTextPaste"||t.startsWith("mouse.")||t.startsWith("keyboard.")||t.startsWith("clipboard.")||t==="menu.pickItem"||t==="savePanel.saveToPath"}function ee(t){return t.startsWith("mouse.")||t.startsWith("keyboard.")||t.startsWith("clipboard.")||t==="menu.pickItem"||t==="savePanel.saveToPath"}function te(t){return t==="windows.focus"||t==="wechat.searchConversation"||t==="wechat.focusMessageInput"||t==="wechat.pasteAndSubmit"||t==="keyboard.primeTextPaste"||t.startsWith("mouse.")||t.startsWith("keyboard.")||t.startsWith("clipboard.set")||t==="clipboard.restore"||t==="menu.pickItem"||t==="savePanel.saveToPath"}export{ue as WeChatChannelHelperClient,U as helperLaunchSpec,D as stopWindowsWeChatChannelHelperProcesses};
|
|
@@ -90,7 +90,7 @@ export type WeChatChannelHumanActivitySnapshot = {
|
|
|
90
90
|
capturesMousePath?: boolean;
|
|
91
91
|
};
|
|
92
92
|
};
|
|
93
|
-
export type WeChatChannelHelperCommandName = 'health.check' | 'processes.list' | 'permissions.check' | 'permissions.requestScreenRecording' | 'permissions.requestAccessibility' | 'permissions.requestInputMonitoring' | 'activity.snapshot' | 'automation.lease.acquire' | 'automation.lease.release' | 'automation.lease.status' | 'automation.lease.simulateInterruption' | 'windows.ensureReady' | 'windows.list' | 'windows.enumerateRaw' | 'windows.focus' | 'windows.capture' | 'windows.captureAndOcr' | 'windows.cleanupOverlays' | 'windows.recoverWeChat' | 'screen.capture' | 'ocr.recognize' | 'mouse.click' | 'mouse.rightClick' | 'mouse.scroll' | 'keyboard.type' | 'keyboard.shortcut' | '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
|
+
export type WeChatChannelHelperCommandName = 'health.check' | 'processes.list' | 'permissions.check' | 'permissions.requestScreenRecording' | 'permissions.requestAccessibility' | 'permissions.requestInputMonitoring' | 'activity.snapshot' | 'automation.lease.acquire' | 'automation.lease.release' | 'automation.lease.status' | 'automation.lease.simulateInterruption' | 'windows.ensureReady' | 'windows.list' | 'windows.enumerateRaw' | 'windows.focus' | 'windows.capture' | 'windows.captureAndOcr' | 'windows.cleanupOverlays' | 'windows.recoverWeChat' | 'screen.capture' | 'ocr.recognize' | 'mouse.click' | 'mouse.rightClick' | 'mouse.scroll' | 'keyboard.type' | 'keyboard.shortcut' | 'keyboard.primeTextPaste' | 'clipboard.snapshot' | 'clipboard.restore' | 'clipboard.setText' | 'clipboard.setFiles' | 'clipboard.setImage' | 'clipboard.readFileUrls' | 'clipboard.readAttachment' | 'menu.pickItem' | 'savePanel.saveToPath' | 'image.cropHash' | 'wechat.searchConversation' | 'wechat.focusMessageInput' | 'wechat.pasteAndSubmit';
|
|
94
94
|
export type WeChatChannelHelperCommand = {
|
|
95
95
|
id: string;
|
|
96
96
|
command: WeChatChannelHelperCommandName;
|
|
@@ -126,6 +126,12 @@ export declare function selectWeChatWindowForBinding(options: HelperBackedObserv
|
|
|
126
126
|
foregroundMode?: WeChatChannelForegroundMode;
|
|
127
127
|
forceVision?: boolean;
|
|
128
128
|
}): Promise<WeChatChannelWindowInfo>;
|
|
129
|
+
export declare function detectWeChatMessageInputPointByServerVision(input: HelperBackedObserveOptions & {
|
|
130
|
+
window: WeChatChannelWindowInfo;
|
|
131
|
+
}): Promise<WeChatChannelScreenPoint>;
|
|
132
|
+
export declare function detectWeChatSearchInputPointByServerVision(input: HelperBackedObserveOptions & {
|
|
133
|
+
window: WeChatChannelWindowInfo;
|
|
134
|
+
}): Promise<WeChatChannelScreenPoint>;
|
|
129
135
|
export declare function ensureHelperPreflight(helper: WeChatChannelHelperTransport, traceId?: string): Promise<void>;
|
|
130
136
|
export declare function ensureHelperPermissionPreflight(helper: WeChatChannelHelperTransport, traceId?: string): Promise<void>;
|
|
131
137
|
export declare function ensureWeChatWindowReady(helper: WeChatChannelHelperTransport, traceId?: string, options?: {
|
|
@@ -149,6 +155,9 @@ export declare function openConversationBySearch(input: {
|
|
|
149
155
|
helper: WeChatChannelHelperTransport;
|
|
150
156
|
window: WeChatChannelWindowInfo;
|
|
151
157
|
binding: WeChatChannelBindingConfig;
|
|
158
|
+
runtime?: WeChatChannelRuntime;
|
|
159
|
+
api?: WeChatChannelObserveApi;
|
|
160
|
+
workDir?: string;
|
|
152
161
|
traceId?: string;
|
|
153
162
|
searchDelayMs?: number;
|
|
154
163
|
settleToBottom?: boolean;
|
|
@@ -156,7 +165,6 @@ export declare function openConversationBySearch(input: {
|
|
|
156
165
|
opened: boolean;
|
|
157
166
|
reason: string;
|
|
158
167
|
}>;
|
|
159
|
-
export declare function fallbackWeChatMessageInputPointForWindow(window: WeChatChannelWindowInfo): WeChatChannelScreenPoint | null;
|
|
160
168
|
export declare function captureWeChatWindow(helper: WeChatChannelHelperTransport, windowId: string, traceId?: string, bounds?: WeChatChannelWindowInfo['bounds']): Promise<WeChatChannelScreenshotWithData>;
|
|
161
169
|
export declare function captureAndRecognizeWeChatWindow(helper: WeChatChannelHelperTransport, windowId: string, traceId?: string, bounds?: WeChatChannelWindowInfo['bounds']): Promise<WeChatChannelWindowObservation>;
|
|
162
170
|
export declare function recognizeWeChatScreenshot(helper: WeChatChannelHelperTransport, screenshot: WeChatChannelScreenshotWithData, traceId?: string): Promise<WeChatChannelOcrResult>;
|