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.
- package/dist/publish-build-manifest.json +70 -25
- package/dist/src/channels/runtime.d.ts +5 -1
- package/dist/src/channels/runtime.js +3 -3
- package/dist/src/channels/wechat-channel/helper-assets.d.ts +59 -1
- package/dist/src/channels/wechat-channel/helper-assets.js +1 -1
- package/dist/src/channels/wechat-channel/helper-client.js +2 -2
- package/dist/src/channels/wechat-channel/helper-protocol.d.ts +1 -1
- package/dist/src/channels/wechat-channel/observer.d.ts +32 -2
- package/dist/src/channels/wechat-channel/observer.js +6 -6
- package/dist/src/channels/wechat-channel/preflight.d.ts +7 -0
- package/dist/src/channels/wechat-channel/preflight.js +1 -1
- package/dist/src/channels/wechat-channel/runner.d.ts +0 -1
- package/dist/src/channels/wechat-channel/runner.js +1 -1
- package/dist/src/channels/wechat-channel/scheduler.d.ts +4 -3
- package/dist/src/channels/wechat-channel/scheduler.js +1 -1
- package/dist/src/channels/wechat-rpa.d.ts +5 -1
- package/dist/src/channels/wechat-rpa.js +4 -4
- package/dist/src/commands/runtime.d.ts +89 -0
- package/dist/src/commands/runtime.js +1 -0
- package/dist/src/commands/wechat/command.d.ts +2 -0
- package/dist/src/commands/wechat/command.js +1 -0
- package/dist/src/commands/wechat/direct.d.ts +14 -0
- package/dist/src/commands/wechat/direct.js +2 -0
- package/dist/src/commands/wechat/doctor.d.ts +2 -0
- package/dist/src/commands/wechat/doctor.js +1 -0
- package/dist/src/commands/wechat/ipc.d.ts +9 -0
- package/dist/src/commands/wechat/ipc.js +1 -0
- package/dist/src/commands/wechat/operations.d.ts +3 -0
- package/dist/src/commands/wechat/operations.js +4 -0
- package/dist/src/commands/wechat/output.d.ts +7 -0
- package/dist/src/commands/wechat/output.js +4 -0
- package/dist/src/commands/wechat/types.d.ts +141 -0
- package/dist/src/commands/wechat/types.js +1 -0
- package/dist/src/commands/wechat/utils.d.ts +14 -0
- package/dist/src/commands/wechat/utils.js +1 -0
- package/dist/src/commands/wechat.d.ts +6 -127
- package/dist/src/commands/wechat.js +1 -7
- package/dist/src/devtools/wechat-channel-action-smoke.d.ts +2 -0
- package/dist/src/devtools/wechat-channel-action-smoke.js +8 -8
- package/dist/src/index.js +2 -2
- package/dist/src/manager/runtime.d.ts +3 -1
- package/dist/src/manager/runtime.js +5 -5
- package/package.json +1 -1
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"jsMinified": true,
|
|
4
4
|
"minifier": "esbuild",
|
|
5
|
-
"fileCount":
|
|
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":
|
|
165
|
-
"afterBytes":
|
|
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":
|
|
220
|
-
"afterBytes":
|
|
219
|
+
"beforeBytes": 17196,
|
|
220
|
+
"afterBytes": 9141
|
|
221
221
|
},
|
|
222
222
|
{
|
|
223
223
|
"file": "src/channels/wechat-channel/helper-client.js",
|
|
224
|
-
"beforeBytes":
|
|
225
|
-
"afterBytes":
|
|
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":
|
|
270
|
-
"afterBytes":
|
|
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":
|
|
290
|
-
"afterBytes":
|
|
289
|
+
"beforeBytes": 5271,
|
|
290
|
+
"afterBytes": 4135
|
|
291
291
|
},
|
|
292
292
|
{
|
|
293
293
|
"file": "src/channels/wechat-channel/runner.js",
|
|
294
|
-
"beforeBytes":
|
|
295
|
-
"afterBytes":
|
|
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":
|
|
305
|
-
"afterBytes":
|
|
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":
|
|
335
|
-
"afterBytes":
|
|
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":
|
|
395
|
-
"afterBytes":
|
|
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":
|
|
410
|
-
"afterBytes":
|
|
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":
|
|
455
|
-
"afterBytes":
|
|
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":
|
|
475
|
-
"afterBytes":
|
|
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
|
|
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
|
|
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' | '
|
|
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
|
|
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
|
|
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||!
|
|
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
|
|
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>;
|