shennian 0.2.105 → 0.2.107
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 +37 -37
- package/dist/src/channels/runtime.d.ts +2 -0
- package/dist/src/channels/runtime.js +3 -3
- package/dist/src/channels/wechat-channel/automation-lane.d.ts +18 -0
- package/dist/src/channels/wechat-channel/automation-lane.js +1 -0
- package/dist/src/channels/wechat-channel/client.d.ts +31 -0
- package/dist/src/channels/wechat-channel/client.js +1 -1
- package/dist/src/channels/wechat-channel/helper-client.d.ts +23 -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/observer.d.ts +7 -1
- package/dist/src/channels/wechat-channel/observer.js +6 -6
- package/dist/src/channels/wechat-channel/outbound-sender.d.ts +1 -0
- package/dist/src/channels/wechat-channel/outbound-sender.js +1 -1
- package/dist/src/channels/wechat-channel/preflight.d.ts +0 -3
- package/dist/src/channels/wechat-channel/preflight.js +1 -1
- package/dist/src/channels/wechat-channel/runner.d.ts +4 -3
- package/dist/src/channels/wechat-channel/runner.js +1 -1
- package/dist/src/channels/wechat-channel/runtime.d.ts +2 -0
- package/dist/src/channels/wechat-channel/runtime.js +1 -1
- package/dist/src/channels/wechat-channel/scheduler.d.ts +5 -1
- package/dist/src/channels/wechat-channel/scheduler.js +1 -1
- package/dist/src/channels/wechat-rpa/product-channel.d.ts +4 -1
- package/dist/src/channels/wechat-rpa/product-channel.js +6 -5
- package/dist/src/channels/wechat-rpa.d.ts +2 -0
- package/dist/src/channels/wechat-rpa.js +4 -4
- package/dist/src/commands/wechat/ipc.d.ts +1 -1
- package/dist/src/commands/wechat/ipc.js +1 -1
- package/dist/src/commands/wechat/output.js +3 -3
- package/dist/src/commands/wechat/types.d.ts +1 -0
- package/dist/src/devtools/wechat-channel-action-smoke.js +8 -8
- package/dist/src/manager/runtime.d.ts +3 -0
- package/dist/src/manager/runtime.js +10 -10
- package/node_modules/@shennian/wire/dist/machine.d.ts +3 -0
- package/node_modules/@shennian/wire/dist/machine.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/src/channels/wechat-channel/macos-helper-app-bridge.d.ts +0 -1
- package/dist/src/channels/wechat-channel/macos-helper-app-bridge.js +0 -2
|
@@ -161,8 +161,8 @@
|
|
|
161
161
|
},
|
|
162
162
|
{
|
|
163
163
|
"file": "src/channels/runtime.js",
|
|
164
|
-
"beforeBytes":
|
|
165
|
-
"afterBytes":
|
|
164
|
+
"beforeBytes": 29842,
|
|
165
|
+
"afterBytes": 16331
|
|
166
166
|
},
|
|
167
167
|
{
|
|
168
168
|
"file": "src/channels/secret-registry.js",
|
|
@@ -179,10 +179,15 @@
|
|
|
179
179
|
"beforeBytes": 2405,
|
|
180
180
|
"afterBytes": 1090
|
|
181
181
|
},
|
|
182
|
+
{
|
|
183
|
+
"file": "src/channels/wechat-channel/automation-lane.js",
|
|
184
|
+
"beforeBytes": 990,
|
|
185
|
+
"afterBytes": 451
|
|
186
|
+
},
|
|
182
187
|
{
|
|
183
188
|
"file": "src/channels/wechat-channel/client.js",
|
|
184
|
-
"beforeBytes":
|
|
185
|
-
"afterBytes":
|
|
189
|
+
"beforeBytes": 6545,
|
|
190
|
+
"afterBytes": 3682
|
|
186
191
|
},
|
|
187
192
|
{
|
|
188
193
|
"file": "src/channels/wechat-channel/cooldown.js",
|
|
@@ -221,13 +226,13 @@
|
|
|
221
226
|
},
|
|
222
227
|
{
|
|
223
228
|
"file": "src/channels/wechat-channel/helper-client.js",
|
|
224
|
-
"beforeBytes":
|
|
225
|
-
"afterBytes":
|
|
229
|
+
"beforeBytes": 27641,
|
|
230
|
+
"afterBytes": 14420
|
|
226
231
|
},
|
|
227
232
|
{
|
|
228
233
|
"file": "src/channels/wechat-channel/helper-protocol.js",
|
|
229
|
-
"beforeBytes":
|
|
230
|
-
"afterBytes":
|
|
234
|
+
"beforeBytes": 8198,
|
|
235
|
+
"afterBytes": 3837
|
|
231
236
|
},
|
|
232
237
|
{
|
|
233
238
|
"file": "src/channels/wechat-channel/human-coordination.js",
|
|
@@ -244,11 +249,6 @@
|
|
|
244
249
|
"beforeBytes": 12414,
|
|
245
250
|
"afterBytes": 5534
|
|
246
251
|
},
|
|
247
|
-
{
|
|
248
|
-
"file": "src/channels/wechat-channel/macos-helper-app-bridge.js",
|
|
249
|
-
"beforeBytes": 2249,
|
|
250
|
-
"afterBytes": 1288
|
|
251
|
-
},
|
|
252
252
|
{
|
|
253
253
|
"file": "src/channels/wechat-channel/media-cache-resolver.js",
|
|
254
254
|
"beforeBytes": 9983,
|
|
@@ -266,8 +266,8 @@
|
|
|
266
266
|
},
|
|
267
267
|
{
|
|
268
268
|
"file": "src/channels/wechat-channel/observer.js",
|
|
269
|
-
"beforeBytes":
|
|
270
|
-
"afterBytes":
|
|
269
|
+
"beforeBytes": 91619,
|
|
270
|
+
"afterBytes": 42490
|
|
271
271
|
},
|
|
272
272
|
{
|
|
273
273
|
"file": "src/channels/wechat-channel/outbound-ledger.js",
|
|
@@ -276,8 +276,8 @@
|
|
|
276
276
|
},
|
|
277
277
|
{
|
|
278
278
|
"file": "src/channels/wechat-channel/outbound-sender.js",
|
|
279
|
-
"beforeBytes":
|
|
280
|
-
"afterBytes":
|
|
279
|
+
"beforeBytes": 17978,
|
|
280
|
+
"afterBytes": 9108
|
|
281
281
|
},
|
|
282
282
|
{
|
|
283
283
|
"file": "src/channels/wechat-channel/pacing.js",
|
|
@@ -286,23 +286,23 @@
|
|
|
286
286
|
},
|
|
287
287
|
{
|
|
288
288
|
"file": "src/channels/wechat-channel/preflight.js",
|
|
289
|
-
"beforeBytes":
|
|
290
|
-
"afterBytes":
|
|
289
|
+
"beforeBytes": 4888,
|
|
290
|
+
"afterBytes": 3736
|
|
291
291
|
},
|
|
292
292
|
{
|
|
293
293
|
"file": "src/channels/wechat-channel/runner.js",
|
|
294
|
-
"beforeBytes":
|
|
295
|
-
"afterBytes":
|
|
294
|
+
"beforeBytes": 14053,
|
|
295
|
+
"afterBytes": 7073
|
|
296
296
|
},
|
|
297
297
|
{
|
|
298
298
|
"file": "src/channels/wechat-channel/runtime.js",
|
|
299
|
-
"beforeBytes":
|
|
300
|
-
"afterBytes":
|
|
299
|
+
"beforeBytes": 3559,
|
|
300
|
+
"afterBytes": 1666
|
|
301
301
|
},
|
|
302
302
|
{
|
|
303
303
|
"file": "src/channels/wechat-channel/scheduler.js",
|
|
304
|
-
"beforeBytes":
|
|
305
|
-
"afterBytes":
|
|
304
|
+
"beforeBytes": 22489,
|
|
305
|
+
"afterBytes": 10203
|
|
306
306
|
},
|
|
307
307
|
{
|
|
308
308
|
"file": "src/channels/wechat-channel/vector-store.js",
|
|
@@ -326,13 +326,13 @@
|
|
|
326
326
|
},
|
|
327
327
|
{
|
|
328
328
|
"file": "src/channels/wechat-rpa/product-channel.js",
|
|
329
|
-
"beforeBytes":
|
|
330
|
-
"afterBytes":
|
|
329
|
+
"beforeBytes": 17884,
|
|
330
|
+
"afterBytes": 9598
|
|
331
331
|
},
|
|
332
332
|
{
|
|
333
333
|
"file": "src/channels/wechat-rpa.js",
|
|
334
|
-
"beforeBytes":
|
|
335
|
-
"afterBytes":
|
|
334
|
+
"beforeBytes": 55361,
|
|
335
|
+
"afterBytes": 29024
|
|
336
336
|
},
|
|
337
337
|
{
|
|
338
338
|
"file": "src/channels/wecom.js",
|
|
@@ -411,8 +411,8 @@
|
|
|
411
411
|
},
|
|
412
412
|
{
|
|
413
413
|
"file": "src/commands/wechat/ipc.js",
|
|
414
|
-
"beforeBytes":
|
|
415
|
-
"afterBytes":
|
|
414
|
+
"beforeBytes": 6350,
|
|
415
|
+
"afterBytes": 3643
|
|
416
416
|
},
|
|
417
417
|
{
|
|
418
418
|
"file": "src/commands/wechat/operations.js",
|
|
@@ -421,8 +421,8 @@
|
|
|
421
421
|
},
|
|
422
422
|
{
|
|
423
423
|
"file": "src/commands/wechat/output.js",
|
|
424
|
-
"beforeBytes":
|
|
425
|
-
"afterBytes":
|
|
424
|
+
"beforeBytes": 13578,
|
|
425
|
+
"afterBytes": 8410
|
|
426
426
|
},
|
|
427
427
|
{
|
|
428
428
|
"file": "src/commands/wechat/types.js",
|
|
@@ -451,8 +451,8 @@
|
|
|
451
451
|
},
|
|
452
452
|
{
|
|
453
453
|
"file": "src/devtools/wechat-channel-action-smoke.js",
|
|
454
|
-
"beforeBytes":
|
|
455
|
-
"afterBytes":
|
|
454
|
+
"beforeBytes": 70042,
|
|
455
|
+
"afterBytes": 36838
|
|
456
456
|
},
|
|
457
457
|
{
|
|
458
458
|
"file": "src/devtools/wechat-channel-download-report.js",
|
|
@@ -516,8 +516,8 @@
|
|
|
516
516
|
},
|
|
517
517
|
{
|
|
518
518
|
"file": "src/manager/runtime.js",
|
|
519
|
-
"beforeBytes":
|
|
520
|
-
"afterBytes":
|
|
519
|
+
"beforeBytes": 59678,
|
|
520
|
+
"afterBytes": 30593
|
|
521
521
|
},
|
|
522
522
|
{
|
|
523
523
|
"file": "src/native-fusion/config.js",
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type { ExternalChannelConfig, ExternalChannelView, ExternalMessageEvent, ExternalReply } from './base.js';
|
|
2
2
|
import type { ExternalChannelSessionStatus } from '@shennian/wire';
|
|
3
3
|
import type { WeChatRpaProductRunnerFactory } from './wechat-rpa/product-channel.js';
|
|
4
|
+
import type { WeChatAutomationLane } from './wechat-channel/automation-lane.js';
|
|
4
5
|
export type ChannelRuntimeOptions = {
|
|
5
6
|
createWeChatRpaProductRunner?: WeChatRpaProductRunnerFactory;
|
|
7
|
+
weChatAutomationLane?: WeChatAutomationLane;
|
|
6
8
|
};
|
|
7
9
|
export declare class ChannelRuntime {
|
|
8
10
|
private onExternalMessage;
|
|
@@ -1,5 +1,5 @@
|
|
|
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}
|
|
1
|
+
import v from"node:crypto";import g from"node:fs";import u from"node:path";import{ChannelConfigRegistry as M}from"./registry.js";import{ChannelSecretRegistry as N}from"./secret-registry.js";import{WeComChannelAdapter as D}from"./wecom.js";import{WeChatRpaChannelAdapter as B}from"./wechat-rpa.js";import{ExternalWebSocketChannelAdapter as x}from"./websocket.js";import{splitExternalReplyText as F}from"./reply-split.js";class J{onExternalMessage;createReplyTarget;configs=new M;secrets=new N;adapters=new Map;completedReplyKeys=new Map;recentMessages=new Map;constructor(e,t,s={}){this.onExternalMessage=e,this.createReplyTarget=t;const o=new D(a=>this.ingest({...a,type:"external.message"}));this.adapters.set(o.type,o);const r=new x(a=>this.ingest({...a,type:"external.message"}));this.adapters.set(r.type,r);const i=new B(a=>this.ingest(a),{createProductRunner:s.createWeChatRpaProductRunner,automationLane:s.weChatAutomationLane});this.adapters.set(i.type,i)}async start(){for(const e of this.configs.list().filter(t=>t.enabled))await this.adapters.get(e.type)?.connect(e).catch(()=>{})}async stop(){for(const e of this.configs.list())await this.adapters.get(e.type)?.disconnect(e).catch(()=>{})}ingest(e){const t=this.findRecentMessageDuplicate(e);if(t)return t;const s=this.configs.get(e.channelId),o=e.managerSessionId??s?.sessionId??s?.managerSessionId;if(!o)throw new Error(`No session bound for channel ${e.channelId}`);const r=e.replyTarget||this.createReplyTarget({managerSessionId:o,channelId:e.channelId,conversationId:e.conversationId,messageId:e.messageId}),i={...e,replyTarget:r};return this.onExternalMessage(o,i),this.recordRecentMessage(i),i}async reply(e){const t=this.configs.get(e.channelId);if(!t)return{ok:!1,error:`Unknown channel: ${e.channelId}`};if((t.sessionId??t.managerSessionId)!==e.managerSessionId)return{ok:!1,error:"Channel is not bound to this session"};const s=this.adapters.get(t.type);if(!s)return{ok:!1,error:`Unsupported channel type: ${t.type}`};try{const o=W(t.type,e);if(!o.length)return{ok:!1,error:"Reply text or attachment is required"};let r=!1;for(const i of o){const a=i.idempotencyKey;if(a&&this.isReplyCompleted(t,e.conversationId,a))continue;const c=await s.send(t,{...e,...i});if(c?.status==="queued"){r=!0;continue}if(c?.status==="manual-review")return{ok:!1,error:c.reason||"Reply requires manual review before retry"};a&&this.markReplyCompleted(t,e.conversationId,a)}return r?{ok:!0,pending:!0}:{ok:!0}}catch(o){return{ok:!1,error:o instanceof Error?o.message:String(o)}}}isReplyCompleted(e,t,s){return this.loadReplyCompletionSet(e).has(A(e.id,t,s))}markReplyCompleted(e,t,s){const o=this.loadReplyCompletionSet(e);o.add(A(e.id,t,s));try{$(e.workDir,o)}catch{}}loadReplyCompletionSet(e){const t=u.resolve(e.workDir),s=this.completedReplyKeys.get(t);if(s)return s;const o=T(e.workDir);return this.completedReplyKeys.set(t,o),o}async getDefaultReplyTarget(e){const t=this.configs.list().find(r=>(r.sessionId??r.managerSessionId)===e&&r.enabled);if(!t)throw new Error("No enabled external channel is bound to this session");const s=this.adapters.get(t.type);if(!s?.defaultConversation)throw new Error(`External channel ${t.type} has no default conversation`);const o=await s.defaultConversation(t);return{channelId:t.id,conversationId:o.conversationId}}getManagerChannel(e,t,s={}){const o=this.configs.list().filter(c=>(c.sessionId??c.managerSessionId)===e&&c.type===t),r=o.find(c=>c.enabled)??o.at(-1);if(!r)return null;const i=this.secrets.get(r.secretRef),a=this.adapters.get(r.type)?.runtimeStatus?.(r)??{};return{id:r.id,type:r.type,name:r.name,sessionId:r.sessionId??r.managerSessionId,managerSessionId:r.managerSessionId,workDir:r.workDir,agentType:r.agentType,agentSessionId:r.agentSessionId,modelId:r.modelId,enabled:r.enabled,wsUrl:i?.wsUrl??"",token:s.includeSecret?i?.token??"":"",tokenConfigured:!!i?.token,canReply:!!i?.canReply,systemPrompt:typeof i?.systemPrompt=="string"?i.systemPrompt:"",...C(i),...a}}getChannelById(e,t={}){const s=this.configs.get(e);if(!s)return null;const o=this.secrets.get(s.secretRef),r=this.adapters.get(s.type)?.runtimeStatus?.(s)??{};return{id:s.id,type:s.type,name:s.name,sessionId:s.sessionId??s.managerSessionId,managerSessionId:s.managerSessionId,workDir:s.workDir,agentType:s.agentType,agentSessionId:s.agentSessionId,modelId:s.modelId,enabled:s.enabled,wsUrl:o?.wsUrl??"",token:t.includeSecret?o?.token??"":"",tokenConfigured:!!o?.token,canReply:!!o?.canReply,systemPrompt:typeof o?.systemPrompt=="string"?o.systemPrompt:"",...C(o),...r}}getChannelStatusById(e){const t=this.configs.get(e);if(!t)return null;const s=this.secrets.get(t.secretRef),o=this.adapters.get(t.type)?.runtimeStatus?.(t)??{};return{configured:!0,connected:y(t,s),type:t.type,channelId:t.id,name:t.name,canReply:!!s?.canReply,systemPrompt:typeof s?.systemPrompt=="string"?s.systemPrompt:"",...h(s),...o}}getManagerChannelStatus(e){const t=this.configs.list().find(r=>(r.sessionId??r.managerSessionId)===e&&r.enabled);if(!t)return null;const s=this.secrets.get(t.secretRef),o=this.adapters.get(t.type)?.runtimeStatus?.(t)??{};return{configured:!0,connected:y(t,s),type:t.type,channelId:t.id,name:t.name,canReply:!!s?.canReply,systemPrompt:typeof s?.systemPrompt=="string"?s.systemPrompt:"",...h(s),...o}}listManagerChannelStatuses(){return this.configs.list().filter(e=>e.enabled).map(e=>({managerSessionId:e.sessionId??e.managerSessionId,status:this.getManagerChannelStatus(e.sessionId??e.managerSessionId)})).filter(e=>!!e.status)}listManagerExternalChannels(e){return this.configs.list().filter(t=>t.enabled&&(t.sessionId??t.managerSessionId)===e).map(t=>{const s=this.secrets.get(t.secretRef),o=this.adapters.get(t.type)?.runtimeStatus?.(t)??{};return{configured:!0,connected:y(t,s),type:t.type,channelId:t.id,name:t.name,canReply:!!s?.canReply,systemPrompt:typeof s?.systemPrompt=="string"?s.systemPrompt:"",...h(s),...o}})}async syncManagerWeChatRpaChannel(e){const t=this.configs.list().find(i=>i.enabled&&i.type==="wechat-rpa"&&(i.sessionId??i.managerSessionId)===e);if(!t)throw new Error("No enabled WeChat RPA channel is bound to this session");const s=this.adapters.get(t.type);if(!s?.syncNow)throw new Error("WeChat RPA channel does not support manual sync");const o=Date.now()-120*1e3,r=K(await s.syncNow(t)??[],this.getRecentMessages(t.id,o));return{channel:this.getManagerChannel(e,"wechat-rpa",{includeSecret:!0}),messages:r}}async cancelManagerWeChatRpaOutbound(e){const t=this.configs.list().find(r=>r.enabled&&r.type==="wechat-rpa"&&(r.sessionId??r.managerSessionId)===e.managerSessionId);if(!t)throw new Error("No enabled WeChat RPA channel is bound to this session");const s=this.adapters.get(t.type);if(!s?.cancelOutbound)throw new Error("WeChat RPA channel does not support outbound cancellation");const o=await s.cancelOutbound(t,{idempotencyKey:e.idempotencyKey,replyId:e.replyId,reason:e.reason});return{cancelled:o.cancelled,status:o.status,channel:this.getManagerChannel(e.managerSessionId,"wechat-rpa",{includeSecret:!0})}}recordRecentMessage(e){const t=this.recentMessages.get(e.channelId)??[];this.findRecentMessageDuplicate(e)||(t.push(e),this.recentMessages.set(e.channelId,t.slice(-50)))}findRecentMessageDuplicate(e){const t=I(e);return t?(this.recentMessages.get(e.channelId)??[]).find(o=>I(o)===t)??null:null}getRecentMessages(e,t){return(this.recentMessages.get(e)??[]).filter(o=>{const r=Date.parse(o.receivedAt);return!Number.isFinite(r)||r>=t})}async upsertManagerChannel(e){const t=this.configs.get(e.id),s=this.configs.list(),o=e.sessionId||e.managerSessionId,r={id:e.id,type:e.type,name:e.name?.trim()||t?.name||"\u5916\u90E8\u6D88\u606F\u901A\u9053",sessionId:o,managerSessionId:o,workDir:e.workDir,agentType:e.agentType||t?.agentType,agentSessionId:e.agentSessionId??t?.agentSessionId??null,modelId:e.modelId??t?.modelId??null,enabled:e.enabled,secretRef:t?.secretRef||`channel:${e.id}`},i=this.secrets.get(r.secretRef),a=e.wsUrl?.trim()||i?.wsUrl||"",c=e.token?.trim()||i?.token||"",p=e.canReply??i?.canReply??!1,m=e.systemPrompt??(typeof i?.systemPrompt=="string"?i.systemPrompt:"");if(r.enabled&&(!a||!c))throw new Error("WebSocket \u5730\u5740\u548C Token \u5FC5\u586B");const f=s.filter(l=>l.id!==r.id).map(l=>(l.sessionId??l.managerSessionId)===o&&l.type===e.type?{...l,enabled:!1}:l);f.push(r),this.configs.replaceAll(f),(a||c)&&this.secrets.upsert(r.secretRef,{type:"websocket",wsUrl:a,token:c,canReply:p,systemPrompt:m});const d=this.adapters.get(r.type);for(const l of s)(l.sessionId??l.managerSessionId)===o&&l.type===e.type&&l.enabled&&await this.adapters.get(l.type)?.disconnect(l).catch(()=>{});return r.enabled&&d?.connect(r).catch(()=>{}),this.getManagerChannel(o,e.type,{includeSecret:!0})}async upsertManagerWeChatRpaChannel(e){const t=this.configs.get(e.id),s=this.configs.list(),o=e.sessionId||e.managerSessionId,r=w(e.groups);if(e.enabled&&!r.length)throw new Error("WeChat RPA \u81F3\u5C11\u9700\u8981\u914D\u7F6E\u4E00\u4E2A\u7FA4");if(e.enabled&&r.length>1)throw new Error("WeChat RPA \u6BCF\u4E2A\u5BF9\u8BDD\u53EA\u80FD\u7ED1\u5B9A\u4E00\u4E2A\u7FA4");const i={id:e.id,type:"wechat-rpa",name:e.name?.trim()||t?.name||"\u672C\u673A\u5FAE\u4FE1 RPA",sessionId:o,managerSessionId:o,workDir:e.workDir,agentType:e.agentType||t?.agentType,agentSessionId:e.agentSessionId??t?.agentSessionId??null,modelId:e.modelId??t?.modelId??null,enabled:e.enabled,secretRef:t?.secretRef||`channel:${e.id}`},a=this.secrets.get(i.secretRef),c=e.source||(a?.source==="wechat-channel"||a?.source==="macos-probe"||a?.source==="fixture-jsonl"||a?.source==="macos-flow"||a?.source==="windows-visual-flow"||a?.source==="wechat-rpa-lab"?a.source:E());if(e.enabled&&c==="windows-visual-flow")throw new Error("\u65E7 Windows visual flow \u5DF2\u5F52\u6863\uFF1BmacOS \u548C Windows \u6B63\u5F0F\u901A\u9053\u90FD\u4F7F\u7528 wechat-channel");const p=e.privacyConsentAccepted??!!a?.privacyConsentAccepted;if(e.enabled&&!p)throw new Error("\u542F\u7528\u524D\u9700\u8981\u786E\u8BA4\u5FAE\u4FE1\u901A\u9053\u6570\u636E\u4E0E\u9690\u79C1\u6388\u6743");const m=s.filter(d=>d.id!==i.id).map(d=>(d.sessionId??d.managerSessionId)===o&&d.type==="wechat-rpa"?{...d,enabled:!1}:d);m.push(i),this.configs.replaceAll(m),this.secrets.upsert(i.secretRef,{type:"wechat-rpa",source:c,groups:r,pollIntervalMs:R(e.pollIntervalMs,a?.pollIntervalMs),recentLimit:R(e.recentLimit,a?.recentLimit),idleSeconds:R(e.idleSeconds,a?.idleSeconds),forceForeground:e.forceForeground??(a?.forceForeground===void 0?!0:!!a.forceForeground),noRestore:e.noRestore??(a?.noRestore===void 0?!0:!!a.noRestore),downloadAttachments:e.downloadAttachments??(a?.downloadAttachments===void 0?!0:!!a.downloadAttachments),downloadAttachmentsDir:e.downloadAttachmentsDir?.trim()||b(a?.downloadAttachmentsDir),selfNickname:e.selfNickname?.trim()||b(a?.selfNickname),privacyConsentAccepted:p,flowScriptPath:e.flowScriptPath?.trim()||b(a?.flowScriptPath),canReply:e.canReply??a?.canReply??!1,systemPrompt:e.systemPrompt??(typeof a?.systemPrompt=="string"?a.systemPrompt:"")});const f=this.adapters.get(i.type);for(const d of s)(d.sessionId??d.managerSessionId)===o&&d.type==="wechat-rpa"&&d.enabled&&await this.adapters.get(d.type)?.disconnect(d).catch(()=>{});return i.enabled&&f?.connect(i).catch(()=>{}),this.getManagerChannel(o,"wechat-rpa",{includeSecret:!0})}}function y(n,e){return!e||e.type!==n.type?!1:n.type==="wechat-rpa"?!0:n.type==="websocket"?!!(e.wsUrl&&e.token):n.type==="wecom"?!!(e.token||e.botId||e.secret):!!e.token}function C(n){return!n||n.type!=="wechat-rpa"?{}:{wechatRpaSource:typeof n.source=="string"?n.source:"",wechatRpaGroups:w(Array.isArray(n.groups)?n.groups:[]),pollIntervalMs:Number.isFinite(n.pollIntervalMs)?Number(n.pollIntervalMs):void 0,recentLimit:Number.isFinite(n.recentLimit)?Number(n.recentLimit):void 0,idleSeconds:Number.isFinite(n.idleSeconds)?Number(n.idleSeconds):void 0,forceForeground:n.forceForeground===void 0?!0:!!n.forceForeground,noRestore:n.noRestore===void 0?void 0:!!n.noRestore,downloadAttachments:n.downloadAttachments===void 0?!0:!!n.downloadAttachments,downloadAttachmentsDir:typeof n.downloadAttachmentsDir=="string"?n.downloadAttachmentsDir:"",selfNickname:typeof n.selfNickname=="string"?n.selfNickname:"",wechatRpaPrivacyConsentAccepted:!!n.privacyConsentAccepted,wechatRpaServerDecisionAvailable:!0,wechatRpaPreflightChecks:k(n)}}function h(n){return!n||n.type!=="wechat-rpa"?{}:{wechatRpaSource:typeof n.source=="string"?n.source:null,wechatRpaGroups:w(Array.isArray(n.groups)?n.groups:[]),pollIntervalMs:Number.isFinite(n.pollIntervalMs)?Number(n.pollIntervalMs):null,recentLimit:Number.isFinite(n.recentLimit)?Number(n.recentLimit):null,idleSeconds:Number.isFinite(n.idleSeconds)?Number(n.idleSeconds):null,forceForeground:n.forceForeground===void 0?!0:!!n.forceForeground,noRestore:n.noRestore===void 0?null:!!n.noRestore,downloadAttachments:n.downloadAttachments===void 0?!0:!!n.downloadAttachments,downloadAttachmentsDir:typeof n.downloadAttachmentsDir=="string"?n.downloadAttachmentsDir:null,selfNickname:typeof n.selfNickname=="string"?n.selfNickname:null,wechatRpaPrivacyConsentAccepted:!!n.privacyConsentAccepted,wechatRpaServerDecisionAvailable:!0,wechatRpaPreflightChecks:k(n)}}function w(n){const e=new Set,t=[];for(const s of n){const o=String(s?.name||"").replace(/\s+/g," ").trim();!o||e.has(o)||(e.add(o),t.push({name:o}))}return t}function E(){return"wechat-channel"}function k(n){const e=[];if(n.lastHelperPermissions&&typeof n.lastHelperPermissions=="object"){const t=n.lastHelperPermissions;e.push({code:"wechat_window_unavailable",ok:t.wechatWindowAvailable!==!1,severity:"blocking",message:t.wechatWindowAvailable===!1?"\u5FAE\u4FE1\u7A97\u53E3\u4E0D\u53EF\u7528\uFF0C\u8BF7\u6253\u5F00\u5E76\u767B\u5F55\u5FAE\u4FE1\u540E\u518D\u8BD5\u3002":"\u5FAE\u4FE1\u7A97\u53E3\u53EF\u7528\u3002"})}return e.push({code:"platform_unsupported",ok:n.source!=="windows-visual-flow",severity:"blocking",message:n.source==="windows-visual-flow"?"\u65E7 Windows visual flow \u5DF2\u5F52\u6863\uFF0C\u8BF7\u6539\u7528 wechat-channel\u3002":"\u5F53\u524D\u914D\u7F6E\u4F7F\u7528\u8DE8\u5E73\u53F0\u5FAE\u4FE1\u901A\u9053\u3002"}),e.push({code:"privacy_consent_required",ok:!!n.privacyConsentAccepted,severity:"blocking",message:n.privacyConsentAccepted?"\u5DF2\u786E\u8BA4\u5FAE\u4FE1\u901A\u9053\u6570\u636E\u4E0E\u9690\u79C1\u6388\u6743\u3002":"\u542F\u7528\u524D\u9700\u8981\u786E\u8BA4\u5FAE\u4FE1\u901A\u9053\u6570\u636E\u4E0E\u9690\u79C1\u6388\u6743\u3002"}),e.push({code:"server_decision_unavailable",ok:!0,severity:"blocking",message:"\u670D\u52A1\u7AEF\u5224\u65AD\u80FD\u529B\u53EF\u7528\u3002"}),e}function W(n,e){const t=F(e.text);if(!t.length&&!e.attachment)return[];if(n==="wechat-rpa"&&e.attachment&&t.length<=1)return[{text:t[0]??"",attachment:e.attachment,idempotencyKey:e.idempotencyKey}];const s=t.map((o,r)=>({text:o,attachment:void 0,idempotencyKey:t.length>1&&e.idempotencyKey?`${e.idempotencyKey}:${r+1}`:e.idempotencyKey}));return e.attachment&&s.push({text:"",attachment:e.attachment,idempotencyKey:t.length&&e.idempotencyKey?`${e.idempotencyKey}:attachment`:e.idempotencyKey}),s}function K(...n){const e=new Set,t=[];for(const s of n.flat()){const o=I(s);if(!o){t.push(s);continue}e.has(o)||(e.add(o),t.push(s))}return t}function I(n){const e=S(n.channelId),t=S(n.conversationId),s=S(n.messageId);return!e||!t||!s?null:`${e}
|
|
2
2
|
${t}
|
|
3
|
-
${
|
|
3
|
+
${s}`}function S(n){return typeof n=="string"?n.trim():""}function A(n,e,t){return v.createHash("sha256").update(`${n}
|
|
4
4
|
${e}
|
|
5
|
-
${t}`).digest("hex").slice(0,32)}function P(
|
|
5
|
+
${t}`).digest("hex").slice(0,32)}function P(n){return u.join(n,".shennian","external-reply-idempotency.json")}function T(n){try{const e=JSON.parse(g.readFileSync(P(n),"utf8")),t=Array.isArray(e.completed)?e.completed:[];return new Set(t.map(s=>typeof s=="string"?s:s&&typeof s=="object"&&typeof s.key=="string"?s.key:"").filter(Boolean))}catch{return new Set}}function $(n,e){const t=P(n);g.mkdirSync(u.dirname(t),{recursive:!0});const s=Array.from(e).slice(-500);g.writeFileSync(t,JSON.stringify({updatedAt:new Date().toISOString(),completed:s.map(o=>({key:o}))},null,2)),e.clear();for(const o of s)e.add(o)}function R(n,e){const t=Number(n??e);return Number.isFinite(t)&&t>=0?t:void 0}function b(n){return typeof n=="string"&&n.trim()?n.trim():void 0}export{J as ChannelRuntime,W as planExternalReplySends};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type WeChatAutomationLane = {
|
|
2
|
+
run<T>(label: string, operation: () => Promise<T>): Promise<T>;
|
|
3
|
+
snapshot(): WeChatAutomationLaneSnapshot;
|
|
4
|
+
};
|
|
5
|
+
export type WeChatAutomationLaneSnapshot = {
|
|
6
|
+
active: boolean;
|
|
7
|
+
queued: number;
|
|
8
|
+
lastLabel: string | null;
|
|
9
|
+
};
|
|
10
|
+
export declare class SerialWeChatAutomationLane implements WeChatAutomationLane {
|
|
11
|
+
private tail;
|
|
12
|
+
private active;
|
|
13
|
+
private queued;
|
|
14
|
+
private lastLabel;
|
|
15
|
+
run<T>(label: string, operation: () => Promise<T>): Promise<T>;
|
|
16
|
+
snapshot(): WeChatAutomationLaneSnapshot;
|
|
17
|
+
}
|
|
18
|
+
export declare function createWeChatAutomationLane(): WeChatAutomationLane;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
class l{tail=Promise.resolve();active=!1;queued=0;lastLabel=null;async run(t,a){this.queued+=1;const i=this.tail;let e;this.tail=new Promise(s=>{e=s}),await i.catch(()=>{}),this.queued-=1,this.active=!0,this.lastLabel=t;try{return await a()}finally{this.active=!1,e()}}snapshot(){return{active:this.active,queued:this.queued,lastLabel:this.lastLabel}}}function r(){return new l}export{l as SerialWeChatAutomationLane,r as createWeChatAutomationLane};
|
|
@@ -67,6 +67,29 @@ export type WeChatChannelEmbedVisualResponse = {
|
|
|
67
67
|
usageRefId?: string;
|
|
68
68
|
latencyMs?: number | null;
|
|
69
69
|
};
|
|
70
|
+
export type WeChatChannelClassifyWindowInput = {
|
|
71
|
+
screenshot: {
|
|
72
|
+
mimeType: string;
|
|
73
|
+
dataBase64: string;
|
|
74
|
+
width: number;
|
|
75
|
+
height: number;
|
|
76
|
+
windowId?: string | null;
|
|
77
|
+
};
|
|
78
|
+
traceId?: string;
|
|
79
|
+
};
|
|
80
|
+
export type WeChatChannelClassifyWindowResponse = {
|
|
81
|
+
ok: true;
|
|
82
|
+
status?: string;
|
|
83
|
+
windowKind?: 'chat_main' | 'article' | 'settings' | 'login' | 'other';
|
|
84
|
+
conversationTitle?: string | null;
|
|
85
|
+
isTargetConversation?: boolean;
|
|
86
|
+
confidence?: number;
|
|
87
|
+
reasonCode?: string;
|
|
88
|
+
warnings?: string[];
|
|
89
|
+
usageRefId?: string;
|
|
90
|
+
latencyMs?: number | null;
|
|
91
|
+
modelVersion?: string | null;
|
|
92
|
+
};
|
|
70
93
|
export type WeChatChannelApiClient = ReturnType<typeof createWeChatChannelApiClient>;
|
|
71
94
|
export declare class WeChatChannelApiError extends Error {
|
|
72
95
|
readonly reasonCode: string;
|
|
@@ -122,6 +145,12 @@ export type WeChatChannelRuntimePolicyResponse = {
|
|
|
122
145
|
defaultPollIntervalSeconds?: number;
|
|
123
146
|
minPollIntervalSeconds?: number;
|
|
124
147
|
maxPollIntervalSeconds?: number;
|
|
148
|
+
current?: {
|
|
149
|
+
id?: string;
|
|
150
|
+
status?: string | null;
|
|
151
|
+
pollIntervalSeconds?: number | null;
|
|
152
|
+
foregroundPolicy?: string | null;
|
|
153
|
+
} | null;
|
|
125
154
|
scanPolicy?: {
|
|
126
155
|
plan?: 'free' | 'pro' | 'team';
|
|
127
156
|
defaultPollIntervalSeconds?: number;
|
|
@@ -167,6 +196,7 @@ export type WeChatChannelRunStatusInput = {
|
|
|
167
196
|
checkedAt?: string;
|
|
168
197
|
screenRecording?: boolean | 'granted' | 'denied' | 'restricted' | 'unknown' | 'not_determined';
|
|
169
198
|
accessibility?: boolean | 'granted' | 'denied' | 'restricted' | 'unknown' | 'not_determined';
|
|
199
|
+
inputMonitoring?: boolean | 'granted' | 'denied' | 'restricted' | 'unknown' | 'not_determined';
|
|
170
200
|
automation?: boolean | 'granted' | 'denied' | 'restricted' | 'unknown' | 'not_determined';
|
|
171
201
|
wechatRunning?: boolean;
|
|
172
202
|
wechatAppDetected?: boolean;
|
|
@@ -179,6 +209,7 @@ export declare function createWeChatChannelApiClient(options?: WeChatChannelApiC
|
|
|
179
209
|
observe: (runtime: WeChatChannelRuntime, binding: WeChatChannelBindingConfig, input: WeChatChannelObserveInput) => Promise<WeChatChannelObserveResponse>;
|
|
180
210
|
structureWindow: (runtime: WeChatChannelRuntime, binding: WeChatChannelBindingConfig, input: WeChatChannelStructureWindowInput) => Promise<WeChatChannelStructureWindowResponse>;
|
|
181
211
|
embedVisual: (runtime: WeChatChannelRuntime, binding: WeChatChannelBindingConfig, input: WeChatChannelEmbedVisualInput) => Promise<WeChatChannelEmbedVisualResponse>;
|
|
212
|
+
classifyWindow: (runtime: WeChatChannelRuntime, binding: WeChatChannelBindingConfig, input: WeChatChannelClassifyWindowInput) => Promise<WeChatChannelClassifyWindowResponse>;
|
|
182
213
|
ingest: (runtime: WeChatChannelRuntime, binding: WeChatChannelBindingConfig, input: {
|
|
183
214
|
idempotencyKey: string;
|
|
184
215
|
messages: WeChatChannelObservedMessage[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{SERVERS as I}from"../../region.js";import{loadConfig as h}from"../../config/index.js";class u extends Error{reasonCode;statusCode;constructor(a,i,t){super(a),this.reasonCode=i,this.statusCode=t,this.name="WeChatChannelApiError"}}function
|
|
1
|
+
import{SERVERS as I}from"../../region.js";import{loadConfig as h}from"../../config/index.js";class u extends Error{reasonCode;statusCode;constructor(a,i,t){super(a),this.reasonCode=i,this.statusCode=t,this.name="WeChatChannelApiError"}}function p(o={}){const a=h(),i=f(o.serverUrl||a.serverUrl||I.cn.url),t=o.machineToken||a.machineToken,m=o.fetchImpl||fetch;if(!t)throw new Error("WeChat channel requires a paired machine token");async function r(s,n){const e=await m(`${i}/api/channels/wechat${s}`,{method:n===void 0?"GET":"POST",headers:{authorization:`Bearer ${t}`,...n===void 0?{}:{"content-type":"application/json"}},body:n===void 0?void 0:JSON.stringify(n)}),c=await e.text(),d=c?JSON.parse(c):null;if(!e.ok||d?.ok===!1){const l=d?.reasonCode||e.statusText||"request_failed";throw new u(`WeChat channel API ${s} failed: ${l}`,l,e.status)}return d}return{getRuntimePolicy:()=>r("/runtime-policy"),upsertRuntime:(s,n)=>r("/runtime",{runtimeId:s.runtimeId,machineId:s.machineId,pollIntervalSeconds:Math.round(s.policy.pollIntervalMs/1e3),foregroundPolicy:s.foregroundPolicy,clientRuntimeVersion:String(s.policy.runtimeVersion),...n?y(n):{}}),observe:(s,n,e)=>r("/observe",{runtimeId:s.runtimeId,bindingId:n.bindingId,sessionId:n.sessionId,machineId:s.machineId,conversationName:n.conversationDisplayName,schemaVersion:s.policy.runtimeVersion,screenshots:e.screenshots,edgeOcrBlocks:e.edgeOcrBlocks??[],visibleConversationFingerprints:e.visibleConversationFingerprints??[],localLedgerTailAnchors:e.localLedgerTailAnchors??[]}),structureWindow:(s,n,e)=>r("/structure-window",{runtimeId:s.runtimeId,bindingId:n.bindingId,sessionId:n.sessionId,machineId:s.machineId,conversationName:n.conversationDisplayName,schemaVersion:s.policy.runtimeVersion,screenshots:e.screenshots,edgeOcrBlocks:e.edgeOcrBlocks??[],visibleConversationFingerprints:e.visibleConversationFingerprints??[],localLedgerTailAnchors:e.localLedgerTailAnchors??[]}),embedVisual:(s,n,e)=>r("/embed-visual",{runtimeId:s.runtimeId,bindingId:n.bindingId,sessionId:n.sessionId,machineId:s.machineId,conversationName:n.conversationDisplayName,schemaVersion:s.policy.runtimeVersion,screenshots:e.screenshots??[],visualBlocks:e.visualBlocks,traceId:e.traceId}),classifyWindow:(s,n,e)=>r("/classify-window",{runtimeId:s.runtimeId,bindingId:n.bindingId,sessionId:n.sessionId,machineId:s.machineId,conversationName:n.conversationDisplayName,screenshot:e.screenshot,traceId:e.traceId}),ingest:(s,n,e)=>r("/ingest",{runtimeId:s.runtimeId,idempotencyKey:e.idempotencyKey,bindingId:n.bindingId,sessionId:n.sessionId,machineId:s.machineId,messages:e.messages}),reportOutboundStatus:(s,n,e)=>r("/outbound-status",{runtimeId:s.runtimeId,replyId:e.replyId,idempotencyKey:e.idempotencyKey,bindingId:n.bindingId,sessionId:n.sessionId,machineId:s.machineId,status:e.status,replyBaseRevision:e.replyBaseRevision,sentAt:e.sentAt,confirmedAt:e.confirmedAt,failureCode:e.failureCode,lastErrorSummary:e.lastErrorSummary,nextAttemptAt:e.nextAttemptAt,attemptCount:e.attemptCount,commitStage:e.commitStage,copyableContentRef:e.copyableContentRef,copyableText:e.copyableText,agentVisible:e.agentVisible}),reportRunStatus:(s,n,e)=>r("/run-status",{runtimeId:s.runtimeId,bindingId:n.bindingId,sessionId:n.sessionId,machineId:s.machineId,status:e.status,reasonCode:e.reasonCode,traceId:e.traceId,lastErrorSummary:e.lastErrorSummary,preflight:e.preflight})}}function y(o){return{bindingId:o.bindingId,sessionId:o.sessionId,conversationName:o.conversationDisplayName,enabled:o.enabled,allowReply:o.allowReply,downloadMedia:o.downloadMedia}}function f(o){return o.replace(/\/+$/,"")}export{u as WeChatChannelApiError,p as createWeChatChannelApiClient};
|
|
@@ -28,6 +28,7 @@ export type WeChatChannelHelperRequestTraceEvent = {
|
|
|
28
28
|
export declare class WeChatChannelHelperClient {
|
|
29
29
|
private options;
|
|
30
30
|
private child;
|
|
31
|
+
private socket;
|
|
31
32
|
private lines;
|
|
32
33
|
private readyState;
|
|
33
34
|
private warmupState;
|
|
@@ -38,20 +39,42 @@ export declare class WeChatChannelHelperClient {
|
|
|
38
39
|
constructor(options: WeChatChannelHelperClientOptions);
|
|
39
40
|
start(): Promise<WeChatChannelHelperReady>;
|
|
40
41
|
private startFresh;
|
|
42
|
+
private startStdioFresh;
|
|
43
|
+
private startDarwinAppSocketFresh;
|
|
41
44
|
request<T = unknown>(command: WeChatChannelHelperCommandName, params?: Record<string, unknown>, traceId?: string): Promise<WeChatChannelHelperResponse<T>>;
|
|
42
45
|
private sendRequest;
|
|
43
46
|
private sendRawRequest;
|
|
44
47
|
private guardUnsafeWindowsCommand;
|
|
48
|
+
private requestFirstMissingMacPermissionPrompt;
|
|
45
49
|
private cleanupWindowsOverlaysAfterCommand;
|
|
46
50
|
private cleanupWindowsOverlays;
|
|
47
51
|
healthCheck(traceId?: string): Promise<WeChatChannelHelperResponse<WeChatChannelHelperHealthResult>>;
|
|
48
52
|
getReadyState(): WeChatChannelHelperReady | null;
|
|
49
53
|
getWarmupState(): WeChatChannelHelperWarmupSnapshot | null;
|
|
50
54
|
stop(): Promise<void>;
|
|
55
|
+
private hasTransport;
|
|
56
|
+
private writeFrame;
|
|
51
57
|
private handleLine;
|
|
52
58
|
private captureWarmupSnapshot;
|
|
53
59
|
private captureStderr;
|
|
54
60
|
private formatHelperExitMessage;
|
|
55
61
|
private rejectAllPending;
|
|
56
62
|
private emitRequestTrace;
|
|
63
|
+
private connectDarwinHelperSocket;
|
|
57
64
|
}
|
|
65
|
+
type LegacyRawHelperLaunchSpec = {
|
|
66
|
+
transport: 'legacy-stdio';
|
|
67
|
+
command: string;
|
|
68
|
+
args: string[];
|
|
69
|
+
};
|
|
70
|
+
type DarwinAppSocketHelperLaunchSpec = {
|
|
71
|
+
transport: 'macos-socket';
|
|
72
|
+
command: string;
|
|
73
|
+
args: string[];
|
|
74
|
+
appPath: string;
|
|
75
|
+
runtimeDir: string;
|
|
76
|
+
runtimeFile: string;
|
|
77
|
+
};
|
|
78
|
+
export type HelperLaunchSpec = LegacyRawHelperLaunchSpec | DarwinAppSocketHelperLaunchSpec;
|
|
79
|
+
export declare function helperLaunchSpec(helperPath: string, args: string[], platform?: NodeJS.Platform): HelperLaunchSpec;
|
|
80
|
+
export {};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{spawn as
|
|
2
|
-
`)}catch(
|
|
3
|
-
|
|
1
|
+
import{spawn as w}from"node:child_process";import{randomUUID as E}from"node:crypto";import f from"node:fs";import W from"node:net";import _ from"node:os";import d from"node:path";import{createInterface as m}from"node:readline";import{decideWeChatChannelActivityGate as b,normalizeWeChatChannelActivitySnapshot as T}from"./human-coordination.js";import{createWeChatChannelHelperHello as y,extractWeChatChannelHelperWarmupSnapshot as v,timeoutForWeChatChannelHelperCommand as P,validateWeChatChannelHelperReady as g}from"./helper-protocol.js";const R=["en","v"].join("");class re{options;child=null;socket=null;lines=null;readyState=null;warmupState=null;stderrTail="";startPromise=null;requestQueueTail=Promise.resolve();pending=new Map;constructor(e){this.options=e}async start(){return this.hasTransport()&&this.readyState?this.readyState:this.startPromise?this.startPromise:(this.hasTransport()&&await this.stop(),this.startPromise=this.startFresh().catch(async e=>{throw await this.stop().catch(()=>{}),e}).finally(()=>{this.startPromise=null}),this.startPromise)}async startFresh(){const e=H(this.options.helperPath,this.options.args??[]);return e.transport==="macos-socket"?this.startDarwinAppSocketFresh(e):this.startStdioFresh(e)}async startStdioFresh(e){const t=w(e.command,e.args,{cwd:this.options.cwd,stdio:["pipe","pipe","pipe"],windowsHide:!0});this.child=t,this.stderrTail="",this.lines=m({input:t.stdout}),this.lines.on("line",i=>this.handleLine(i)),t.stderr.on("data",i=>{this.captureStderr(i)}),t.stdin.on("error",i=>{this.rejectAllPending(i instanceof Error?i:new Error(String(i)))}),t.once("error",i=>{this.child===t&&(this.child=null,this.readyState=null,this.lines?.close(),this.lines=null),this.rejectAllPending(i instanceof Error?i:new Error(String(i)))}),t.once("exit",(i,o)=>{this.child===t&&(this.child=null,this.readyState=null,this.lines?.close(),this.lines=null),this.rejectAllPending(new Error(this.formatHelperExitMessage(i,o)))});const s=new Promise((i,o)=>{const a=setTimeout(()=>o(new Error("WeChat channel helper handshake timed out")),1e4),l=u=>{clearTimeout(a),this.lines?.off("line",c),t.off("exit",n),o(new Error(`WeChat channel helper failed to start: ${u.message}`))},n=(u,h)=>{clearTimeout(a),t.off("error",l),this.lines?.off("line",c),o(new Error(this.formatHelperExitMessage(u,h)))},c=u=>{let h;try{h=JSON.parse(u)}catch{clearTimeout(a),t.off("error",l),this.lines?.off("line",c),o(new Error("WeChat channel helper sent invalid handshake JSON"));return}if(!C(h))return;clearTimeout(a),t.off("error",l),t.off("exit",n),this.lines?.off("line",c);const p=g(h,this.options.expectedHelperVersion,this.options.requiredCapabilities);p.ok?(this.readyState=h,this.captureWarmupSnapshot(h),i(h)):o(new Error(`${p.errorCode}: ${p.errorSummary}`))};t.once("error",l),t.once("exit",n),this.lines?.on("line",c)});try{t.stdin.write(`${JSON.stringify(y(this.options.expectedHelperVersion,this.options.requiredCapabilities))}
|
|
2
|
+
`)}catch(i){throw await this.stop().catch(()=>{}),i}return s}async startDarwinAppSocketFresh(e){const t=await this.connectDarwinHelperSocket(e);this.socket=t,this.stderrTail="",this.lines=m({input:t}),this.lines.on("line",i=>this.handleLine(i)),t.on("error",i=>{this.socket===t&&(this.socket=null,this.readyState=null,this.lines?.close(),this.lines=null),this.rejectAllPending(i instanceof Error?i:new Error(String(i)))}),t.once("close",()=>{this.socket===t&&(this.socket=null,this.readyState=null,this.lines?.close(),this.lines=null),this.rejectAllPending(new Error("WeChat channel helper socket closed"))});const s=new Promise((i,o)=>{const a=setTimeout(()=>o(new Error("WeChat channel helper socket handshake timed out")),1e4),l=u=>{clearTimeout(a),this.lines?.off("line",c),t.off("close",n),o(new Error(`WeChat channel helper socket failed: ${u.message}`))},n=()=>{clearTimeout(a),t.off("error",l),this.lines?.off("line",c),o(new Error("WeChat channel helper socket closed before handshake"))},c=u=>{let h;try{h=JSON.parse(u)}catch{clearTimeout(a),t.off("error",l),t.off("close",n),this.lines?.off("line",c),o(new Error("WeChat channel helper sent invalid socket handshake JSON"));return}if(!C(h))return;clearTimeout(a),t.off("error",l),t.off("close",n),this.lines?.off("line",c);const p=g(h,this.options.expectedHelperVersion,this.options.requiredCapabilities);p.ok?(this.readyState=h,this.captureWarmupSnapshot(h),i(h)):o(new Error(`${p.errorCode}: ${p.errorSummary}`))};t.once("error",l),t.once("close",n),this.lines?.on("line",c)});try{this.writeFrame(y(this.options.expectedHelperVersion,this.options.requiredCapabilities))}catch(i){throw await this.stop().catch(()=>{}),i}return s}async request(e,t,s){const i=this.requestQueueTail.catch(()=>{}).then(()=>this.sendRequest(e,t,s));return this.requestQueueTail=i.then(()=>{},()=>{}),i}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.hasTransport()||!this.readyState)&&await this.start(),!this.hasTransport())throw new Error("WeChat channel helper is not started");const i=E(),o=P(e),a=Date.now();this.emitRequestTrace({phase:"request",at:new Date(a).toISOString(),id:i,command:e,traceId:s,params:t,timeoutMs:o});const l=new Promise((n,c)=>{const u=setTimeout(()=>{this.pending.delete(i),c(new Error(`helper_command_timeout: ${e}`))},o);this.pending.set(i,{resolve:n,reject:c,timer:u})});try{this.writeFrame({id:i,command:e,params:t,traceId:s})}catch(n){const c=this.pending.get(i);throw c&&clearTimeout(c.timer),this.pending.delete(i),this.emitRequestTrace({phase:"error",at:new Date().toISOString(),id:i,command:e,traceId:s,durationMs:Date.now()-a,errorSummary:n instanceof Error?n.message:String(n)}),n}try{const n=await l;return this.emitRequestTrace({phase:"response",at:new Date().toISOString(),id:i,command:e,traceId:s,durationMs:Date.now()-a,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:i,command:e,traceId:s,durationMs:Date.now()-a,errorSummary:n instanceof Error?n.message:String(n)}),n}}async guardUnsafeWindowsCommand(e,t){if(!this.options.guardUnsafeWindowsCommands||!I(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 i=s.result??{},o=await this.requestFirstMissingMacPermissionPrompt(i,t);if(o==="screen-recording")throw new Error("permission_screen_recording_missing");if(o==="accessibility")throw new Error("permission_accessibility_missing");if(o==="input-monitoring")throw new Error("permission_input_monitoring_missing");if(i.automation===!1)throw new Error("permission_automation_missing");if(i.wechatRunning===!1)throw new Error("wechat_not_running");if(M(i))throw new Error("wechat_login_required");if(q(i))throw new Error("windows_visible_desktop_unavailable");if(i.dpiMappingAvailable===!1||i.displayTopologySupported===!1)throw new Error("dpi_mapping_failed");if(i.wechatWindowAvailable===!1)throw new Error("wechat_window_unavailable");if(i.wechatMainWindowResponsive===!1)throw new Error("wechat_window_unresponsive");if(!this.options.skipUserActivityGuard&&j(e)){const a=await this.sendRawRequest("activity.snapshot",{},t);if(!a.ok)throw new Error(`${a.errorCode??"user_activity_unknown"}: ${a.errorSummary??e}`);const l=b({snapshot:T(a.result),stage:"dangerous_action"});if(!l.ok)throw new Error(`user_active:${l.reasonCode}`)}await this.cleanupWindowsOverlays("before",e,t)}async requestFirstMissingMacPermissionPrompt(e,t){const s=U(e);if(!s||process.platform!=="darwin")return s;const i=V(s);try{await this.sendRawRequest(i,{},t)}catch{}return s}async cleanupWindowsOverlaysAfterCommand(e,t){!this.options.guardUnsafeWindowsCommands||!J(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 i=await this.sendRawRequest("windows.cleanupOverlays",{stage:e,command:t,includeGeneratedMediaPreviews:!0,includeToolingTerminals:!1,waitMs:e==="after"?220:120},s);if(!i.ok)throw new Error(`${i.errorCode??"overlay_cleanup_failed"}: ${i.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,t=this.socket;this.child=null,this.socket=null,this.readyState=null,this.lines?.close(),this.lines=null,this.rejectAllPending(new Error("WeChat channel helper stopped")),t&&!t.destroyed&&t.destroy(),!(!e||e.killed)&&e.kill("SIGTERM")}hasTransport(){return!!this.child||!!this.socket}writeFrame(e){const t=`${JSON.stringify(e)}
|
|
3
|
+
`;if(this.socket){this.socket.write(t);return}if(this.child){this.child.stdin.write(t);return}throw new Error("WeChat channel helper is not started")}handleLine(e){let t;try{t=JSON.parse(e)}catch{return}if(!G(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=v(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{}}async connectDarwinHelperSocket(e){try{return await S(e.runtimeFile)}catch{f.rmSync(e.runtimeFile,{force:!0})}await N(e);const t=Date.now()+1e4;let s=null;for(;Date.now()<t;)try{return await S(e.runtimeFile)}catch(i){s=i instanceof Error?i:new Error(String(i)),await L(150)}throw new Error(`WeChat channel Helper.app socket did not become ready: ${s?.message??"runtime file missing"}`)}}function M(r){return r.wechatWindowAvailable!==!1?!1:![...r.captureCandidates??[],...r.restoreCandidates??[],...r.hiddenRestoreCandidates??[]].some($)}function q(r){return r.windowsVisibleDesktopAvailable===!1||r.rdpVisibleDesktopAvailable===!1||r.desktopSessionVisible===!1||r.screenLocked===!0||r.rdpDisconnected===!0}function A(r){const e=String(r.appName||"").toLowerCase();return e==="wechat"||e==="weixin"}function $(r){return!(!A(r)||r.visible===!1||r.minimized===!0)}function H(r,e,t=process.platform){if(t==="darwin"){const s=x(r);if(s&&e.length===0){const i=D();return{transport:"macos-socket",command:"/usr/bin/open",args:["-g",s,"--args","--socket-runtime",i],appPath:s,runtimeDir:i,runtimeFile:d.join(i,"runtime.json")}}}return{transport:"legacy-stdio",command:r,args:e}}function x(r){const e=d.resolve(r),t=`${d.sep}Contents${d.sep}MacOS${d.sep}`,s=e.lastIndexOf(t);if(s<0)return null;const i=e.slice(0,s);return i.endsWith(".app")?i:null}function D(){const r=O().SHENNIAN_HELPER_RUNTIME_DIR?.trim();return r?d.resolve(r):d.join(_.homedir(),"Library","Application Support","Shennian","Helper")}function O(){return globalThis.process?.[R]??{}}function F(r){let e;try{e=JSON.parse(f.readFileSync(r,"utf8"))}catch(s){throw new Error(`runtime_file_unreadable: ${s instanceof Error?s.message:String(s)}`)}if(!e||typeof e!="object")throw new Error("runtime_file_invalid");const t=e.socketPath;if(typeof t!="string"||!t.trim())throw new Error("runtime_socket_missing");return{socketPath:t}}async function S(r){const{socketPath:e}=F(r);return new Promise((t,s)=>{const i=W.createConnection(e),o=setTimeout(()=>{i.destroy(),s(new Error(`runtime_socket_connect_timeout: ${e}`))},1e3);i.once("connect",()=>{clearTimeout(o),i.off("error",a),t(i)});const a=l=>{clearTimeout(o),i.destroy(),s(l)};i.once("error",a)})}async function N(r){f.mkdirSync(r.runtimeDir,{recursive:!0,mode:448});try{await k(r.command,r.args)}catch(e){if(!(e instanceof Error?e.message:String(e)).includes("-1712"))throw e;await k(r.command,["-n",...r.args])}}async function k(r,e){await new Promise((t,s)=>{const i=w(r,e,{stdio:["ignore","ignore","pipe"],windowsHide:!0});let o="";i.stderr.on("data",a=>{o=(o+String(a)).slice(-2e3)}),i.once("error",s),i.once("exit",(a,l)=>{a===0?t():s(new Error(`open Helper.app failed (${l?`signal=${l}`:`code=${a}`}${o.trim()?`, stderr=${o.trim()}`:""})`))})})}function L(r){return new Promise(e=>setTimeout(e,r))}function U(r){return r.screenRecording===!1?"screen-recording":r.accessibility===!1?"accessibility":r.inputMonitoring===!1?"input-monitoring":null}function V(r){return r==="screen-recording"?"permissions.requestScreenRecording":r==="accessibility"?"permissions.requestAccessibility":"permissions.requestInputMonitoring"}function C(r){if(!r||typeof r!="object")return!1;const e=r;return e.type==="ready"&&typeof e.helperVersion=="string"&&typeof e.protocolVersion=="number"&&Array.isArray(e.capabilities)&&typeof e.pid=="number"}function G(r){if(!r||typeof r!="object")return!1;const e=r;return typeof e.id=="string"&&typeof e.ok=="boolean"&&typeof e.latencyMs=="number"}function I(r){return r==="windows.focus"||r==="windows.capture"||r==="screen.capture"||r==="ocr.recognize"||r==="image.cropHash"||r==="wechat.searchConversation"||r==="wechat.focusMessageInput"||r.startsWith("mouse.")||r.startsWith("keyboard.")||r.startsWith("clipboard.")||r==="menu.pickItem"||r==="savePanel.saveToPath"}function J(r){return r.startsWith("mouse.")||r.startsWith("keyboard.")||r.startsWith("clipboard.")||r==="menu.pickItem"||r==="savePanel.saveToPath"}function j(r){return r==="windows.focus"||r==="wechat.searchConversation"||r==="wechat.focusMessageInput"||r.startsWith("mouse.")||r.startsWith("keyboard.")||r.startsWith("clipboard.set")||r==="clipboard.restore"||r==="menu.pickItem"||r==="savePanel.saveToPath"}export{re as WeChatChannelHelperClient,H as helperLaunchSpec};
|
|
@@ -90,7 +90,7 @@ export type WeChatChannelHumanActivitySnapshot = {
|
|
|
90
90
|
capturesMousePath?: boolean;
|
|
91
91
|
};
|
|
92
92
|
};
|
|
93
|
-
export type WeChatChannelHelperCommandName = 'health.check' | 'processes.list' | 'permissions.check' | 'permissions.requestScreenRecording' | 'permissions.requestAccessibility' | 'activity.snapshot' | 'automation.lease.acquire' | 'automation.lease.release' | 'automation.lease.status' | 'automation.lease.simulateInterruption' | 'windows.ensureReady' | 'windows.list' | 'windows.enumerateRaw' | 'windows.focus' | 'windows.capture' | 'windows.captureAndOcr' | 'windows.cleanupOverlays' | 'windows.recoverWeChat' | 'screen.capture' | 'ocr.recognize' | 'mouse.click' | 'mouse.rightClick' | 'mouse.scroll' | 'keyboard.type' | 'keyboard.shortcut' | 'clipboard.snapshot' | 'clipboard.restore' | 'clipboard.setText' | 'clipboard.setFiles' | 'clipboard.setImage' | 'clipboard.readFileUrls' | 'clipboard.readAttachment' | 'menu.pickItem' | 'savePanel.saveToPath' | 'image.cropHash' | 'wechat.searchConversation' | 'wechat.focusMessageInput';
|
|
93
|
+
export type WeChatChannelHelperCommandName = 'health.check' | 'processes.list' | 'permissions.check' | 'permissions.requestScreenRecording' | 'permissions.requestAccessibility' | 'permissions.requestInputMonitoring' | 'activity.snapshot' | 'automation.lease.acquire' | 'automation.lease.release' | 'automation.lease.status' | 'automation.lease.simulateInterruption' | 'windows.ensureReady' | 'windows.list' | 'windows.enumerateRaw' | 'windows.focus' | 'windows.capture' | 'windows.captureAndOcr' | 'windows.cleanupOverlays' | 'windows.recoverWeChat' | 'screen.capture' | 'ocr.recognize' | 'mouse.click' | 'mouse.rightClick' | 'mouse.scroll' | 'keyboard.type' | 'keyboard.shortcut' | 'clipboard.snapshot' | 'clipboard.restore' | 'clipboard.setText' | 'clipboard.setFiles' | 'clipboard.setImage' | 'clipboard.readFileUrls' | 'clipboard.readAttachment' | 'menu.pickItem' | 'savePanel.saveToPath' | 'image.cropHash' | 'wechat.searchConversation' | 'wechat.focusMessageInput';
|
|
94
94
|
export type WeChatChannelHelperCommand = {
|
|
95
95
|
id: string;
|
|
96
96
|
command: WeChatChannelHelperCommandName;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const w=1,i={healthCheck:2e3,permissionsCheck:1e4,activitySnapshot:1e3,automationLease:6e3,windowList:1e4,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"],u=[...a,"contextMenu"],c=[...u,"automationLease"],p=c,l=[...a,"overlayCleanup","wechatRecovery"],C=[...l,"contextMenu"],h=[...C,"automationLease"];function H(e){return e==="observe"?[...a]:e==="download"?[...u]:[...c]}function A(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
|
|
1
|
+
const w=1,i={healthCheck:2e3,permissionsCheck:1e4,activitySnapshot:1e3,automationLease:6e3,windowList:1e4,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"],u=[...a,"contextMenu"],c=[...u,"automationLease"],p=c,l=[...a,"overlayCleanup","wechatRecovery"],C=[...l,"contextMenu"],h=[...C,"automationLease"];function H(e){return e==="observe"?[...a]:e==="download"?[...u]:[...c]}function A(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 n=r.filter(_=>!e.capabilities.includes(_));return n.length?{ok:!1,errorCode:"helper_capability_missing",errorSummary:`Helper missing capabilities: ${n.join(", ")}`}:{ok:!0}}function d(e){if(!e||typeof e!="object")return null;const t=e;if(!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==="processes.list"?i.windowList:e==="permissions.check"||e==="permissions.requestScreenRecording"||e==="permissions.requestAccessibility"||e==="permissions.requestInputMonitoring"?i.permissionsCheck:e==="activity.snapshot"?i.activitySnapshot:e.startsWith("automation.lease.")?i.automationLease:e==="windows.list"||e==="windows.enumerateRaw"?i.windowList: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 o(t,r,"startedAt"),o(t,r,"readyAt"),o(t,r,"warmupStartedAt"),o(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"),o(t,r,"errorCode"),o(t,r,"errorSummary"),Object.keys(r).length>0?r:void 0}function o(e,t,r){typeof e[r]=="string"&&(t[r]=e[r])}function s(e,t,r){const n=e[r];typeof n=="number"&&Number.isFinite(n)&&n>=0&&(t[r]=n)}export{u as WECHAT_CHANNEL_DOWNLOAD_HELPER_CAPABILITIES,i as WECHAT_CHANNEL_HELPER_COMMAND_TIMEOUT_MS,w as WECHAT_CHANNEL_HELPER_PROTOCOL_VERSION,a as WECHAT_CHANNEL_OBSERVE_HELPER_CAPABILITIES,p as WECHAT_CHANNEL_REQUIRED_HELPER_CAPABILITIES,c 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,H as requiredWeChatChannelHelperCapabilitiesForProfile,A as requiredWindowsWeChatChannelHelperCapabilitiesForProfile,W as timeoutForWeChatChannelHelperCommand,S as validateWeChatChannelHelperReady};
|
|
@@ -67,7 +67,7 @@ type WeChatChannelWindowObservation = {
|
|
|
67
67
|
ocr: WeChatChannelOcrResult;
|
|
68
68
|
};
|
|
69
69
|
export type WeChatChannelObservePipeline = 'server-observe' | 'edge-structure';
|
|
70
|
-
export type WeChatChannelObserveApi = Pick<WeChatChannelApiClient, 'observe'> & Partial<Pick<WeChatChannelApiClient, 'structureWindow' | 'embedVisual'>>;
|
|
70
|
+
export type WeChatChannelObserveApi = Pick<WeChatChannelApiClient, 'observe'> & Partial<Pick<WeChatChannelApiClient, 'structureWindow' | 'embedVisual' | 'classifyWindow'>>;
|
|
71
71
|
export type WeChatChannelObserveActivityGuardEvent = {
|
|
72
72
|
phase: 'snapshot' | 'lease-acquire' | 'lease-status' | 'lease-release';
|
|
73
73
|
at: string;
|
|
@@ -80,6 +80,7 @@ export type WeChatChannelObserveActivityGuardEvent = {
|
|
|
80
80
|
export type WeChatChannelObserveActivityGuardOptions = {
|
|
81
81
|
policy?: Partial<WeChatChannelActivityGatePolicy>;
|
|
82
82
|
useAutomationLease?: boolean;
|
|
83
|
+
preflightIdleSettleTimeoutMs?: number;
|
|
83
84
|
onEvent?: (event: WeChatChannelObserveActivityGuardEvent) => void;
|
|
84
85
|
};
|
|
85
86
|
export declare class WeChatChannelUserActivityAbort extends Error {
|
|
@@ -121,7 +122,12 @@ export type WeChatChannelLocalOutboundEchoAnchor = {
|
|
|
121
122
|
expectedEchoAnchor?: string | null;
|
|
122
123
|
};
|
|
123
124
|
export declare function observeWeChatChannelBindingViaHelper(options: HelperBackedObserveOptions): Promise<WeChatChannelObservedMessage[]>;
|
|
125
|
+
export declare function selectWeChatWindowForBinding(options: HelperBackedObserveOptions, input?: {
|
|
126
|
+
foregroundMode?: WeChatChannelForegroundMode;
|
|
127
|
+
forceVision?: boolean;
|
|
128
|
+
}): Promise<WeChatChannelWindowInfo>;
|
|
124
129
|
export declare function ensureHelperPreflight(helper: WeChatChannelHelperTransport, traceId?: string): Promise<void>;
|
|
130
|
+
export declare function ensureHelperPermissionPreflight(helper: WeChatChannelHelperTransport, traceId?: string): Promise<void>;
|
|
125
131
|
export declare function ensureWeChatWindowReady(helper: WeChatChannelHelperTransport, traceId?: string, options?: {
|
|
126
132
|
foreground?: WeChatChannelForegroundMode;
|
|
127
133
|
}): Promise<WeChatChannelWindowInfo>;
|