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