shennian 0.2.107 → 0.2.109
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 +47 -37
- package/dist/src/agents/model-registry/discovery.js +2 -2
- package/dist/src/agents/pi-context.d.ts +1 -1
- package/dist/src/agents/pi-context.js +3 -3
- package/dist/src/channels/runtime.d.ts +2 -0
- package/dist/src/channels/runtime.js +1 -1
- package/dist/src/channels/secret-registry.d.ts +2 -0
- package/dist/src/channels/wechat-channel/client.d.ts +9 -2
- package/dist/src/channels/wechat-channel/client.js +1 -1
- package/dist/src/channels/wechat-channel/helper-assets.d.ts +1 -1
- package/dist/src/channels/wechat-channel/helper-assets.js +1 -1
- package/dist/src/channels/wechat-channel/helper-client.d.ts +5 -0
- package/dist/src/channels/wechat-channel/helper-client.js +3 -3
- package/dist/src/channels/wechat-channel/observer.js +6 -6
- package/dist/src/channels/wechat-channel/runner.js +1 -1
- package/dist/src/channels/wechat-channel/runtime.d.ts +1 -0
- package/dist/src/channels/wechat-channel/runtime.js +1 -1
- package/dist/src/channels/wechat-channel/scheduler.d.ts +1 -1
- package/dist/src/channels/wechat-channel/scheduler.js +1 -1
- package/dist/src/channels/wechat-rpa/product-channel.d.ts +1 -0
- package/dist/src/channels/wechat-rpa/product-channel.js +4 -4
- package/dist/src/channels/wechat-rpa.d.ts +2 -0
- package/dist/src/channels/wechat-rpa.js +2 -2
- package/dist/src/commands/helper-runtime-official-download.d.ts +80 -0
- package/dist/src/commands/helper-runtime-official-download.js +1 -0
- package/dist/src/commands/runtime.d.ts +23 -0
- package/dist/src/commands/runtime.js +1 -1
- package/dist/src/commands/wechat/command.js +1 -1
- package/dist/src/commands/wechat/ipc.js +1 -1
- package/dist/src/commands/wechat/output.js +3 -3
- package/dist/src/manager/runtime.js +10 -10
- package/dist/src/session/handlers/wechat-helper-runtime.d.ts +31 -0
- package/dist/src/session/handlers/wechat-helper-runtime.js +1 -0
- package/dist/src/session/manager.d.ts +3 -0
- package/dist/src/session/manager.js +1 -1
- package/node_modules/@shennian/wire/dist/methods.d.ts +1 -1
- package/node_modules/@shennian/wire/dist/methods.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"jsMinified": true,
|
|
4
4
|
"minifier": "esbuild",
|
|
5
|
-
"fileCount":
|
|
5
|
+
"fileCount": 134,
|
|
6
6
|
"files": [
|
|
7
7
|
{
|
|
8
8
|
"file": "bin/shennian.js",
|
|
@@ -91,8 +91,8 @@
|
|
|
91
91
|
},
|
|
92
92
|
{
|
|
93
93
|
"file": "src/agents/model-registry/discovery.js",
|
|
94
|
-
"beforeBytes":
|
|
95
|
-
"afterBytes":
|
|
94
|
+
"beforeBytes": 6870,
|
|
95
|
+
"afterBytes": 3204
|
|
96
96
|
},
|
|
97
97
|
{
|
|
98
98
|
"file": "src/agents/model-registry/parsers.js",
|
|
@@ -131,8 +131,8 @@
|
|
|
131
131
|
},
|
|
132
132
|
{
|
|
133
133
|
"file": "src/agents/pi-context.js",
|
|
134
|
-
"beforeBytes":
|
|
135
|
-
"afterBytes":
|
|
134
|
+
"beforeBytes": 8076,
|
|
135
|
+
"afterBytes": 4816
|
|
136
136
|
},
|
|
137
137
|
{
|
|
138
138
|
"file": "src/agents/pi.js",
|
|
@@ -161,8 +161,8 @@
|
|
|
161
161
|
},
|
|
162
162
|
{
|
|
163
163
|
"file": "src/channels/runtime.js",
|
|
164
|
-
"beforeBytes":
|
|
165
|
-
"afterBytes":
|
|
164
|
+
"beforeBytes": 30001,
|
|
165
|
+
"afterBytes": 16436
|
|
166
166
|
},
|
|
167
167
|
{
|
|
168
168
|
"file": "src/channels/secret-registry.js",
|
|
@@ -186,8 +186,8 @@
|
|
|
186
186
|
},
|
|
187
187
|
{
|
|
188
188
|
"file": "src/channels/wechat-channel/client.js",
|
|
189
|
-
"beforeBytes":
|
|
190
|
-
"afterBytes":
|
|
189
|
+
"beforeBytes": 8067,
|
|
190
|
+
"afterBytes": 4463
|
|
191
191
|
},
|
|
192
192
|
{
|
|
193
193
|
"file": "src/channels/wechat-channel/cooldown.js",
|
|
@@ -221,13 +221,13 @@
|
|
|
221
221
|
},
|
|
222
222
|
{
|
|
223
223
|
"file": "src/channels/wechat-channel/helper-assets.js",
|
|
224
|
-
"beforeBytes":
|
|
225
|
-
"afterBytes":
|
|
224
|
+
"beforeBytes": 17193,
|
|
225
|
+
"afterBytes": 9150
|
|
226
226
|
},
|
|
227
227
|
{
|
|
228
228
|
"file": "src/channels/wechat-channel/helper-client.js",
|
|
229
|
-
"beforeBytes":
|
|
230
|
-
"afterBytes":
|
|
229
|
+
"beforeBytes": 31634,
|
|
230
|
+
"afterBytes": 16339
|
|
231
231
|
},
|
|
232
232
|
{
|
|
233
233
|
"file": "src/channels/wechat-channel/helper-protocol.js",
|
|
@@ -266,8 +266,8 @@
|
|
|
266
266
|
},
|
|
267
267
|
{
|
|
268
268
|
"file": "src/channels/wechat-channel/observer.js",
|
|
269
|
-
"beforeBytes":
|
|
270
|
-
"afterBytes":
|
|
269
|
+
"beforeBytes": 93667,
|
|
270
|
+
"afterBytes": 43613
|
|
271
271
|
},
|
|
272
272
|
{
|
|
273
273
|
"file": "src/channels/wechat-channel/outbound-ledger.js",
|
|
@@ -291,18 +291,18 @@
|
|
|
291
291
|
},
|
|
292
292
|
{
|
|
293
293
|
"file": "src/channels/wechat-channel/runner.js",
|
|
294
|
-
"beforeBytes":
|
|
295
|
-
"afterBytes":
|
|
294
|
+
"beforeBytes": 14059,
|
|
295
|
+
"afterBytes": 7079
|
|
296
296
|
},
|
|
297
297
|
{
|
|
298
298
|
"file": "src/channels/wechat-channel/runtime.js",
|
|
299
|
-
"beforeBytes":
|
|
300
|
-
"afterBytes":
|
|
299
|
+
"beforeBytes": 3768,
|
|
300
|
+
"afterBytes": 1755
|
|
301
301
|
},
|
|
302
302
|
{
|
|
303
303
|
"file": "src/channels/wechat-channel/scheduler.js",
|
|
304
|
-
"beforeBytes":
|
|
305
|
-
"afterBytes":
|
|
304
|
+
"beforeBytes": 24204,
|
|
305
|
+
"afterBytes": 10896
|
|
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": 17962,
|
|
330
|
+
"afterBytes": 9649
|
|
331
331
|
},
|
|
332
332
|
{
|
|
333
333
|
"file": "src/channels/wechat-rpa.js",
|
|
334
|
-
"beforeBytes":
|
|
335
|
-
"afterBytes":
|
|
334
|
+
"beforeBytes": 55482,
|
|
335
|
+
"afterBytes": 29086
|
|
336
336
|
},
|
|
337
337
|
{
|
|
338
338
|
"file": "src/channels/wecom.js",
|
|
@@ -364,6 +364,11 @@
|
|
|
364
364
|
"beforeBytes": 6503,
|
|
365
365
|
"afterBytes": 4455
|
|
366
366
|
},
|
|
367
|
+
{
|
|
368
|
+
"file": "src/commands/helper-runtime-official-download.js",
|
|
369
|
+
"beforeBytes": 12608,
|
|
370
|
+
"afterBytes": 6718
|
|
371
|
+
},
|
|
367
372
|
{
|
|
368
373
|
"file": "src/commands/manager.js",
|
|
369
374
|
"beforeBytes": 17401,
|
|
@@ -381,8 +386,8 @@
|
|
|
381
386
|
},
|
|
382
387
|
{
|
|
383
388
|
"file": "src/commands/runtime.js",
|
|
384
|
-
"beforeBytes":
|
|
385
|
-
"afterBytes":
|
|
389
|
+
"beforeBytes": 31882,
|
|
390
|
+
"afterBytes": 17827
|
|
386
391
|
},
|
|
387
392
|
{
|
|
388
393
|
"file": "src/commands/tools.js",
|
|
@@ -396,8 +401,8 @@
|
|
|
396
401
|
},
|
|
397
402
|
{
|
|
398
403
|
"file": "src/commands/wechat/command.js",
|
|
399
|
-
"beforeBytes":
|
|
400
|
-
"afterBytes":
|
|
404
|
+
"beforeBytes": 7697,
|
|
405
|
+
"afterBytes": 4690
|
|
401
406
|
},
|
|
402
407
|
{
|
|
403
408
|
"file": "src/commands/wechat/direct.js",
|
|
@@ -411,8 +416,8 @@
|
|
|
411
416
|
},
|
|
412
417
|
{
|
|
413
418
|
"file": "src/commands/wechat/ipc.js",
|
|
414
|
-
"beforeBytes":
|
|
415
|
-
"afterBytes":
|
|
419
|
+
"beforeBytes": 8947,
|
|
420
|
+
"afterBytes": 4830
|
|
416
421
|
},
|
|
417
422
|
{
|
|
418
423
|
"file": "src/commands/wechat/operations.js",
|
|
@@ -421,8 +426,8 @@
|
|
|
421
426
|
},
|
|
422
427
|
{
|
|
423
428
|
"file": "src/commands/wechat/output.js",
|
|
424
|
-
"beforeBytes":
|
|
425
|
-
"afterBytes":
|
|
429
|
+
"beforeBytes": 14154,
|
|
430
|
+
"afterBytes": 8863
|
|
426
431
|
},
|
|
427
432
|
{
|
|
428
433
|
"file": "src/commands/wechat/types.js",
|
|
@@ -516,8 +521,8 @@
|
|
|
516
521
|
},
|
|
517
522
|
{
|
|
518
523
|
"file": "src/manager/runtime.js",
|
|
519
|
-
"beforeBytes":
|
|
520
|
-
"afterBytes":
|
|
524
|
+
"beforeBytes": 60524,
|
|
525
|
+
"afterBytes": 31029
|
|
521
526
|
},
|
|
522
527
|
{
|
|
523
528
|
"file": "src/native-fusion/config.js",
|
|
@@ -614,10 +619,15 @@
|
|
|
614
619
|
"beforeBytes": 8800,
|
|
615
620
|
"afterBytes": 4161
|
|
616
621
|
},
|
|
622
|
+
{
|
|
623
|
+
"file": "src/session/handlers/wechat-helper-runtime.js",
|
|
624
|
+
"beforeBytes": 6999,
|
|
625
|
+
"afterBytes": 3768
|
|
626
|
+
},
|
|
617
627
|
{
|
|
618
628
|
"file": "src/session/manager.js",
|
|
619
|
-
"beforeBytes":
|
|
620
|
-
"afterBytes":
|
|
629
|
+
"beforeBytes": 15299,
|
|
630
|
+
"afterBytes": 7862
|
|
621
631
|
},
|
|
622
632
|
{
|
|
623
633
|
"file": "src/session/projection.js",
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{createInterface as
|
|
2
|
-
`,p=>{p&&(clearTimeout(u),r.delete(o),s(p))})}):Promise.reject(new Error("codex app-server stdin unavailable"))}async function
|
|
1
|
+
import{createInterface as f}from"node:readline";import{resolveBuiltinCommand as c,spawnResolvedCommand as v}from"../command-spec.js";import{fallbackClaudeAliasModels as M,discoverClaudeAliasModelsFromEnv as w,fallbackGeminiModels as C,parseCodexAppServerModels as y,parseCursorModels as h,parseOpenCodeModels as g}from"./parsers.js";import{runResolvedCommand as m}from"./runner.js";import{DISCOVERY_WORKDIR as E}from"./types.js";import{buildAgentProcessEnv as x,readLatestUserEnv as O}from"../../agent-env.js";import{CODEX_APP_SERVER_CLIENT_INFO as R}from"../codex-utils.js";function l(e,r,o,i,a,n){return e.stdin?new Promise((t,s)=>{const u=setTimeout(()=>{r.delete(o),s(new Error(`codex app-server request timed out: ${i}`))},n);r.set(o,{resolve:t,reject:s,timer:u}),e.stdin.write(`${JSON.stringify({id:o,method:i,params:a})}
|
|
2
|
+
`,p=>{p&&(clearTimeout(u),r.delete(o),s(p))})}):Promise.reject(new Error("codex app-server stdin unavailable"))}async function S(e){const r=v(e,["app-server","--listen","stdio://"],{cwd:E,stdio:["pipe","pipe","pipe"],env:x({NO_COLOR:"1"})}),o=new Map;let i=1;f({input:r.stdout}).on("line",n=>{if(!n.trim())return;let t;try{t=JSON.parse(n)}catch{return}if(t.id==null)return;const s=o.get(t.id);s&&(clearTimeout(s.timer),o.delete(t.id),t.error?s.reject(new Error(t.error.message||`codex app-server error ${t.error.code??""}`.trim())):s.resolve(t.result))});try{await l(r,o,i++,"initialize",{clientInfo:R,capabilities:{experimentalApi:!0}},1e4);const[n,t]=await Promise.all([l(r,o,i++,"model/list",{},15e3),l(r,o,i++,"config/read",{},15e3).catch(()=>null)]),s=typeof t=="object"&&t!==null&&"config"in t?t.config:void 0;return y({...typeof n=="object"&&n!==null?n:{},config:s})}finally{for(const[n,t]of o)clearTimeout(t.timer),t.reject(new Error("codex app-server stopped")),o.delete(n);r.kill("SIGTERM"),setTimeout(()=>r.kill("SIGKILL"),1e3).unref()}}async function A(){const e=c("cursor");if(!e)return[];const r=await m(e,["--list-models"]);return h(r.stdout)}async function I(){const e=c("opencode");if(!e)return[];const r=await m(e,["models"]);return g(r.stdout)}async function T(){if(!c("claude"))return[];const e=w(O());return e.length>0?e:M()}async function _(){const e=c("codex");return e?await S(e).catch(()=>[]):[]}async function b(){return c("gemini")?C():[]}const d=[{id:"qwen3.7-max",name:"Qwen 3.7 Max",provider:"dashscope",isDefault:!0}],k=[{id:"codex",name:"Codex",provider:"shennian",isDefault:!0},{id:"claude",name:"Claude Code",provider:"shennian"}];async function P(e){if(!e.serverUrl||!e.authToken)return d;try{const r=await fetch(`${e.serverUrl.replace(/\/$/,"")}/api/models`,{headers:{Authorization:`Bearer ${e.authToken}`}});if(!r.ok)return d;const o=await r.json(),i=(o.models??[]).map(a=>({id:a.id,name:a.name?.trim()||a.id,provider:a.provider,isDefault:a.id===o.default}));return i.length>0?i:d}catch{return d}}async function D(e,r){switch(e){case"claude":return T();case"codex":return _();case"gemini":return b();case"cursor":return A();case"openclaw":return[];case"opencode":return I();case"pi":return P(r);case"manager":return k}}async function F(e,r){return e.type.startsWith("custom:")?e.models??[]:D(e.type,r)}export{F as discoverModelsForAgent};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { AgentMessage } from '@mariozechner/pi-agent-core';
|
|
2
2
|
import type { Model } from '@mariozechner/pi-ai';
|
|
3
|
-
export declare const PI_DEFAULT_MODEL_ID = "qwen3.
|
|
3
|
+
export declare const PI_DEFAULT_MODEL_ID = "qwen3.7-max";
|
|
4
4
|
type ShellCommandSpec = {
|
|
5
5
|
file: string;
|
|
6
6
|
args: string[];
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import u from"node:fs";import l from"node:path";import{resolveShennianPath as
|
|
1
|
+
import u from"node:fs";import l from"node:path";import{resolveShennianPath as h}from"../config/index.js";const S="qwen3.7-max";function w(e,t=process.platform){return t==="win32"?{file:"powershell.exe",args:["-NoLogo","-NoProfile","-NonInteractive","-ExecutionPolicy","Bypass","-Command",e],shell:"powershell"}:{file:"bash",args:["-c",e],shell:"bash"}}function d(e=S,t={}){return{id:e,name:e,api:"openai-completions",provider:t.provider??"shennian-proxy",baseUrl:t.baseUrl??"",reasoning:!1,input:["text","image"],cost:{input:0,output:0,cacheRead:0,cacheWrite:0},contextWindow:128e3,maxTokens:8192,...t.compat?{compat:t.compat}:{}}}const O=`\u4F60\u662F\u795E\u5FF5\u5185\u7F6E\u7F16\u7A0B\u52A9\u624B\uFF0C\u8FD0\u884C\u5728\u7528\u6237\u672C\u5730\u673A\u5668\u4E0A\u3002
|
|
2
2
|
\u4F60\u53EF\u4EE5\u8BFB\u5199\u6587\u4EF6\u3001\u6267\u884C shell \u547D\u4EE4\u3001\u5E2E\u52A9\u7528\u6237\u5B8C\u6210\u7F16\u7A0B\u548C\u7CFB\u7EDF\u7BA1\u7406\u4EFB\u52A1\u3002
|
|
3
3
|
\u5DE5\u4F5C\u76EE\u5F55\u5DF2\u8BBE\u7F6E\uFF0C\u64CD\u4F5C\u6587\u4EF6\u65F6\u4F7F\u7528\u76F8\u5BF9\u8DEF\u5F84\u6216\u7EDD\u5BF9\u8DEF\u5F84\u5747\u53EF\u3002
|
|
4
4
|
\u5F53\u524D shell \u4F1A\u968F\u64CD\u4F5C\u7CFB\u7EDF\u9009\u62E9\uFF1AWindows \u4F7F\u7528 PowerShell\uFF0CmacOS/Linux \u4F7F\u7528 bash\u3002Windows \u4E0B\u9700\u8981\u771F\u5B9E curl \u65F6\u4F7F\u7528 curl.exe\uFF0C\u907F\u514D PowerShell \u7684 curl \u522B\u540D\u3002
|
|
5
|
-
\u4FDD\u6301\u56DE\u590D\u7B80\u6D01\u3001\u51C6\u786E\uFF0C\u4E2D\u6587\u56DE\u590D\u3002`,
|
|
5
|
+
\u4FDD\u6301\u56DE\u590D\u7B80\u6D01\u3001\u51C6\u786E\uFF0C\u4E2D\u6587\u56DE\u590D\u3002`,x=24e3;function E(e){let t=l.resolve(e||process.cwd());try{const n=u.existsSync(t)?u.statSync(t):null;n&&!n.isDirectory()&&(t=l.dirname(t))}catch{}for(;;){const n=l.join(t,"AGENTS.md");if(u.existsSync(n))return n;const o=l.dirname(t);if(o===t)return null;t=o}}function $(e){const t=E(e);if(!t)return null;try{const n=u.readFileSync(t,"utf-8").trim();if(!n)return null;const o=n.length>x?`${n.slice(0,x)}
|
|
6
6
|
|
|
7
7
|
...\uFF08AGENTS.md \u5DF2\u622A\u65AD\uFF09`:n;return`# AGENTS.md instructions for ${l.dirname(t)}
|
|
8
8
|
|
|
9
|
-
${o}`}catch{return null}}const P=9e4,b=6,N=
|
|
9
|
+
${o}`}catch{return null}}const P=9e4,b=6,N=h("sessions"),k="summary.json",C="snapshot.json",L="messages.jsonl",j="pi-context.json";function R(e){let t=0;for(const n of e)t+=JSON.stringify(n).length;return Math.ceil(t/3)}function D(e){const t=[];for(const n of e){const o=n,s=String(o.role??"unknown");if(Array.isArray(o.content)){for(const r of o.content)if(r.type==="text"&&typeof r.text=="string")t.push(`[${s}] ${r.text.slice(0,500)}`);else if(r.type==="toolCall"){const c=typeof r.name=="string"?r.name:typeof r.toolName=="string"?r.toolName:"tool",i=r.arguments??r.args??{};t.push(`[${s}] tool:${c}(${JSON.stringify(i).slice(0,200)})`)}}}return t.join(`
|
|
10
10
|
`)}function I(e){return JSON.parse(JSON.stringify(e))}function J(e){return l.join(N,e)}function A(e){if(!e||typeof e!="object")return null;const t=e;if(t.type==="text"&&typeof t.text=="string")return t.text;if(t.type==="thinking"&&typeof t.thinking=="string")return`[thinking] ${t.thinking}`;if(t.type==="toolCall"){const n=typeof t.name=="string"?t.name:typeof t.toolName=="string"?t.toolName:"tool",o=t.arguments??t.args??{};return`[toolCall] ${n}(${JSON.stringify(o).slice(0,300)})`}return t.type==="image"&&typeof t.mimeType=="string"?`[image] ${t.mimeType}`:null}function G(e,t){const n=[];for(const r of t){const c=String(r.role??"unknown"),i=Array.isArray(r.content)?r.content:[];for(const p of i){const a=A(p);a&&n.push(`[${c}] ${a}`)}}const s=n.slice(-8).join(`
|
|
11
11
|
`).trim()||e?.trim()||null;return s?s.length>2e3?s.slice(s.length-2e3):s:null}function F(e,t){const n=Math.min(e.length,t.length);for(let o=0;o<n;o++)if(JSON.stringify(e[o])!==JSON.stringify(t[o]))return o;return n}async function U(e,t,n){const o=await fetch(`${e}/api/stream`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${t}`},body:JSON.stringify({model:d("qwen-plus"),context:{messages:[{role:"user",content:[{type:"text",text:n}]}]},options:{temperature:.3,maxTokens:500}})});if(!o.ok||!o.body)return null;const s=o.body.getReader(),r=new TextDecoder,c=[];let i="";for(;;){const{done:p,value:a}=await s.read();if(p)break;i+=r.decode(a,{stream:!0});const m=i.split(`
|
|
12
12
|
`);i=m.pop()??"";for(const g of m){if(!g.startsWith("data: "))continue;const y=g.slice(6).trim();if(!y)continue;const f=JSON.parse(y);f.type==="text_delta"&&typeof f.delta=="string"&&c.push(f.delta)}}return c.join("").trim()||null}export{P as CONTEXT_TOKEN_THRESHOLD,b as KEEP_RECENT_MESSAGES,j as LEGACY_SUMMARY_FILENAME,L as MESSAGES_FILENAME,S as PI_DEFAULT_MODEL_ID,C as SNAPSHOT_FILENAME,k as SUMMARY_FILENAME,O as SYSTEM_PROMPT,G as buildRollingSummary,w as buildShellCommandSpec,I as cloneMessages,d as createPiModel,R as estimateTokens,E as findAgentsMd,J as getSessionDir,$ as loadAgentsMdInstructions,F as longestCommonPrefixLength,D as messagesToText,U as requestProxySummary};
|
|
@@ -117,6 +117,8 @@ export declare class ChannelRuntime {
|
|
|
117
117
|
downloadAttachments?: boolean;
|
|
118
118
|
downloadAttachmentsDir?: string;
|
|
119
119
|
selfNickname?: string;
|
|
120
|
+
selfTriggerMarker?: string;
|
|
121
|
+
deferInitialPoll?: boolean;
|
|
120
122
|
privacyConsentAccepted?: boolean;
|
|
121
123
|
flowScriptPath?: string;
|
|
122
124
|
}): Promise<ExternalChannelView>;
|
|
@@ -1,4 +1,4 @@
|
|
|
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}
|
|
1
|
+
import v from"node:crypto";import g from"node:fs";import u from"node:path";import{ChannelConfigRegistry as M}from"./registry.js";import{ChannelSecretRegistry as N}from"./secret-registry.js";import{WeComChannelAdapter as D}from"./wecom.js";import{WeChatRpaChannelAdapter as B}from"./wechat-rpa.js";import{ExternalWebSocketChannelAdapter as x}from"./websocket.js";import{splitExternalReplyText as F}from"./reply-split.js";class J{onExternalMessage;createReplyTarget;configs=new M;secrets=new N;adapters=new Map;completedReplyKeys=new Map;recentMessages=new Map;constructor(e,t,s={}){this.onExternalMessage=e,this.createReplyTarget=t;const o=new D(a=>this.ingest({...a,type:"external.message"}));this.adapters.set(o.type,o);const r=new x(a=>this.ingest({...a,type:"external.message"}));this.adapters.set(r.type,r);const i=new B(a=>this.ingest(a),{createProductRunner:s.createWeChatRpaProductRunner,automationLane:s.weChatAutomationLane});this.adapters.set(i.type,i)}async start(){for(const e of this.configs.list().filter(t=>t.enabled))await this.adapters.get(e.type)?.connect(e).catch(()=>{})}async stop(){for(const e of this.configs.list())await this.adapters.get(e.type)?.disconnect(e).catch(()=>{})}ingest(e){const t=this.findRecentMessageDuplicate(e);if(t)return t;const s=this.configs.get(e.channelId),o=e.managerSessionId??s?.sessionId??s?.managerSessionId;if(!o)throw new Error(`No session bound for channel ${e.channelId}`);const r=e.replyTarget||this.createReplyTarget({managerSessionId:o,channelId:e.channelId,conversationId:e.conversationId,messageId:e.messageId}),i={...e,replyTarget:r};return this.onExternalMessage(o,i),this.recordRecentMessage(i),i}async reply(e){const t=this.configs.get(e.channelId);if(!t)return{ok:!1,error:`Unknown channel: ${e.channelId}`};if((t.sessionId??t.managerSessionId)!==e.managerSessionId)return{ok:!1,error:"Channel is not bound to this session"};const s=this.adapters.get(t.type);if(!s)return{ok:!1,error:`Unsupported channel type: ${t.type}`};try{const o=W(t.type,e);if(!o.length)return{ok:!1,error:"Reply text or attachment is required"};let r=!1;for(const i of o){const a=i.idempotencyKey;if(a&&this.isReplyCompleted(t,e.conversationId,a))continue;const c=await s.send(t,{...e,...i});if(c?.status==="queued"){r=!0;continue}if(c?.status==="manual-review")return{ok:!1,error:c.reason||"Reply requires manual review before retry"};a&&this.markReplyCompleted(t,e.conversationId,a)}return r?{ok:!0,pending:!0}:{ok:!0}}catch(o){return{ok:!1,error:o instanceof Error?o.message:String(o)}}}isReplyCompleted(e,t,s){return this.loadReplyCompletionSet(e).has(A(e.id,t,s))}markReplyCompleted(e,t,s){const o=this.loadReplyCompletionSet(e);o.add(A(e.id,t,s));try{$(e.workDir,o)}catch{}}loadReplyCompletionSet(e){const t=u.resolve(e.workDir),s=this.completedReplyKeys.get(t);if(s)return s;const o=T(e.workDir);return this.completedReplyKeys.set(t,o),o}async getDefaultReplyTarget(e){const t=this.configs.list().find(r=>(r.sessionId??r.managerSessionId)===e&&r.enabled);if(!t)throw new Error("No enabled external channel is bound to this session");const s=this.adapters.get(t.type);if(!s?.defaultConversation)throw new Error(`External channel ${t.type} has no default conversation`);const o=await s.defaultConversation(t);return{channelId:t.id,conversationId:o.conversationId}}getManagerChannel(e,t,s={}){const o=this.configs.list().filter(c=>(c.sessionId??c.managerSessionId)===e&&c.type===t),r=o.find(c=>c.enabled)??o.at(-1);if(!r)return null;const i=this.secrets.get(r.secretRef),a=this.adapters.get(r.type)?.runtimeStatus?.(r)??{};return{id:r.id,type:r.type,name:r.name,sessionId:r.sessionId??r.managerSessionId,managerSessionId:r.managerSessionId,workDir:r.workDir,agentType:r.agentType,agentSessionId:r.agentSessionId,modelId:r.modelId,enabled:r.enabled,wsUrl:i?.wsUrl??"",token:s.includeSecret?i?.token??"":"",tokenConfigured:!!i?.token,canReply:!!i?.canReply,systemPrompt:typeof i?.systemPrompt=="string"?i.systemPrompt:"",...k(i),...a}}getChannelById(e,t={}){const s=this.configs.get(e);if(!s)return null;const o=this.secrets.get(s.secretRef),r=this.adapters.get(s.type)?.runtimeStatus?.(s)??{};return{id:s.id,type:s.type,name:s.name,sessionId:s.sessionId??s.managerSessionId,managerSessionId:s.managerSessionId,workDir:s.workDir,agentType:s.agentType,agentSessionId:s.agentSessionId,modelId:s.modelId,enabled:s.enabled,wsUrl:o?.wsUrl??"",token:t.includeSecret?o?.token??"":"",tokenConfigured:!!o?.token,canReply:!!o?.canReply,systemPrompt:typeof o?.systemPrompt=="string"?o.systemPrompt:"",...k(o),...r}}getChannelStatusById(e){const t=this.configs.get(e);if(!t)return null;const s=this.secrets.get(t.secretRef),o=this.adapters.get(t.type)?.runtimeStatus?.(t)??{};return{configured:!0,connected:y(t,s),type:t.type,channelId:t.id,name:t.name,canReply:!!s?.canReply,systemPrompt:typeof s?.systemPrompt=="string"?s.systemPrompt:"",...h(s),...o}}getManagerChannelStatus(e){const t=this.configs.list().find(r=>(r.sessionId??r.managerSessionId)===e&&r.enabled);if(!t)return null;const s=this.secrets.get(t.secretRef),o=this.adapters.get(t.type)?.runtimeStatus?.(t)??{};return{configured:!0,connected:y(t,s),type:t.type,channelId:t.id,name:t.name,canReply:!!s?.canReply,systemPrompt:typeof s?.systemPrompt=="string"?s.systemPrompt:"",...h(s),...o}}listManagerChannelStatuses(){return this.configs.list().filter(e=>e.enabled).map(e=>({managerSessionId:e.sessionId??e.managerSessionId,status:this.getManagerChannelStatus(e.sessionId??e.managerSessionId)})).filter(e=>!!e.status)}listManagerExternalChannels(e){return this.configs.list().filter(t=>t.enabled&&(t.sessionId??t.managerSessionId)===e).map(t=>{const s=this.secrets.get(t.secretRef),o=this.adapters.get(t.type)?.runtimeStatus?.(t)??{};return{configured:!0,connected:y(t,s),type:t.type,channelId:t.id,name:t.name,canReply:!!s?.canReply,systemPrompt:typeof s?.systemPrompt=="string"?s.systemPrompt:"",...h(s),...o}})}async syncManagerWeChatRpaChannel(e){const t=this.configs.list().find(i=>i.enabled&&i.type==="wechat-rpa"&&(i.sessionId??i.managerSessionId)===e);if(!t)throw new Error("No enabled WeChat RPA channel is bound to this session");const s=this.adapters.get(t.type);if(!s?.syncNow)throw new Error("WeChat RPA channel does not support manual sync");const o=Date.now()-120*1e3,r=K(await s.syncNow(t)??[],this.getRecentMessages(t.id,o));return{channel:this.getManagerChannel(e,"wechat-rpa",{includeSecret:!0}),messages:r}}async cancelManagerWeChatRpaOutbound(e){const t=this.configs.list().find(r=>r.enabled&&r.type==="wechat-rpa"&&(r.sessionId??r.managerSessionId)===e.managerSessionId);if(!t)throw new Error("No enabled WeChat RPA channel is bound to this session");const s=this.adapters.get(t.type);if(!s?.cancelOutbound)throw new Error("WeChat RPA channel does not support outbound cancellation");const o=await s.cancelOutbound(t,{idempotencyKey:e.idempotencyKey,replyId:e.replyId,reason:e.reason});return{cancelled:o.cancelled,status:o.status,channel:this.getManagerChannel(e.managerSessionId,"wechat-rpa",{includeSecret:!0})}}recordRecentMessage(e){const t=this.recentMessages.get(e.channelId)??[];this.findRecentMessageDuplicate(e)||(t.push(e),this.recentMessages.set(e.channelId,t.slice(-50)))}findRecentMessageDuplicate(e){const t=I(e);return t?(this.recentMessages.get(e.channelId)??[]).find(o=>I(o)===t)??null:null}getRecentMessages(e,t){return(this.recentMessages.get(e)??[]).filter(o=>{const r=Date.parse(o.receivedAt);return!Number.isFinite(r)||r>=t})}async upsertManagerChannel(e){const t=this.configs.get(e.id),s=this.configs.list(),o=e.sessionId||e.managerSessionId,r={id:e.id,type:e.type,name:e.name?.trim()||t?.name||"\u5916\u90E8\u6D88\u606F\u901A\u9053",sessionId:o,managerSessionId:o,workDir:e.workDir,agentType:e.agentType||t?.agentType,agentSessionId:e.agentSessionId??t?.agentSessionId??null,modelId:e.modelId??t?.modelId??null,enabled:e.enabled,secretRef:t?.secretRef||`channel:${e.id}`},i=this.secrets.get(r.secretRef),a=e.wsUrl?.trim()||i?.wsUrl||"",c=e.token?.trim()||i?.token||"",p=e.canReply??i?.canReply??!1,m=e.systemPrompt??(typeof i?.systemPrompt=="string"?i.systemPrompt:"");if(r.enabled&&(!a||!c))throw new Error("WebSocket \u5730\u5740\u548C Token \u5FC5\u586B");const f=s.filter(l=>l.id!==r.id).map(l=>(l.sessionId??l.managerSessionId)===o&&l.type===e.type?{...l,enabled:!1}:l);f.push(r),this.configs.replaceAll(f),(a||c)&&this.secrets.upsert(r.secretRef,{type:"websocket",wsUrl:a,token:c,canReply:p,systemPrompt:m});const d=this.adapters.get(r.type);for(const l of s)(l.sessionId??l.managerSessionId)===o&&l.type===e.type&&l.enabled&&await this.adapters.get(l.type)?.disconnect(l).catch(()=>{});return r.enabled&&d?.connect(r).catch(()=>{}),this.getManagerChannel(o,e.type,{includeSecret:!0})}async upsertManagerWeChatRpaChannel(e){const t=this.configs.get(e.id),s=this.configs.list(),o=e.sessionId||e.managerSessionId,r=w(e.groups);if(e.enabled&&!r.length)throw new Error("WeChat RPA \u81F3\u5C11\u9700\u8981\u914D\u7F6E\u4E00\u4E2A\u7FA4");if(e.enabled&&r.length>1)throw new Error("WeChat RPA \u6BCF\u4E2A\u5BF9\u8BDD\u53EA\u80FD\u7ED1\u5B9A\u4E00\u4E2A\u7FA4");const i={id:e.id,type:"wechat-rpa",name:e.name?.trim()||t?.name||"\u672C\u673A\u5FAE\u4FE1 RPA",sessionId:o,managerSessionId:o,workDir:e.workDir,agentType:e.agentType||t?.agentType,agentSessionId:e.agentSessionId??t?.agentSessionId??null,modelId:e.modelId??t?.modelId??null,enabled:e.enabled,secretRef:t?.secretRef||`channel:${e.id}`},a=this.secrets.get(i.secretRef),c=e.source||(a?.source==="wechat-channel"||a?.source==="macos-probe"||a?.source==="fixture-jsonl"||a?.source==="macos-flow"||a?.source==="windows-visual-flow"||a?.source==="wechat-rpa-lab"?a.source:E());if(e.enabled&&c==="windows-visual-flow")throw new Error("\u65E7 Windows visual flow \u5DF2\u5F52\u6863\uFF1BmacOS \u548C Windows \u6B63\u5F0F\u901A\u9053\u90FD\u4F7F\u7528 wechat-channel");const p=e.privacyConsentAccepted??!!a?.privacyConsentAccepted;if(e.enabled&&!p)throw new Error("\u542F\u7528\u524D\u9700\u8981\u786E\u8BA4\u5FAE\u4FE1\u901A\u9053\u6570\u636E\u4E0E\u9690\u79C1\u6388\u6743");const m=s.filter(d=>d.id!==i.id).map(d=>(d.sessionId??d.managerSessionId)===o&&d.type==="wechat-rpa"?{...d,enabled:!1}:d);m.push(i),this.configs.replaceAll(m),this.secrets.upsert(i.secretRef,{type:"wechat-rpa",source:c,groups:r,pollIntervalMs:R(e.pollIntervalMs,a?.pollIntervalMs),recentLimit:R(e.recentLimit,a?.recentLimit),idleSeconds:R(e.idleSeconds,a?.idleSeconds),forceForeground:e.forceForeground??(a?.forceForeground===void 0?!0:!!a.forceForeground),noRestore:e.noRestore??(a?.noRestore===void 0?!0:!!a.noRestore),downloadAttachments:e.downloadAttachments??(a?.downloadAttachments===void 0?!0:!!a.downloadAttachments),downloadAttachmentsDir:e.downloadAttachmentsDir?.trim()||b(a?.downloadAttachmentsDir),selfNickname:e.selfNickname?.trim()||b(a?.selfNickname),selfTriggerMarker:e.selfTriggerMarker?.trim()||void 0,deferInitialPoll:e.deferInitialPoll===!0?!0:void 0,privacyConsentAccepted:p,flowScriptPath:e.flowScriptPath?.trim()||b(a?.flowScriptPath),canReply:e.canReply??a?.canReply??!1,systemPrompt:e.systemPrompt??(typeof a?.systemPrompt=="string"?a.systemPrompt:"")});const f=this.adapters.get(i.type);for(const d of s)(d.sessionId??d.managerSessionId)===o&&d.type==="wechat-rpa"&&d.enabled&&await this.adapters.get(d.type)?.disconnect(d).catch(()=>{});return i.enabled&&f?.connect(i).catch(()=>{}),this.getManagerChannel(o,"wechat-rpa",{includeSecret:!0})}}function y(n,e){return!e||e.type!==n.type?!1:n.type==="wechat-rpa"?!0:n.type==="websocket"?!!(e.wsUrl&&e.token):n.type==="wecom"?!!(e.token||e.botId||e.secret):!!e.token}function k(n){return!n||n.type!=="wechat-rpa"?{}:{wechatRpaSource:typeof n.source=="string"?n.source:"",wechatRpaGroups:w(Array.isArray(n.groups)?n.groups:[]),pollIntervalMs:Number.isFinite(n.pollIntervalMs)?Number(n.pollIntervalMs):void 0,recentLimit:Number.isFinite(n.recentLimit)?Number(n.recentLimit):void 0,idleSeconds:Number.isFinite(n.idleSeconds)?Number(n.idleSeconds):void 0,forceForeground:n.forceForeground===void 0?!0:!!n.forceForeground,noRestore:n.noRestore===void 0?void 0:!!n.noRestore,downloadAttachments:n.downloadAttachments===void 0?!0:!!n.downloadAttachments,downloadAttachmentsDir:typeof n.downloadAttachmentsDir=="string"?n.downloadAttachmentsDir:"",selfNickname:typeof n.selfNickname=="string"?n.selfNickname:"",wechatRpaPrivacyConsentAccepted:!!n.privacyConsentAccepted,wechatRpaServerDecisionAvailable:!0,wechatRpaPreflightChecks:C(n)}}function h(n){return!n||n.type!=="wechat-rpa"?{}:{wechatRpaSource:typeof n.source=="string"?n.source:null,wechatRpaGroups:w(Array.isArray(n.groups)?n.groups:[]),pollIntervalMs:Number.isFinite(n.pollIntervalMs)?Number(n.pollIntervalMs):null,recentLimit:Number.isFinite(n.recentLimit)?Number(n.recentLimit):null,idleSeconds:Number.isFinite(n.idleSeconds)?Number(n.idleSeconds):null,forceForeground:n.forceForeground===void 0?!0:!!n.forceForeground,noRestore:n.noRestore===void 0?null:!!n.noRestore,downloadAttachments:n.downloadAttachments===void 0?!0:!!n.downloadAttachments,downloadAttachmentsDir:typeof n.downloadAttachmentsDir=="string"?n.downloadAttachmentsDir:null,selfNickname:typeof n.selfNickname=="string"?n.selfNickname:null,wechatRpaPrivacyConsentAccepted:!!n.privacyConsentAccepted,wechatRpaServerDecisionAvailable:!0,wechatRpaPreflightChecks:C(n)}}function w(n){const e=new Set,t=[];for(const s of n){const o=String(s?.name||"").replace(/\s+/g," ").trim();!o||e.has(o)||(e.add(o),t.push({name:o}))}return t}function E(){return"wechat-channel"}function C(n){const e=[];if(n.lastHelperPermissions&&typeof n.lastHelperPermissions=="object"){const t=n.lastHelperPermissions;e.push({code:"wechat_window_unavailable",ok:t.wechatWindowAvailable!==!1,severity:"blocking",message:t.wechatWindowAvailable===!1?"\u5FAE\u4FE1\u7A97\u53E3\u4E0D\u53EF\u7528\uFF0C\u8BF7\u6253\u5F00\u5E76\u767B\u5F55\u5FAE\u4FE1\u540E\u518D\u8BD5\u3002":"\u5FAE\u4FE1\u7A97\u53E3\u53EF\u7528\u3002"})}return e.push({code:"platform_unsupported",ok:n.source!=="windows-visual-flow",severity:"blocking",message:n.source==="windows-visual-flow"?"\u65E7 Windows visual flow \u5DF2\u5F52\u6863\uFF0C\u8BF7\u6539\u7528 wechat-channel\u3002":"\u5F53\u524D\u914D\u7F6E\u4F7F\u7528\u8DE8\u5E73\u53F0\u5FAE\u4FE1\u901A\u9053\u3002"}),e.push({code:"privacy_consent_required",ok:!!n.privacyConsentAccepted,severity:"blocking",message:n.privacyConsentAccepted?"\u5DF2\u786E\u8BA4\u5FAE\u4FE1\u901A\u9053\u6570\u636E\u4E0E\u9690\u79C1\u6388\u6743\u3002":"\u542F\u7528\u524D\u9700\u8981\u786E\u8BA4\u5FAE\u4FE1\u901A\u9053\u6570\u636E\u4E0E\u9690\u79C1\u6388\u6743\u3002"}),e.push({code:"server_decision_unavailable",ok:!0,severity:"blocking",message:"\u670D\u52A1\u7AEF\u5224\u65AD\u80FD\u529B\u53EF\u7528\u3002"}),e}function W(n,e){const t=F(e.text);if(!t.length&&!e.attachment)return[];if(n==="wechat-rpa"&&e.attachment&&t.length<=1)return[{text:t[0]??"",attachment:e.attachment,idempotencyKey:e.idempotencyKey}];const s=t.map((o,r)=>({text:o,attachment:void 0,idempotencyKey:t.length>1&&e.idempotencyKey?`${e.idempotencyKey}:${r+1}`:e.idempotencyKey}));return e.attachment&&s.push({text:"",attachment:e.attachment,idempotencyKey:t.length&&e.idempotencyKey?`${e.idempotencyKey}:attachment`:e.idempotencyKey}),s}function K(...n){const e=new Set,t=[];for(const s of n.flat()){const o=I(s);if(!o){t.push(s);continue}e.has(o)||(e.add(o),t.push(s))}return t}function I(n){const e=S(n.channelId),t=S(n.conversationId),s=S(n.messageId);return!e||!t||!s?null:`${e}
|
|
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}
|
|
@@ -17,6 +17,8 @@ type ChannelSecretRecord = {
|
|
|
17
17
|
downloadAttachments?: boolean;
|
|
18
18
|
downloadAttachmentsDir?: string;
|
|
19
19
|
selfNickname?: string;
|
|
20
|
+
selfTriggerMarker?: string;
|
|
21
|
+
deferInitialPoll?: boolean;
|
|
20
22
|
privacyConsentAccepted?: boolean;
|
|
21
23
|
idleSeconds?: number;
|
|
22
24
|
recentLimit?: number;
|
|
@@ -94,7 +94,8 @@ export type WeChatChannelApiClient = ReturnType<typeof createWeChatChannelApiCli
|
|
|
94
94
|
export declare class WeChatChannelApiError extends Error {
|
|
95
95
|
readonly reasonCode: string;
|
|
96
96
|
readonly statusCode: number;
|
|
97
|
-
|
|
97
|
+
readonly details?: unknown | undefined;
|
|
98
|
+
constructor(message: string, reasonCode: string, statusCode: number, details?: unknown | undefined);
|
|
98
99
|
}
|
|
99
100
|
export type WeChatChannelObserveInput = {
|
|
100
101
|
screenshots: Array<{
|
|
@@ -107,6 +108,7 @@ export type WeChatChannelObserveInput = {
|
|
|
107
108
|
edgeOcrBlocks?: unknown[];
|
|
108
109
|
visibleConversationFingerprints?: unknown[];
|
|
109
110
|
localLedgerTailAnchors?: unknown[];
|
|
111
|
+
traceId?: string;
|
|
110
112
|
};
|
|
111
113
|
export type WeChatChannelObserveResponse = {
|
|
112
114
|
ok: true;
|
|
@@ -203,9 +205,14 @@ export type WeChatChannelRunStatusInput = {
|
|
|
203
205
|
helperReady?: boolean;
|
|
204
206
|
};
|
|
205
207
|
};
|
|
208
|
+
export type WeChatChannelRuntimePreflightInput = WeChatChannelRunStatusInput['preflight'];
|
|
206
209
|
export declare function createWeChatChannelApiClient(options?: WeChatChannelApiClientOptions): {
|
|
207
210
|
getRuntimePolicy: () => Promise<WeChatChannelRuntimePolicyResponse>;
|
|
208
|
-
upsertRuntime: (runtime: WeChatChannelRuntime, binding?: WeChatChannelBindingConfig
|
|
211
|
+
upsertRuntime: (runtime: WeChatChannelRuntime, binding?: WeChatChannelBindingConfig, input?: {
|
|
212
|
+
preflight?: WeChatChannelRuntimePreflightInput;
|
|
213
|
+
helperVersion?: string;
|
|
214
|
+
clientRuntimeVersion?: string;
|
|
215
|
+
}) => Promise<unknown>;
|
|
209
216
|
observe: (runtime: WeChatChannelRuntime, binding: WeChatChannelBindingConfig, input: WeChatChannelObserveInput) => Promise<WeChatChannelObserveResponse>;
|
|
210
217
|
structureWindow: (runtime: WeChatChannelRuntime, binding: WeChatChannelBindingConfig, input: WeChatChannelStructureWindowInput) => Promise<WeChatChannelStructureWindowResponse>;
|
|
211
218
|
embedVisual: (runtime: WeChatChannelRuntime, binding: WeChatChannelBindingConfig, input: WeChatChannelEmbedVisualInput) => Promise<WeChatChannelEmbedVisualResponse>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{SERVERS as
|
|
1
|
+
import{SERVERS as f}from"../../region.js";import{loadConfig as y}from"../../config/index.js";class h extends Error{reasonCode;statusCode;details;constructor(o,d,c,I){super(o),this.reasonCode=d,this.statusCode=c,this.details=I,this.name="WeChatChannelApiError"}}function S(r={}){const o=y(),d=v(r.serverUrl||o.serverUrl||f.cn.url),c=r.machineToken||o.machineToken,I=r.fetchImpl||fetch;if(!c)throw new Error("WeChat channel requires a paired machine token");async function t(s,n){const e=C(n);let a;try{a=await I(`${d}/api/channels/wechat${s}`,{method:n===void 0?"GET":"POST",headers:{authorization:`Bearer ${c}`,...n===void 0?{}:{"content-type":"application/json"},...e?{"x-trace-id":e}:{}},body:n===void 0?void 0:JSON.stringify(n)})}catch(i){const u=i instanceof Error?i.message:String(i||"network request failed");throw new h(`WeChat channel API ${s} network failed: ${u}`,"wechat_channel_network_failed",0,{path:s,traceId:e||null,cause:u})}const m=await a.text();let l=null;try{l=m?JSON.parse(m):null}catch{throw new h(`WeChat channel API ${s} returned invalid JSON`,"wechat_channel_invalid_response",a.status,{path:s,traceId:e||null,text:m.slice(0,200)})}if(!a.ok||l?.ok===!1){const i=p(l?.reasonCode)||a.statusText||"request_failed";throw new h(`WeChat channel API ${s} failed: ${i}`,i,a.status,{path:s,traceId:e||null,statusCode:a.status})}return l}return{getRuntimePolicy:()=>t("/runtime-policy"),upsertRuntime:(s,n,e)=>t("/runtime",{runtimeId:s.runtimeId,machineId:s.machineId,pollIntervalSeconds:Math.round(s.policy.pollIntervalMs/1e3),foregroundPolicy:s.foregroundPolicy,clientRuntimeVersion:e?.clientRuntimeVersion??String(s.policy.runtimeVersion),helperVersion:e?.helperVersion,preflight:e?.preflight,...n?g(n):{}}),observe:(s,n,e)=>t("/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??[],traceId:e.traceId}),structureWindow:(s,n,e)=>t("/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??[],traceId:e.traceId}),embedVisual:(s,n,e)=>t("/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)=>t("/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)=>t("/ingest",{runtimeId:s.runtimeId,idempotencyKey:e.idempotencyKey,bindingId:n.bindingId,sessionId:n.sessionId,machineId:s.machineId,messages:e.messages}),reportOutboundStatus:(s,n,e)=>t("/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)=>t("/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 g(r){return{bindingId:r.bindingId,sessionId:r.sessionId,conversationName:r.conversationDisplayName,enabled:r.enabled,allowReply:r.allowReply,downloadMedia:r.downloadMedia}}function v(r){return r.replace(/\/+$/,"")}function C(r){if(!r||typeof r!="object")return;const o=r.traceId;return(typeof o=="string"?o.trim():"")||void 0}function p(r){return typeof r=="string"?r.trim():""}export{h as WeChatChannelApiError,S as createWeChatChannelApiClient};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const WECHAT_CHANNEL_HELPER_VERSION = "0.1.
|
|
1
|
+
export declare const WECHAT_CHANNEL_HELPER_VERSION = "0.1.10";
|
|
2
2
|
export declare const WECHAT_CHANNEL_HELPER_DIR_ENV = "SHENNIAN_WECHAT_CHANNEL_HELPER_DIR";
|
|
3
3
|
export declare const SHENNIAN_HELPER_RUNTIME_DIR_ENV = "SHENNIAN_HELPER_RUNTIME_DIR";
|
|
4
4
|
export declare const WECHAT_CHANNEL_HELPER_RUNTIME_REQUIRED_REASON = "helper_runtime_required";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import c from"node:fs";import s from"node:path";import E from"node:crypto";const V="0.1.
|
|
1
|
+
import c from"node:fs";import s from"node:path";import E from"node:crypto";const V="0.1.10",S="SHENNIAN_WECHAT_CHANNEL_HELPER_DIR",g="SHENNIAN_HELPER_RUNTIME_DIR",C="helper_runtime_required",u=["en","v"].join("");function $(n={}){const r=n.platform??process.platform;if(r!=="darwin"&&r!=="win32")return{ok:!1,reasonCode:"unsupported_platform",message:"WeChat channel helper is only available on macOS and Windows"};const e=n.baseDir?[s.resolve(n.baseDir)]:A({platform:r,env:n[u]??R(),homedir:n.homedir,includeInstalledDesktop:n.includeInstalledDesktop});let a=null;for(const t of e){const i=_({platform:r,baseDir:t,verifyIntegrity:n.verifyIntegrity});if(i.ok||(n.baseDir||c.existsSync(t))&&(a??=i,n.baseDir))return i}return a??{ok:!1,reasonCode:C,message:x(r,e)}}function _(n){const{platform:r,baseDir:e}=n,a=s.join(e,"manifest.json"),t=F(a);if(!t)return{ok:!1,reasonCode:"manifest_missing",message:`WeChat channel helper manifest is missing: ${a}`};const i=t.platforms[r],o=i?s.join(e,i.executable):"";if(!i||!o||!c.existsSync(o))return{ok:!1,reasonCode:"helper_missing",message:`WeChat channel helper executable is missing: ${o||e}`};if(N({platform:r,baseDir:e,verifyIntegrity:n.verifyIntegrity})&&i.sha256&&E.createHash("sha256").update(c.readFileSync(o)).digest("hex")!==i.sha256)return{ok:!1,reasonCode:"integrity_mismatch",message:`WeChat channel helper integrity mismatch: ${o}`};const f=H(r,o);return f.ok?{ok:!0,helperPath:o,helperDir:e,version:t.helperVersion,manifest:t,warning:i.signed&&i.notarized?void 0:"helper_not_signed_or_notarized"}:{ok:!1,reasonCode:"helper_not_executable",message:f.message}}function H(n,r){if(n!=="darwin")return{ok:!0};try{const e=c.statSync(r);return(e.mode&73)!==0?{ok:!0}:(c.chmodSync(r,e.mode|73),{ok:!0})}catch(e){return{ok:!1,message:`WeChat channel helper is not executable and could not be repaired: ${r}: ${e instanceof Error?e.message:String(e)}`}}}function A(n){const{platform:r,env:e}=n,a=r==="win32"?"windows":"macos",t=[],i=l=>{if(!l)return;const p=s.resolve(l);t.includes(p)||t.push(p)},o=e[S]?.trim();o&&i(j(o,a));const f=e[g]?.trim();if(f)for(const l of P(f,a))i(l);if(b({platform:r,platformDir:a,env:e,homedir:n.homedir,push:i}),n.includeInstalledDesktop===!1)return t;if(r==="darwin"){const l=n.homedir||e.HOME;l&&i(s.join(l,"Applications","Shennian.app","Contents","Resources","wechat-channel",a)),i(s.join("/Applications","Shennian.app","Contents","Resources","wechat-channel",a))}else if(r==="win32"){const l=n.homedir||e.USERPROFILE,p=e.LOCALAPPDATA||(l?s.join(l,"AppData","Local"):""),m=e.ProgramFiles||e.PROGRAMFILES,h=e["ProgramFiles(x86)"]||e["PROGRAMFILES(X86)"];for(const d of[p?s.join(p,"Programs","Shennian"):"",p?s.join(p,"Programs","shennian"):"",m?s.join(m,"Shennian"):"",h?s.join(h,"Shennian"):""])d&&i(s.join(d,"resources","wechat-channel",a))}return t}function b(n){const{platform:r,platformDir:e,env:a,push:t}=n;if(r==="darwin"){const i=n.homedir||a.HOME,o=k({platform:r,env:a,homedir:n.homedir});o&&(t(s.join(o,"Shennian Helper.app","Contents","Resources","wechat-channel",e)),t(s.join(o,"wechat-channel",e))),i&&t(s.join(i,"Applications","Shennian Helper.app","Contents","Resources","wechat-channel",e)),t(s.join("/Applications","Shennian Helper.app","Contents","Resources","wechat-channel",e)),t(s.join("/Library","Application Support","Shennian","Helper","wechat-channel",e))}else if(r==="win32"){const i=n.homedir||a.USERPROFILE,o=a.LOCALAPPDATA||(i?s.join(i,"AppData","Local"):""),f=a.ProgramFiles||a.PROGRAMFILES,l=a["ProgramFiles(x86)"]||a["PROGRAMFILES(X86)"];o&&(t(s.join(o,"Programs","Shennian Helper","resources","wechat-channel",e)),t(s.join(o,"Shennian","Helper","wechat-channel",e))),f&&t(s.join(f,"Shennian Helper","resources","wechat-channel",e)),l&&t(s.join(l,"Shennian Helper","resources","wechat-channel",e))}}function j(n,r){return c.existsSync(s.join(n,"manifest.json"))?n:s.join(n,r)}function P(n,r){if(c.existsSync(s.join(n,"manifest.json")))return[n];const e=s.join(n,"Shennian Helper.app","Contents","Resources","wechat-channel",r),a=s.join(n,"wechat-channel",r),t=s.join(n,r);return[e,a,t]}function k(n){const r=n[u],e=r[g]?.trim();if(e)return s.resolve(e);const a=n.homedir||(n.platform==="win32"?r.USERPROFILE:r.HOME);if(n.platform==="darwin")return a?s.join(a,"Library","Application Support","Shennian","Helper"):null;if(n.platform==="win32"){const t=r.LOCALAPPDATA||(a?s.join(a,"AppData","Local"):"");return t?s.join(t,"Shennian","Helper"):null}return null}function q(n={}){return k({platform:n.platform??process.platform,env:n.env??R(),homedir:n.homedir})}function D(n){const r=s.resolve(n);return O([s.join(r,"helper-runtime-package.json"),s.join(s.dirname(r),"..","helper-runtime-package.json")])}function U(n){const r=n.platform??process.platform;if(r!=="darwin"&&r!=="win32")return{ok:!1,reasonCode:"unsupported_platform",message:"WeChat channel helper runtime package is only available on macOS and Windows"};const e=n.manifestPath?[s.resolve(n.manifestPath)]:n.helperDir?D(n.helperDir):[],a=e.find(t=>c.existsSync(t));if(!a)return{ok:!1,reasonCode:"helper_runtime_package_manifest_missing",message:`Helper runtime package manifest is missing. Checked: ${e.join(", ")||"(none)"}`,manifestPath:e[0]};try{const t=JSON.parse(w(a)),i=I(t,r);return i.ok?{ok:!0,manifestPath:a,manifest:t}:{ok:!1,reasonCode:"helper_runtime_package_manifest_invalid",message:`${i.message}: ${a}`,manifestPath:a}}catch(t){return{ok:!1,reasonCode:"helper_runtime_package_manifest_invalid",message:`Helper runtime package manifest is invalid JSON: ${t instanceof Error?t.message:String(t)}`,manifestPath:a}}}function I(n,r){if(!n||typeof n!="object"||Array.isArray(n))return{ok:!1,message:"manifest must be an object"};const e=n;return e.schemaVersion!==1?{ok:!1,message:"schemaVersion must be 1"}:e.packageKind!=="shennian-helper-runtime"?{ok:!1,message:"packageKind must be shennian-helper-runtime"}:e.platform!==r?{ok:!1,message:`platform must be ${r}`}:e.helperVersion?!Number.isInteger(e.protocolVersion)||Number(e.protocolVersion)<=0?{ok:!1,message:"protocolVersion must be a positive integer"}:L(e.minCliVersion)?!e.sha256||typeof e.sha256.runtimeManifest!="string"?{ok:!1,message:"sha256.runtimeManifest is required"}:y(e.sha256.runtimeManifest)?e.sha256.entrypoint!==null&&typeof e.sha256.entrypoint!="string"?{ok:!1,message:"sha256.entrypoint must be a string or null"}:typeof e.sha256.entrypoint=="string"&&!y(e.sha256.entrypoint)?{ok:!1,message:"sha256.entrypoint must be a sha256 digest or null"}:!e.installTarget?.kind||!e.installTarget.defaultPath?{ok:!1,message:"installTarget.kind and installTarget.defaultPath are required"}:!e.payload?.kind||!e.payload.runtimeManifest||!e.payload.entrypoint?{ok:!1,message:"payload.kind, payload.runtimeManifest, and payload.entrypoint are required"}:r==="darwin"&&e.installTarget.kind!=="app-bundle"?{ok:!1,message:"darwin installTarget.kind must be app-bundle"}:r==="darwin"&&e.payload.kind!=="macos-helper-app"?{ok:!1,message:"darwin payload.kind must be macos-helper-app"}:r==="win32"&&e.installTarget.kind!=="directory"?{ok:!1,message:"win32 installTarget.kind must be directory"}:r==="win32"&&e.payload.kind!=="windows-helper-runtime"?{ok:!1,message:"win32 payload.kind must be windows-helper-runtime"}:!e.signature||typeof e.signature.requiredForRelease!="boolean"||typeof e.signature.signed!="boolean"||typeof e.signature.notarized!="boolean"?{ok:!1,message:"signature.requiredForRelease, signature.signed, and signature.notarized are required"}:{ok:!0}:{ok:!1,message:"sha256.runtimeManifest must be a sha256 digest"}:{ok:!1,message:"minCliVersion must be a semver string"}:{ok:!1,message:"helperVersion is required"}}function L(n){return typeof n=="string"&&/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/.test(n)}function y(n){return/^[a-f0-9]{64}$/i.test(n)}function N(n){return n.verifyIntegrity===!0?!0:n.verifyIntegrity===!1?!1:!v(n.platform,n.baseDir)}function v(n,r){const e=r.replace(/[\\/]+/g,"/");return n==="darwin"?/\/Shennian\.app\/Contents\/Resources\/wechat-channel\/macos$/i.test(e):n==="win32"?/\/resources\/wechat-channel\/windows$/i.test(e):!1}function x(n,r){const e=n==="win32"?"Windows":"macOS",a=r.length>0?` Checked helper directories: ${r.join(", ")}`:"";return`WeChat RPA requires Shennian Helper runtime on ${e}; open Shennian Desktop or use the \u4F7F\u7528\u5FAE\u4FE1 page to install Helper, then retry.${a}`}function R(){return globalThis.process?.[u]??{}}function F(n){try{const r=JSON.parse(w(n));return r.schemaVersion!==1||typeof r.helperVersion!="string"||!r.platforms?null:r}catch{return null}}function w(n){return c.readFileSync(n,"utf8").replace(/^\uFEFF/,"")}function O(n){const r=new Set,e=[];for(const a of n){const t=s.resolve(a);r.has(t)||(r.add(t),e.push(t))}return e}export{g as SHENNIAN_HELPER_RUNTIME_DIR_ENV,S as WECHAT_CHANNEL_HELPER_DIR_ENV,C as WECHAT_CHANNEL_HELPER_RUNTIME_REQUIRED_REASON,V as WECHAT_CHANNEL_HELPER_VERSION,q as getDefaultWeChatHelperRuntimeRoot,D as helperRuntimePackageManifestCandidates,U as readWeChatChannelHelperRuntimePackageManifest,$ as resolveWeChatChannelHelperAsset,I as validateWeChatChannelHelperRuntimePackageManifest};
|
|
@@ -9,6 +9,9 @@ export type WeChatChannelHelperClientOptions = {
|
|
|
9
9
|
skipUserActivityGuard?: boolean;
|
|
10
10
|
cleanupWindowsOverlays?: boolean;
|
|
11
11
|
requestLogger?: (event: WeChatChannelHelperRequestTraceEvent) => void;
|
|
12
|
+
openHelperAppForTest?: (command: string, args: string[]) => Promise<void>;
|
|
13
|
+
macosRuntimeInitialReadyTimeoutMs?: number;
|
|
14
|
+
macosRuntimeRelaunchReadyTimeoutMs?: number;
|
|
12
15
|
};
|
|
13
16
|
export type WeChatChannelHelperRequestTraceEvent = {
|
|
14
17
|
phase: 'request' | 'response' | 'error';
|
|
@@ -60,8 +63,10 @@ export declare class WeChatChannelHelperClient {
|
|
|
60
63
|
private formatHelperExitMessage;
|
|
61
64
|
private rejectAllPending;
|
|
62
65
|
private emitRequestTrace;
|
|
66
|
+
private stopAfterCommandTimeout;
|
|
63
67
|
private connectDarwinHelperSocket;
|
|
64
68
|
}
|
|
69
|
+
export declare function stopWindowsWeChatChannelHelperProcesses(): Promise<void>;
|
|
65
70
|
type LegacyRawHelperLaunchSpec = {
|
|
66
71
|
transport: 'legacy-stdio';
|
|
67
72
|
command: string;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{spawn as w}from"node:child_process";import{randomUUID as
|
|
2
|
-
`)}catch(i){throw await this.stop().catch(()=>{}),i}return s}async startDarwinAppSocketFresh(e){const
|
|
3
|
-
`;if(this.socket){this.socket.write(
|
|
1
|
+
import{spawn as w}from"node:child_process";import{randomUUID as R}from"node:crypto";import f from"node:fs";import v from"node:net";import M from"node:os";import d from"node:path";import{createInterface as g}from"node:readline";import{decideWeChatChannelActivityGate as x,normalizeWeChatChannelActivitySnapshot as A}from"./human-coordination.js";import{createWeChatChannelHelperHello as S,extractWeChatChannelHelperWarmupSnapshot as H,timeoutForWeChatChannelHelperCommand as $,validateWeChatChannelHelperReady as k}from"./helper-protocol.js";const q=["en","v"].join(""),F="shennian-wechat-channel-helper.exe",m=1500;class ue{options;child=null;socket=null;lines=null;readyState=null;warmupState=null;stderrTail="";startPromise=null;requestQueueTail=Promise.resolve();pending=new Map;constructor(e){this.options=e}async start(){return this.hasTransport()&&this.readyState?this.readyState:this.startPromise?this.startPromise:(this.hasTransport()&&await this.stop(),this.startPromise=this.startFresh().catch(async e=>{throw await this.stop().catch(()=>{}),e}).finally(()=>{this.startPromise=null}),this.startPromise)}async startFresh(){const e=U(this.options.helperPath,this.options.args??[]);return e.transport==="macos-socket"?this.startDarwinAppSocketFresh(e):this.startStdioFresh(e)}async startStdioFresh(e){const r=w(e.command,e.args,{cwd:this.options.cwd,stdio:["pipe","pipe","pipe"],windowsHide:!0});this.child=r,this.stderrTail="",this.lines=g({input:r.stdout}),this.lines.on("line",i=>this.handleLine(i)),r.stderr.on("data",i=>{this.captureStderr(i)}),r.stdin.on("error",i=>{this.rejectAllPending(i instanceof Error?i:new Error(String(i)))}),r.once("error",i=>{this.child===r&&(this.child=null,this.readyState=null,this.lines?.close(),this.lines=null),this.rejectAllPending(i instanceof Error?i:new Error(String(i)))}),r.once("exit",(i,n)=>{this.child===r&&(this.child=null,this.readyState=null,this.lines?.close(),this.lines=null),this.rejectAllPending(new Error(this.formatHelperExitMessage(i,n)))});const s=new Promise((i,n)=>{const a=setTimeout(()=>n(new Error("WeChat channel helper handshake timed out")),1e4),l=u=>{clearTimeout(a),this.lines?.off("line",c),r.off("exit",o),n(new Error(`WeChat channel helper failed to start: ${u.message}`))},o=(u,h)=>{clearTimeout(a),r.off("error",l),this.lines?.off("line",c),n(new Error(this.formatHelperExitMessage(u,h)))},c=u=>{let h;try{h=JSON.parse(u)}catch{clearTimeout(a),r.off("error",l),this.lines?.off("line",c),n(new Error("WeChat channel helper sent invalid handshake JSON"));return}if(!b(h))return;clearTimeout(a),r.off("error",l),r.off("exit",o),this.lines?.off("line",c);const p=k(h,this.options.expectedHelperVersion,this.options.requiredCapabilities);p.ok?(this.readyState=h,this.captureWarmupSnapshot(h),i(h)):n(new Error(`${p.errorCode}: ${p.errorSummary}`))};r.once("error",l),r.once("exit",o),this.lines?.on("line",c)});try{r.stdin.write(`${JSON.stringify(S(this.options.expectedHelperVersion,this.options.requiredCapabilities))}
|
|
2
|
+
`)}catch(i){throw await this.stop().catch(()=>{}),i}return s}async startDarwinAppSocketFresh(e){const r=await this.connectDarwinHelperSocket(e);this.socket=r,this.stderrTail="",this.lines=g({input:r}),this.lines.on("line",i=>this.handleLine(i)),r.on("error",i=>{this.socket===r&&(this.socket=null,this.readyState=null,this.lines?.close(),this.lines=null),this.rejectAllPending(i instanceof Error?i:new Error(String(i)))}),r.once("close",()=>{this.socket===r&&(this.socket=null,this.readyState=null,this.lines?.close(),this.lines=null),this.rejectAllPending(new Error("WeChat channel helper socket closed"))});const s=new Promise((i,n)=>{const a=setTimeout(()=>n(new Error("WeChat channel helper socket handshake timed out")),1e4),l=u=>{clearTimeout(a),this.lines?.off("line",c),r.off("close",o),n(new Error(`WeChat channel helper socket failed: ${u.message}`))},o=()=>{clearTimeout(a),r.off("error",l),this.lines?.off("line",c),n(new Error("WeChat channel helper socket closed before handshake"))},c=u=>{let h;try{h=JSON.parse(u)}catch{clearTimeout(a),r.off("error",l),r.off("close",o),this.lines?.off("line",c),n(new Error("WeChat channel helper sent invalid socket handshake JSON"));return}if(!b(h))return;clearTimeout(a),r.off("error",l),r.off("close",o),this.lines?.off("line",c);const p=k(h,this.options.expectedHelperVersion,this.options.requiredCapabilities);p.ok?(this.readyState=h,this.captureWarmupSnapshot(h),i(h)):n(new Error(`${p.errorCode}: ${p.errorSummary}`))};r.once("error",l),r.once("close",o),this.lines?.on("line",c)});try{this.writeFrame(S(this.options.expectedHelperVersion,this.options.requiredCapabilities))}catch(i){throw await this.stop().catch(()=>{}),i}return s}async request(e,r,s){const i=this.requestQueueTail.catch(()=>{}).then(()=>this.sendRequest(e,r,s));return this.requestQueueTail=i.then(()=>{},()=>{}),i}async sendRequest(e,r,s){await this.guardUnsafeWindowsCommand(e,s);try{return await this.sendRawRequest(e,r,s)}finally{await this.cleanupWindowsOverlaysAfterCommand(e,s).catch(()=>{})}}async sendRawRequest(e,r,s){if((!this.hasTransport()||!this.readyState)&&await this.start(),!this.hasTransport())throw new Error("WeChat channel helper is not started");const i=R(),n=$(e),a=Date.now();this.emitRequestTrace({phase:"request",at:new Date(a).toISOString(),id:i,command:e,traceId:s,params:r,timeoutMs:n});const l=new Promise((o,c)=>{const u=setTimeout(()=>{this.pending.get(i)&&(this.pending.delete(i),this.stopAfterCommandTimeout(e).finally(()=>{c(new Error(`helper_command_timeout: ${e}`))}))},n);this.pending.set(i,{resolve:o,reject:c,timer:u})});try{this.writeFrame({id:i,command:e,params:r,traceId:s})}catch(o){const c=this.pending.get(i);throw c&&clearTimeout(c.timer),this.pending.delete(i),this.emitRequestTrace({phase:"error",at:new Date().toISOString(),id:i,command:e,traceId:s,durationMs:Date.now()-a,errorSummary:o instanceof Error?o.message:String(o)}),o}try{const o=await l;return this.emitRequestTrace({phase:"response",at:new Date().toISOString(),id:i,command:e,traceId:s,durationMs:Date.now()-a,ok:o.ok,errorCode:o.errorCode,errorSummary:o.errorSummary,latencyMs:o.latencyMs,result:o.result}),this.captureWarmupSnapshot(o),this.captureWarmupSnapshot(o.result),o}catch(o){throw this.emitRequestTrace({phase:"error",at:new Date().toISOString(),id:i,command:e,traceId:s,durationMs:Date.now()-a,errorSummary:o instanceof Error?o.message:String(o)}),o}}async guardUnsafeWindowsCommand(e,r){if(!this.options.guardUnsafeWindowsCommands||!Z(e))return;const s=await this.sendRawRequest("permissions.check",{},r);if(!s.ok)throw new Error(`${s.errorCode??"permissions_check_failed"}: ${s.errorSummary??e}`);const i=s.result??{},n=await this.requestFirstMissingMacPermissionPrompt(i,r);if(n==="screen-recording")throw new Error("permission_screen_recording_missing");if(n==="accessibility")throw new Error("permission_accessibility_missing");if(n==="input-monitoring")throw new Error("permission_input_monitoring_missing");if(i.automation===!1)throw new Error("permission_automation_missing");if(i.wechatRunning===!1)throw new Error("wechat_not_running");if(O(i))throw new Error("wechat_login_required");if(N(i))throw new Error("windows_visible_desktop_unavailable");if(i.dpiMappingAvailable===!1||i.displayTopologySupported===!1)throw new Error("dpi_mapping_failed");if(i.wechatWindowAvailable===!1)throw new Error("wechat_window_unavailable");if(i.wechatMainWindowResponsive===!1)throw new Error("wechat_window_unresponsive");if(!this.options.skipUserActivityGuard&&te(e)){const a=await this.sendRawRequest("activity.snapshot",{},r);if(!a.ok)throw new Error(`${a.errorCode??"user_activity_unknown"}: ${a.errorSummary??e}`);const l=x({snapshot:A(a.result),stage:"dangerous_action"});if(!l.ok)throw new Error(`user_active:${l.reasonCode}`)}await this.cleanupWindowsOverlays("before",e,r)}async requestFirstMissingMacPermissionPrompt(e,r){const s=K(e);if(!s||process.platform!=="darwin")return s;const i=Y(s);try{await this.sendRawRequest(i,{},r)}catch{}return s}async cleanupWindowsOverlaysAfterCommand(e,r){!this.options.guardUnsafeWindowsCommands||!ee(e)||await this.cleanupWindowsOverlays("after",e,r)}async cleanupWindowsOverlays(e,r,s){if(this.options.cleanupWindowsOverlays===!1)return;if(!this.readyState?.capabilities.includes("overlayCleanup"))throw new Error("helper_capability_missing: overlayCleanup");const i=await this.sendRawRequest("windows.cleanupOverlays",{stage:e,command:r,includeGeneratedMediaPreviews:!0,includeToolingTerminals:!1,waitMs:e==="after"?220:120},s);if(!i.ok)throw new Error(`${i.errorCode??"overlay_cleanup_failed"}: ${i.errorSummary??r}`)}async healthCheck(e){return this.request("health.check",{},e)}getReadyState(){return this.readyState?{...this.readyState,capabilities:[...this.readyState.capabilities]}:null}getWarmupState(){return this.warmupState?{warmState:this.warmupState.warmState,metrics:this.warmupState.metrics?{...this.warmupState.metrics}:void 0}:null}async stop(){const e=this.child,r=this.socket;this.child=null,this.socket=null,this.readyState=null,this.lines?.close(),this.lines=null,this.rejectAllPending(new Error("WeChat channel helper stopped")),r&&!r.destroyed&&r.destroy(),e&&await J(e)}hasTransport(){return!!this.child||!!this.socket}writeFrame(e){const r=`${JSON.stringify(e)}
|
|
3
|
+
`;if(this.socket){this.socket.write(r);return}if(this.child){this.child.stdin.write(r);return}throw new Error("WeChat channel helper is not started")}handleLine(e){let r;try{r=JSON.parse(e)}catch{return}if(!X(r))return;const s=this.pending.get(r.id);s&&(clearTimeout(s.timer),this.pending.delete(r.id),s.resolve(r))}captureWarmupSnapshot(e){const r=H(e);r&&(this.warmupState=r)}captureStderr(e){const r=Buffer.isBuffer(e)?e.toString("utf8"):String(e??"");r&&(this.stderrTail=(this.stderrTail+r).slice(-2e3))}formatHelperExitMessage(e,r){const s=[e===null?null:`code=${e}`,r?`signal=${r}`:null,this.stderrTail.trim()?`stderr=${this.stderrTail.trim()}`:null].filter(Boolean);return s.length?`WeChat channel helper process exited (${s.join(", ")})`:"WeChat channel helper process exited"}rejectAllPending(e){for(const[r,s]of this.pending)clearTimeout(s.timer),s.reject(e),this.pending.delete(r)}emitRequestTrace(e){try{this.options.requestLogger?.(e)}catch{}}async stopAfterCommandTimeout(e){await this.stop().catch(()=>{}),process.platform==="win32"&&await D().catch(()=>{}),this.emitRequestTrace({phase:"error",at:new Date().toISOString(),id:`timeout-cleanup:${e}`,command:e,errorSummary:"helper process stopped after command timeout"})}async connectDarwinHelperSocket(e){try{return await _(e.runtimeFile)}catch{f.rmSync(e.runtimeFile,{force:!0})}await W(e,{},this.options.openHelperAppForTest);const r=await T(e.runtimeFile,this.options.macosRuntimeInitialReadyTimeoutMs??5e3);if(r.ok)return r.socket;f.rmSync(e.runtimeFile,{force:!0}),await W(e,{forceNewInstance:!0},this.options.openHelperAppForTest);const s=await T(e.runtimeFile,this.options.macosRuntimeRelaunchReadyTimeoutMs??1e4);if(s.ok)return s.socket;throw new Error(`WeChat channel Helper.app socket did not become ready: ${s.error.message}`)}}async function D(){process.platform==="win32"&&await C(["taskkill","/IM",F,"/T","/F"],[0,128])}function O(t){return t.wechatWindowAvailable!==!1?!1:![...t.captureCandidates??[],...t.restoreCandidates??[],...t.hiddenRestoreCandidates??[]].some(L)}function N(t){return t.windowsVisibleDesktopAvailable===!1||t.rdpVisibleDesktopAvailable===!1||t.desktopSessionVisible===!1||t.screenLocked===!0||t.rdpDisconnected===!0}function I(t){const e=String(t.appName||"").toLowerCase();return e==="wechat"||e==="weixin"}function L(t){return!(!I(t)||t.visible===!1||t.minimized===!0)}function U(t,e,r=process.platform){if(r==="darwin"){const s=G(t);if(s&&e.length===0){const i=V();return{transport:"macos-socket",command:"/usr/bin/open",args:["-g",s,"--args","--socket-runtime",i],appPath:s,runtimeDir:i,runtimeFile:d.join(i,"runtime.json")}}}return{transport:"legacy-stdio",command:t,args:e}}function G(t){const e=d.resolve(t),r=`${d.sep}Contents${d.sep}MacOS${d.sep}`,s=e.lastIndexOf(r);if(s<0)return null;const i=e.slice(0,s);return i.endsWith(".app")?i:null}function V(){const t=j().SHENNIAN_HELPER_RUNTIME_DIR?.trim();return t?d.resolve(t):d.join(M.homedir(),"Library","Application Support","Shennian","Helper")}async function J(t){if(E(t))return;if(process.platform==="win32"&&t.pid){t.kill("SIGTERM"),await C(["taskkill","/PID",String(t.pid),"/T","/F"],[0,128]).catch(()=>{}),await y(t,m);return}t.kill("SIGTERM"),!await y(t,m)&&t.pid&&P(t.pid)&&(t.kill("SIGKILL"),await y(t,m))}function E(t){return t.exitCode!==null||t.signalCode!==null}function y(t,e){return E(t)?Promise.resolve(!0):new Promise(r=>{const s=setTimeout(()=>{t.off("exit",i),r(!1)},e),i=()=>{clearTimeout(s),r(!0)};t.once("exit",i)})}function C(t,e){return new Promise((r,s)=>{const[i,...n]=t,a=w(i,n,{stdio:"ignore",windowsHide:!0});a.once("error",s),a.once("exit",l=>{l!==null&&e.includes(l)?r():s(new Error(`${i} exited ${l}`))})})}function j(){return globalThis.process?.[q]??{}}function z(t){let e;try{e=JSON.parse(f.readFileSync(t,"utf8"))}catch(i){throw new Error(`runtime_file_unreadable: ${i instanceof Error?i.message:String(i)}`)}if(!e||typeof e!="object")throw new Error("runtime_file_invalid");const r=e.socketPath;if(typeof r!="string"||!r.trim())throw new Error("runtime_socket_missing");const s=e.pid;return{socketPath:r,...typeof s=="number"&&Number.isInteger(s)&&s>0?{pid:s}:{}}}async function _(t){const{socketPath:e,pid:r}=z(t);if(r&&!P(r))throw new Error(`runtime_process_stale: ${r}`);return new Promise((s,i)=>{const n=v.createConnection(e),a=setTimeout(()=>{n.destroy(),i(new Error(`runtime_socket_connect_timeout: ${e}`))},1e3);n.once("connect",()=>{clearTimeout(a),n.off("error",l),s(n)});const l=o=>{clearTimeout(a),n.destroy(),i(o)};n.once("error",l)})}async function T(t,e){const r=Date.now()+e;let s=null;for(;Date.now()<r;)try{return{ok:!0,socket:await _(t)}}catch(i){s=i instanceof Error?i:new Error(String(i));const n=r-Date.now();n>0&&await Q(Math.min(150,n))}return{ok:!1,error:s??new Error("runtime file missing")}}async function W(t,e={},r=B){f.mkdirSync(t.runtimeDir,{recursive:!0,mode:448});const s=e.forceNewInstance?["-n",...t.args]:t.args;try{await r(t.command,s)}catch(i){if(!(i instanceof Error?i.message:String(i)).includes("-1712"))throw i;await r(t.command,["-n",...t.args])}}function P(t){try{return process.kill(t,0),!0}catch{return!1}}async function B(t,e){await new Promise((r,s)=>{const i=w(t,e,{stdio:["ignore","ignore","pipe"],windowsHide:!0});let n="";i.stderr.on("data",a=>{n=(n+String(a)).slice(-2e3)}),i.once("error",s),i.once("exit",(a,l)=>{a===0?r():s(new Error(`open Helper.app failed (${l?`signal=${l}`:`code=${a}`}${n.trim()?`, stderr=${n.trim()}`:""})`))})})}function Q(t){return new Promise(e=>setTimeout(e,t))}function K(t){return t.screenRecording===!1?"screen-recording":t.accessibility===!1?"accessibility":t.inputMonitoring===!1?"input-monitoring":null}function Y(t){return t==="screen-recording"?"permissions.requestScreenRecording":t==="accessibility"?"permissions.requestAccessibility":"permissions.requestInputMonitoring"}function b(t){if(!t||typeof t!="object")return!1;const e=t;return e.type==="ready"&&typeof e.helperVersion=="string"&&typeof e.protocolVersion=="number"&&Array.isArray(e.capabilities)&&typeof e.pid=="number"}function X(t){if(!t||typeof t!="object")return!1;const e=t;return typeof e.id=="string"&&typeof e.ok=="boolean"&&typeof e.latencyMs=="number"}function Z(t){return t==="windows.focus"||t==="windows.capture"||t==="screen.capture"||t==="ocr.recognize"||t==="image.cropHash"||t==="wechat.searchConversation"||t==="wechat.focusMessageInput"||t.startsWith("mouse.")||t.startsWith("keyboard.")||t.startsWith("clipboard.")||t==="menu.pickItem"||t==="savePanel.saveToPath"}function ee(t){return t.startsWith("mouse.")||t.startsWith("keyboard.")||t.startsWith("clipboard.")||t==="menu.pickItem"||t==="savePanel.saveToPath"}function te(t){return t==="windows.focus"||t==="wechat.searchConversation"||t==="wechat.focusMessageInput"||t.startsWith("mouse.")||t.startsWith("keyboard.")||t.startsWith("clipboard.set")||t==="clipboard.restore"||t==="menu.pickItem"||t==="savePanel.saveToPath"}export{ue as WeChatChannelHelperClient,U as helperLaunchSpec,D as stopWindowsWeChatChannelHelperProcesses};
|