shennian 0.2.99 → 0.2.101
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/publish-build-manifest.json +77 -27
- package/dist/src/channels/runtime.d.ts +5 -1
- package/dist/src/channels/runtime.js +3 -3
- package/dist/src/channels/wechat-channel/helper-assets.d.ts +59 -1
- package/dist/src/channels/wechat-channel/helper-assets.js +1 -1
- package/dist/src/channels/wechat-channel/helper-client.js +3 -3
- package/dist/src/channels/wechat-channel/helper-protocol.d.ts +1 -1
- package/dist/src/channels/wechat-channel/observer.d.ts +32 -2
- package/dist/src/channels/wechat-channel/observer.js +6 -6
- package/dist/src/channels/wechat-channel/outbound-sender.d.ts +2 -0
- package/dist/src/channels/wechat-channel/outbound-sender.js +1 -1
- package/dist/src/channels/wechat-channel/pacing.d.ts +3 -0
- package/dist/src/channels/wechat-channel/pacing.js +1 -0
- package/dist/src/channels/wechat-channel/preflight.d.ts +7 -0
- package/dist/src/channels/wechat-channel/preflight.js +1 -1
- package/dist/src/channels/wechat-channel/runner.d.ts +0 -1
- package/dist/src/channels/wechat-channel/runner.js +1 -1
- package/dist/src/channels/wechat-channel/scheduler.d.ts +4 -3
- package/dist/src/channels/wechat-channel/scheduler.js +1 -1
- package/dist/src/channels/wechat-rpa.d.ts +5 -1
- package/dist/src/channels/wechat-rpa.js +4 -4
- package/dist/src/commands/runtime.d.ts +89 -0
- package/dist/src/commands/runtime.js +1 -0
- package/dist/src/commands/wechat/command.d.ts +2 -0
- package/dist/src/commands/wechat/command.js +1 -0
- package/dist/src/commands/wechat/direct.d.ts +14 -0
- package/dist/src/commands/wechat/direct.js +2 -0
- package/dist/src/commands/wechat/doctor.d.ts +2 -0
- package/dist/src/commands/wechat/doctor.js +1 -0
- package/dist/src/commands/wechat/ipc.d.ts +9 -0
- package/dist/src/commands/wechat/ipc.js +1 -0
- package/dist/src/commands/wechat/operations.d.ts +3 -0
- package/dist/src/commands/wechat/operations.js +4 -0
- package/dist/src/commands/wechat/output.d.ts +7 -0
- package/dist/src/commands/wechat/output.js +4 -0
- package/dist/src/commands/wechat/types.d.ts +141 -0
- package/dist/src/commands/wechat/types.js +1 -0
- package/dist/src/commands/wechat/utils.d.ts +14 -0
- package/dist/src/commands/wechat/utils.js +1 -0
- package/dist/src/commands/wechat.d.ts +6 -127
- package/dist/src/commands/wechat.js +1 -7
- package/dist/src/devtools/wechat-channel-action-smoke.d.ts +5 -0
- package/dist/src/devtools/wechat-channel-action-smoke.js +8 -8
- package/dist/src/index.js +2 -2
- package/dist/src/manager/runtime.d.ts +3 -1
- package/dist/src/manager/runtime.js +5 -5
- package/package.json +1 -1
|
@@ -1,127 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
reasonCode?: string;
|
|
8
|
-
message: string;
|
|
9
|
-
};
|
|
10
|
-
export type WeChatDoctorResult = {
|
|
11
|
-
ok: boolean;
|
|
12
|
-
checks: WeChatDoctorCheck[];
|
|
13
|
-
serverUrl?: string;
|
|
14
|
-
machineId?: string;
|
|
15
|
-
};
|
|
16
|
-
export type WeChatDoctorOptions = {
|
|
17
|
-
serverUrl?: string;
|
|
18
|
-
machineToken?: string;
|
|
19
|
-
machineId?: string;
|
|
20
|
-
fetchImpl?: typeof fetch;
|
|
21
|
-
};
|
|
22
|
-
type IpcContext = {
|
|
23
|
-
url: string;
|
|
24
|
-
token: string;
|
|
25
|
-
};
|
|
26
|
-
export type WeChatToolBinding = {
|
|
27
|
-
sessionId: string;
|
|
28
|
-
channelId: string;
|
|
29
|
-
conversationId: string;
|
|
30
|
-
conversationName: string;
|
|
31
|
-
workDir: string;
|
|
32
|
-
};
|
|
33
|
-
export type WeChatToolTransport = 'auto' | 'daemon' | 'direct';
|
|
34
|
-
export type WeChatToolOptions = {
|
|
35
|
-
conversation: string;
|
|
36
|
-
sessionId?: string;
|
|
37
|
-
workDir?: string;
|
|
38
|
-
source?: string;
|
|
39
|
-
download?: 'auto' | 'never';
|
|
40
|
-
transport?: WeChatToolTransport;
|
|
41
|
-
traceId?: string;
|
|
42
|
-
timeoutMs?: number;
|
|
43
|
-
recentLimit?: number;
|
|
44
|
-
pollIntervalMs?: number;
|
|
45
|
-
fetchImpl?: typeof fetch;
|
|
46
|
-
ipc?: IpcContext;
|
|
47
|
-
directRuntime?: WeChatDirectToolRuntime;
|
|
48
|
-
};
|
|
49
|
-
export type WeChatDirectToolRuntime = {
|
|
50
|
-
readLatest(binding: WeChatToolBinding, options: WeChatReadLatestOptions): Promise<ExternalMessageEvent[]>;
|
|
51
|
-
send(binding: WeChatToolBinding, options: WeChatSendOptions): Promise<{
|
|
52
|
-
pending?: boolean;
|
|
53
|
-
sendStatus?: WeChatSendResult['sendStatus'];
|
|
54
|
-
outDir?: string;
|
|
55
|
-
helperTracePath?: string | null;
|
|
56
|
-
}>;
|
|
57
|
-
};
|
|
58
|
-
export type WeChatSendOptions = WeChatToolOptions & {
|
|
59
|
-
text: string;
|
|
60
|
-
attachment?: ExternalReplyAttachment;
|
|
61
|
-
idempotencyKey?: string;
|
|
62
|
-
dryRun?: boolean;
|
|
63
|
-
};
|
|
64
|
-
export type WeChatReadLatestOptions = WeChatToolOptions & {
|
|
65
|
-
limit?: number;
|
|
66
|
-
};
|
|
67
|
-
export type WeChatSendResult = {
|
|
68
|
-
ok: true;
|
|
69
|
-
operation: 'write';
|
|
70
|
-
conversation: string;
|
|
71
|
-
conversationName: string;
|
|
72
|
-
conversationId: string;
|
|
73
|
-
sessionId: string;
|
|
74
|
-
channelId: string;
|
|
75
|
-
traceId?: string;
|
|
76
|
-
outDir: string;
|
|
77
|
-
helperTracePath?: string | null;
|
|
78
|
-
sendStatus: 'dry_run' | 'queued' | 'sent' | 'sent_unconfirmed' | 'confirmed_echo' | 'manual_review' | 'pending' | 'ok';
|
|
79
|
-
pending?: boolean;
|
|
80
|
-
attachment?: Pick<ExternalReplyAttachment, 'kind' | 'name' | 'mimeType' | 'size' | 'localPath'>;
|
|
81
|
-
attachments: Array<Pick<ExternalReplyAttachment, 'kind' | 'name' | 'mimeType' | 'size' | 'localPath'>>;
|
|
82
|
-
dryRun?: boolean;
|
|
83
|
-
reasonCode?: null;
|
|
84
|
-
message?: string;
|
|
85
|
-
};
|
|
86
|
-
export type WeChatReadLatestResult = {
|
|
87
|
-
ok: true;
|
|
88
|
-
operation: 'read';
|
|
89
|
-
conversation: string;
|
|
90
|
-
conversationName: string;
|
|
91
|
-
conversationId: string;
|
|
92
|
-
sessionId: string;
|
|
93
|
-
channelId: string;
|
|
94
|
-
traceId?: string;
|
|
95
|
-
outDir: string;
|
|
96
|
-
helperTracePath?: string | null;
|
|
97
|
-
messages: ExternalMessageEvent[];
|
|
98
|
-
attachments: ExternalMessageAttachment[];
|
|
99
|
-
count: number;
|
|
100
|
-
reasonCode?: null;
|
|
101
|
-
message?: string;
|
|
102
|
-
};
|
|
103
|
-
export type WeChatToolErrorResult = {
|
|
104
|
-
ok: false;
|
|
105
|
-
operation: 'read' | 'write';
|
|
106
|
-
conversation?: string;
|
|
107
|
-
reasonCode: string;
|
|
108
|
-
message: string;
|
|
109
|
-
};
|
|
110
|
-
export declare function runWeChatDoctor(options?: WeChatDoctorOptions): Promise<WeChatDoctorResult>;
|
|
111
|
-
export declare function runWeChatSend(options: WeChatSendOptions): Promise<WeChatSendResult>;
|
|
112
|
-
export declare function runWeChatReadLatest(options: WeChatReadLatestOptions): Promise<WeChatReadLatestResult>;
|
|
113
|
-
export declare function sendDirectWeChatMessageOnce(binding: WeChatToolBinding, options: WeChatSendOptions): Promise<{
|
|
114
|
-
pending?: boolean;
|
|
115
|
-
sendStatus?: WeChatSendResult['sendStatus'];
|
|
116
|
-
outDir?: string;
|
|
117
|
-
helperTracePath?: string | null;
|
|
118
|
-
}>;
|
|
119
|
-
export declare function readDirectWeChatLatestOnce(binding: WeChatToolBinding, options: WeChatReadLatestOptions): Promise<{
|
|
120
|
-
messages: ExternalMessageEvent[];
|
|
121
|
-
outDir: string;
|
|
122
|
-
helperTracePath: string | null;
|
|
123
|
-
}>;
|
|
124
|
-
export declare function registerWeChatCommand(program: Command): void;
|
|
125
|
-
export declare function buildWeChatToolErrorResult(operation: 'read' | 'write', conversation: string | undefined, error: unknown): WeChatToolErrorResult;
|
|
126
|
-
export declare function buildWeChatReadMarkdown(result: WeChatReadLatestResult): string;
|
|
127
|
-
export {};
|
|
1
|
+
export { registerWeChatCommand } from './wechat/command.js';
|
|
2
|
+
export * from './wechat/direct.js';
|
|
3
|
+
export * from './wechat/doctor.js';
|
|
4
|
+
export * from './wechat/operations.js';
|
|
5
|
+
export * from './wechat/output.js';
|
|
6
|
+
export * from './wechat/types.js';
|
|
@@ -1,7 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
${n.conversationId}
|
|
3
|
-
${t}
|
|
4
|
-
${Date.now()}`),i=await w(o,"/external/reply",{managerSessionId:n.sessionId,channelId:n.channelId,conversationId:n.conversationId,text:t,attachment:e.attachment,idempotencyKey:s},e.fetchImpl,e.timeoutMs),d=await te(o,n,e),c=d?ue(d,s,i.pending===!0):i.pending===!0?"queued":"ok";return{ok:!0,operation:"write",conversation:n.conversationName,conversationName:n.conversationName,conversationId:n.conversationId,sessionId:n.sessionId,channelId:n.channelId,traceId:e.traceId,outDir:n.workDir,helperTracePath:null,sendStatus:c,pending:i.pending===!0&&(c==="queued"||c==="pending"),attachment:a,attachments:a?[a]:[],reasonCode:null,message:d?c==="confirmed_echo"?"WeChat message sent and confirmed.":c==="manual_review"?"WeChat message was submitted but needs manual review.":"WeChat message submitted to local daemon.":"WeChat message submitted to local daemon; confirmation sync timed out."}}async function Z(e){const t=v(e.limit,10),n=R(e),a=P(e);if(!a){const c=e.directRuntime?{messages:await e.directRuntime.readLatest(n,{...e,limit:t,recentLimit:e.recentLimit??t}),outDir:n.workDir,helperTracePath:null}:await X(n,{...e,limit:t,recentLimit:e.recentLimit??t}),l=c.messages.filter(p=>p.conversationId===n.conversationId||p.conversationName===n.conversationName).slice(-t),u=x(l);return{ok:!0,operation:"read",conversation:n.conversationName,conversationName:n.conversationName,conversationId:n.conversationId,sessionId:n.sessionId,channelId:n.channelId,traceId:e.traceId,outDir:c.outDir,helperTracePath:c.helperTracePath,messages:l,attachments:u,count:l.length,reasonCode:null,message:`Read ${l.length} WeChat message(s).`}}if(a.mode==="tool"){const c=await w(a.ctx,"/wechat-rpa/tool/read",{managerSessionId:n.sessionId,binding:n,limit:t,download:e.download,traceId:e.traceId,timeoutMs:e.timeoutMs},e.fetchImpl,e.timeoutMs),u=(Array.isArray(c.messages)?c.messages.filter(U):[]).filter(m=>m.conversationId===n.conversationId||m.conversationName===n.conversationName).slice(-t),p=x(u);return{ok:!0,operation:"read",conversation:n.conversationName,conversationName:n.conversationName,conversationId:n.conversationId,sessionId:n.sessionId,channelId:n.channelId,traceId:e.traceId,outDir:h(c.outDir)||n.workDir,helperTracePath:h(c.helperTracePath)||null,messages:u,attachments:p,count:u.length,reasonCode:null,message:`Read ${u.length} WeChat message(s).`}}const r=a.ctx;await W(r,n,{...e,recentLimit:e.recentLimit??t});const o=await w(r,"/wechat-rpa/channel/sync",{managerSessionId:n.sessionId},e.fetchImpl,e.timeoutMs),i=(Array.isArray(o.messages)?o.messages.filter(U):[]).filter(c=>c.conversationId===n.conversationId||c.conversationName===n.conversationName).slice(-t),d=x(i);return{ok:!0,operation:"read",conversation:n.conversationName,conversationName:n.conversationName,conversationId:n.conversationId,sessionId:n.sessionId,channelId:n.channelId,traceId:e.traceId,outDir:n.workDir,helperTracePath:null,messages:i,attachments:d,count:i.length,reasonCode:null,message:`Read ${i.length} WeChat message(s).`}}async function G(e,t){const n=Q(t.attachment),a=M(e,t,{kind:"send",steps:["open-read",n],text:t.text,attachment:t.attachment}),r=await D(a),o=r.steps.find(i=>i.step===n);if(!r.ok||!o?.ok){const i=o?.reasonCode||r.steps.find(c=>!c.ok&&!c.skipped)?.reasonCode||"wechat_direct_send_failed",d=o?.errorSummary||r.steps.find(c=>!c.ok&&!c.skipped)?.errorSummary||i;throw new Error(`${i}: ${d}`)}const s=b(o.details?.sendStatus)??"sent_unconfirmed";return{pending:s==="queued"||s==="pending"||s==="sent_unconfirmed",sendStatus:s,outDir:r.outDir,helperTracePath:r.helperTracePath}}function Q(e){return e?e.kind==="image"?"send-image":e.kind==="video"?"send-video":"send-file":"send-text"}async function X(e,t){const n=t.download!=="never",a=n?"download-visible-media":"structure-window",r=M(e,t,{kind:"read",steps:[a],allowEmptyMedia:n}),o=await D(r),s=o.steps.find(u=>u.step===a);if(!o.ok&&s?.reasonCode!=="no_observed_messages"){const u=s?.reasonCode||o.steps.find(m=>!m.ok&&!m.skipped)?.reasonCode||"wechat_direct_read_failed",p=s?.errorSummary||o.steps.find(m=>!m.ok&&!m.skipped)?.errorSummary||u;throw new Error(`${u}: ${p}`)}const i=y.join(o.outDir,n?"download-visible-messages.json":"structure-window-messages.json"),d=oe(i),c=re(e,t),l=ae(e);return{messages:d.map(u=>B(c,l,u)).filter(u=>u!=null),outDir:o.outDir,helperTracePath:o.helperTracePath}}function We(e){const t=e.command("wechat").description("Use WeChat through Shennian local runtime");t.option("--conversation <name>","WeChat conversation/group name"),t.command("doctor").description("Check local Shennian, pairing, entitlement, credits, and WeChat runtime readiness").option("--json","Output JSON",!1).action(async a=>{const r=await H();if(a.json)console.log(JSON.stringify(r,null,2));else for(const o of r.checks){const s=o.ok?"\u2713":o.status==="warning"?"!":"\u2717";console.log(`${s} ${o.id}: ${o.message}`)}r.ok||(process.exitCode=1)}),t.command("write").aliases(["send"]).description("Write a message to a local WeChat conversation through Shennian").argument("[text...]","Message text").option("--conversation <name>","WeChat conversation/group name").option("--text <text>","Message text").option("--file <path>","File attachment path").option("--image <path>","Image attachment path").option("--video <path>","Video attachment path").option("--session-id <id>","Bind to an existing Shennian chat/session; omit for direct local WeChat tool mode").option("--work-dir <path>","Local tool work dir; defaults under ~/.shennian/wechat-cli").option("--idempotency-key <key>","Idempotency key").option("--dry-run","Validate the request and output the planned write without sending",!1).option("--trace-id <id>","Trace id included in JSON output").option("--timeout <ms>","Manager IPC request timeout in milliseconds").option("--transport <mode>","Execution transport: auto, daemon, or direct","auto").option("--format <format>","Output format: json or text","text").option("--json","Output JSON",!1).action(async(a,r,o)=>{let s;try{s=T(o,r.conversation);const i=r.text??a.join(" "),d=fe(r),c=await V({conversation:s,text:i,attachment:d,sessionId:r.sessionId,workDir:r.workDir,idempotencyKey:r.idempotencyKey,dryRun:r.dryRun,traceId:r.traceId,timeoutMs:r.timeout?v(r.timeout,24e4):void 0,transport:A(r.transport)});ie(c,r.json||r.format==="json")}catch(i){E("write",s||r.conversation,i,r.json||r.format==="json")}}),t.command("read").aliases(["read-latest","read_latest","latest"]).description("Read recent messages from a local WeChat conversation through Shennian").argument("[count]","Number of latest messages","10").option("--conversation <name>","WeChat conversation/group name").option("--limit <n>","Number of latest messages").option("--format <format>","Output format: markdown or json","markdown").option("--download <mode>","Attachment download mode: auto or never","auto").option("--trace-id <id>","Trace id included in JSON output").option("--timeout <ms>","Manager IPC request timeout in milliseconds").option("--session-id <id>","Bind to an existing Shennian chat/session; omit for direct local WeChat tool mode").option("--work-dir <path>","Local tool work dir; defaults under ~/.shennian/wechat-cli").option("--transport <mode>","Execution transport: auto, daemon, or direct","auto").option("--json","Output JSON",!1).action(async(a,r,o)=>{let s,i="markdown";try{s=T(o,r.conversation),i=pe(r.json?"json":r.format);const d=await Z({conversation:s,limit:v(r.limit??a,10),download:ge(r.download),traceId:r.traceId,timeoutMs:r.timeout?v(r.timeout,24e4):void 0,sessionId:r.sessionId,workDir:r.workDir,transport:A(r.transport)});ce(d,i)}catch(d){E("read",s||r.conversation,d,r.json||i==="json")}})}function C(e){return{ok:e.checks.every(t=>t.ok||t.status==="warning"),checks:e.checks,serverUrl:e.serverUrl,machineId:e.machineId}}function g(e,t){return{id:e,ok:!0,status:"ready",message:t}}function Y(e,t,n){return{id:e,ok:!1,status:"warning",reasonCode:t,message:n}}function S(e,t,n){return{id:e,ok:!1,status:"blocked",reasonCode:t,message:n}}function ee(e){return e.replace(/\/+$/,"")}function N(){const e=$("runtime","manager-ipc.json");let t;try{t=JSON.parse(_.readFileSync(e,"utf-8"))}catch{throw new Error("Manager IPC is not available. Open Shennian Desktop or run `shennian start` first.")}const n=Number(t.pid);if(Number.isInteger(n)&&n>0&&!ye(n))throw new Error(`Manager IPC runtime file is stale for PID ${n}. Restart Shennian Desktop or run \`shennian restart\`.`);const a=typeof t.url=="string"?t.url.replace(/\/+$/,""):"",r=typeof t.token=="string"?t.token:"";if(!a||!r)throw new Error(`Manager IPC runtime file is incomplete: ${e}`);return{url:a,token:r}}function ne(){try{return N()}catch{return null}}function P(e){if(e.directRuntime)return null;const t=e.transport??"auto";if(t==="direct")return null;if(e.ipc)return{ctx:e.ipc,mode:"session"};if(e.sessionId?.trim())return{ctx:N(),mode:"session"};if(t==="daemon")return{ctx:N(),mode:"tool"};const n=ne();return n?{ctx:n,mode:"tool"}:null}async function W(e,t,n){return w(e,"/wechat-rpa/channel/upsert",{managerSessionId:t.sessionId,id:t.channelId,name:`WeChat ${t.conversationName}`,workDir:t.workDir,enabled:!0,groups:[{name:t.conversationName}],canReply:!0,source:n.source||"wechat-channel",recentLimit:v(n.recentLimit,20),pollIntervalMs:v(n.pollIntervalMs,3e4),forceForeground:!0,downloadAttachments:n.download!=="never",privacyConsentAccepted:!0},n.fetchImpl,n.timeoutMs)}async function w(e,t,n,a=fetch,r){const o=String(n.managerSessionId||"");if(!o)throw new Error("managerSessionId is required");const s=r?new AbortController:null,i=s?setTimeout(()=>s.abort(),r):null;try{const d=await a(`${e.url}${t}`,{method:"POST",headers:{authorization:`Bearer ${e.token}`,"content-type":"application/json","x-shennian-manager-session-id":o},body:JSON.stringify(n),signal:s?.signal}),c=await d.json().catch(()=>({ok:!1,error:d.statusText}));if(!d.ok||!c.ok)throw new Error(c.error||`Manager IPC failed: ${d.status}`);return c}finally{i&&clearTimeout(i)}}function R(e){const t=e.conversation.trim();if(!t)throw new Error("--conversation is required");const n=O("wechat-cli-conversation",t),a=e.sessionId?.trim()||`wechat-cli-${n.slice(-24)}`,r=`wechat-rpa:${a}`;return{sessionId:a,channelId:r,conversationId:J(t),conversationName:t,workDir:y.resolve(e.workDir||$("wechat-cli",L(t)))}}async function te(e,t,n){try{return await w(e,"/wechat-rpa/channel/sync",{managerSessionId:t.sessionId},n.fetchImpl,n.timeoutMs)}catch(a){if(ke(a))return null;throw a}}function M(e,t,n){const a=t.traceId||`wechat-cli-${n.kind}-${Date.now().toString(36)}`,r=y.join(e.workDir,"runs",L(a)),o=n.attachment;return{conversation:e.conversationName,mode:"custom",steps:n.steps,outDir:r,workDir:e.workDir,traceId:a,runtimeId:e.channelId,machineId:process.env.SHENNIAN_MACHINE_ID||k().machineId||"local",sessionId:e.sessionId,serverUrl:k().serverUrl,platform:se(),text:n.text,...o?.localPath&&o.kind==="image"?{imagePath:o.localPath}:{},...o?.localPath&&o.kind==="video"?{videoPath:o.localPath}:{},...o?.localPath&&o.kind!=="image"&&o.kind!=="video"?{filePath:o.localPath}:{},allowEmptyMedia:n.allowEmptyMedia,skipRuntimeUpsert:!0,requireServer:!1}}function re(e,t){return{id:e.channelId,type:"wechat-rpa",name:`WeChat ${e.conversationName}`,sessionId:e.sessionId,managerSessionId:e.sessionId,workDir:e.workDir,enabled:!0,secretRef:`wechat-cli:${e.channelId}`}}function ae(e){return{bindingId:F(e.channelId,e.conversationName),sessionId:e.sessionId,conversationDisplayName:e.conversationName,enabled:!0,allowReply:!0,downloadMedia:!0}}function oe(e){try{const t=JSON.parse(_.readFileSync(e,"utf-8"));return Array.isArray(t)?t.filter(f):[]}catch{return[]}}function b(e){return e==="dry_run"||e==="queued"||e==="sent"||e==="sent_unconfirmed"||e==="confirmed_echo"||e==="manual_review"||e==="pending"||e==="ok"?e:null}function se(){if(process.platform==="darwin")return"darwin";if(process.platform==="win32")return"win32"}function T(e,t){const n=e.parent?.opts()??{},a=t||n.conversation||"";if(!a.trim())throw new Error("--conversation is required");return a}function ie(e,t){if(t){console.log(JSON.stringify(e,null,2));return}console.log(e.sendStatus)}function ce(e,t){if(t==="json"){console.log(JSON.stringify(e,null,2));return}console.log(le(e).trimEnd())}function E(e,t,n,a){const r=de(e,t,n);a?console.log(JSON.stringify(r,null,2)):console.error(`${r.reasonCode}: ${r.message}`),process.exitCode=1}function de(e,t,n){const a=n instanceof Error?n.message:String(n||"wechat_tool_failed"),r=I(a)||"wechat_tool_failed",o=/^([a-z][a-z0-9_]*)(?::\s*(.*))?$/i.exec(r),s=o?.[1]||"wechat_tool_failed",i=I(o?.[2]||r),d=i&&i!==s?i:s;return{ok:!1,operation:e,...t?.trim()?{conversation:t.trim()}:{},reasonCode:s,message:d}}function le(e){const t=[];if(e.messages.length===0)return`\uFF08\u6CA1\u6709\u8BFB\u5230\uFF09
|
|
5
|
-
`;for(const n of e.messages)t.push(`${he(n)}: ${me(n)}`);return`${t.join(`
|
|
6
|
-
`)}
|
|
7
|
-
`}function ue(e,t,n){const a=f(e.channel)?e.channel:{},o=(Array.isArray(a.wechatRpaPendingReplies)?a.wechatRpaPendingReplies:[]).filter(f).find(c=>h(c.idempotencyKey)===t),s=h(o?.status);if(s==="confirmed_echo"||s==="sent_unconfirmed"||s==="manual_review"||s==="queued")return s;const d=(Array.isArray(a.wechatRpaRecentTaskSummaries)?a.wechatRpaRecentTaskSummaries:[]).filter(f).map(c=>h(c.status)).find(Boolean);return d==="sent"?"sent":d==="confirmed_echo"?"confirmed_echo":d==="manual_review"?"manual_review":n?"pending":"ok"}function me(e){const t=e.attachments||[],n=I(e.text);return n&&t.length===0?n:n&&t.length>0?`${n} ${t.map(j).join("\u3001")}`:t.length>0?t.map(j).join("\u3001"):"\uFF08\u6D88\u606F\uFF09"}function he(e){return I(e.sender.name||e.sender.id||"\u5BF9\u65B9")}function j(e){const t=I(e.name||y.basename(e.localPath||"")||e.type||"\u9644\u4EF6"),n=e.localPath||e.url||"",a=e.availability&&e.availability!=="edge-local"?` (${e.availability})`:"";return n?`[${ve(t)}](${Ie(n)})${a}`:`${t}${a}`}function fe(e){const t=[e.file?{kind:"file",path:e.file}:null,e.image?{kind:"image",path:e.image}:null,e.video?{kind:"video",path:e.video}:null].filter(n=>!!n);if(t.length>1)throw new Error("Pass only one of --file, --image, or --video");if(t.length!==0)return K(t[0].path,t[0].kind)}function pe(e){const t=String(e||"markdown").trim().toLowerCase();if(t==="json")return"json";if(t==="markdown"||t==="md"||t==="text")return"markdown";throw new Error(`Unsupported --format: ${e}. Use markdown or json.`)}function ge(e){const t=String(e||"auto").trim().toLowerCase();if(t==="auto"||t==="never")return t;throw new Error(`Unsupported --download: ${e}. Use auto or never.`)}function A(e){const t=String(e||"auto").trim().toLowerCase();if(t==="auto"||t==="daemon"||t==="direct")return t;throw new Error(`Unsupported --transport: ${e}. Use auto, daemon, or direct.`)}function we(e){return{kind:e.kind,name:e.name,mimeType:e.mimeType,size:e.size,localPath:e.localPath}}function x(e){return e.flatMap(t=>t.attachments??[])}function Ie(e){const t=e.replace(/\\/g,"/");return/[\s()<>]/.test(t)?`<${t.replace(/[<>]/g,"")}>`:t}function ve(e){return I(e).replace(/([\\\]])/g,"\\$1")}function I(e){return String(e||"").replace(/\s+/g," ").trim()}function ye(e){try{return process.kill(e,0),!0}catch{return!1}}function ke(e){if(!e||typeof e!="object")return!1;const t=e,n=typeof t.name=="string"?t.name:"",a=typeof t.message=="string"?t.message:"";return n==="AbortError"||t.code==="ABORT_ERR"||/aborted|abort/i.test(a)}function O(e,t){return`${e}:${q.createHash("sha256").update(t).digest("hex").slice(0,32)}`}function L(e){return e.trim().replace(/[^a-zA-Z0-9._-]+/g,"_").replace(/^_+|_+$/g,"")||"conversation"}function v(e,t){const n=Number(e);return!Number.isFinite(n)||n<=0?t:Math.floor(n)}function U(e){return!!e&&typeof e=="object"&&!Array.isArray(e)&&e.type==="external.message"&&typeof e.channelId=="string"&&typeof e.conversationId=="string"&&typeof e.messageId=="string"&&typeof e.text=="string"}function f(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}function h(e){return typeof e=="string"&&e?e:void 0}export{le as buildWeChatReadMarkdown,de as buildWeChatToolErrorResult,X as readDirectWeChatLatestOnce,We as registerWeChatCommand,H as runWeChatDoctor,Z as runWeChatReadLatest,V as runWeChatSend,G as sendDirectWeChatMessageOnce};
|
|
1
|
+
import{registerWeChatCommand as e}from"./wechat/command.js";export*from"./wechat/direct.js";export*from"./wechat/doctor.js";export*from"./wechat/operations.js";export*from"./wechat/output.js";export*from"./wechat/types.js";export{e as registerWeChatCommand};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { buildBottomPageReport, buildDownloadVisibleMediaReport } from './wechat-channel-download-report.js';
|
|
2
|
+
import { type WeChatChannelObserveActivityGuardOptions } from '../channels/wechat-channel/observer.js';
|
|
2
3
|
import { type WeChatChannelRuntimePlatform } from '../channels/wechat-channel/runtime.js';
|
|
3
4
|
export type WeChatChannelActionSmokeStep = 'open-read' | 'send-text' | 'send-file' | 'send-image' | 'send-video' | 'structure-window' | 'server-observe' | 'read-bottom-page' | 'download-visible-media' | 'send-and-confirm' | 'human-send-preflight-busy' | 'human-send-preflight-busy-then-idle' | 'human-send-user-active-timeout' | 'human-send-takeover-during-run' | 'human-send-takeover-then-idle' | 'human-send-normal' | 'human-observe-deferred';
|
|
4
5
|
export type WeChatChannelActionSmokeOptions = {
|
|
@@ -19,6 +20,7 @@ export type WeChatChannelActionSmokeOptions = {
|
|
|
19
20
|
videoPath?: string;
|
|
20
21
|
downloadAnchorText?: string;
|
|
21
22
|
allowEmptyMedia?: boolean;
|
|
23
|
+
activityGuard?: WeChatChannelObserveActivityGuardOptions;
|
|
22
24
|
skipRuntimeUpsert?: boolean;
|
|
23
25
|
requireServer: boolean;
|
|
24
26
|
};
|
|
@@ -95,3 +97,6 @@ export declare function buildSendAndConfirmEvidence(input: WeChatChannelSendAndC
|
|
|
95
97
|
failedOutboundCount: number;
|
|
96
98
|
manualReviewCount: number;
|
|
97
99
|
};
|
|
100
|
+
export declare function countMarkerOccurrencesInOcrBlocks(blocks: Array<{
|
|
101
|
+
text?: string | null;
|
|
102
|
+
}>, marker: string): number;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import D from"node:crypto";import{spawnSync as ne}from"node:child_process";import w from"node:fs";import i from"node:path";import{fileURLToPath as oe}from"node:url";import{buildBottomPageReport as M,buildDownloadVisibleMediaReport as q}from"./wechat-channel-download-report.js";import{buildWeChatMessagesMarkdown as y}from"./wechat-channel-markdown-report.js";import{loadConfig as re}from"../config/index.js";import{createWeChatChannelApiClient as se}from"../channels/wechat-channel/client.js";import{resolveWeChatChannelHelperAsset as ae,WECHAT_CHANNEL_HELPER_VERSION as ie}from"../channels/wechat-channel/helper-assets.js";import{WeChatChannelHelperClient as de}from"../channels/wechat-channel/helper-client.js";import{requiredWindowsWeChatChannelHelperCapabilitiesForProfile as j}from"../channels/wechat-channel/helper-protocol.js";import{classifyWeChatOutboundEchoes as ce,enqueueWeChatOutboundReply as K}from"../channels/wechat-channel/outbound-ledger.js";import{loadWeChatChannelLedger as le,saveWeChatChannelLedger as ue,updateWeChatChannelBindingLedger as me}from"../channels/wechat-channel/ledger.js";import{WeChatChannelOutboundSender as U,sendQueuedWeChatOutboundRecords as S}from"../channels/wechat-channel/outbound-sender.js";import{captureWeChatWindow as W,ensureWeChatWindowReady as C,fallbackWeChatMessageInputPointForWindow as he,focusKnownWeChatWindow as k,observeWeChatChannelBindingViaHelper as fe,openConversationBySearch as I,recognizeWeChatScreenshot as E}from"../channels/wechat-channel/observer.js";import{createWeChatChannelRuntime as ge}from"../channels/wechat-channel/runtime.js";function pe(e){const t=!e.sessionId&&e.observePipeline==="edge-structure";if(!e.sessionId&&!t)throw new Error("session_id_required: pass --session-id <existing Shennian session id> for server observe steps");return t?"direct-edge-structure":"session-bound"}const we="ABC",be="wechat-channel-action-smoke";function ve(e){const t=e.filter(l=>l!=="--"),n=st(t),o=it(t,"--step").map(at).filter(l=>l!=null),r=re(),s=g(t,"--trace-id")||`wechat-action-smoke-${Date.now().toString(36)}`,a=i.resolve(g(t,"--out")||g(t,"--output")||i.join(process.cwd(),".dev-runtime","wechat-channel","action-smoke",s)),d=o.length?dt(o):ye(n);return{conversation:g(t,"--conversation")||g(t,"--group")||we,mode:o.length?"custom":n,steps:d,outDir:a,workDir:i.resolve(g(t,"--work-dir")||process.cwd()),traceId:s,runtimeId:g(t,"--runtime-id")||be,machineId:g(t,"--machine-id")||process.env.SHENNIAN_MACHINE_ID||r.machineId||"local",sessionId:g(t,"--session-id")||process.env.SHENNIAN_EXTERNAL_SESSION_ID||process.env.SHENNIAN_MANAGER_SESSION_ID,serverUrl:g(t,"--server-url")||r.serverUrl,platform:nt(g(t,"--platform")),text:g(t,"--text"),filePath:F(g(t,"--file")),imagePath:F(g(t,"--image")),videoPath:F(g(t,"--video")),downloadAnchorText:g(t,"--download-anchor-text")||g(t,"--media-anchor-text"),allowEmptyMedia:t.includes("--allow-empty-media"),requireServer:t.includes("--require-server")}}function ye(e){return e==="send"?["open-read","send-text","send-file","send-image","send-video"]:e==="observe"?["structure-window"]:e==="all"?["open-read","send-text","send-file","send-image","send-video","structure-window"]:["open-read"]}async function Se(e){const t=Date.now(),n=new Date(t).toISOString();w.mkdirSync(e.outDir,{recursive:!0});const o=i.join(e.outDir,"helper-trace.jsonl");w.writeFileSync(o,"");const r=Qe(e),s=r.bindings[0],a=Ze(e,o);await a.start();const d={ok:!1,conversation:e.conversation,outDir:e.outDir,helperTracePath:o,traceId:e.traceId,runtimeId:e.runtimeId,machineId:e.machineId,startedAt:n,finishedAt:n,durationMs:0,steps:[]};try{for(const l of e.steps){if(Ce(l)&&d.steps.some(c=>c.step==="open-read"&&!c.ok&&!c.skipped)){d.steps.push({step:l,ok:!1,reasonCode:"open_read_required",errorSummary:"open-read failed; refusing to run send step against the current WeChat window"});continue}d.steps.push(await ke({step:l,options:e,runtime:r,binding:s,helper:a}))}}finally{await a.stop().catch(()=>{}),d.finishedAt=new Date().toISOString(),d.durationMs=Date.now()-t,d.ok=d.steps.every(l=>l.ok||l.skipped),f(i.join(e.outDir,"summary.json"),d)}return d}function Ce(e){return e==="send-text"||e==="send-file"||e==="send-image"||e==="send-video"||e==="send-and-confirm"}async function ke(e){try{return e.step==="open-read"?await Re(e):e.step==="structure-window"?await Le(e):e.step==="server-observe"?await Oe(e):e.step==="read-bottom-page"?await Ne(e):e.step==="download-visible-media"?await Fe(e):e.step==="send-and-confirm"?await Ee(e):e.step.startsWith("human-")?await Ae(e):await V(e)}catch(t){const n=ft(t),o=await Ie(e,n,t);return{step:e.step,ok:!1,reasonCode:n,errorSummary:t instanceof Error?t.message:String(t),userAction:gt(n),...o?{details:o}:{}}}}async function Ie(e,t,n){if(!xe(t))return;const o=i.join(e.options.outDir,`${e.step}-failure-diagnostics.json`),r={step:e.step,reasonCode:t,errorSummary:n instanceof Error?n.message:String(n),capturedAt:new Date().toISOString()};try{r.permissions=await e.helper.request("permissions.check",{},e.options.traceId)}catch(s){r.permissionsError=s instanceof Error?s.message:String(s)}try{r.windows=await e.helper.request("windows.list",{},e.options.traceId)}catch(s){r.windowsError=s instanceof Error?s.message:String(s)}return f(o,r),{failureDiagnosticsPath:o}}function xe(e){return e.startsWith("wechat_")||e.startsWith("permission_")||e.startsWith("user_active")}async function Ae(e){if(e.step==="human-observe-deferred")return await _e(e);const t=e.step,n=`human-${_(`${e.options.traceId}:${t}:${Date.now()}`)}`,o={version:1,runtimeId:e.runtime.runtimeId,records:[]},r=K({ledger:o,replyId:`human:${t}:${n}`,idempotencyKey:`human:${n}`,bindingId:e.binding.bindingId,runtimeId:e.runtime.runtimeId,sessionId:e.binding.sessionId,conversationName:e.binding.conversationDisplayName,replyBaseRevision:0,text:`${n} ${e.options.text||"Shennian WeChat human coordination smoke"}`}),s=G(e.helper,J(t)),a=s.actionLog,d=async H=>{a.push(`openConversation:${H}`);const ee=await C(s,e.options.traceId),te=await k(s,ee,e.options.traceId);return I({helper:s,window:te,settleToBottom:!1,binding:{...e.binding,conversationDisplayName:H},traceId:e.options.traceId})},l=new U({helper:s,platform:e.runtime.policy.platform,openConversation:d,traceId:e.options.traceId,activityGatePolicy:e.step==="human-send-user-active-timeout"?{keyDownThresholdMs:3e4,mouseMovedThresholdMs:3e4,mouseClickThresholdMs:3e4,scrollWheelThresholdMs:3e4}:void 0});let h=await S({ledger:o,bindingId:e.binding.bindingId,currentLastInboundRevision:0,sender:l,now:new Date("2026-06-15T00:00:00.000Z"),maxUserActivityWaitMs:e.step==="human-send-user-active-timeout"?1e3:void 0});e.step==="human-send-user-active-timeout"&&(h=await S({ledger:o,bindingId:e.binding.bindingId,currentLastInboundRevision:0,sender:l,now:new Date("2026-06-15T00:00:02.000Z"),maxUserActivityWaitMs:1e3})),(e.step==="human-send-preflight-busy-then-idle"||e.step==="human-send-takeover-then-idle")&&(s.setScenario(Ue()),h=await S({ledger:o,bindingId:e.binding.bindingId,currentLastInboundRevision:0,sender:l,now:new Date("2026-06-15T00:00:06.000Z")}));const m=Ve(e.step,r)?await N({helper:s,binding:e.binding,marker:n,traceId:e.options.traceId,outDir:e.options.outDir,filePrefix:e.step}):0,p=m>0?ce({ledger:o,bindingId:e.binding.bindingId,messages:[{stableMessageKey:`human-echo:${n}`,senderRole:"self",kind:"text",anchorText:r.text,normalizedText:r.text,textExcerpt:r.text,observedAt:new Date().toISOString()}]}):{confirmedRecords:[],manualReviewRecords:[],remainingMessages:[]},u=o.records[0],b=Ge(u,h,p.confirmedRecords),v={case:e.step,marker:n,runStatus:b,reasonCode:u.deferReason||u.failureCode||null,outboundStatus:u.sendStatus,attemptCount:u.attemptCount??0,nextAttemptAt:u.nextAttemptAt??null,observedMarkerCount:m,actionLog:a,clipboardRestore:a.includes("clipboard.restore")?"attempted":"not_needed",commitStage:u.commitStage??null};return f(i.join(e.options.outDir,`${e.step}-summary.json`),v),{step:e.step,ok:Je(e.step,v),reasonCode:v.reasonCode||void 0,details:v}}async function _e(e){const t=G(e.helper,J("human-observe-deferred")),n=await t.request("activity.snapshot",{},e.options.traceId),o=!!(n.ok&&n.result&&typeof n.result=="object"&&Number(n.result.keyDownSecondsAgo)<5),r={case:e.step,marker:null,runStatus:o?"observe_deferred_user_active":"baseline_or_no_change",reasonCode:o?"recent_keyboard_activity":null,outboundStatus:null,attemptCount:0,nextAttemptAt:null,observedMarkerCount:0,actionLog:t.actionLog,clipboardRestore:"not_needed"};return f(i.join(e.options.outDir,`${e.step}-summary.json`),r),{step:e.step,ok:o&&!t.actionLog.some(s=>s.startsWith("windows.")||s.startsWith("wechat.")||s.startsWith("clipboard.")),reasonCode:r.reasonCode||void 0,details:r}}async function Re(e){const t=await C(e.helper,e.options.traceId),n=await k(e.helper,t,e.options.traceId),o=await I({helper:e.helper,window:n,binding:e.binding,traceId:e.options.traceId});if(!o.opened)throw await De({helper:e.helper,window:n,outDir:e.options.outDir,traceId:e.options.traceId,reason:o.reason||"conversation_not_opened"}),new Error(o.reason||"conversation_not_opened");const r=await W(e.helper,n.windowId,e.options.traceId),s=T(e.options.outDir,"open-read-full-window",r),a=await E(e.helper,r,e.options.traceId);return f(i.join(e.options.outDir,"open-read-ocr.json"),a),{step:"open-read",ok:!0,details:{openedReason:o.reason,windowId:n.windowId,screenshotPath:s,screenshot:{width:r.width,height:r.height,mimeType:r.mimeType},ocrBlockCount:a.blocks?.length??0,visibleConversationFingerprintCount:a.visibleConversationFingerprints?.length??0}}}async function De(e){try{const t=await W(e.helper,e.window.windowId,e.traceId,e.window.bounds),n=T(e.outDir,"open-read-failure-window",t),o=await E(e.helper,t,e.traceId);f(i.join(e.outDir,"open-read-failure-ocr.json"),o),f(i.join(e.outDir,"open-read-failure-summary.json"),{reason:e.reason,windowId:e.window.windowId,screenshotPath:n,screenshot:{width:t.width,height:t.height,mimeType:t.mimeType},ocrBlockCount:o.blocks?.length??0,visibleConversationFingerprintCount:o.visibleConversationFingerprints?.length??0,topTexts:(o.blocks??[]).slice(0,30).map(r=>({text:r.text,bbox:r.bbox}))})}catch(t){f(i.join(e.outDir,"open-read-failure-summary.json"),{reason:e.reason,captureError:t instanceof Error?t.message:String(t)})}}async function V(e){return await Me(e,ot(e.step,e.options))}async function Me(e,t){if(t.skipped)return{step:e.step,ok:!0,skipped:!0,reasonCode:t.reasonCode};const n={version:1,runtimeId:e.runtime.runtimeId,records:[]},o=`${e.options.traceId}:${e.step}:${t.text}:${t.attachmentPath||""}`,r=t.text?await N({helper:e.helper,binding:e.binding,marker:t.text,traceId:e.options.traceId,outDir:e.options.outDir,filePrefix:`${e.step}-before-send`,settleToBottom:!1}).catch(()=>null):null;K({ledger:n,replyId:`action-smoke:${e.step}:${_(o)}`,idempotencyKey:`action-smoke:${_(o)}`,bindingId:e.binding.bindingId,runtimeId:e.runtime.runtimeId,sessionId:e.binding.sessionId,conversationName:e.binding.conversationDisplayName,replyBaseRevision:0,text:t.text,attachmentLocalRefs:t.attachmentPath?[t.attachmentPath]:[]});const s=new U({helper:e.helper,platform:e.runtime.policy.platform,traceId:e.options.traceId,openConversation:async p=>{const u=await C(e.helper,e.options.traceId),b=await k(e.helper,u,e.options.traceId);return{...await I({helper:e.helper,window:b,settleToBottom:!1,binding:{...e.binding,conversationDisplayName:p},traceId:e.options.traceId}),inputPoint:he(b)}}}),a=await Te({ledger:n,bindingId:e.binding.bindingId,sender:s});await R(900);const d=n.records[0],l=a.failedRecords[0]?.failureCode||a.manualReviewRecords[0]?.failureCode||a.waitingRecords[0]?.deferReason||a.staleRecords[0]?.failureCode,c=a.sentRecords.length===1&&a.failedRecords.length===0&&a.waitingRecords.length===0&&a.manualReviewRecords.length===0&&a.staleRecords.length===0,h=c&&t.text?await ze({helper:e.helper,binding:e.binding,marker:t.text,traceId:e.options.traceId,outDir:e.options.outDir,filePrefix:`${e.step}-after-send`,settleToBottom:!1,attempts:3,targetGreaterThan:r}):null,m=Pe({sendAccepted:c,text:t.text,beforeTextCount:r,afterTextCount:h});return{step:e.step,ok:m.ok,reasonCode:l||m.reasonCode,errorSummary:a.failedRecords[0]?.lastErrorSummary||a.manualReviewRecords[0]?.lastErrorSummary,details:{text:t.text,attachmentPath:t.attachmentPath??null,helperTracePath:i.join(e.options.outDir,"helper-trace.jsonl"),confirmationRequired:m.confirmationRequired,sendAccepted:m.sendAccepted,beforeTextCount:r,afterTextCount:h,sentRecords:a.sentRecords.length,failedRecords:a.failedRecords.length,waitingRecords:a.waitingRecords.length,manualReviewRecords:a.manualReviewRecords.length,staleRecords:a.staleRecords.length,sendStatus:d?.sendStatus,failureCode:d?.failureCode??null,lastErrorSummary:d?.lastErrorSummary??null,deferReason:d?.deferReason,commitStage:d?.commitStage??null,attemptCount:d?.attemptCount??0,nextAttemptAt:d?.nextAttemptAt??null}}}function Pe(e){const t=!!(e.text&&e.text.trim());if(!e.sendAccepted)return{ok:!1,reasonCode:void 0,sendAccepted:!1,confirmationRequired:t};if(!t)return{ok:!0,sendAccepted:!0,confirmationRequired:t};const n=typeof e.beforeTextCount=="number"&&Number.isFinite(e.beforeTextCount)?e.beforeTextCount:null,o=typeof e.afterTextCount=="number"&&Number.isFinite(e.afterTextCount)?e.afterTextCount:0,r=n==null?o>0:o>n;return{ok:r,reasonCode:r?void 0:"sent_text_not_observed",sendAccepted:!0,confirmationRequired:t}}async function Te(e){const t=e.maxAttempts??24,n=e.maxWaitMs??9e4,o=Date.now();let r=await S({ledger:e.ledger,bindingId:e.bindingId,currentLastInboundRevision:0,sender:e.sender,maxUserActivityWaitMs:n});for(let s=2;r.waitingRecords.length>0&&s<=t&&!(Date.now()-o>=n);s+=1){const a=je(r.waitingRecords[0]?.nextAttemptAt,o,n);await R(a),r=await S({ledger:e.ledger,bindingId:e.bindingId,currentLastInboundRevision:0,sender:e.sender,maxUserActivityWaitMs:n})}return r}function je(e,t,n){const o=Math.max(0,n-(Date.now()-t));if(o<=0)return 0;const r=e?new Date(e).getTime():Number.NaN,s=Number.isFinite(r)?r-Date.now():1e3;return Math.max(500,Math.min(Math.max(500,s),Math.min(5e3,o)))}function We(e){const t=e.sendResults.filter(u=>!z(u)),n=e.sendResults.filter(u=>!u.skipped).map(u=>u.details?.text&&typeof u.details.text=="string"?u.details.text:"").filter(Boolean),o=e.observedMessages.map(u=>[u.normalizedText||"",u.anchorText||"",u.textExcerpt||""].join(`
|
|
1
|
+
import R from"node:crypto";import{spawnSync as se}from"node:child_process";import b from"node:fs";import i from"node:path";import{fileURLToPath as ae}from"node:url";import{buildBottomPageReport as D,buildDownloadVisibleMediaReport as q}from"./wechat-channel-download-report.js";import{buildWeChatMessagesMarkdown as y}from"./wechat-channel-markdown-report.js";import{loadConfig as ie}from"../config/index.js";import{createWeChatChannelApiClient as de}from"../channels/wechat-channel/client.js";import{resolveWeChatChannelHelperAsset as ce,WECHAT_CHANNEL_HELPER_VERSION as ue}from"../channels/wechat-channel/helper-assets.js";import{WeChatChannelHelperClient as le}from"../channels/wechat-channel/helper-client.js";import{requiredWindowsWeChatChannelHelperCapabilitiesForProfile as T}from"../channels/wechat-channel/helper-protocol.js";import{classifyWeChatOutboundEchoes as me,enqueueWeChatOutboundReply as K}from"../channels/wechat-channel/outbound-ledger.js";import{loadWeChatChannelLedger as fe,saveWeChatChannelLedger as he,updateWeChatChannelBindingLedger as ge}from"../channels/wechat-channel/ledger.js";import{waitForWeChatChannelPacing as U}from"../channels/wechat-channel/pacing.js";import{WeChatChannelOutboundSender as G,sendQueuedWeChatOutboundRecords as S}from"../channels/wechat-channel/outbound-sender.js";import{captureWeChatWindow as j,ensureWeChatWindowReady as C,fallbackWeChatMessageInputPointForWindow as pe,focusKnownWeChatWindow as k,observeWeChatChannelBindingViaHelper as be,openConversationBySearch as x,recognizeWeChatScreenshot as W}from"../channels/wechat-channel/observer.js";import{createWeChatChannelRuntime as we}from"../channels/wechat-channel/runtime.js";function ve(e){const t=!e.sessionId&&e.observePipeline==="edge-structure";if(!e.sessionId&&!t)throw new Error("session_id_required: pass --session-id <existing Shennian session id> for server observe steps");return t?"direct-edge-structure":"session-bound"}const ye="ABC",Se="wechat-channel-action-smoke";function Ce(e){const t=e.filter(l=>l!=="--"),n=ct(t),o=lt(t,"--step").map(ut).filter(l=>l!=null),r=ie(),s=g(t,"--trace-id")||`wechat-action-smoke-${Date.now().toString(36)}`,a=i.resolve(g(t,"--out")||g(t,"--output")||i.join(process.cwd(),".dev-runtime","wechat-channel","action-smoke",s)),d=o.length?mt(o):ke(n);return{conversation:g(t,"--conversation")||g(t,"--group")||ye,mode:o.length?"custom":n,steps:d,outDir:a,workDir:i.resolve(g(t,"--work-dir")||process.cwd()),traceId:s,runtimeId:g(t,"--runtime-id")||Se,machineId:g(t,"--machine-id")||process.env.SHENNIAN_MACHINE_ID||r.machineId||"local",sessionId:g(t,"--session-id")||process.env.SHENNIAN_EXTERNAL_SESSION_ID||process.env.SHENNIAN_MANAGER_SESSION_ID,serverUrl:g(t,"--server-url")||r.serverUrl,platform:at(g(t,"--platform")),text:g(t,"--text"),filePath:F(g(t,"--file")),imagePath:F(g(t,"--image")),videoPath:F(g(t,"--video")),downloadAnchorText:g(t,"--download-anchor-text")||g(t,"--media-anchor-text"),allowEmptyMedia:t.includes("--allow-empty-media"),requireServer:t.includes("--require-server")}}function ke(e){return e==="send"?["open-read","send-text","send-file","send-image","send-video"]:e==="observe"?["structure-window"]:e==="all"?["open-read","send-text","send-file","send-image","send-video","structure-window"]:["open-read"]}async function xe(e){const t=Date.now(),n=new Date(t).toISOString();b.mkdirSync(e.outDir,{recursive:!0});const o=i.join(e.outDir,"helper-trace.jsonl");b.writeFileSync(o,"");const r=et(e),s=r.bindings[0],a=tt(e,o);await a.start();const d={ok:!1,conversation:e.conversation,outDir:e.outDir,helperTracePath:o,traceId:e.traceId,runtimeId:e.runtimeId,machineId:e.machineId,startedAt:n,finishedAt:n,durationMs:0,steps:[]};try{for(const l of e.steps){if(Ie(l)&&d.steps.some(c=>c.step==="open-read"&&!c.ok&&!c.skipped)){d.steps.push({step:l,ok:!1,reasonCode:"open_read_required",errorSummary:"open-read failed; refusing to run send step against the current WeChat window"});continue}d.steps.push(await Ae({step:l,options:e,runtime:r,binding:s,helper:a}))}}finally{await a.stop().catch(()=>{}),d.finishedAt=new Date().toISOString(),d.durationMs=Date.now()-t,d.ok=d.steps.every(l=>l.ok||l.skipped),h(i.join(e.outDir,"summary.json"),d)}return d}function Ie(e){return e==="send-text"||e==="send-file"||e==="send-image"||e==="send-video"||e==="send-and-confirm"}async function Ae(e){try{return e.step==="open-read"?await Pe(e):e.step==="structure-window"?await Ke(e):e.step==="server-observe"?await qe(e):e.step==="read-bottom-page"?await Le(e):e.step==="download-visible-media"?await He(e):e.step==="send-and-confirm"?await Fe(e):e.step.startsWith("human-")?await De(e):await z(e)}catch(t){const n=wt(t),o=await _e(e,n,t);return{step:e.step,ok:!1,reasonCode:n,errorSummary:t instanceof Error?t.message:String(t),userAction:vt(n),...o?{details:o}:{}}}}async function _e(e,t,n){if(!Re(t))return;const o=i.join(e.options.outDir,`${e.step}-failure-diagnostics.json`),r={step:e.step,reasonCode:t,errorSummary:n instanceof Error?n.message:String(n),capturedAt:new Date().toISOString()};try{r.permissions=await e.helper.request("permissions.check",{},e.options.traceId)}catch(s){r.permissionsError=s instanceof Error?s.message:String(s)}try{r.windows=await e.helper.request("windows.list",{},e.options.traceId)}catch(s){r.windowsError=s instanceof Error?s.message:String(s)}return h(o,r),{failureDiagnosticsPath:o}}function Re(e){return e.startsWith("wechat_")||e.startsWith("permission_")||e.startsWith("user_active")}async function De(e){if(e.step==="human-observe-deferred")return await Me(e);const t=e.step,n=`human-${_(`${e.options.traceId}:${t}:${Date.now()}`)}`,o={version:1,runtimeId:e.runtime.runtimeId,records:[]},r=K({ledger:o,replyId:`human:${t}:${n}`,idempotencyKey:`human:${n}`,bindingId:e.binding.bindingId,runtimeId:e.runtime.runtimeId,sessionId:e.binding.sessionId,conversationName:e.binding.conversationDisplayName,replyBaseRevision:0,text:`${n} ${e.options.text||"Shennian WeChat human coordination smoke"}`}),s=J(e.helper,Q(t)),a=s.actionLog,d=async H=>{a.push(`openConversation:${H}`);const oe=await C(s,e.options.traceId),re=await k(s,oe,e.options.traceId);return x({helper:s,window:re,settleToBottom:!1,binding:{...e.binding,conversationDisplayName:H},traceId:e.options.traceId})},l=new G({helper:s,platform:e.runtime.policy.platform,openConversation:d,traceId:e.options.traceId,activityGatePolicy:e.step==="human-send-user-active-timeout"?{keyDownThresholdMs:3e4,mouseMovedThresholdMs:3e4,mouseClickThresholdMs:3e4,scrollWheelThresholdMs:3e4}:void 0});let f=await S({ledger:o,bindingId:e.binding.bindingId,currentLastInboundRevision:0,sender:l,now:new Date("2026-06-15T00:00:00.000Z"),maxUserActivityWaitMs:e.step==="human-send-user-active-timeout"?1e3:void 0});e.step==="human-send-user-active-timeout"&&(f=await S({ledger:o,bindingId:e.binding.bindingId,currentLastInboundRevision:0,sender:l,now:new Date("2026-06-15T00:00:02.000Z"),maxUserActivityWaitMs:1e3})),(e.step==="human-send-preflight-busy-then-idle"||e.step==="human-send-takeover-then-idle")&&(s.setScenario(Ve()),f=await S({ledger:o,bindingId:e.binding.bindingId,currentLastInboundRevision:0,sender:l,now:new Date("2026-06-15T00:00:06.000Z")}));const m=Je(e.step,r)?await B({helper:s,binding:e.binding,marker:n,traceId:e.options.traceId,outDir:e.options.outDir,filePrefix:e.step}):0,p=m>0?me({ledger:o,bindingId:e.binding.bindingId,messages:[{stableMessageKey:`human-echo:${n}`,senderRole:"self",kind:"text",anchorText:r.text,normalizedText:r.text,textExcerpt:r.text,observedAt:new Date().toISOString()}]}):{confirmedRecords:[],manualReviewRecords:[],remainingMessages:[]},u=o.records[0],w=Xe(u,f,p.confirmedRecords),v={case:e.step,marker:n,runStatus:w,reasonCode:u.deferReason||u.failureCode||null,outboundStatus:u.sendStatus,attemptCount:u.attemptCount??0,nextAttemptAt:u.nextAttemptAt??null,observedMarkerCount:m,actionLog:a,clipboardRestore:a.includes("clipboard.restore")?"attempted":"not_needed",commitStage:u.commitStage??null};return h(i.join(e.options.outDir,`${e.step}-summary.json`),v),{step:e.step,ok:Ye(e.step,v),reasonCode:v.reasonCode||void 0,details:v}}async function Me(e){const t=J(e.helper,Q("human-observe-deferred")),n=await t.request("activity.snapshot",{},e.options.traceId),o=!!(n.ok&&n.result&&typeof n.result=="object"&&Number(n.result.keyDownSecondsAgo)<5),r={case:e.step,marker:null,runStatus:o?"observe_deferred_user_active":"baseline_or_no_change",reasonCode:o?"recent_keyboard_activity":null,outboundStatus:null,attemptCount:0,nextAttemptAt:null,observedMarkerCount:0,actionLog:t.actionLog,clipboardRestore:"not_needed"};return h(i.join(e.options.outDir,`${e.step}-summary.json`),r),{step:e.step,ok:o&&!t.actionLog.some(s=>s.startsWith("windows.")||s.startsWith("wechat.")||s.startsWith("clipboard.")),reasonCode:r.reasonCode||void 0,details:r}}async function Pe(e){const t=await C(e.helper,e.options.traceId),n=await k(e.helper,t,e.options.traceId),o=await x({helper:e.helper,window:n,binding:e.binding,traceId:e.options.traceId});if(!o.opened)throw await Te({helper:e.helper,window:n,outDir:e.options.outDir,traceId:e.options.traceId,reason:o.reason||"conversation_not_opened"}),new Error(o.reason||"conversation_not_opened");const r=await j(e.helper,n.windowId,e.options.traceId),s=P(e.options.outDir,"open-read-full-window",r),a=await W(e.helper,r,e.options.traceId);return h(i.join(e.options.outDir,"open-read-ocr.json"),a),{step:"open-read",ok:!0,details:{openedReason:o.reason,windowId:n.windowId,screenshotPath:s,screenshot:{width:r.width,height:r.height,mimeType:r.mimeType},ocrBlockCount:a.blocks?.length??0,visibleConversationFingerprintCount:a.visibleConversationFingerprints?.length??0}}}async function Te(e){try{const t=await j(e.helper,e.window.windowId,e.traceId,e.window.bounds),n=P(e.outDir,"open-read-failure-window",t),o=await W(e.helper,t,e.traceId);h(i.join(e.outDir,"open-read-failure-ocr.json"),o),h(i.join(e.outDir,"open-read-failure-summary.json"),{reason:e.reason,windowId:e.window.windowId,screenshotPath:n,screenshot:{width:t.width,height:t.height,mimeType:t.mimeType},ocrBlockCount:o.blocks?.length??0,visibleConversationFingerprintCount:o.visibleConversationFingerprints?.length??0,topTexts:(o.blocks??[]).slice(0,30).map(r=>({text:r.text,bbox:r.bbox}))})}catch(t){h(i.join(e.outDir,"open-read-failure-summary.json"),{reason:e.reason,captureError:t instanceof Error?t.message:String(t)})}}async function z(e){return await je(e,it(e.step,e.options))}async function je(e,t){if(t.skipped)return{step:e.step,ok:!0,skipped:!0,reasonCode:t.reasonCode};const n={version:1,runtimeId:e.runtime.runtimeId,records:[]},o=`${e.options.traceId}:${e.step}:${t.text}:${t.attachmentPath||""}`,r=t.text?await B({helper:e.helper,binding:e.binding,marker:t.text,traceId:e.options.traceId,outDir:e.options.outDir,filePrefix:`${e.step}-before-send`,settleToBottom:!1}).catch(()=>null):null;K({ledger:n,replyId:`action-smoke:${e.step}:${_(o)}`,idempotencyKey:`action-smoke:${_(o)}`,bindingId:e.binding.bindingId,runtimeId:e.runtime.runtimeId,sessionId:e.binding.sessionId,conversationName:e.binding.conversationDisplayName,replyBaseRevision:0,text:t.text,attachmentLocalRefs:t.attachmentPath?[t.attachmentPath]:[]});const s=new G({helper:e.helper,platform:e.runtime.policy.platform,traceId:e.options.traceId,openConversation:async p=>{const u=await C(e.helper,e.options.traceId),w=await k(e.helper,u,e.options.traceId);return{...await x({helper:e.helper,window:w,settleToBottom:!1,binding:{...e.binding,conversationDisplayName:p},traceId:e.options.traceId}),inputPoint:pe(w)}}}),a=await Ee({ledger:n,bindingId:e.binding.bindingId,sender:s});await U("send-after-submit",`${e.options.traceId}:${e.step}:after-submit`);const d=n.records[0],l=a.failedRecords[0]?.failureCode||a.manualReviewRecords[0]?.failureCode||a.waitingRecords[0]?.deferReason||a.staleRecords[0]?.failureCode,c=a.sentRecords.length===1&&a.failedRecords.length===0&&a.waitingRecords.length===0&&a.manualReviewRecords.length===0&&a.staleRecords.length===0,f=c&&t.text?await Ze({helper:e.helper,binding:e.binding,marker:t.text,traceId:e.options.traceId,outDir:e.options.outDir,filePrefix:`${e.step}-after-send`,settleToBottom:!1,attempts:3,targetGreaterThan:r}):null,m=We({sendAccepted:c,text:t.text,beforeTextCount:r,afterTextCount:f});return{step:e.step,ok:m.ok,reasonCode:l||m.reasonCode,errorSummary:a.failedRecords[0]?.lastErrorSummary||a.manualReviewRecords[0]?.lastErrorSummary,details:{text:t.text,attachmentPath:t.attachmentPath??null,helperTracePath:i.join(e.options.outDir,"helper-trace.jsonl"),confirmationRequired:m.confirmationRequired,sendAccepted:m.sendAccepted,beforeTextCount:r,afterTextCount:f,sentRecords:a.sentRecords.length,failedRecords:a.failedRecords.length,waitingRecords:a.waitingRecords.length,manualReviewRecords:a.manualReviewRecords.length,staleRecords:a.staleRecords.length,sendStatus:d?.sendStatus,failureCode:d?.failureCode??null,lastErrorSummary:d?.lastErrorSummary??null,deferReason:d?.deferReason,commitStage:d?.commitStage??null,attemptCount:d?.attemptCount??0,nextAttemptAt:d?.nextAttemptAt??null}}}function We(e){const t=!!(e.text&&e.text.trim());if(!e.sendAccepted)return{ok:!1,reasonCode:void 0,sendAccepted:!1,confirmationRequired:t};if(!t)return{ok:!0,sendAccepted:!0,confirmationRequired:t};const n=typeof e.beforeTextCount=="number"&&Number.isFinite(e.beforeTextCount)?e.beforeTextCount:null,o=typeof e.afterTextCount=="number"&&Number.isFinite(e.afterTextCount)?e.afterTextCount:0,r=n==null?o>0:o>n;return{ok:r,reasonCode:r?void 0:"sent_text_not_observed",sendAccepted:!0,confirmationRequired:t}}async function Ee(e){const t=e.maxAttempts??24,n=e.maxWaitMs??9e4,o=Date.now();let r=await S({ledger:e.ledger,bindingId:e.bindingId,currentLastInboundRevision:0,sender:e.sender,maxUserActivityWaitMs:n});for(let s=2;r.waitingRecords.length>0&&s<=t&&!(Date.now()-o>=n);s+=1){const a=$e(r.waitingRecords[0]?.nextAttemptAt,o,n);await L(a),r=await S({ledger:e.ledger,bindingId:e.bindingId,currentLastInboundRevision:0,sender:e.sender,maxUserActivityWaitMs:n})}return r}function $e(e,t,n){const o=Math.max(0,n-(Date.now()-t));if(o<=0)return 0;const r=e?new Date(e).getTime():Number.NaN,s=Number.isFinite(r)?r-Date.now():1e3;return Math.max(500,Math.min(Math.max(500,s),Math.min(5e3,o)))}function Be(e){const t=e.sendResults.filter(u=>!V(u)),n=e.sendResults.filter(u=>!u.skipped).map(u=>u.details?.text&&typeof u.details.text=="string"?u.details.text:"").filter(Boolean),o=e.observedMessages.map(u=>[u.normalizedText||"",u.anchorText||"",u.textExcerpt||""].join(`
|
|
2
2
|
`)).join(`
|
|
3
|
-
`),r=n.filter(u=>o.includes(u)),s=e.sendResults.map(u=>typeof u.details?.sendStatus=="string"?u.details.sendStatus:"").filter(Boolean),a=e.sendResults.filter(z).length,d=$(t,"waitingRecords"),l=$(t,"failedRecords"),c=$(t,"manualReviewRecords"),h=t.find(u=>!u.ok||u.skipped),m=s.length===n.length&&s.every(u=>u==="sent_unconfirmed"||u==="confirmed_echo"),p=!h&&n.length>0&&r.length>0&&m&&d===0&&l===0&&c===0;return{ok:p,reasonCode:p?void 0:h?.reasonCode||(d>0?"outbound_send_waiting":void 0)||(l>0?"outbound_send_failed":void 0)||(c>0?"outbound_manual_review":void 0)||(m?void 0:"outbound_status_not_accepted")||(r.length?"partial_sent_messages_observed":"sent_messages_not_observed"),sentTexts:n,confirmedTexts:r,outboundStatuses:s,skippedFixtureCount:a,pendingOutboundCount:d,failedOutboundCount:l,manualReviewCount:c}}function z(e){return e.skipped===!0&&e.reasonCode==="ffmpeg_unavailable_for_video_fixture"}function $(e,t){return e.reduce((n,o)=>{const r=o.details?.[t];return n+(typeof r=="number"&&Number.isFinite(r)?r:0)},0)}async function Ee(e){const t=["send-text","send-file","send-image","send-video"],n=[];for(const c of t)n.push(await V({...e,step:c}));await R(1200);const o=await $e(e,3),r=M(o.messages),s=i.join(e.options.outDir,"bottom-page.md"),a=y(o.messages,{conversation:e.options.conversation,attachmentsDirName:i.basename(o.attachmentsDir)});f(i.join(e.options.outDir,"bottom-page-summary.json"),r),f(i.join(e.options.outDir,"server-observe-messages.json"),o.messages),A(s,a);const d=We({sendResults:n,observedMessages:o.messages}),l=d.ok&&o.messages.length>0;return{step:"send-and-confirm",ok:l,reasonCode:l?void 0:d.reasonCode,errorSummary:n.find(c=>!c.ok||c.skipped)?.errorSummary,details:{sent:n.map(c=>({step:c.step,ok:c.ok,skipped:c.skipped,reasonCode:c.reasonCode,errorSummary:c.errorSummary,text:c.details?.text,attachmentPath:c.details?.attachmentPath,sendStatus:c.details?.sendStatus,deferReason:c.details?.deferReason,failureCode:c.details?.failureCode,commitStage:c.details?.commitStage,attemptCount:c.details?.attemptCount,nextAttemptAt:c.details?.nextAttemptAt,waitingRecords:c.details?.waitingRecords,failedRecords:c.details?.failedRecords,manualReviewRecords:c.details?.manualReviewRecords})),sentTexts:d.sentTexts,confirmedTexts:d.confirmedTexts,outboundStatuses:d.outboundStatuses,skippedFixtureCount:d.skippedFixtureCount,pendingOutboundCount:d.pendingOutboundCount,failedOutboundCount:d.failedOutboundCount,manualReviewCount:d.manualReviewCount,observedCount:o.messages.length,byKind:r.byKind,attachmentSummary:r.attachmentSummary,attachmentsDir:o.attachmentsDir,reportPath:i.join(e.options.outDir,"bottom-page-summary.json"),markdownPath:s,markdown:a}}}async function $e(e,t){let n;for(let o=1;o<=t;o+=1)try{return await x(e)}catch(r){if(n=r,o>=t||!Be(r))break;try{const s=await C(e.helper,e.options.traceId),a=await k(e.helper,s,e.options.traceId);await I({helper:e.helper,window:a,binding:e.binding,traceId:e.options.traceId})}catch{}await R(1500*o)}throw n}function Be(e){const t=e instanceof Error?e.message:String(e||"");return/conversation_not_visible|wechat_window_unavailable|wechat_window_not_found|title_not_confirmed|server_observe_failed|timeout/i.test(t)}async function Ne(e){const t=await x(e),n=M(t.messages),o=i.join(e.options.outDir,"bottom-page.md"),r=y(t.messages,{conversation:e.options.conversation,attachmentsDirName:i.basename(t.attachmentsDir)});return f(i.join(e.options.outDir,"bottom-page-summary.json"),n),f(i.join(e.options.outDir,"server-observe-messages.json"),t.messages),A(o,r),{step:"read-bottom-page",ok:n.messageCount>0,reasonCode:n.messageCount>0?void 0:"no_observed_messages",details:{messageCount:n.messageCount,byKind:n.byKind,attachmentSummary:n.attachmentSummary,attachmentsDir:t.attachmentsDir,reportPath:i.join(e.options.outDir,"bottom-page-summary.json"),markdownPath:o,markdown:r}}}async function Fe(e){const t=await x(e,{observePipeline:"edge-structure",persistLocalLedger:!0});B(e.options.outDir,"download-visible",t.observationEvidence);const n=M(t.messages),o=i.join(e.options.outDir,"download-visible.md"),r=y(t.messages,{conversation:e.options.conversation,attachmentsDirName:i.basename(t.attachmentsDir)}),s=q(t.messages,t.attachmentsDir,{observePipeline:t.observePipeline,downloadAnchorText:e.options.downloadAnchorText,ledgerPath:t.ledgerPath,ledgerBinding:t.ledgerBinding,traceHashes:t.traceHashes});f(i.join(e.options.outDir,"bottom-page-summary.json"),n),f(i.join(e.options.outDir,"download-visible-summary.json"),s),f(i.join(e.options.outDir,"download-visible-messages.json"),t.messages),A(o,r);const a=!!(e.options.allowEmptyMedia&&s.reasonCode==="no_visible_media_candidates"&&t.messages.length>0),d=s.ok||a;return{step:"download-visible-media",ok:d,reasonCode:d?void 0:s.reasonCode,details:{mediaCandidateCount:s.mediaCandidateCount,localAttachmentCount:s.localAttachmentCount,originalLocalAttachmentCount:s.originalLocalAttachmentCount,previewAttachmentCount:s.previewAttachmentCount,missingLocalPathCount:s.missingLocalPathCount,pendingAttachmentCount:s.pendingAttachmentCount,byKind:s.byKind,byAvailability:s.byAvailability,reasonCodes:s.reasonCodes,mediaReasonCode:s.reasonCode,allowEmptyMediaRead:a,attachmentsDir:t.attachmentsDir,reportPath:i.join(e.options.outDir,"download-visible-summary.json"),markdownPath:o,markdown:r,phases:s.phases,observePipeline:s.observePipeline,ledgerPath:s.ledgerPath,ledgerRevision:s.ledger?.revision,traceHashes:s.traceHashes,traceEvents:s.traceEvents}}}async function Oe(e){const t=await x(e,{observePipeline:"server-observe"});B(e.options.outDir,"server-observe",t.observationEvidence),f(i.join(e.options.outDir,"server-observe-messages.json"),t.messages);const n=i.join(e.options.outDir,"server-observe.md"),o=y(t.messages,{conversation:e.options.conversation,attachmentsDirName:i.basename(t.attachmentsDir)});A(n,o);const r=Q(t.messages);return{step:"server-observe",ok:t.messages.length>0,reasonCode:t.messages.length>0?void 0:"no_observed_messages",details:{observedCount:t.messages.length,byKind:O(t.messages.map(s=>s.kind||"unknown")),attachmentSummary:r,attachmentsDir:t.attachmentsDir,markdownPath:n,markdown:o}}}async function Le(e){const t=await x(e,{observePipeline:"edge-structure",downloadMedia:!1});B(e.options.outDir,"structure-window",t.observationEvidence),f(i.join(e.options.outDir,"structure-window-messages.json"),t.messages);const n=i.join(e.options.outDir,"structure-window.md"),o=y(t.messages,{conversation:e.options.conversation,attachmentsDirName:i.basename(t.attachmentsDir)});A(n,o);const r=Q(t.messages);return{step:"structure-window",ok:t.messages.length>0,reasonCode:t.messages.length>0?void 0:"no_observed_messages",details:{observedCount:t.messages.length,byKind:O(t.messages.map(s=>s.kind||"unknown")),attachmentSummary:r,attachmentsDir:t.attachmentsDir,markdownPath:n,markdown:o}}}async function x(e,t={}){const n=pe({sessionId:e.options.sessionId,observePipeline:t.observePipeline}),o=se({serverUrl:e.options.serverUrl});e.options.skipRuntimeUpsert||(n==="direct-edge-structure"?await o.upsertRuntime(e.runtime):await o.upsertRuntime(e.runtime,{...e.binding,enabled:!1,allowReply:!1}));const r=i.join(e.options.outDir,"inbound-attachments"),s=t.persistLocalLedger?ct(e.options):void 0,a=s?le(s,e.runtime.runtimeId):void 0;let d,l,c;const h=await fe({runtime:e.runtime,binding:{...e.binding,downloadMedia:t.downloadMedia??e.binding.downloadMedia},helper:e.helper,api:o,workDir:e.options.workDir,attachmentsDir:r,traceId:e.options.traceId,mediaAnchorText:e.options.downloadAnchorText,observePipeline:t.observePipeline,localLedgerTailAnchors:a?lt(a.bindings[e.binding.bindingId]):void 0,onObservationEvidence:({capture:u,ocr:b})=>{d=qe(u),l=Ke(b),c=He(u,b,{captureHash:d,ocrHash:l})}});let m,p;if(a&&s){const u=me({ledger:a,bindingId:e.binding.bindingId,observedMessages:h});m=u.binding,p=Y({bindingId:e.binding.bindingId,revision:m.revision,messages:u.newMessages}),ue(s,a)}return{messages:h,attachmentsDir:r,observePipeline:t.observePipeline,...s?{ledgerPath:s}:{},...m?{ledgerBinding:m}:{},traceHashes:{...d?{captureHash:d}:{},...l?{ocrHash:l}:{},...p?{ingestPayloadHash:p}:{}},...c?{observationEvidence:c}:{}}}function He(e,t,n){return{capture:e,report:{screenshot:{width:e.width,height:e.height,mimeType:e.mimeType,...e.windowId?{windowId:e.windowId}:{}},hashes:n,ocrBlockCount:t.blocks?.length??0,visibleConversationFingerprintCount:t.visibleConversationFingerprints?.length??0,topTexts:(t.blocks??[]).slice(0,40).map(o=>({text:o.text,...o.bbox?{bbox:o.bbox}:{},...typeof o.confidence=="number"?{confidence:o.confidence}:{}}))}}}function B(e,t,n){if(!n)return;const o=n.capture.dataBase64?T(e,`${t}-observation-window`,n.capture):void 0;f(i.join(e,`${t}-observation-evidence.json`),{...n.report,...o?{screenshotPath:o}:{}})}function qe(e){if(e.dataBase64)return`sha256:${D.createHash("sha256").update(Buffer.from(e.dataBase64,"base64")).digest("hex")}`}function Ke(e){return Y({blocks:(e.blocks??[]).map(t=>({text:t.text??"",bbox:t.bbox??null,confidence:typeof t.confidence=="number"?Number(t.confidence.toFixed(4)):null})),visibleConversationFingerprints:e.visibleConversationFingerprints??[]})}function G(e,t){let n=t,o=0,r=0,s="";const a=[];return{actionLog:a,setScenario(l){n=l,o=0,r=0},async request(l,c,h){if(a.push(l),l==="activity.snapshot"){if(o+=1,o<=n.preflightBusySnapshots)return{id:`human-${o}`,ok:!0,result:{keyDownSecondsAgo:.2,mouseMovedSecondsAgo:.2,scrollWheelSecondsAgo:.2,privacy:{capturesKeyContent:!1,capturesMousePath:!1}},latencyMs:0,traceId:h};if(n.forceIdleAfterBusy)return{id:`human-${o}`,ok:!0,result:{keyDownSecondsAgo:60,mouseMovedSecondsAgo:60,leftMouseDownSecondsAgo:60,rightMouseDownSecondsAgo:60,scrollWheelSecondsAgo:60,privacy:{capturesKeyContent:!1,capturesMousePath:!1}},latencyMs:0,traceId:h}}if(l==="automation.lease.acquire"){const m=await e.request(l,c,h),p=m.result&&typeof m.result=="object"?m.result:{};return s=typeof p.leaseId=="string"?p.leaseId:"",m}return l==="automation.lease.status"?(r+=1,n.simulateInterruptionAtLeaseStatus===r?(a.push("automation.lease.simulateInterruption"),{id:`human-lease-${r}`,ok:!0,result:{active:!0,leaseId:s,interrupted:!0,interruptReason:n.interruptionReason||"recent_mouse_activity"},latencyMs:0,traceId:h}):{id:`human-lease-${r}`,ok:!0,result:{active:!0,leaseId:s,interrupted:!1},latencyMs:0,traceId:h}):e.request(l,c,h)}}}function J(e){return e==="human-send-preflight-busy"||e==="human-send-preflight-busy-then-idle"||e==="human-send-user-active-timeout"||e==="human-observe-deferred"?{preflightBusySnapshots:100}:e==="human-send-takeover-during-run"||e==="human-send-takeover-then-idle"?{preflightBusySnapshots:0,forceIdleAfterBusy:!0,simulateInterruptionAtLeaseStatus:2,interruptionReason:"recent_mouse_activity"}:{preflightBusySnapshots:0,forceIdleAfterBusy:!0}}function Ue(){return{preflightBusySnapshots:0,forceIdleAfterBusy:!0}}function Ve(e,t){return t.sendStatus!=="sent_unconfirmed"?!1:e==="human-send-normal"||e==="human-send-preflight-busy-then-idle"||e==="human-send-takeover-then-idle"}async function N(e){const t=await C(e.helper,e.traceId),n=await k(e.helper,t,e.traceId);if(!(await I({helper:e.helper,window:n,binding:e.binding,traceId:e.traceId,settleToBottom:e.settleToBottom})).opened)return 0;const r=await W(e.helper,n.windowId,e.traceId),s=T(e.outDir,`${e.filePrefix}-observe-marker`,r),a=await E(e.helper,r,e.traceId);return f(i.join(e.outDir,`${e.filePrefix}-observe-marker-ocr.json`),{screenshotPath:s,marker:e.marker,ocr:a}),(a.blocks||[]).map(l=>l.text||"").join(`
|
|
4
|
-
`).
|
|
5
|
-
`)}}function
|
|
3
|
+
`),r=n.filter(u=>o.includes(u)),s=e.sendResults.map(u=>typeof u.details?.sendStatus=="string"?u.details.sendStatus:"").filter(Boolean),a=e.sendResults.filter(V).length,d=E(t,"waitingRecords"),l=E(t,"failedRecords"),c=E(t,"manualReviewRecords"),f=t.find(u=>!u.ok||u.skipped),m=s.length===n.length&&s.every(u=>u==="sent_unconfirmed"||u==="confirmed_echo"),p=!f&&n.length>0&&r.length>0&&m&&d===0&&l===0&&c===0;return{ok:p,reasonCode:p?void 0:f?.reasonCode||(d>0?"outbound_send_waiting":void 0)||(l>0?"outbound_send_failed":void 0)||(c>0?"outbound_manual_review":void 0)||(m?void 0:"outbound_status_not_accepted")||(r.length?"partial_sent_messages_observed":"sent_messages_not_observed"),sentTexts:n,confirmedTexts:r,outboundStatuses:s,skippedFixtureCount:a,pendingOutboundCount:d,failedOutboundCount:l,manualReviewCount:c}}function V(e){return e.skipped===!0&&e.reasonCode==="ffmpeg_unavailable_for_video_fixture"}function E(e,t){return e.reduce((n,o)=>{const r=o.details?.[t];return n+(typeof r=="number"&&Number.isFinite(r)?r:0)},0)}async function Fe(e){const t=["send-text"],n=[];for(const c of t)n.push(await z({...e,step:c}));await L(1200);const o=await Ne(e,3,{observePipeline:"edge-structure"}),r=D(o.messages),s=i.join(e.options.outDir,"bottom-page.md"),a=y(o.messages,{conversation:e.options.conversation,attachmentsDirName:i.basename(o.attachmentsDir)});h(i.join(e.options.outDir,"bottom-page-summary.json"),r),h(i.join(e.options.outDir,"server-observe-messages.json"),o.messages),A(s,a);const d=Be({sendResults:n,observedMessages:o.messages}),l=d.ok&&o.messages.length>0;return{step:"send-and-confirm",ok:l,reasonCode:l?void 0:d.reasonCode,errorSummary:n.find(c=>!c.ok||c.skipped)?.errorSummary,details:{sent:n.map(c=>({step:c.step,ok:c.ok,skipped:c.skipped,reasonCode:c.reasonCode,errorSummary:c.errorSummary,text:c.details?.text,attachmentPath:c.details?.attachmentPath,sendStatus:c.details?.sendStatus,deferReason:c.details?.deferReason,failureCode:c.details?.failureCode,commitStage:c.details?.commitStage,attemptCount:c.details?.attemptCount,nextAttemptAt:c.details?.nextAttemptAt,waitingRecords:c.details?.waitingRecords,failedRecords:c.details?.failedRecords,manualReviewRecords:c.details?.manualReviewRecords})),sentTexts:d.sentTexts,confirmedTexts:d.confirmedTexts,outboundStatuses:d.outboundStatuses,skippedFixtureCount:d.skippedFixtureCount,pendingOutboundCount:d.pendingOutboundCount,failedOutboundCount:d.failedOutboundCount,manualReviewCount:d.manualReviewCount,observedCount:o.messages.length,byKind:r.byKind,attachmentSummary:r.attachmentSummary,attachmentsDir:o.attachmentsDir,reportPath:i.join(e.options.outDir,"bottom-page-summary.json"),markdownPath:s,markdown:a}}}async function Ne(e,t,n={}){let o;for(let r=1;r<=t;r+=1)try{return await I(e,n)}catch(s){if(o=s,r>=t||!Oe(s))break;try{const a=await C(e.helper,e.options.traceId),d=await k(e.helper,a,e.options.traceId);await x({helper:e.helper,window:d,binding:e.binding,traceId:e.options.traceId})}catch{}await U("observe-retry",`${e.options.traceId}:observe-retry:${r}`,1500*r)}throw o}function Oe(e){const t=e instanceof Error?e.message:String(e||"");return/conversation_not_visible|wechat_window_unavailable|wechat_window_not_found|title_not_confirmed|server_observe_failed|timeout/i.test(t)}async function Le(e){const t=await I(e),n=D(t.messages),o=i.join(e.options.outDir,"bottom-page.md"),r=y(t.messages,{conversation:e.options.conversation,attachmentsDirName:i.basename(t.attachmentsDir)});return h(i.join(e.options.outDir,"bottom-page-summary.json"),n),h(i.join(e.options.outDir,"server-observe-messages.json"),t.messages),A(o,r),{step:"read-bottom-page",ok:n.messageCount>0,reasonCode:n.messageCount>0?void 0:"no_observed_messages",details:{messageCount:n.messageCount,byKind:n.byKind,attachmentSummary:n.attachmentSummary,attachmentsDir:t.attachmentsDir,reportPath:i.join(e.options.outDir,"bottom-page-summary.json"),markdownPath:o,markdown:r}}}async function He(e){const t=await I(e,{observePipeline:"edge-structure",persistLocalLedger:!0});$(e.options.outDir,"download-visible",t.observationEvidence);const n=D(t.messages),o=i.join(e.options.outDir,"download-visible.md"),r=y(t.messages,{conversation:e.options.conversation,attachmentsDirName:i.basename(t.attachmentsDir)}),s=q(t.messages,t.attachmentsDir,{observePipeline:t.observePipeline,downloadAnchorText:e.options.downloadAnchorText,ledgerPath:t.ledgerPath,ledgerBinding:t.ledgerBinding,traceHashes:t.traceHashes});h(i.join(e.options.outDir,"bottom-page-summary.json"),n),h(i.join(e.options.outDir,"download-visible-summary.json"),s),h(i.join(e.options.outDir,"download-visible-messages.json"),t.messages),A(o,r);const a=!!(e.options.allowEmptyMedia&&s.reasonCode==="no_visible_media_candidates"&&t.messages.length>0),d=s.ok||a;return{step:"download-visible-media",ok:d,reasonCode:d?void 0:s.reasonCode,details:{mediaCandidateCount:s.mediaCandidateCount,localAttachmentCount:s.localAttachmentCount,originalLocalAttachmentCount:s.originalLocalAttachmentCount,previewAttachmentCount:s.previewAttachmentCount,missingLocalPathCount:s.missingLocalPathCount,pendingAttachmentCount:s.pendingAttachmentCount,byKind:s.byKind,byAvailability:s.byAvailability,reasonCodes:s.reasonCodes,mediaReasonCode:s.reasonCode,allowEmptyMediaRead:a,attachmentsDir:t.attachmentsDir,reportPath:i.join(e.options.outDir,"download-visible-summary.json"),markdownPath:o,markdown:r,phases:s.phases,observePipeline:s.observePipeline,ledgerPath:s.ledgerPath,ledgerRevision:s.ledger?.revision,traceHashes:s.traceHashes,traceEvents:s.traceEvents}}}async function qe(e){const t=await I(e,{observePipeline:"server-observe"});$(e.options.outDir,"server-observe",t.observationEvidence),h(i.join(e.options.outDir,"server-observe-messages.json"),t.messages);const n=i.join(e.options.outDir,"server-observe.md"),o=y(t.messages,{conversation:e.options.conversation,attachmentsDirName:i.basename(t.attachmentsDir)});A(n,o);const r=Y(t.messages);return{step:"server-observe",ok:t.messages.length>0,reasonCode:t.messages.length>0?void 0:"no_observed_messages",details:{observedCount:t.messages.length,byKind:N(t.messages.map(s=>s.kind||"unknown")),attachmentSummary:r,attachmentsDir:t.attachmentsDir,markdownPath:n,markdown:o}}}async function Ke(e){const t=await I(e,{observePipeline:"edge-structure",downloadMedia:!1});$(e.options.outDir,"structure-window",t.observationEvidence),h(i.join(e.options.outDir,"structure-window-messages.json"),t.messages);const n=i.join(e.options.outDir,"structure-window.md"),o=y(t.messages,{conversation:e.options.conversation,attachmentsDirName:i.basename(t.attachmentsDir)});A(n,o);const r=Y(t.messages);return{step:"structure-window",ok:t.messages.length>0,reasonCode:t.messages.length>0?void 0:"no_observed_messages",details:{observedCount:t.messages.length,byKind:N(t.messages.map(s=>s.kind||"unknown")),attachmentSummary:r,attachmentsDir:t.attachmentsDir,markdownPath:n,markdown:o}}}async function I(e,t={}){const n=ve({sessionId:e.options.sessionId,observePipeline:t.observePipeline}),o=de({serverUrl:e.options.serverUrl});e.options.skipRuntimeUpsert||(n==="direct-edge-structure"?await o.upsertRuntime(e.runtime):await o.upsertRuntime(e.runtime,{...e.binding,enabled:!1,allowReply:!1}));const r=i.join(e.options.outDir,"inbound-attachments");b.mkdirSync(r,{recursive:!0});const s=t.persistLocalLedger?ft(e.options):void 0,a=s?fe(s,e.runtime.runtimeId):void 0;let d,l,c;const f=await be({runtime:e.runtime,binding:{...e.binding,downloadMedia:t.downloadMedia??e.binding.downloadMedia},helper:e.helper,api:o,workDir:e.options.workDir,attachmentsDir:r,traceId:e.options.traceId,mediaAnchorText:e.options.downloadAnchorText,activityGuard:e.options.activityGuard,observePipeline:t.observePipeline,localLedgerTailAnchors:a?ht(a.bindings[e.binding.bindingId]):void 0,onObservationEvidence:({capture:u,ocr:w})=>{d=Ge(u),l=ze(w),c=Ue(u,w,{captureHash:d,ocrHash:l})}});let m,p;if(a&&s){const u=ge({ledger:a,bindingId:e.binding.bindingId,observedMessages:f});m=u.binding,p=ne({bindingId:e.binding.bindingId,revision:m.revision,messages:u.newMessages}),he(s,a)}return{messages:f,attachmentsDir:r,observePipeline:t.observePipeline,...s?{ledgerPath:s}:{},...m?{ledgerBinding:m}:{},traceHashes:{...d?{captureHash:d}:{},...l?{ocrHash:l}:{},...p?{ingestPayloadHash:p}:{}},...c?{observationEvidence:c}:{}}}function Ue(e,t,n){return{capture:e,report:{screenshot:{width:e.width,height:e.height,mimeType:e.mimeType,...e.windowId?{windowId:e.windowId}:{}},hashes:n,ocrBlockCount:t.blocks?.length??0,visibleConversationFingerprintCount:t.visibleConversationFingerprints?.length??0,topTexts:(t.blocks??[]).slice(0,40).map(o=>({text:o.text,...o.bbox?{bbox:o.bbox}:{},...typeof o.confidence=="number"?{confidence:o.confidence}:{}}))}}}function $(e,t,n){if(!n)return;const o=n.capture.dataBase64?P(e,`${t}-observation-window`,n.capture):void 0;h(i.join(e,`${t}-observation-evidence.json`),{...n.report,...o?{screenshotPath:o}:{}})}function Ge(e){if(e.dataBase64)return`sha256:${R.createHash("sha256").update(Buffer.from(e.dataBase64,"base64")).digest("hex")}`}function ze(e){return ne({blocks:(e.blocks??[]).map(t=>({text:t.text??"",bbox:t.bbox??null,confidence:typeof t.confidence=="number"?Number(t.confidence.toFixed(4)):null})),visibleConversationFingerprints:e.visibleConversationFingerprints??[]})}function J(e,t){let n=t,o=0,r=0,s="";const a=[];return{actionLog:a,setScenario(l){n=l,o=0,r=0},async request(l,c,f){if(a.push(l),l==="activity.snapshot"){if(o+=1,o<=n.preflightBusySnapshots)return{id:`human-${o}`,ok:!0,result:{keyDownSecondsAgo:.2,mouseMovedSecondsAgo:.2,scrollWheelSecondsAgo:.2,privacy:{capturesKeyContent:!1,capturesMousePath:!1}},latencyMs:0,traceId:f};if(n.forceIdleAfterBusy)return{id:`human-${o}`,ok:!0,result:{keyDownSecondsAgo:60,mouseMovedSecondsAgo:60,leftMouseDownSecondsAgo:60,rightMouseDownSecondsAgo:60,scrollWheelSecondsAgo:60,privacy:{capturesKeyContent:!1,capturesMousePath:!1}},latencyMs:0,traceId:f}}if(l==="automation.lease.acquire"){const m=await e.request(l,c,f),p=m.result&&typeof m.result=="object"?m.result:{};return s=typeof p.leaseId=="string"?p.leaseId:"",m}return l==="automation.lease.status"?(r+=1,n.simulateInterruptionAtLeaseStatus===r?(a.push("automation.lease.simulateInterruption"),{id:`human-lease-${r}`,ok:!0,result:{active:!0,leaseId:s,interrupted:!0,interruptReason:n.interruptionReason||"recent_mouse_activity"},latencyMs:0,traceId:f}):{id:`human-lease-${r}`,ok:!0,result:{active:!0,leaseId:s,interrupted:!1},latencyMs:0,traceId:f}):e.request(l,c,f)}}}function Q(e){return e==="human-send-preflight-busy"||e==="human-send-preflight-busy-then-idle"||e==="human-send-user-active-timeout"||e==="human-observe-deferred"?{preflightBusySnapshots:100}:e==="human-send-takeover-during-run"||e==="human-send-takeover-then-idle"?{preflightBusySnapshots:0,forceIdleAfterBusy:!0,simulateInterruptionAtLeaseStatus:2,interruptionReason:"recent_mouse_activity"}:{preflightBusySnapshots:0,forceIdleAfterBusy:!0}}function Ve(){return{preflightBusySnapshots:0,forceIdleAfterBusy:!0}}function Je(e,t){return t.sendStatus!=="sent_unconfirmed"?!1:e==="human-send-normal"||e==="human-send-preflight-busy-then-idle"||e==="human-send-takeover-then-idle"}async function B(e){const t=await C(e.helper,e.traceId),n=await k(e.helper,t,e.traceId);if(!(await x({helper:e.helper,window:n,binding:e.binding,traceId:e.traceId,settleToBottom:e.settleToBottom})).opened)return 0;const r=await j(e.helper,n.windowId,e.traceId),s=P(e.outDir,`${e.filePrefix}-observe-marker`,r),a=await W(e.helper,r,e.traceId);return h(i.join(e.outDir,`${e.filePrefix}-observe-marker-ocr.json`),{screenshotPath:s,marker:e.marker,ocr:a}),Qe(a.blocks||[],e.marker)}function Qe(e,t){const n=t.trim();if(!n)return 0;const o=e.map(a=>a.text||"").join(`
|
|
4
|
+
`),r=X(o,n);if(r>0)return r;const s=Z(n);return s.length<8?0:X(Z(o),s)}function Z(e){return e.normalize("NFKC").replace(/[\s\u200B-\u200D\uFEFF]+/g,"")}function X(e,t){if(!t)return 0;let n=0,o=0;for(;;){const r=e.indexOf(t,o);if(r<0)break;n+=1,o=r+t.length}return n}async function Ze(e){const t=Math.max(1,e.attempts??3),n=typeof e.targetGreaterThan=="number"&&Number.isFinite(e.targetGreaterThan)?e.targetGreaterThan:null;let o=0;for(let r=0;r<t;r+=1){if(o=await B({helper:e.helper,binding:e.binding,marker:e.marker,traceId:e.traceId,outDir:e.outDir,filePrefix:`${e.filePrefix}-${r+1}`,settleToBottom:e.settleToBottom}),n==null?o>0:o>n)return o;r<t-1&&await L(800)}return o}function Xe(e,t,n){return n.length>0||e.sendStatus==="confirmed_echo"?"confirmed_echo":t.waitingRecords.length>0?"send_waiting_user_idle":t.failedRecords.length>0?e.failureCode==="user_active_timeout"?"failed:user_active_timeout":"outbound_send_failed":t.manualReviewRecords.length>0||e.sendStatus==="manual_review"?"manual_review":e.sendStatus==="sent_unconfirmed"?"sent_unconfirmed":e.sendStatus}function Ye(e,t){return e==="human-send-preflight-busy"?t.outboundStatus==="queued"&&t.observedMarkerCount===0&&!t.actionLog.includes("clipboard.setText")&&!t.actionLog.includes("keyboard.shortcut"):e==="human-send-user-active-timeout"?t.outboundStatus==="failed"&&t.runStatus==="failed:user_active_timeout"&&t.observedMarkerCount===0&&!t.actionLog.includes("clipboard.setText")&&!t.actionLog.includes("keyboard.shortcut"):e==="human-send-takeover-during-run"?(t.outboundStatus==="queued"||t.outboundStatus==="manual_review")&&t.observedMarkerCount===0:e==="human-send-preflight-busy-then-idle"||e==="human-send-takeover-then-idle"||e==="human-send-normal"?t.outboundStatus==="confirmed_echo"||t.outboundStatus==="sent_unconfirmed"&&t.observedMarkerCount===1:t.runStatus==="observe_deferred_user_active"}function et(e){return we({runtimeId:e.runtimeId,machineId:e.machineId,pollIntervalMs:6e4,platform:e.platform,foregroundPolicy:"work",bindings:[{bindingId:`action-smoke:${_(e.conversation)}`,sessionId:e.sessionId||`action-smoke:${_(e.runtimeId)}`,conversationDisplayName:e.conversation,enabled:!0,allowReply:!0,downloadMedia:!0}]})}function tt(e,t){const n=ce({platform:e.platform});if(!n.ok)throw new Error(`${n.reasonCode}: ${n.message}`);return new le({helperPath:n.helperPath,expectedHelperVersion:n.version||ue,requiredCapabilities:st(e),guardUnsafeWindowsCommands:(e.platform??process.platform)==="win32",cleanupWindowsOverlays:(e.platform??process.platform)==="win32",requestLogger:nt(t)})}function nt(e){return t=>{b.appendFileSync(e,`${JSON.stringify(ot(t))}
|
|
5
|
+
`)}}function ot(e){const t={phase:e.phase,at:e.at,id:e.id,command:e.command};return e.traceId&&(t.traceId=e.traceId),e.timeoutMs!=null&&(t.timeoutMs=e.timeoutMs),e.durationMs!=null&&(t.durationMs=e.durationMs),e.params!==void 0&&(t.params=M(e.params)),e.ok!==void 0&&(t.ok=e.ok),e.errorCode&&(t.errorCode=e.errorCode),e.errorSummary&&(t.errorSummary=e.errorSummary),e.latencyMs!=null&&(t.latencyMs=e.latencyMs),e.result!==void 0&&(t.result=M(e.result)),t}function M(e,t="",n=0){if(e==null||typeof e=="number"||typeof e=="boolean")return e;if(typeof e=="string")return rt(t,e)?{type:"string",length:e.length,sha256:R.createHash("sha256").update(e).digest("hex").slice(0,16),preview:e.slice(0,120)}:e;if(Array.isArray(e)){const s=e.slice(0,10).map(a=>M(a,t,n+1));return e.length>s.length?{type:"array",length:e.length,items:s}:s}if(typeof e!="object")return String(e);if(n>=5)return{type:"object",truncated:!0};const o=e,r={};for(const[s,a]of Object.entries(o))r[s]=M(a,s,n+1);return r}function rt(e,t){return t.length>240?!0:/(base64|bytes|data|image|screenshot|png|jpeg|jpg|buffer)/i.test(e)&&t.length>48}function st(e){if((e.platform??process.platform)==="win32")return e.steps.some(t=>t.startsWith("send")||t.startsWith("human-send"))?T("send"):e.steps.some(t=>t==="download-visible-media")?T("download"):T("observe")}function at(e){const t=String(e||"").trim().toLowerCase();if(t){if(t==="windows"||t==="win32")return"win32";if(t==="macos"||t==="darwin")return"darwin";throw new Error(`unsupported_wechat_smoke_platform:${e}`)}}function it(e,t){const n=i.join(t.outDir,"fixtures");b.mkdirSync(n,{recursive:!0});const o=new Date().toISOString().replace(/[-:.TZ]/g,"").slice(0,14);if(e==="send-text")return{text:t.text||`codex-product-action text ${o}`};if(e==="send-file"){const r=t.filePath||i.join(n,`wechat-action-smoke-${o}.txt`);return t.filePath||b.writeFileSync(r,`Shennian WeChat product action smoke
|
|
6
6
|
${o}
|
|
7
|
-
`,"utf8"),{text:`codex-product-action file ${o}`,attachmentPath:r}}if(e==="send-image"){const r=t.imagePath||i.join(n,`wechat-action-smoke-${o}.png`);return t.imagePath||
|
|
8
|
-
`,"utf8")}function A(e,t){
|
|
9
|
-
`)}async function
|
|
10
|
-
`),process.exit(1)});export{
|
|
7
|
+
`,"utf8"),{text:`codex-product-action file ${o}`,attachmentPath:r}}if(e==="send-image"){const r=t.imagePath||i.join(n,`wechat-action-smoke-${o}.png`);return t.imagePath||gt(r),{text:`codex-product-action image ${o}`,attachmentPath:r}}if(e==="send-video"){const r=t.videoPath||i.join(n,`wechat-action-smoke-${o}.mp4`);return!t.videoPath&&!pt(r)?{skipped:!0,reasonCode:"ffmpeg_unavailable_for_video_fixture"}:{text:`codex-product-action video ${o}`,attachmentPath:r}}throw new Error(`step ${e} is not a send step`)}function Y(e){const t=e.flatMap(n=>dt(n.mediaMetadata));return{count:t.length,byAvailability:N(t.map(n=>ee(n,"availability")||"unknown")),localPaths:t.map(n=>ee(n,"localPath")).filter(n=>!!n)}}function dt(e){if(!e||typeof e!="object")return[];const t=e;return Array.isArray(t.attachments)?t.attachments.filter(te):te(t.attachment)?[t.attachment]:t.localPath||t.url||t.availability||t.name||t.fileName?[t]:[]}function ct(e){const t=g(e,"--mode")||(e[0]?.startsWith("--")?"":e[0]);return t==="send"||t==="observe"||t==="all"||t==="open-read"?t:"open-read"}function ut(e){return e==="open-read"||e==="send-text"||e==="send-file"||e==="send-image"||e==="send-video"||e==="structure-window"||e==="server-observe"||e==="read-bottom-page"||e==="download-visible-media"||e==="send-and-confirm"||e==="human-send-preflight-busy"||e==="human-send-preflight-busy-then-idle"||e==="human-send-user-active-timeout"||e==="human-send-takeover-during-run"||e==="human-send-takeover-then-idle"||e==="human-send-normal"||e==="human-observe-deferred"?e:null}function lt(e,t){const n=[];for(let o=0;o<e.length;o+=1)e[o]===t&&e[o+1]&&n.push(e[o+1]);return n}function g(e,t){const n=e.indexOf(t),o=n>=0?e[n+1]:void 0;return o&&!o.startsWith("--")?o:void 0}function F(e){return e?i.resolve(e):void 0}function mt(e){return Array.from(new Set(e))}function P(e,t,n){const o=n.mimeType.includes("jpeg")?".jpg":".png",r=i.join(e,`${t}${o}`);return b.writeFileSync(r,Buffer.from(n.dataBase64,"base64")),r}function h(e,t){b.mkdirSync(i.dirname(e),{recursive:!0}),b.writeFileSync(e,`${JSON.stringify(t,null,2)}
|
|
8
|
+
`,"utf8")}function A(e,t){b.mkdirSync(i.dirname(e),{recursive:!0}),b.writeFileSync(e,t,"utf8")}function ft(e){return i.join(e.outDir,"wechat-channel-ledger.json")}function ht(e,t=3){return(e?.recent??[]).slice(-t).map(n=>({stableMessageKey:n.stableMessageKey,senderRole:n.senderRole,kind:n.kind,anchorText:n.anchorText??n.normalizedText??n.textExcerpt??null,anchorMetadata:n.anchorMetadata??null}))}function gt(e){b.writeFileSync(e,Buffer.from("iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAIAAAAlC+aJAAAAVElEQVR4nO3PMQ0AMAgAMUTof2kOKWzo8mCqkmeX9waYtwOwG4DbANwG4DYAtwG4DcBtAG4DcBuA2wDcBuA2ALcBuA3AbQBuA3AbgNsA3AbgNgC3AbgNwG0AbgNwG8BPDyCqAzmh3yHRAAAAAElFTkSuQmCC","base64"))}function pt(e){return se("ffmpeg",["-y","-f","lavfi","-i","testsrc=size=160x120:rate=5","-t","1","-pix_fmt","yuv420p",e],{encoding:"utf8",stdio:"ignore",timeout:2e4}).status===0&&b.existsSync(e)}function N(e){const t={};for(const n of e)t[n]=(t[n]??0)+1;return t}function ee(e,t){return typeof e[t]=="string"?e[t].trim():""}function te(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}function _(e){return R.createHash("sha256").update(e).digest("hex").slice(0,16)}function ne(e){return`sha256:${R.createHash("sha256").update(bt(e)).digest("hex")}`}function bt(e){return JSON.stringify(O(e))}function O(e){if(Array.isArray(e))return e.map(O);if(!e||typeof e!="object")return e;const t=e;return Object.fromEntries(Object.keys(t).sort().map(n=>[n,O(t[n])]))}function wt(e){const t=e instanceof Error?e.message:String(e),n=t.split(":",1)[0]?.trim();return n&&/^[a-z0-9_/-]+$/i.test(n)?n.replace(/\//g,"_"):/permission|accessibility|screen|automation/i.test(t)?"permission_missing":/wechat.*window|window.*unavailable/i.test(t)?"wechat_window_unavailable":/observe|VLM|server|api/i.test(t)?"server_observe_failed":"action_smoke_failed"}function vt(e){if(e==="wechat_duplicate_instance")return"\u8BF7\u5173\u95ED\u91CD\u590D\u767B\u5F55\u3001\u5DF2\u767B\u5F55\u63D0\u793A\u6216\u7B2C\u4E8C\u4E2A\u5FAE\u4FE1\u4E3B\u7A97\u53E3\uFF0C\u53EA\u4FDD\u7559\u4E00\u4E2A\u5DF2\u767B\u5F55\u7684 Windows \u5FAE\u4FE1\u4E3B\u7A97\u53E3\u540E\u91CD\u8BD5\u3002"}function L(e){return new Promise(t=>setTimeout(t,e))}function yt(e){process.stdout.write(`${JSON.stringify({ok:e.ok,conversation:e.conversation,outDir:e.outDir,durationMs:e.durationMs,steps:e.steps.map(t=>({step:t.step,ok:t.ok,skipped:t.skipped,reasonCode:t.reasonCode,userAction:t.userAction,details:t.details,errorSummary:t.errorSummary}))},null,2)}
|
|
9
|
+
`)}async function St(){const e=Ce(process.argv.slice(2)),t=await xe(e);yt(t),(!t.ok||e.requireServer&&t.steps.some(n=>n.step==="server-observe"&&!n.ok))&&process.exit(1)}process.argv[1]&&i.resolve(process.argv[1])===ae(import.meta.url)&&St().catch(e=>{process.stderr.write(`${e instanceof Error?e.stack||e.message:String(e)}
|
|
10
|
+
`),process.exit(1)});export{D as buildBottomPageReport,q as buildDownloadVisibleMediaReport,Be as buildSendAndConfirmEvidence,We as buildSendStepAcceptance,Qe as countMarkerOccurrencesInOcrBlocks,$e as nextSmokeDrainWaitMs,Ce as parseWeChatChannelActionSmokeArgs,ve as resolveWeChatSmokeObserveSessionMode,xe as runWeChatChannelActionSmoke,ke as stepsForMode};
|
package/dist/src/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{Command as V}from"commander";import u from"chalk";import M from"node:os";import R from"node:fs";import{loadConfig as w,saveConfig as G,getConfigPath as J,getShennianDir as j,resolveShennianPath as A}from"./config/index.js";import{CliRelayClient as H}from"./relay/client.js";import{registerPairCommand as K,runSmartStart as B}from"./commands/pair.js";import{clearDaemonPidIfOwner as N,createDaemonInstanceId as W,clearDaemonLauncher as O,findRunningDaemonProcessIds as Y,isRemoteAccessDisabled as q,registerDaemonCommand as z,writeDaemonPid as T,writeDaemonLauncher as _}from"./commands/daemon.js";import{registerAgentCommand as Q}from"./commands/agent.js";import{registerManagerCommand as X}from"./commands/manager.js";import{registerExternalCommand as Z}from"./commands/external.js";import{registerWeChatCommand as ee}from"./commands/wechat.js";import{
|
|
2
|
-
Disconnecting...`)),v({level:"info",wsEvent:"daemon.stop"}),clearInterval(o),C.cleanup(),I?.stop(),l.disconnect(),N(process.pid,i),O(i),process.exit(0)};process.on("SIGINT",U),process.on("SIGTERM",U)});const F=a.command("config").description("View or modify configuration");F.command("show",{isDefault:!0}).description("Show current configuration").action(()=>{console.log(`Config file: ${J()}`);const t={...w()};if(t.machineToken&&(t.machineToken=t.machineToken.slice(0,12)+"..."),t.serverUrl){const o=
|
|
1
|
+
import{Command as V}from"commander";import u from"chalk";import M from"node:os";import R from"node:fs";import{loadConfig as w,saveConfig as G,getConfigPath as J,getShennianDir as j,resolveShennianPath as A}from"./config/index.js";import{CliRelayClient as H}from"./relay/client.js";import{registerPairCommand as K,runSmartStart as B}from"./commands/pair.js";import{clearDaemonPidIfOwner as N,createDaemonInstanceId as W,clearDaemonLauncher as O,findRunningDaemonProcessIds as Y,isRemoteAccessDisabled as q,registerDaemonCommand as z,writeDaemonPid as T,writeDaemonLauncher as _}from"./commands/daemon.js";import{registerAgentCommand as Q}from"./commands/agent.js";import{registerManagerCommand as X}from"./commands/manager.js";import{registerExternalCommand as Z}from"./commands/external.js";import{registerWeChatCommand as ee}from"./commands/wechat.js";import{registerRuntimeCommand as ne}from"./commands/runtime.js";import{registerUpgradeCommand as te}from"./commands/upgrade.js";import{registerToolsCommand as oe}from"./commands/tools.js";import{SessionManager as re}from"./session/manager.js";import{SERVERS as k,regionToUrl as se,urlToRegion as ae}from"./region.js";import{getCurrentVersion as P,handleStartupCrashCheck as ie,checkForUpdate as ce,isUpgradeVersionInCooldown as le,recordUpgradeFailure as me}from"./upgrade/engine.js";import{detectAgents as pe}from"./agents/detect.js";import{augmentProcessPath as L}from"./env-path.js";L();const b=P(),de=3e4,ge=5*6e4;import{getCachedAgentInfos as ue,resolveAgentInfos as fe}from"./agents/model-registry.js";import{initCliLogReporter as he,reportLog as v}from"./log-reporter.js";import{isNativeFusionEnabled as ve}from"./native-fusion/config.js";import{NativeSessionFusionService as Se}from"./native-fusion/service.js";import{startDaemonLogRetention as Ie}from"./daemon-log.js";const we=j(),De=A("daemon.pid");function ye(){try{const n=R.readFileSync(De,"utf-8").trim();if(n.startsWith("{")){const o=JSON.parse(n),r=Number(o.pid);return Number.isInteger(r)&&r>0?r:null}const t=parseInt(n,10);return Number.isInteger(t)&&t>0?t:null}catch{return null}}function $e(n){return n.replace(/^https:\/\//,"wss://").replace(/^http:\/\//,"ws://")}function Re(n){const t=[`trigger=${n.trigger??"unknown"}`,`phase=${n.phase}`,`code=${n.code??"unknown"}`,`attempt=${n.reconnectAttempt}`];return n.reason&&t.push(`reason=${n.reason}`),n.error&&t.push(`error=${n.error}`),t.join(" ")}async function x(n,t=5e3){const o=Date.now();for(;Date.now()-o<t;){try{process.kill(n,0)}catch{return!0}await new Promise(r=>setTimeout(r,100))}try{return process.kill(n,0),!1}catch{return!0}}const a=new V;a.name("shennian").description("Shennian \u2014 AI Agent Control Plane").version(b),a.option("--api <url>","Server URL override").option("--name <name>","Machine name",M.hostname()).action(async n=>{const t=w(),o=n.api??process.env.SHENNIAN_DESKTOP_SERVER_URL??t.serverUrl??void 0;await B(o,n.name)}),a.command("run-service",{hidden:!0}).description("(internal) Connect to relay server, called by the background service").option("--api <url>","Server URL override").action(async n=>{const t=A("env.json");try{const e=JSON.parse(R.readFileSync(t,"utf-8"));for(const[g,m]of Object.entries(e))process.env[g]||(process.env[g]=m);L()}catch{}const o=Ie();q()&&(console.log(`[${new Date().toISOString()}] remote access disabled, service start skipped`),clearInterval(o),process.exit(0));const r=!!(process.env.INVOCATION_ID||process.env.JOURNAL_STREAM||process.env.SHENNIAN_DESKTOP_SERVER_URL),f=async(e,g)=>{console.log(`[${new Date().toISOString()}] ${g} (PID ${e})`),process.kill(e,"SIGTERM"),await x(e)||(process.kill(e,"SIGKILL"),await x(e,2e3))};try{const e=ye();if(e&&e!==process.pid)try{process.kill(e,0),r?await f(e,"managed start taking over from existing daemon"):(console.log(`[${new Date().toISOString()}] daemon already running (PID ${e}), skipping duplicate start`),process.exit(0))}catch{}}catch{}const p=Y(process.pid);if(p.length>0)if(r)for(const e of p)try{await f(e,"managed start taking over from orphan daemon")}catch{}else console.log(`[${new Date().toISOString()}] daemon already running (PID ${p[0]}), skipping duplicate start`),process.exit(0);const i=W();T(process.pid,i,{version:b}),_(process.pid,void 0,i),process.on("exit",()=>{N(process.pid,i),O(i)}),await ie()&&(console.log(`[${new Date().toISOString()}] Rolled back to previous version, restarting...`),process.exit(0));const s=w();s.machineToken||(console.error(u.red("\u2717 Not paired yet. Run: shennian")),process.exit(1));const c=n.api??process.env.SHENNIAN_DESKTOP_SERVER_URL??s.serverUrl??k.cn.url,S=`${$e(c)}/relay/machine`,d=P(),y=pe(),h=y.map(e=>e.type),$=ue(y);s.machineId&&he(c,s.machineId),console.log(`[${new Date().toISOString()}] Connecting to ${S}... (v${d}) agents: ${h.join(",")}`),v({level:"info",wsEvent:"daemon.start",metadata:{version:d,agents:h}});let I=null;const l=new H({serverUrl:S,machineToken:s.machineToken,cliVersion:d,agentList:h,onConnected:()=>{console.log(`[${new Date().toISOString()}] \u2713 Connected`),v({level:"info",wsEvent:"daemon.connected"}),$.some(e=>e.models.length>0)&&l.sendEvent({type:"event",event:"machine.agents",payload:{agentList:h,agents:$}}),fe(y,{serverUrl:s.serverUrl??c,authToken:s.machineToken??s.accessToken}).then(e=>{JSON.stringify(e)!==JSON.stringify($)&&l.sendEvent({type:"event",event:"machine.agents",payload:{agentList:h,agents:e}})}).catch(()=>{}),import("./upgrade/engine.js").then(({readUpgradeAttempt:e,clearUpgradeAttempt:g})=>{const m=e();g(),m&&(console.log(`[${new Date().toISOString()}] [upgrade] Reporting success: ${m.from} \u2192 ${m.to}`),l.sendEvent({type:"event",event:"upgrade",payload:{state:"success",machineId:"self",from:m.from,to:m.to}}))}).catch(()=>{}),ke(l,s.autoUpgrade??"patch",d),I?.handleConnected()},onDisconnected:e=>{console.log(`[${new Date().toISOString()}] \u26A0 Disconnected, reconnecting... ${Re(e)}`),v({level:"warn",wsEvent:"daemon.disconnected",metadata:{code:e.code,reason:e.reason,error:e.error,phase:e.phase,trigger:e.trigger,reconnectAttempt:e.reconnectAttempt}})},onReq:e=>{console.log(`[${new Date().toISOString()}] [req] ${e.method}`),v({level:"info",type:"ws",wsEvent:e.method,wsDirection:"in",traceId:e.traceId}),C.handleReq(e)}});I=ve()?new Se(l):null;const C=new re(l,I,d);R.mkdirSync(we,{recursive:!0}),T(process.pid,i,{version:d}),_(process.pid,void 0,i),l.connect(),process.stdin.resume();const U=()=>{console.log(u.gray(`
|
|
2
|
+
Disconnecting...`)),v({level:"info",wsEvent:"daemon.stop"}),clearInterval(o),C.cleanup(),I?.stop(),l.disconnect(),N(process.pid,i),O(i),process.exit(0)};process.on("SIGINT",U),process.on("SIGTERM",U)});const F=a.command("config").description("View or modify configuration");F.command("show",{isDefault:!0}).description("Show current configuration").action(()=>{console.log(`Config file: ${J()}`);const t={...w()};if(t.machineToken&&(t.machineToken=t.machineToken.slice(0,12)+"..."),t.serverUrl){const o=ae(t.serverUrl);console.log(`Region: ${o} (${k[o].label})`)}console.log(JSON.stringify(t,null,2))}),F.command("set").description("Update a config value").argument("<key>",'Config key (e.g. "server")').argument("<value>",'Config value (e.g. "cn" or "global")').action((n,t)=>{const o=w();if(n==="server"){t!=="cn"&&t!=="global"&&(console.error(u.red('\u2717 Value must be "cn" or "global"')),process.exit(1));const r=t;o.serverUrl=se(r),G(o),console.log(u.green(`\u2713 Server set to ${k[r].label}`)),console.log(u.yellow(" Restart the background service for this to take effect: shennian stop && shennian start"));return}console.error(u.red(`\u2717 Unknown config key: ${n}. Supported: server`)),process.exit(1)}),K(a),z(a),Q(a),X(a),Z(a),ee(a),ne(a),te(a),oe(a),a.parse();async function ke(n,t,o){if(t==="none")return;let r=!1;const f=async()=>{if(r)return;let p;try{p=await ce(o)}catch{return}if(!p.hasUpdate)return;const{changeType:i,current:E,latest:s}=p;if(!(t==="patch"&&i!=="patch")&&!(t==="minor"&&i==="major")&&!le(s)){console.log(`[${new Date().toISOString()}] [upgrade] ${E} \u2192 ${s} (${i}), auto-upgrading...`),r=!0;try{const{handleUpgradeStart:c}=await import("./commands/upgrade.js");await c(n,"auto-upgrade",s,{currentVersion:o})}catch(c){const D=c instanceof Error?c.message:String(c),S=me(s,D);console.log(`[${new Date().toISOString()}] [upgrade] ${s} failed, retry after ${new Date(S.nextRetryAt).toISOString()}: ${D}`)}finally{r=!1}}};setTimeout(()=>{f()},de),setInterval(()=>{f()},ge)}
|
|
@@ -2,11 +2,13 @@ import type { AgentEvent, AgentAdapter } from '../agents/adapter.js';
|
|
|
2
2
|
import { type AgentType, type ExternalChannelSessionStatus, type ReqFrame } from '@shennian/wire';
|
|
3
3
|
import { ManagerRegistry } from './registry.js';
|
|
4
4
|
import type { SessionManagerRuntime } from '../session/types.js';
|
|
5
|
-
import { ChannelRuntime } from '../channels/runtime.js';
|
|
5
|
+
import { ChannelRuntime, type ChannelRuntimeOptions } from '../channels/runtime.js';
|
|
6
6
|
export type LocalReqDispatcher = (req: ReqFrame) => Promise<void>;
|
|
7
7
|
type ManagerRuntimeServiceOptions = {
|
|
8
8
|
getRuntime: () => SessionManagerRuntime;
|
|
9
9
|
dispatchReq: LocalReqDispatcher;
|
|
10
|
+
channelRuntime?: ChannelRuntime;
|
|
11
|
+
createWeChatRpaProductRunner?: ChannelRuntimeOptions['createWeChatRpaProductRunner'];
|
|
10
12
|
};
|
|
11
13
|
export declare function setManagerRuntimeService(service: ManagerRuntimeService | null): void;
|
|
12
14
|
export declare function getManagerRuntimeService(): ManagerRuntimeService | null;
|