shennian 0.2.100 → 0.2.101
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/publish-build-manifest.json +70 -25
- package/dist/src/channels/runtime.d.ts +5 -1
- package/dist/src/channels/runtime.js +3 -3
- package/dist/src/channels/wechat-channel/helper-assets.d.ts +59 -1
- package/dist/src/channels/wechat-channel/helper-assets.js +1 -1
- package/dist/src/channels/wechat-channel/helper-client.js +2 -2
- package/dist/src/channels/wechat-channel/helper-protocol.d.ts +1 -1
- package/dist/src/channels/wechat-channel/observer.d.ts +32 -2
- package/dist/src/channels/wechat-channel/observer.js +6 -6
- package/dist/src/channels/wechat-channel/preflight.d.ts +7 -0
- package/dist/src/channels/wechat-channel/preflight.js +1 -1
- package/dist/src/channels/wechat-channel/runner.d.ts +0 -1
- package/dist/src/channels/wechat-channel/runner.js +1 -1
- package/dist/src/channels/wechat-channel/scheduler.d.ts +4 -3
- package/dist/src/channels/wechat-channel/scheduler.js +1 -1
- package/dist/src/channels/wechat-rpa.d.ts +5 -1
- package/dist/src/channels/wechat-rpa.js +4 -4
- package/dist/src/commands/runtime.d.ts +89 -0
- package/dist/src/commands/runtime.js +1 -0
- package/dist/src/commands/wechat/command.d.ts +2 -0
- package/dist/src/commands/wechat/command.js +1 -0
- package/dist/src/commands/wechat/direct.d.ts +14 -0
- package/dist/src/commands/wechat/direct.js +2 -0
- package/dist/src/commands/wechat/doctor.d.ts +2 -0
- package/dist/src/commands/wechat/doctor.js +1 -0
- package/dist/src/commands/wechat/ipc.d.ts +9 -0
- package/dist/src/commands/wechat/ipc.js +1 -0
- package/dist/src/commands/wechat/operations.d.ts +3 -0
- package/dist/src/commands/wechat/operations.js +4 -0
- package/dist/src/commands/wechat/output.d.ts +7 -0
- package/dist/src/commands/wechat/output.js +4 -0
- package/dist/src/commands/wechat/types.d.ts +141 -0
- package/dist/src/commands/wechat/types.js +1 -0
- package/dist/src/commands/wechat/utils.d.ts +14 -0
- package/dist/src/commands/wechat/utils.js +1 -0
- package/dist/src/commands/wechat.d.ts +6 -127
- package/dist/src/commands/wechat.js +1 -7
- package/dist/src/devtools/wechat-channel-action-smoke.d.ts +2 -0
- package/dist/src/devtools/wechat-channel-action-smoke.js +8 -8
- package/dist/src/index.js +2 -2
- package/dist/src/manager/runtime.d.ts +3 -1
- package/dist/src/manager/runtime.js +5 -5
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import{defaultWeChatChannelAttachmentDir as Me,resolveVisibleWeChatChannelMedia as Se}from"./media-resolver.js";import{updateWeChatChannelBindingLedger as ke}from"./ledger.js";import{defaultWeChatChannelVectorStorePath as Te,loadWeChatChannelVectorStore as _e,saveWeChatChannelVectorStore as Be,upsertWeChatChannelVectorReferences as We}from"./vector-store.js";import{normalizeWeChatChannelAttachmentAvailability as j,normalizeWeChatChannelMessageKind as Fe}from"./core/schema.js";import{waitForWeChatChannelPacing as U,weChatChannelPacingDelayMs as Ae}from"./pacing.js";import{matchWeChatConversationFingerprints as Ee}from"./fingerprint.js";import{normalizeWeChatAnchorText as v,weChatTextSimilarity as Y}from"./anchor.js";const Re=3,Oe=5,P="wechat_login_required",A="wechat_duplicate_instance";async function Zt(e){let t=await dt(e.helper,e.traceId);t=await bt(e.helper,t,e.traceId);const n=await de({helper:e.helper,window:t,binding:e.binding,traceId:e.traceId});if(!n.opened)throw new Error(n.reason||"conversation_not_opened");let{capture:r,ocr:i}=await T({helper:e.helper,window:t,traceId:e.traceId});if(ye({capture:r,ocr:i,binding:e.binding})){t=await se(e.helper,e.traceId);const o=await de({helper:e.helper,window:t,binding:e.binding,traceId:e.traceId});if(!o.opened)throw new Error(o.reason||"wechat_window_obstructed");if({capture:r,ocr:i}=await T({helper:e.helper,window:t,traceId:e.traceId}),ye({capture:r,ocr:i,binding:e.binding}))throw new Error("wechat_window_obstructed")}return e.onObservationEvidence?.({window:t,capture:r,ocr:i}),Le(e)?Pe({...e,window:t,capture:r,ocr:i}):H({...e,window:t,capture:r,ocr:i})}function Le(e){return e.observePipeline==="edge-structure"?!0:e.observePipeline==="server-observe"?!1:!!e.api.structureWindow}async function H(e){const t=await he(e),n=await e.api.observe(e.runtime,e.binding,{screenshots:[{captureIndex:0,mimeType:t.mimeType,dataBase64:t.dataBase64,width:t.width,height:t.height}],edgeOcrBlocks:e.ocr.blocks??[],visibleConversationFingerprints:e.ocr.visibleConversationFingerprints??[],localLedgerTailAnchors:e.localLedgerTailAnchors??[]}),r=await J({helper:e.helper,runtime:e.runtime,binding:e.binding,messages:n.observedMessages??[],workDir:e.workDir,attachmentsDir:e.attachmentsDir,window:e.window,screenshot:t,windowId:e.window.windowId,traceId:e.traceId,mediaAnchorText:e.mediaAnchorText});return pe(r,ve({ocr:e.ocr,screenshot:t,binding:e.binding,anchors:e.localOutboundEchoAnchors??[]}))}async function Pe(e){if(!e.api.structureWindow)throw new Error("wechat_structure_window_unavailable");const t=await he(e);let n;try{n=await e.api.structureWindow(e.runtime,e.binding,{screenshots:[{captureIndex:0,mimeType:t.mimeType,dataBase64:t.dataBase64,width:t.width,height:t.height}],edgeOcrBlocks:e.ocr.blocks??[],visibleConversationFingerprints:e.ocr.visibleConversationFingerprints??[],localLedgerTailAnchors:e.localLedgerTailAnchors??[]})}catch(c){if(ze(c))return H(e);throw c}const r=He(n.structuredMessages??n.observedMessages??[]),i=await De({...e,messages:r,screenshot:t}),o=await J({helper:e.helper,runtime:e.runtime,binding:e.binding,messages:i.messages,workDir:e.workDir,attachmentsDir:e.attachmentsDir,window:e.window,screenshot:t,windowId:e.window.windowId,traceId:e.traceId,mediaAnchorText:e.mediaAnchorText}),a=pe(o,ve({ocr:e.ocr,screenshot:t,binding:e.binding,anchors:e.localOutboundEchoAnchors??[]}));return e.localLedger?ke({ledger:e.localLedger,bindingId:e.binding.bindingId,observedMessages:a,baselineOnly:e.baselineOnly,vectorReferences:i.vectorReferences}).newMessages:a}function ze(e){return e instanceof Error&&/\bedge_runtime_disabled\b/.test(e.message)}async function De(e){const t=Ke(e.messages);if(!t.length)return{messages:e.messages,vectorReferences:[]};const n=e.api.embedVisual?await Ve({...e,seedBlocks:t}):t.filter(a=>u(a.vectorBase64)||u(a.signature));if(!n.length)return{messages:e.messages,vectorReferences:[]};const r=e.vectorStorePath??Te(e.workDir||process.cwd(),e.runtime.runtimeId),i=_e(r,e.runtime.runtimeId),o=We({store:i,bindingId:e.binding.bindingId,blocks:n});return o.length?(Be(r,i),{messages:Ue(e.messages,o),vectorReferences:o}):{messages:e.messages,vectorReferences:[]}}function Ke(e){const t=[];for(const n of e)for(const[r,i]of(n.visualBlocks??[]).entries()){const o=u(i.blockId)||`${n.stableMessageKey}:visual-${r}`,a=u(i.blockKind)||n.kind||"visual";t.push({...i,stableMessageKey:n.stableMessageKey,blockId:o,blockKind:a,...i.bbox!==void 0?{bbox:i.bbox}:n.bbox!==void 0?{bbox:n.bbox}:{}})}return t}async function $e(e){if(!e.api.embedVisual)return[];const t=await e.api.embedVisual(e.runtime,e.binding,{screenshots:[{captureIndex:0,mimeType:e.screenshot.mimeType,dataBase64:e.screenshot.dataBase64,width:e.screenshot.width,height:e.screenshot.height}],visualBlocks:e.seedBlocks,traceId:e.traceId});return je(t.visualBlocks??t.embeddedBlocks??[],e.seedBlocks)}async function Ve(e){try{return await $e(e)}catch(t){if(qe(t))throw t;return[]}}function qe(e){return!e||typeof e!="object"?!1:e.reasonCode==="insufficient_credits"?!0:e instanceof Error&&/\binsufficient_credits\b/.test(e.message)}function je(e,t){return e.map((n,r)=>{const i=t[r];return{...n,stableMessageKey:u(n.stableMessageKey)||u(i?.stableMessageKey),blockId:u(n.blockId)||u(i?.blockId),blockKind:u(n.blockKind)||u(i?.blockKind)||"visual",...n.bbox!==void 0?{bbox:n.bbox}:i?.bbox!==void 0?{bbox:i.bbox}:{}}}).filter(n=>u(n.stableMessageKey)&&u(n.blockId))}function Ue(e,t){const n=new Map;for(const r of t){const i=n.get(r.stableMessageKey)??[];i.push(r),n.set(r.stableMessageKey,i)}return e.map(r=>{const i=n.get(r.stableMessageKey);return i?.length?{...r,visualBlocks:Ye(r,i)}:r})}function Ye(e,t){const r=(e.visualBlocks??[]).map((o,a)=>{const s=u(o.blockId)||`${e.stableMessageKey}:visual-${a}`,c=t.find(d=>d.blockId===s);return c?G(c,o):X(o)}),i=new Set(r.map(o=>o.blockId));for(const o of t)i.has(o.blockId)||r.push(G(o));return r}function G(e,t){return{...t?X(t):{},stableMessageKey:e.stableMessageKey,blockId:e.blockId,blockKind:e.blockKind,vectorStoreKey:e.vectorStoreKey,model:e.model,...e.modelVersion!==void 0?{modelVersion:e.modelVersion}:{},dims:e.dims,...e.signature?{signature:e.signature}:{},...e.bbox!==void 0?{bbox:e.bbox}:{}}}function X(e){const{vectorBase64:t,...n}=e;return n}function He(e){return e.map((t,n)=>Ge(t,n)).filter(t=>t!=null)}function Ge(e,t){if(!b(e))return null;const n=Fe(e.kind),r=Je(e.senderRole),i=g(e.normalizedText),o=g(e.anchorText)??i??g(e.textExcerpt),a=u(e.stableMessageKey)||Xe({message:e,index:t,senderRole:r,kind:n,anchorText:o});return{stableMessageKey:a,senderRole:r,...g(e.senderName)!==null?{senderName:g(e.senderName)}:{},kind:n,...i!==null?{normalizedText:i}:{},...o!==null?{anchorText:o}:{},...N(e,"anchorMetadata")?{anchorMetadata:e.anchorMetadata}:{},...N(e,"neighborContext")?{neighborContext:e.neighborContext}:{},...g(e.textExcerpt)!==null?{textExcerpt:g(e.textExcerpt)}:{},...N(e,"bbox")?{bbox:e.bbox}:{},...N(e,"mediaMetadata")?{mediaMetadata:Ze(e.mediaMetadata)}:{},...typeof e.isBaseline=="boolean"?{isBaseline:e.isBaseline}:{},...g(e.deliveryStatus)!==null?{deliveryStatus:g(e.deliveryStatus)??void 0}:{},...g(e.observedAt)!==null?{observedAt:g(e.observedAt)??void 0}:{},...Array.isArray(e.visualBlocks)?{visualBlocks:e.visualBlocks}:{},...o?{}:{anchorText:a}}}function Xe(e){const t=b(e.message.bbox)?JSON.stringify(e.message.bbox):"";return`structured-window:${[e.index,e.senderRole,e.kind,e.anchorText||u(e.message.textExcerpt)||"",t].join(":").slice(0,180)}`}function Ze(e){if(!b(e))return e;const t={...e};return N(t,"availability")&&(t.availability=j(t.availability)),b(t.attachment)&&(t.attachment=Z(t.attachment)),Array.isArray(t.attachments)&&(t.attachments=t.attachments.map(n=>b(n)?Z(n):n)),t}function Z(e){const t={...e};return N(t,"availability")&&(t.availability=j(t.availability)),t}function Je(e){return e==="self"||e==="contact"||e==="system"||e==="unknown"?e:"unknown"}async function J(e){if(e.binding.downloadMedia===!1)return e.messages;const t=e.messages.map((o,a)=>ot(o,e.screenshot,e.window,it(e.messages,a))).filter((o,a)=>Qe(e.messages,a,e.mediaAnchorText)).filter(o=>o!=null);if(!t.length)return e.messages;const n=e.attachmentsDir??Me(e.workDir||process.cwd(),e.runtime.runtimeId,e.binding.bindingId),r=await Se({helper:e.helper,candidates:t,attachmentsDir:n,screenshot:e.screenshot,windowId:e.windowId,window:e.window,traceId:e.traceId,platform:e.runtime.policy.platform,stabilityCheck:async()=>{const o=await k({helper:e.helper,window:e.window,binding:e.binding,traceId:e.traceId});return o.ok?{ok:!0}:{ok:!1,reasonCode:o.reason}}});if(!r.length)return e.messages;const i=new Map(r.map(o=>[o.messageKey,o]));return e.messages.map(o=>{const a=i.get(o.stableMessageKey);return a?{...o,mediaMetadata:at(o.mediaMetadata,a.attachment,a.reasonCode,a.attemptReasonCodes,a.resolveTrace)}:o})}function Qe(e,t,n){const r=ee(n);if(!r)return!0;const i=et(r),o=e.map((a,s)=>ee(rt(a)).includes(r)?s:-1).filter(a=>a>=0);if(!o.length)return t===tt(e,i);for(const a of o)if(t>=a&&t<=a+8||t===a-1&&!e.slice(a+1,Math.min(e.length,a+9)).some(Q))return!0;return!1}function et(e){return e.includes("video")||e.includes("vide0")?"video":e.includes("image")||e.includes("photo")||e.includes("ph0t0")||e.includes("picture")?"image":e.includes("file")||e.includes("document")?"file":null}function tt(e,t=null){for(let n=e.length-1;n>=0;n-=1)if(Q(e[n]))return nt(e[n],t)?n:-1;return-1}function Q(e){if(!e)return!1;const t=String(e.kind||"").toLowerCase(),n=b(e.mediaMetadata)?e.mediaMetadata:{},r=b(n.attachment)?n.attachment:{};return te(t,n,r)}function nt(e,t){if(!e||!t)return!0;const n=String(e.kind||"").toLowerCase(),r=b(e.mediaMetadata)?e.mediaMetadata:{},i=b(r.attachment)?r.attachment:{},o=String(i.type||"").toLowerCase(),a=String(i.mimeType||"").toLowerCase(),s=String(i.name||i.localPath||e.anchorText||e.textExcerpt||"").toLowerCase();return t==="video"?n.includes("video")||o==="video"||a.startsWith("video/")||/\.(mp4|mov|m4v|avi|mkv|webm)$/i.test(s):t==="image"?/image|photo|picture/.test(n)||o==="image"||a.startsWith("image/")||/\.(png|jpe?g|gif|webp|heic|tiff?|bmp)$/i.test(s):/file|document/.test(n)||o==="file"||!/image|photo|picture|video/.test(n)&&o!=="image"&&o!=="video"&&!a.startsWith("image/")&&!a.startsWith("video/")}function rt(e){if(!e)return"";const t=e;return[e.normalizedText,e.anchorText,e.textExcerpt,t.text].map(n=>String(n||"")).join(`
|
|
2
|
-
`)}function
|
|
3
|
-
`)||null}function ot(e,t,n,r){const i=String(e.kind||"").toLowerCase(),o=b(e.mediaMetadata)?e.mediaMetadata:{},a=b(o.attachment)?o.attachment:{},s=u(a.availability)||u(o.availability),c=u(a.localPath)||u(o.localPath);if(s==="edge-local"&&c||!te(i,o,a))return null;const d=E(e.bbox)??E(o.bbox)??E(o.downloadActionBbox),h=E(o.downloadActionBbox),f=ne(d,t,n),l=ct(d,t,n),m=ne(h,t,n);return{messageKey:e.stableMessageKey,kind:u(a.type)||u(a.kind)||u(o.messageType)||i,fileName:u(a.name)||u(a.fileName)||u(o.fileName)||null,mimeType:u(a.mimeType)||u(o.mimeType)||null,size:I(a.size)??I(o.size),mediaStatus:st(o,a),observedAt:e.observedAt??null,contextText:r??e.normalizedText??e.anchorText??e.textExcerpt??null,...f?{bbox:f}:{},...l?{screenshotBbox:l}:{},...m?{downloadActionBbox:m}:{}}}function at(e,t,n,r,i){const o=b(e)?{...e}:{};return{...o,availability:b(t)?t.availability:o.availability,mediaStatus:b(t)&&t.availability==="edge-local"?"downloaded":o.mediaStatus,attachment:t,edgeResolveReasonCode:n,...r?.length?{edgeResolveAttempts:r}:{},...i?{edgeResolveTrace:i}:{}}}function st(e,t){const n=u(e.mediaStatus);if(n==="not_downloaded")return"not_downloaded";if(n==="downloaded"||n==="available")return"available";const r=u(t.availability)||u(e.availability);return r==="pending-download"?"not_downloaded":r==="metadata-only"?"metadata_only":n||null}function te(e,t,n){const r=`${e} ${u(t.messageType)} ${u(n.type)} ${u(n.kind)}`.toLowerCase();return/image|photo|video|file|document/.test(r)}function E(e){if(!b(e))return null;const t=I(e.x),n=I(e.y),r=I(e.width),i=I(e.height);return t==null||n==null||r==null||i==null?null:{x:t,y:n,width:r,height:i,...typeof e.coordinateSpace=="string"?{coordinateSpace:e.coordinateSpace}:{}}}function ne(e,t,n){if(!e)return null;if(e.coordinateSpace==="screen")return e;const r=W(e,t,n);if(!r)return e;const i=n.bounds;if(!i?.width||!i?.height)return{...e,x:r.x-e.width/2,y:r.y-e.height/2,coordinateSpace:"screen"};const o=i.width/t.width,a=i.height/t.height,s=r.x,c=r.y;return{x:s-e.width*o/2,y:c-e.height*a/2,width:e.width*o,height:e.height*a,coordinateSpace:"screen"}}function ct(e,t,n){if(!e)return null;if(e.coordinateSpace!=="screen")return{...e,coordinateSpace:"screenshotPixel"};const r=n.bounds;if(!r?.width||!r?.height)return null;const i=t.width/r.width,o=t.height/r.height;return{x:(e.x-r.x)*i,y:(e.y-r.y)*o,width:e.width*i,height:e.height*o,coordinateSpace:"screenshotPixel"}}function b(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}function N(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function u(e){return typeof e=="string"?e.trim():""}function g(e){return u(e)||null}function I(e){const t=Number(e);return Number.isFinite(t)?t:null}async function Jt(e,t){await re(e,t)}async function re(e,t){let n=await e.request("permissions.check",{},t);w(n,"permissions.check");const r=n.result??{};if(r.screenRecording===!1)throw new Error("permission_screen_recording_missing");if(r.automation===!1)throw new Error("permission_automation_missing");if(r.wechatRunning===!1)throw new Error("wechat_not_running");if(ut(r))throw new Error(P);if(lt(r))throw new Error(A);if(r.wechatMainWindowResponsive===!1)throw new Error("wechat_window_unresponsive");if(ie([...r.captureCandidates??[],...r.restoreCandidates??[],...r.hiddenRestoreCandidates??[],...r.duplicateMainWindows??[],...r.unsafeAuxiliaryWindows??[]]))throw new Error(A);if(r.wechatWindowAvailable===!1&&String(r.platform??"").toLowerCase()!=="win32")throw new Error("wechat_window_unavailable");return r}async function dt(e,t){const n=await re(e,t);if(String(n.platform??"").toLowerCase()==="win32"){const i=await e.request("windows.ensureReady",{activate:!0,allowRecovery:!1,allowLaunch:!1},t);w(i,"windows.ensureReady");const o=i.result;if(!o?.windowId)throw new Error("helper_invalid_response: windows.ensureReady missing WeChat window data");return o}const r=await se(e,t);if(!r?.windowId)throw new Error("helper_invalid_response: windows.list missing WeChat window data");return r}function ie(e,t){const n=e.filter(y).filter(i=>i.visible!==!1&&i.minimized!==!0),r=t??n.slice().sort((i,o)=>M(o)-M(i))[0];return n.find(i=>ht(i,r))??null}function lt(e){return e.duplicateInstanceBlocked===!0||Number(e.duplicateMainWindowCount??0)>0||Number(e.unsafeAuxiliaryWindowCount??0)>0||(e.duplicateMainWindows??[]).some(y)||(e.unsafeAuxiliaryWindows??[]).some(y)}function ut(e){if(Number(e.duplicateMainWindowCount??0)>0||(e.duplicateMainWindows??[]).some(y))return!1;const t=e.unsafeAuxiliaryWindows??[];return Number(e.unsafeAuxiliaryWindowCount??0)>0||t.some(ae)?![...e.captureCandidates??[],...e.restoreCandidates??[],...e.hiddenRestoreCandidates??[]].some(D)||e.wechatWindowAvailable===!1:!1}function z(e){const t=e.filter(y).filter(i=>i.visible!==!1&&i.minimized!==!0),n=t.filter(D);if(n.length>1)throw new Error(A);const r=n.slice().sort((i,o)=>M(o)-M(i))[0];if(ie(t,r))throw new Error(A)}function oe(e){const t=e.filter(y),n=t.filter(o=>o.visible!==!1&&o.minimized!==!0),r=n.filter(D);return(r.length?r:n.length?n:t).slice().sort((o,a)=>M(a)-M(o))[0]??null}function ht(e,t){return!(!y(e)||t&&wt(e,t)||e.visible===!1||e.minimized===!0||!ae(e)||t&&ft(e,t))}function ae(e){if(!y(e)||e.visible===!1||e.minimized===!0)return!1;const t=Number(e.bounds?.width),n=Number(e.bounds?.height);if(!Number.isFinite(t)||!Number.isFinite(n))return!1;const r=t>=360&&t<=720&&n>=420&&n<=840&&n>t,i=t>=420&&t<=680&&n>=260&&n<=460;return r||i}function ft(e,t){const n=Number(e.rootProcessId),r=Number(t.rootProcessId);if(!Number.isFinite(n)||!Number.isFinite(r)||n!==r)return!1;const i=Number(e.zOrder),o=Number(t.zOrder);return!Number.isFinite(i)||!Number.isFinite(o)?!1:i>o}function D(e){const t=Number(e.bounds?.width),n=Number(e.bounds?.height);return Number.isFinite(t)&&Number.isFinite(n)&&t>=760&&n>=600}function M(e){const t=Number(e.bounds?.width),n=Number(e.bounds?.height);return Number.isFinite(t)&&Number.isFinite(n)?t*n:0}function wt(e,t){return!!(e.windowId&&t.windowId&&e.windowId===t.windowId)}async function Qt(e,t){let n=await e.request("windows.list",{},t);w(n,"windows.list");let r=n.result?.windows??[];z(r);let i=oe(r);if(!i)throw new Error("wechat_window_not_found");const o=await e.request("windows.focus",{windowId:i.windowId},t);return w(o,"windows.focus"),await ce(e,i,t)}async function bt(e,t,n){if(!t.windowId)throw new Error("wechat_window_not_found");const r=await e.request("windows.focus",{windowId:t.windowId},n);return w(r,"windows.focus"),t}async function se(e,t){const n=await e.request("windows.list",{},t);w(n,"windows.list");const r=n.result?.windows??[];z(r);const i=oe(r);if(!i)throw new Error("wechat_window_not_found");const o=await e.request("windows.focus",{windowId:i.windowId},t);return w(o,"windows.focus"),await ce(e,i,t)}async function ce(e,t,n){let r;try{r=await e.request("windows.list",{},n)}catch{return t}if(!r.ok)return t;const i=(r.result?.windows??[]).filter(y);return z(i),i.find(o=>o.windowId===t.windowId)??i[0]??t}async function K(e){const{capture:t,ocr:n}=e.observation??await T(e),r=R({capture:t,ocr:n,window:e.window});if(r)return{opened:!1,reason:r};const i=Ee({visibleItems:Mt(n,t,e.allowOcrTitleRows!==!1),targets:[{bindingId:e.binding.bindingId,conversationDisplayName:e.binding.conversationDisplayName}]})[0];if(!i)return{opened:!1,reason:"conversation_not_visible"};const o=$t(i.item.bbox,t,e.window);if(!o)return{opened:!1,reason:"conversation_bbox_missing"};const a=await e.helper.request("mouse.click",{x:o.x,y:o.y,coordinateSpace:"screen",windowId:e.window.windowId},e.traceId);return w(a,"mouse.click"),{opened:!0,reason:i.reason}}async function de(e){let t={opened:!1,reason:"conversation_not_opened"};for(let r=0;r<Re;r+=1){if(t=await mt(e),t.opened||t.reason!=="conversation_title_not_confirmed")return t;await S(e,"open-retry",`retry:${r}`)}const n=await le(e);return n.opened?{opened:!0,reason:`${n.reason}:final-visible-recovery`}:t}async function mt(e){const t=await T(e),n=R({capture:t.capture,ocr:t.ocr,window:e.window});if(n)return{opened:!1,reason:n};let r=!1;if(V(t.ocr,t.capture,e.binding.conversationDisplayName))return e.settleToBottom===!0&&await B({helper:e.helper,window:e.window,traceId:e.traceId,observation:t}),{opened:!0,reason:"current-conversation-title-confirmed"};if(!r){const f=await K({...e,observation:t});if(f.opened){if(await S(e,"open-click-settle","visible-initial"),(await k(e)).ok)return{opened:!0,reason:f.reason};const m=await le(e);if(m.opened)return m}}const i=Ct(e,t),o=await e.helper.request("wechat.searchConversation",{conversationName:e.binding.conversationDisplayName,waitMs:e.searchDelayMs??_(e,"search-input-settle","search-input"),...i?{searchPoint:i}:{}},e.traceId);w(o,"wechat.searchConversation");const a=await gt(e);if(a.opened){if(await S(e,"open-click-settle","search-result-click"),(await k(e)).ok)return e.settleToBottom===!0&&await B({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:a.reason};const l=await e.helper.request("wechat.searchConversation",{conversationName:e.binding.conversationDisplayName,waitMs:e.searchDelayMs??_(e,"search-input-settle","search-input-retry-after-click"),...i?{searchPoint:i}:{}},e.traceId);w(l,"wechat.searchConversation")}let s=a.observation;const c=x(e.binding.conversationDisplayName)&&a.reason==="search_result_not_visible";if(!x(e.binding.conversationDisplayName)){await It(e.helper,e.traceId),await S(e,"open-click-settle","search-return");const f=await k(e);if(f.ok)return e.settleToBottom===!0&&await B({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:f.reason};const l=await e.helper.request("wechat.searchConversation",{conversationName:e.binding.conversationDisplayName,waitMs:e.searchDelayMs??_(e,"search-input-settle","search-input-retry-after-return"),...i?{searchPoint:i}:{}},e.traceId);w(l,"wechat.searchConversation"),s=void 0}const d=await K({...e,...s?{observation:s}:{},allowOcrTitleRows:!c});if(!d.opened)return d;await S(e,"open-click-settle","visible-final");const h=await k(e);return h.ok?(e.settleToBottom===!0&&await B({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:d.reason}):{opened:!1,reason:h.reason}}async function le(e){const t=await K(e);if(!t.opened)return t;await S(e,"open-click-settle","visible-retry");const n=await k(e);return n.ok?(e.settleToBottom===!0&&await B({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:`${t.reason}:retry-visible-title-confirmed`}):{opened:!1,reason:n.reason}}function en(e){const t=e.bounds,n=Number(t?.x),r=Number(t?.y),i=Number(t?.width),o=Number(t?.height);if(![n,r,i,o].every(Number.isFinite)||i<=0||o<=0)return null;const a=Math.max(520,Math.min(i-160,i*.68)),s=Math.max(48,Math.min(88,o*.08));return{x:Math.round(n+a),y:Math.round(r+o-s),coordinateSpace:"screen"}}async function gt(e){let t;const n=e.searchDelayMs===0?0:_(e,"search-result-probe","search-result-probe"),r=x(e.binding.conversationDisplayName)?Oe:1;for(let i=0;i<r;i+=1){i>0&&n>0&&await we(n);const o=await xt(e);t=o;const{capture:a,ocr:s}=o,c=R({capture:a,ocr:s,window:e.window});if(c)return{opened:!1,reason:c,observation:o};const d=Tt(s,a,e.binding.conversationDisplayName);if(!d){if(_t(s)||Bt(s,e.binding.conversationDisplayName))break;continue}const h=W(d.bbox,a,yt(a,e.window));if(!h)return{opened:!1,reason:"search_result_bbox_missing",observation:o};const f=await e.helper.request("mouse.click",{x:h.x,y:h.y,coordinateSpace:"screen",windowId:e.window.windowId},e.traceId);return w(f,"mouse.click"),{opened:!0,reason:d.reason,observation:o}}return{opened:!1,reason:"search_result_not_visible",observation:t}}async function xt(e){if(e.window.bounds&&x(e.binding.conversationDisplayName)){const t=await e.helper.request("screen.capture",{bounds:e.window.bounds},e.traceId);if(t.ok&&t.result?.dataBase64&&t.result.width&&t.result.height){const n={...t.result,windowId:e.window.windowId,bounds:t.result.bounds??e.window.bounds},r=await fe(e.helper,n,e.traceId);return{capture:n,ocr:r}}t.errorCode&&t.errorCode!=="helper_unknown_command"&&t.errorCode!=="helper_command_unsupported"&&w(t,"screen.capture")}return T(e)}function yt(e,t){return e.bounds?{...t,bounds:e.bounds}:t}async function T(e){let{capture:t,ocr:n}=await $(e.helper,e.window.windowId,e.traceId,e.window.bounds);return await vt({helper:e.helper,window:e.window,capture:t,ocr:n,traceId:e.traceId})&&(await U("preview-dismiss-settle",`${e.traceId||""}:${e.window.windowId}:preview-dismiss`),{capture:t,ocr:n}=await $(e.helper,e.window.windowId,e.traceId,e.window.bounds)),{capture:t,ocr:n}}async function vt(e){const t=pt(e.ocr,e.capture,e.window);if(!t)return!1;for(const n of[async()=>e.helper.request("keyboard.shortcut",{key:"escape",modifiers:[]},e.traceId),async()=>e.helper.request("mouse.click",{x:t.x,y:t.y,coordinateSpace:"screen",windowId:e.window.windowId},e.traceId)])if((await n()).ok)return!0;return!1}function pt(e,t,n){const r=e.blocks??[],i=r.find(c=>{const d=String(c.text||""),h=c.bbox;if(!h)return!1;const f=Number(h.x),l=Number(h.y);return Number.isFinite(f)&&Number.isFinite(l)&&l>t.height*.06&&l<t.height*.22&&f>t.width*.3&&/查看原视频|查看原图|原视频|原图|视频|图片|图像/.test(d)});if(!i)return null;const o=i.bbox,a=Number(o?.y);if(!Number.isFinite(a))return null;const s=r.filter(c=>{const d=String(c.text||"").trim(),h=c.bbox;if(!h||d!=="\xD7")return!1;const f=Number(h.x),l=Number(h.y);return Number.isFinite(f)&&Number.isFinite(l)&&f>t.width*.55&&f<t.width*.9&&Math.abs(l-a)<t.height*.08}).sort((c,d)=>Number(d.bbox?.x??0)-Number(c.bbox?.x??0))[0];return W(s?.bbox,t,n)}function Ct(e,t){const{capture:n,ocr:r}=t,i=(r.blocks??[]).find(o=>{const a=String(o.text||"").trim().toLowerCase(),s=o.bbox;if(!s||!/^(搜索|search)$/.test(a))return!1;const c=Number(s.x),d=Number(s.y);return Number.isFinite(c)&&Number.isFinite(d)&&c<n.width*.22&&d<n.height*.12});return W(i?.bbox,n,e.window)}async function ue(e,t,n,r){const i=await e.request("windows.capture",{windowId:t,scope:"full-window",...r?{bounds:r}:{}},n);w(i,"windows.capture");const o=i.result;if(!o?.dataBase64||!o.mimeType||!o.width||!o.height)throw new Error("helper_invalid_response: windows.capture missing screenshot data");return o}async function he(e){return e.capture.dataBase64?e.capture:ue(e.helper,e.window.windowId,e.traceId,e.window.bounds)}async function $(e,t,n,r){const i=await e.request("windows.captureAndOcr",{windowId:t,scope:"full-window",...r?{bounds:r}:{}},n);if(i.ok&&i.result?.capture&&i.result?.ocr)return{capture:i.result.capture,ocr:i.result.ocr};!i.ok&&i.errorCode&&i.errorCode!=="helper_unknown_command"&&i.errorCode!=="helper_command_unsupported"&&w(i,"windows.captureAndOcr");const o=await ue(e,t,n,r),a=await fe(e,o,n);return{capture:o,ocr:a}}async function fe(e,t,n){const r=await e.request("ocr.recognize",{mimeType:t.mimeType,dataBase64:t.dataBase64,width:t.width,height:t.height},n);return w(r,"ocr.recognize"),r.result??{}}function w(e,t){if(!e.ok)throw new Error(`${e.errorCode||"helper_command_failed"}: ${e.errorSummary||t}`)}function y(e){const t=`${e.appName||""} ${e.title||""} ${e.className||""}`.toLowerCase();return t.includes("wechat")||t.includes("weixin")||t.includes("\u5FAE\u4FE1")||t.includes("qwindowicon")}function we(e){return new Promise(t=>setTimeout(t,e))}function _(e,t,n){return e.searchDelayMs===0?0:Ae(t,Nt(e,n))}async function S(e,t,n){const r=_(e,t,n);r>0&&await we(r)}function Nt(e,t){return`${e.traceId||""}:${e.binding.conversationDisplayName}:${t}`}async function It(e,t){const n=await e.request("keyboard.shortcut",{key:"return",modifiers:[]},t);w(n,"keyboard.shortcut")}async function B(e){if(!e.window.bounds)return;const t=e.observation,n=Vt(t?.ocr,t?.capture,e.window);if(n){const r=await e.helper.request("mouse.click",{x:n.x,y:n.y,coordinateSpace:"screen",windowId:e.window.windowId},e.traceId);w(r,"mouse.click"),await U("jump-latest-settle",`${e.traceId||""}:${e.window.windowId}:jump-latest`)}}async function k(e){const{capture:t,ocr:n}=await $(e.helper,e.window.windowId,e.traceId,e.window.bounds),r=R({capture:t,ocr:n,window:e.window});return r?{ok:!1,reason:r}:V(n,t,e.binding.conversationDisplayName)?{ok:!0,reason:"target-conversation-title-confirmed"}:{ok:!1,reason:"conversation_title_not_confirmed"}}function R(e){const t=(e.ocr.blocks??[]).map(r=>At(r.text)).filter(Boolean),n=t.join("");return/(扫码登录|仅传输文件|重新登录|为了安全|安全验证|登录微信|微信登录|该账号已登录|帐号已登录|账号已登录|当前账号已登录|当前帐号已登录|已登录|进入微信|進入微信)/u.test(n)||Ft(e,t)?P:null}function Mt(e,t,n=!0){const r=n?St(e.blocks??[],t):[];return r.length?r:e.visibleConversationFingerprints??[]}function St(e,t){const n=e.map(i=>be(i)).filter(i=>!!i).filter(i=>i.text.length>=2).filter(i=>i.x>t.width*.08&&i.x<t.width*.38).filter(i=>i.y>t.height*.06&&i.y<t.height*.96).filter(i=>!me(i.text)),r=new Set;return n.filter(i=>!kt(i,n,t)).sort((i,o)=>i.y-o.y||i.x-o.x).map(i=>{const o=i.text.trim();return{title:o,bbox:{x:Math.max(0,i.x-24),y:Math.max(0,i.y-Math.max(24,i.height)),width:Math.max(120,t.width*.3),height:Math.max(48,i.height+40),coordinateSpace:"screenshotPixel"},fingerprint:o.toLowerCase()}}).filter(i=>{const o=F(i.title);return!o||r.has(o)?!1:(r.add(o),!0)})}function be(e){const t=e.bbox,n=String(e.text||"").normalize("NFKC").trim(),r=Number(t?.x),i=Number(t?.y),o=Number(t?.width),a=Number(t?.height);return!n||![r,i,o,a].every(Number.isFinite)?null:{text:n,x:r,y:i,width:o,height:a}}function kt(e,t,n){const r=Math.max(36,Math.min(64,n.height*.058));return t.some(i=>i!==e&&i.y<e.y&&e.y-i.y<=r&&Math.abs(i.x-e.x)<=16&&i.height>=e.height*.85)}function me(e){const t=p(e);return/^(搜索|微信|通讯录|收藏|设置|文件传输助手|search)$/iu.test(t)||O(t)||Wt(t)||/^包含[::]?/u.test(t)||/搜索网络结果/u.test(t)}function Tt(e,t,n){const r=(e.blocks??[]).map(s=>be(s)).filter(s=>!!s),i=r.filter(s=>O(p(s.text))).sort((s,c)=>s.y-c.y||s.x-c.x),o=["\u7FA4\u804A","GroupChats","\u8054\u7CFB\u4EBA","Contacts"];for(const s of o){const c=i.findIndex(l=>p(l.text)===s);if(c<0)continue;const d=i[c],h=i[c+1]?.y??Number.POSITIVE_INFINITY,f=r.filter(l=>l.y>d.y&&l.y<h).filter(l=>l.x<t.width*.7).filter(l=>l.text.length>=2).filter(l=>!O(p(l.text))).filter(l=>!xe(l,r)).filter(l=>q(l.text,n)).sort((l,m)=>l.y-m.y||l.x-m.x)[0];if(f)return{bbox:ge(f,t),reason:"search-result-title"}}const a=r.filter(s=>s.y>t.height*.12&&s.y<t.height*.82).filter(s=>s.x>t.width*.08&&s.x<t.width*.45).filter(s=>s.text.length>=2).filter(s=>!me(s.text)).filter(s=>!xe(s,r)).filter(s=>q(s.text,n)).sort((s,c)=>s.y-c.y||s.x-c.x)[0];return x(n)?null:a?{bbox:ge(a,t),reason:"search-result-title"}:null}function ge(e,t){return{x:Math.max(0,e.x-24),y:Math.max(0,e.y-12),width:Math.max(e.width+t.width*.18,160),height:Math.max(e.height+28,44),coordinateSpace:"screenshotPixel"}}function O(e){return/^(群聊|联系人|聊天记录|公众号|小程序|GroupChats|Contacts|ChatHistory|OfficialAccounts|MiniPrograms)$/u.test(e)}function _t(e){return(e.blocks??[]).some(t=>O(p(t.text)))}function Bt(e,t){return(e.visibleConversationFingerprints??[]).some(n=>q(n.title,t))}function p(e){return String(e||"").normalize("NFKC").replace(/[^\p{L}\p{N}_-]/gu,"").trim()}function xe(e,t){return/^包含[::]?/u.test(e.text.trim())?!0:t.some(n=>n!==e&&Math.abs(n.y-e.y)<=Math.max(18,e.height)&&n.x<e.x&&/^包含[::]?/u.test(n.text.trim()))}function Wt(e){const t=String(e||"").trim();return!!(/^(星期[一二三四五六日天]|周[一二三四五六日天])\s*\d{1,2}:\d{2}$/u.test(t)||/^\d{1,2}:\d{2}(?::\d{2})?$/u.test(t)||/^\d{4}[-/.年]\d{1,2}(?:[-/.月]\d{1,2}日?)?$/u.test(t)||/^\d{1,2}[-/.月]\d{1,2}日?$/u.test(t))}function Ft(e,t){if(!y(e.window)||(e.ocr.visibleConversationFingerprints??[]).length>0)return!1;const n=Number(e.window.bounds?.width||e.capture.width),r=Number(e.window.bounds?.height||e.capture.height);if(![n,r].every(Number.isFinite)||!(n>=360&&n<=720&&r>=420&&r<=840&&r>n))return!1;const o=t.filter(a=>a&&!/^[x*×]+$/iu.test(a));return o.length<=4&&!o.some(a=>/搜索|search/u.test(a))}function At(e){return String(e||"").normalize("NFKC").replace(/\s+/g,"").trim()}function V(e,t,n){const r=x(n)?F(n):L(n);return r?(e.blocks??[]).some(i=>{const o=x(n)?F(i.text):L(i.text);if(!o||!Ce(o,r))return!1;const a=i.bbox;if(!a)return!1;const s=Number(a.x),c=Number(a.y);if(!Number.isFinite(s)||!Number.isFinite(c))return!1;const d=s>t.width*.32&&s<t.width*.78&&c<t.height*.16,h=s<t.width*.18&&c<t.height*.12;return d||h}):!1}function ye(e){if(V(e.ocr,e.capture,e.binding.conversationDisplayName))return!1;const t=(e.ocr.blocks??[]).map(s=>String(s.text||"").normalize("NFKC").replace(/\s+/g," ").trim()).filter(Boolean),n=t.join(`
|
|
4
|
-
`),
|
|
5
|
-
`);return/\.(txt|md|rtf|docx?|pdf)\b/i.test(o)&&
|
|
6
|
-
`);if(/\b\d+\s*x\s*\d+\b/i.test(
|
|
1
|
+
import{defaultWeChatChannelAttachmentDir as _e,resolveVisibleWeChatChannelMedia as Te}from"./media-resolver.js";import{updateWeChatChannelBindingLedger as We}from"./ledger.js";import{defaultWeChatChannelVectorStorePath as Be,loadWeChatChannelVectorStore as Ae,saveWeChatChannelVectorStore as Ee,upsertWeChatChannelVectorReferences as Fe}from"./vector-store.js";import{normalizeWeChatChannelAttachmentAvailability as H,normalizeWeChatChannelMessageKind as Re}from"./core/schema.js";import{waitForWeChatChannelPacing as Y,weChatChannelPacingDelayMs as Le}from"./pacing.js";import{matchWeChatConversationFingerprints as Oe}from"./fingerprint.js";import{normalizeWeChatAnchorText as v,weChatTextSimilarity as X}from"./anchor.js";import{decideWeChatChannelActivityGate as $e,normalizeWeChatChannelActivitySnapshot as ze}from"./human-coordination.js";const De=3,Pe=5,D="wechat_login_required",F="wechat_duplicate_instance";class T extends Error{reasonCode;stage;action;constructor(t){super(`user_active:${t.reasonCode}:${t.stage}:${t.action}`),this.name="WeChatChannelUserActivityAbort",this.reasonCode=t.reasonCode,this.stage=t.stage,this.action=t.action}}async function sn(e){return await Ve(e.helper,e.activityGuard,e.traceId).withLease("observe","observe-binding",async n=>Ke({...e,helper:n}))}async function Ke(e){const t=e.foregroundMode??"required";let n=await bt(e.helper,e.traceId,{foreground:t});if(t!=="background"&&(n=await Ct(e.helper,n,e.traceId)),t==="background"){const{capture:a,ocr:s}=await M({helper:e.helper,window:n,traceId:e.traceId}),c=B({capture:a,ocr:s,window:n});if(c)throw new Error(c);if(!$(s,a,e.binding.conversationDisplayName))throw new Error("conversation_not_visible_background");if(j({capture:a,ocr:s,binding:e.binding}))throw new Error("wechat_window_obstructed_background");return e.onObservationEvidence?.({window:n,capture:a,ocr:s}),J(e)?Q({...e,window:n,capture:a,ocr:s}):P({...e,window:n,capture:a,ocr:s})}const i=await fe({helper:e.helper,window:n,binding:e.binding,traceId:e.traceId});if(!i.opened)throw new Error(i.reason||"conversation_not_opened");let{capture:r,ocr:o}=await M({helper:e.helper,window:n,traceId:e.traceId});if(j({capture:r,ocr:o,binding:e.binding})){n=await kt(e.helper,e.traceId);const a=await fe({helper:e.helper,window:n,binding:e.binding,traceId:e.traceId});if(!a.opened)throw new Error(a.reason||"wechat_window_obstructed");if({capture:r,ocr:o}=await M({helper:e.helper,window:n,traceId:e.traceId}),j({capture:r,ocr:o,binding:e.binding}))throw new Error("wechat_window_obstructed")}return e.onObservationEvidence?.({window:n,capture:r,ocr:o}),J(e)?Q({...e,window:n,capture:r,ocr:o}):P({...e,window:n,capture:r,ocr:o})}function Ve(e,t,n){if(!t)return{helper:e,withLease:async(s,c,d)=>await d(e)};let i=null;const r=s=>{t.onEvent?.({...s,at:new Date().toISOString()})},o=async(s,c)=>{if(i){const u=await e.request("automation.lease.status",{},n),h=u.result??{},w=u.ok&&h.interrupted?Z(h.interruptReason):u.ok?void 0:u.errorCode||"user_activity_unknown";if(r({phase:"lease-status",stage:s,action:c,ok:u.ok===!0&&!h.interrupted,...w?{reasonCode:w}:{}}),!u.ok)throw new T({reasonCode:u.errorCode||"user_activity_unknown",stage:s,action:c});if(h.active===!1||h.leaseId!==i||!h.interrupted)return;throw new T({reasonCode:Z(h.interruptReason),stage:s,action:c})}const d=await e.request("activity.snapshot",{},n);if(!d.ok)throw r({phase:"snapshot",stage:s,action:c,ok:!1,reasonCode:d.errorCode||"user_activity_unknown"}),new T({reasonCode:d.errorCode||"user_activity_unknown",stage:s,action:c});const l=$e({snapshot:ze(d.result),stage:s,policy:t.policy});if(r({phase:"snapshot",stage:s,action:c,ok:l.ok,...l.ok?{}:{reasonCode:l.reasonCode,waitMs:l.waitMs}}),!l.ok)throw new T({reasonCode:l.reasonCode,stage:s,action:c})},a={request:async(s,c,d)=>{const l=qe(s);l&&await o(l.stage,`${s}:before`);const u=await e.request(s,c,d);return l&&u.ok&&await o(l.stage,`${s}:after`),u}};return{helper:a,async withLease(s,c,d){if(await o(s,`${c}:preflight`),t.useAutomationLease===!1)return await d(a);const l=await e.request("automation.lease.acquire",{owner:"wechat-channel",purpose:`observe:${c}`,ttlMs:12e4},n);if(i=typeof l.result?.leaseId=="string"?l.result.leaseId:null,r({phase:"lease-acquire",stage:s,action:c,ok:l.ok===!0,...l.ok?{}:{reasonCode:l.errorCode||"user_activity_unknown"}}),!l.ok)throw i=null,new T({reasonCode:l.errorCode||"user_activity_unknown",stage:s,action:c});try{const u=await d(a);return await o(s,`${c}:complete`),u}finally{const u=i;if(i=null,u){const h=await e.request("automation.lease.release",{leaseId:u},n).catch(w=>({ok:!1,errorCode:"automation_lease_release_failed",errorSummary:w instanceof Error?w.message:String(w)}));r({phase:"lease-release",stage:s,action:c,ok:h.ok===!0,...h.ok?{}:{reasonCode:h.errorCode||"automation_lease_release_failed"}})}}}}}function qe(e){return e==="windows.ensureReady"||e==="windows.focus"||e==="wechat.searchConversation"?{stage:"open_conversation"}:e==="keyboard.shortcut"||e==="mouse.click"||e==="mouse.rightClick"||e==="mouse.scroll"||e==="clipboard.snapshot"||e==="clipboard.readAttachment"||e==="menu.pickItem"?{stage:"dangerous_action"}:null}function Z(e){return e==="recent_mouse_activity"||e==="recent_mouse_click"||e==="recent_scroll_activity"||e==="recent_keyboard_activity"||e==="frontmost_app_changed"?e:"user_activity_unknown"}function J(e){return e.observePipeline==="edge-structure"?!0:e.observePipeline==="server-observe"?!1:!!e.api.structureWindow}async function P(e){const t=await me(e),n=await e.api.observe(e.runtime,e.binding,{screenshots:[{captureIndex:0,mimeType:t.mimeType,dataBase64:t.dataBase64,width:t.width,height:t.height}],edgeOcrBlocks:e.ocr.blocks??[],visibleConversationFingerprints:e.ocr.visibleConversationFingerprints??[],localLedgerTailAnchors:e.localLedgerTailAnchors??[]}),i=await re({helper:e.helper,runtime:e.runtime,binding:e.binding,messages:n.observedMessages??[],workDir:e.workDir,attachmentsDir:e.attachmentsDir,window:e.window,screenshot:t,windowId:e.window.windowId,traceId:e.traceId,mediaAnchorText:e.mediaAnchorText});return Ne(i,ke({ocr:e.ocr,screenshot:t,binding:e.binding,anchors:e.localOutboundEchoAnchors??[]}))}async function Q(e){if(!e.api.structureWindow)throw new Error("wechat_structure_window_unavailable");const t=await me(e);let n;try{n=await e.api.structureWindow(e.runtime,e.binding,{screenshots:[{captureIndex:0,mimeType:t.mimeType,dataBase64:t.dataBase64,width:t.width,height:t.height}],edgeOcrBlocks:e.ocr.blocks??[],visibleConversationFingerprints:e.ocr.visibleConversationFingerprints??[],localLedgerTailAnchors:e.localLedgerTailAnchors??[]})}catch(c){if(Ue(c))return P(e);throw c}const i=et(n.structuredMessages??n.observedMessages??[]),r=await je({...e,messages:i,screenshot:t}),o=await re({helper:e.helper,runtime:e.runtime,binding:e.binding,messages:r.messages,workDir:e.workDir,attachmentsDir:e.attachmentsDir,window:e.window,screenshot:t,windowId:e.window.windowId,traceId:e.traceId,mediaAnchorText:e.mediaAnchorText}),a=Ne(o,ke({ocr:e.ocr,screenshot:t,binding:e.binding,anchors:e.localOutboundEchoAnchors??[]}));return e.localLedger?We({ledger:e.localLedger,bindingId:e.binding.bindingId,observedMessages:a,baselineOnly:e.baselineOnly,vectorReferences:r.vectorReferences}).newMessages:a}function Ue(e){return e instanceof Error&&/\bedge_runtime_disabled\b/.test(e.message)}async function je(e){const t=Ge(e.messages);if(!t.length)return{messages:e.messages,vectorReferences:[]};const n=e.api.embedVisual?await Ye({...e,seedBlocks:t}):t.filter(a=>f(a.vectorBase64)||f(a.signature));if(!n.length)return{messages:e.messages,vectorReferences:[]};const i=e.vectorStorePath??Be(e.workDir||process.cwd(),e.runtime.runtimeId),r=Ae(i,e.runtime.runtimeId),o=Fe({store:r,bindingId:e.binding.bindingId,blocks:n});return o.length?(Ee(i,r),{messages:Je(e.messages,o),vectorReferences:o}):{messages:e.messages,vectorReferences:[]}}function Ge(e){const t=[];for(const n of e)for(const[i,r]of(n.visualBlocks??[]).entries()){const o=f(r.blockId)||`${n.stableMessageKey}:visual-${i}`,a=f(r.blockKind)||n.kind||"visual";t.push({...r,stableMessageKey:n.stableMessageKey,blockId:o,blockKind:a,...r.bbox!==void 0?{bbox:r.bbox}:n.bbox!==void 0?{bbox:n.bbox}:{}})}return t}async function He(e){if(!e.api.embedVisual)return[];const t=await e.api.embedVisual(e.runtime,e.binding,{screenshots:[{captureIndex:0,mimeType:e.screenshot.mimeType,dataBase64:e.screenshot.dataBase64,width:e.screenshot.width,height:e.screenshot.height}],visualBlocks:e.seedBlocks,traceId:e.traceId});return Ze(t.visualBlocks??t.embeddedBlocks??[],e.seedBlocks)}async function Ye(e){try{return await He(e)}catch(t){if(Xe(t))throw t;return[]}}function Xe(e){return!e||typeof e!="object"?!1:e.reasonCode==="insufficient_credits"?!0:e instanceof Error&&/\binsufficient_credits\b/.test(e.message)}function Ze(e,t){return e.map((n,i)=>{const r=t[i];return{...n,stableMessageKey:f(n.stableMessageKey)||f(r?.stableMessageKey),blockId:f(n.blockId)||f(r?.blockId),blockKind:f(n.blockKind)||f(r?.blockKind)||"visual",...n.bbox!==void 0?{bbox:n.bbox}:r?.bbox!==void 0?{bbox:r.bbox}:{}}}).filter(n=>f(n.stableMessageKey)&&f(n.blockId))}function Je(e,t){const n=new Map;for(const i of t){const r=n.get(i.stableMessageKey)??[];r.push(i),n.set(i.stableMessageKey,r)}return e.map(i=>{const r=n.get(i.stableMessageKey);return r?.length?{...i,visualBlocks:Qe(i,r)}:i})}function Qe(e,t){const i=(e.visualBlocks??[]).map((o,a)=>{const s=f(o.blockId)||`${e.stableMessageKey}:visual-${a}`,c=t.find(d=>d.blockId===s);return c?ee(c,o):te(o)}),r=new Set(i.map(o=>o.blockId));for(const o of t)r.has(o.blockId)||i.push(ee(o));return i}function ee(e,t){return{...t?te(t):{},stableMessageKey:e.stableMessageKey,blockId:e.blockId,blockKind:e.blockKind,vectorStoreKey:e.vectorStoreKey,model:e.model,...e.modelVersion!==void 0?{modelVersion:e.modelVersion}:{},dims:e.dims,...e.signature?{signature:e.signature}:{},...e.bbox!==void 0?{bbox:e.bbox}:{}}}function te(e){const{vectorBase64:t,...n}=e;return n}function et(e){return e.map((t,n)=>tt(t,n)).filter(t=>t!=null)}function tt(e,t){if(!m(e))return null;const n=Re(e.kind),i=it(e.senderRole),r=g(e.normalizedText),o=g(e.anchorText)??r??g(e.textExcerpt),a=f(e.stableMessageKey)||nt({message:e,index:t,senderRole:i,kind:n,anchorText:o});return{stableMessageKey:a,senderRole:i,...g(e.senderName)!==null?{senderName:g(e.senderName)}:{},kind:n,...r!==null?{normalizedText:r}:{},...o!==null?{anchorText:o}:{},...k(e,"anchorMetadata")?{anchorMetadata:e.anchorMetadata}:{},...k(e,"neighborContext")?{neighborContext:e.neighborContext}:{},...g(e.textExcerpt)!==null?{textExcerpt:g(e.textExcerpt)}:{},...k(e,"bbox")?{bbox:e.bbox}:{},...k(e,"mediaMetadata")?{mediaMetadata:rt(e.mediaMetadata)}:{},...typeof e.isBaseline=="boolean"?{isBaseline:e.isBaseline}:{},...g(e.deliveryStatus)!==null?{deliveryStatus:g(e.deliveryStatus)??void 0}:{},...g(e.observedAt)!==null?{observedAt:g(e.observedAt)??void 0}:{},...Array.isArray(e.visualBlocks)?{visualBlocks:e.visualBlocks}:{},...o?{}:{anchorText:a}}}function nt(e){const t=m(e.message.bbox)?JSON.stringify(e.message.bbox):"";return`structured-window:${[e.index,e.senderRole,e.kind,e.anchorText||f(e.message.textExcerpt)||"",t].join(":").slice(0,180)}`}function rt(e){if(!m(e))return e;const t={...e};return k(t,"availability")&&(t.availability=H(t.availability)),m(t.attachment)&&(t.attachment=ne(t.attachment)),Array.isArray(t.attachments)&&(t.attachments=t.attachments.map(n=>m(n)?ne(n):n)),t}function ne(e){const t={...e};return k(t,"availability")&&(t.availability=H(t.availability)),t}function it(e){return e==="self"||e==="contact"||e==="system"||e==="unknown"?e:"unknown"}async function re(e){if(e.binding.downloadMedia===!1)return e.messages;const t=e.messages.map((o,a)=>ut(o,e.screenshot,e.window,lt(e.messages,a))).filter((o,a)=>ot(e.messages,a,e.mediaAnchorText)).filter(o=>o!=null);if(!t.length)return e.messages;const n=e.attachmentsDir??_e(e.workDir||process.cwd(),e.runtime.runtimeId,e.binding.bindingId),i=await Te({helper:e.helper,candidates:t,attachmentsDir:n,screenshot:e.screenshot,windowId:e.windowId,window:e.window,traceId:e.traceId,platform:e.runtime.policy.platform,stabilityCheck:async()=>{const o=await _({helper:e.helper,window:e.window,binding:e.binding,traceId:e.traceId});return o.ok?{ok:!0}:{ok:!1,reasonCode:o.reason}}});if(!i.length)return e.messages;const r=new Map(i.map(o=>[o.messageKey,o]));return e.messages.map(o=>{const a=r.get(o.stableMessageKey);return a?{...o,mediaMetadata:ht(o.mediaMetadata,a.attachment,a.reasonCode,a.attemptReasonCodes,a.resolveTrace)}:o})}function ot(e,t,n){const i=oe(n);if(!i)return!0;const r=at(i),o=e.map((a,s)=>oe(dt(a)).includes(i)?s:-1).filter(a=>a>=0);if(!o.length)return t===st(e,r);for(const a of o)if(t>=a&&t<=a+8||t===a-1&&!e.slice(a+1,Math.min(e.length,a+9)).some(ie))return!0;return!1}function at(e){return e.includes("video")||e.includes("vide0")?"video":e.includes("image")||e.includes("photo")||e.includes("ph0t0")||e.includes("picture")?"image":e.includes("file")||e.includes("document")?"file":null}function st(e,t=null){for(let n=e.length-1;n>=0;n-=1)if(ie(e[n]))return ct(e[n],t)?n:-1;return-1}function ie(e){if(!e)return!1;const t=String(e.kind||"").toLowerCase(),n=m(e.mediaMetadata)?e.mediaMetadata:{},i=m(n.attachment)?n.attachment:{};return ae(t,n,i)}function ct(e,t){if(!e||!t)return!0;const n=String(e.kind||"").toLowerCase(),i=m(e.mediaMetadata)?e.mediaMetadata:{},r=m(i.attachment)?i.attachment:{},o=String(r.type||"").toLowerCase(),a=String(r.mimeType||"").toLowerCase(),s=String(r.name||r.localPath||e.anchorText||e.textExcerpt||"").toLowerCase();return t==="video"?n.includes("video")||o==="video"||a.startsWith("video/")||/\.(mp4|mov|m4v|avi|mkv|webm)$/i.test(s):t==="image"?/image|photo|picture/.test(n)||o==="image"||a.startsWith("image/")||/\.(png|jpe?g|gif|webp|heic|tiff?|bmp)$/i.test(s):/file|document/.test(n)||o==="file"||!/image|photo|picture|video/.test(n)&&o!=="image"&&o!=="video"&&!a.startsWith("image/")&&!a.startsWith("video/")}function dt(e){if(!e)return"";const t=e;return[e.normalizedText,e.anchorText,e.textExcerpt,t.text].map(n=>String(n||"")).join(`
|
|
2
|
+
`)}function oe(e){return String(e||"").normalize("NFKC").replace(/[o]/giu,"0").replace(/[^\p{L}\p{N}]/gu,"").toLowerCase()}function lt(e,t){return[e[t-1],e[t],e[t+1]].map(i=>i&&(i.normalizedText||i.anchorText||i.textExcerpt)||"").map(i=>String(i||"").trim()).filter(Boolean).join(`
|
|
3
|
+
`)||null}function ut(e,t,n,i){const r=String(e.kind||"").toLowerCase(),o=m(e.mediaMetadata)?e.mediaMetadata:{},a=m(o.attachment)?o.attachment:{},s=f(a.availability)||f(o.availability),c=f(a.localPath)||f(o.localPath);if(s==="edge-local"&&c||!ae(r,o,a))return null;const d=R(e.bbox)??R(o.bbox)??R(o.downloadActionBbox),l=R(o.downloadActionBbox),u=se(d,t,n),h=wt(d,t,n),w=se(l,t,n);return{messageKey:e.stableMessageKey,kind:f(a.type)||f(a.kind)||f(o.messageType)||r,fileName:f(a.name)||f(a.fileName)||f(o.fileName)||null,mimeType:f(a.mimeType)||f(o.mimeType)||null,size:N(a.size)??N(o.size),mediaStatus:ft(o,a),observedAt:e.observedAt??null,contextText:i??e.normalizedText??e.anchorText??e.textExcerpt??null,...u?{bbox:u}:{},...h?{screenshotBbox:h}:{},...w?{downloadActionBbox:w}:{}}}function ht(e,t,n,i,r){const o=m(e)?{...e}:{};return{...o,availability:m(t)?t.availability:o.availability,mediaStatus:m(t)&&t.availability==="edge-local"?"downloaded":o.mediaStatus,attachment:t,edgeResolveReasonCode:n,...i?.length?{edgeResolveAttempts:i}:{},...r?{edgeResolveTrace:r}:{}}}function ft(e,t){const n=f(e.mediaStatus);if(n==="not_downloaded")return"not_downloaded";if(n==="downloaded"||n==="available")return"available";const i=f(t.availability)||f(e.availability);return i==="pending-download"?"not_downloaded":i==="metadata-only"?"metadata_only":n||null}function ae(e,t,n){const i=`${e} ${f(t.messageType)} ${f(n.type)} ${f(n.kind)}`.toLowerCase();return/image|photo|video|file|document/.test(i)}function R(e){if(!m(e))return null;const t=N(e.x),n=N(e.y),i=N(e.width),r=N(e.height);return t==null||n==null||i==null||r==null?null:{x:t,y:n,width:i,height:r,...typeof e.coordinateSpace=="string"?{coordinateSpace:e.coordinateSpace}:{}}}function se(e,t,n){if(!e)return null;if(e.coordinateSpace==="screen")return e;const i=A(e,t,n);if(!i)return e;const r=n.bounds;if(!r?.width||!r?.height)return{...e,x:i.x-e.width/2,y:i.y-e.height/2,coordinateSpace:"screen"};const o=r.width/t.width,a=r.height/t.height,s=i.x,c=i.y;return{x:s-e.width*o/2,y:c-e.height*a/2,width:e.width*o,height:e.height*a,coordinateSpace:"screen"}}function wt(e,t,n){if(!e)return null;if(e.coordinateSpace!=="screen")return{...e,coordinateSpace:"screenshotPixel"};const i=n.bounds;if(!i?.width||!i?.height)return null;const r=t.width/i.width,o=t.height/i.height;return{x:(e.x-i.x)*r,y:(e.y-i.y)*o,width:e.width*r,height:e.height*o,coordinateSpace:"screenshotPixel"}}function m(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}function k(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function f(e){return typeof e=="string"?e.trim():""}function g(e){return f(e)||null}function N(e){const t=Number(e);return Number.isFinite(t)?t:null}async function cn(e,t){await ce(e,t)}async function ce(e,t,n={}){let i=await e.request("permissions.check",{},t);b(i,"permissions.check");const r=i.result??{};if(r.screenRecording===!1)throw new Error("permission_screen_recording_missing");if(r.automation===!1)throw new Error("permission_automation_missing");if(r.wechatRunning===!1)throw new Error("wechat_not_running");if(gt(r))throw new Error(D);if(mt(r))throw new Error(F);if(yt(r))throw new Error("windows_visible_desktop_unavailable");if(r.dpiMappingAvailable===!1||r.displayTopologySupported===!1)throw new Error("dpi_mapping_failed");if(r.wechatMainWindowResponsive===!1)throw new Error("wechat_window_unresponsive");if(de([...r.captureCandidates??[],...r.restoreCandidates??[],...r.hiddenRestoreCandidates??[],...r.duplicateMainWindows??[],...r.unsafeAuxiliaryWindows??[]]))throw new Error(F);if(r.wechatWindowAvailable===!1&&String(r.platform??"").toLowerCase()!=="win32"&&n.allowUnavailableWindow!==!0)throw new Error("wechat_window_unavailable");return r}async function bt(e,t,n={}){const r=(n.foreground??"required")!=="background",o=await ce(e,t,{allowUnavailableWindow:r});if(String(o.platform??"").toLowerCase()==="win32"){const c=await e.request("windows.ensureReady",{activate:r,allowRecovery:!1,allowLaunch:!1},t);b(c,"windows.ensureReady");const d=c.result;if(!d?.windowId)throw new Error("helper_invalid_response: windows.ensureReady missing WeChat window data");return d}const a=await e.request("windows.ensureReady",{restore:r,focus:r},t);b(a,"windows.ensureReady");const s=a.result;if(!s?.windowId)throw new Error("helper_invalid_response: windows.ensureReady missing WeChat window data");return s}function de(e,t){const n=e.filter(x).filter(r=>r.visible!==!1&&r.minimized!==!0),i=t??n.slice().sort((r,o)=>I(o)-I(r))[0];return n.find(r=>xt(r,i))??null}function mt(e){return e.duplicateInstanceBlocked===!0||Number(e.duplicateMainWindowCount??0)>0||Number(e.unsafeAuxiliaryWindowCount??0)>0||(e.duplicateMainWindows??[]).some(x)||(e.unsafeAuxiliaryWindows??[]).some(x)}function gt(e){if(Number(e.duplicateMainWindowCount??0)>0||(e.duplicateMainWindows??[]).some(x))return!1;const t=e.unsafeAuxiliaryWindows??[];return Number(e.unsafeAuxiliaryWindowCount??0)>0||t.some(ue)?![...e.captureCandidates??[],...e.restoreCandidates??[],...e.hiddenRestoreCandidates??[]].some(V)||e.wechatWindowAvailable===!1:!1}function yt(e){return e.windowsVisibleDesktopAvailable===!1||e.rdpVisibleDesktopAvailable===!1||e.desktopSessionVisible===!1||e.screenLocked===!0||e.rdpDisconnected===!0}function K(e){const t=e.filter(x).filter(r=>r.visible!==!1&&r.minimized!==!0),n=t.filter(V);if(n.length>1)throw new Error(F);const i=n.slice().sort((r,o)=>I(o)-I(r))[0];if(de(t,i))throw new Error(F)}function le(e){const t=e.filter(x),n=t.filter(o=>o.visible!==!1&&o.minimized!==!0),i=n.filter(V);return(i.length?i:n.length?n:t).slice().sort((o,a)=>I(a)-I(o))[0]??null}function xt(e,t){return!(!x(e)||t&&pt(e,t)||e.visible===!1||e.minimized===!0||!ue(e)||t&&vt(e,t))}function ue(e){if(!x(e)||e.visible===!1||e.minimized===!0)return!1;const t=Number(e.bounds?.width),n=Number(e.bounds?.height);if(!Number.isFinite(t)||!Number.isFinite(n))return!1;const i=t>=360&&t<=720&&n>=420&&n<=840&&n>t,r=t>=420&&t<=680&&n>=260&&n<=460;return i||r}function vt(e,t){const n=Number(e.rootProcessId),i=Number(t.rootProcessId);if(!Number.isFinite(n)||!Number.isFinite(i)||n!==i)return!1;const r=Number(e.zOrder),o=Number(t.zOrder);return!Number.isFinite(r)||!Number.isFinite(o)?!1:r>o}function V(e){const t=Number(e.bounds?.width),n=Number(e.bounds?.height);return Number.isFinite(t)&&Number.isFinite(n)&&t>=760&&n>=600}function I(e){const t=Number(e.bounds?.width),n=Number(e.bounds?.height);return Number.isFinite(t)&&Number.isFinite(n)?t*n:0}function pt(e,t){return!!(e.windowId&&t.windowId&&e.windowId===t.windowId)}async function dn(e,t){let n=await e.request("windows.list",{},t);b(n,"windows.list");let i=n.result?.windows??[];K(i);let r=le(i);if(!r)throw new Error("wechat_window_not_found");const o=await e.request("windows.focus",{windowId:r.windowId},t);return b(o,"windows.focus"),await he(e,r,t)}async function Ct(e,t,n){if(!t.windowId)throw new Error("wechat_window_not_found");const i=await e.request("windows.focus",{windowId:t.windowId},n);return b(i,"windows.focus"),t}async function kt(e,t){const n=await e.request("windows.list",{},t);b(n,"windows.list");const i=n.result?.windows??[];K(i);const r=le(i);if(!r)throw new Error("wechat_window_not_found");const o=await e.request("windows.focus",{windowId:r.windowId},t);return b(o,"windows.focus"),await he(e,r,t)}async function he(e,t,n){let i;try{i=await e.request("windows.list",{},n)}catch{return t}if(!i.ok)return t;const r=(i.result?.windows??[]).filter(x);return K(r),r.find(o=>o.windowId===t.windowId)??r[0]??t}async function q(e){const{capture:t,ocr:n}=e.observation??await M(e),i=B({capture:t,ocr:n,window:e.window});if(i)return{opened:!1,reason:i};const r=Oe({visibleItems:Et(n,t,e.allowOcrTitleRows!==!1),targets:[{bindingId:e.binding.bindingId,conversationDisplayName:e.binding.conversationDisplayName}]})[0];if(!r)return{opened:!1,reason:"conversation_not_visible"};const o=Xt(r.item.bbox,t,e.window);if(!o)return{opened:!1,reason:"conversation_bbox_missing"};const a=await e.helper.request("mouse.click",{x:o.x,y:o.y,coordinateSpace:"screen",windowId:e.window.windowId},e.traceId);return b(a,"mouse.click"),{opened:!0,reason:r.reason}}async function fe(e){let t={opened:!1,reason:"conversation_not_opened"};for(let i=0;i<De;i+=1){if(t=await Nt(e),t.opened||t.reason!=="conversation_title_not_confirmed")return t;await S(e,"open-retry",`retry:${i}`)}const n=await we(e);return n.opened?{opened:!0,reason:`${n.reason}:final-visible-recovery`}:t}async function Nt(e){const t=await M(e),n=B({capture:t.capture,ocr:t.ocr,window:e.window});if(n)return{opened:!1,reason:n};let i=!1;if($(t.ocr,t.capture,e.binding.conversationDisplayName))return e.settleToBottom===!0&&await W({helper:e.helper,window:e.window,traceId:e.traceId,observation:t}),{opened:!0,reason:"current-conversation-title-confirmed"};if(!i){const u=await q({...e,observation:t});if(u.opened){if(await S(e,"open-click-settle","visible-initial"),(await _(e)).ok)return{opened:!0,reason:u.reason};const w=await we(e);if(w.opened)return w}}const r=Wt(e,t),o=await e.helper.request("wechat.searchConversation",{conversationName:e.binding.conversationDisplayName,waitMs:e.searchDelayMs??L(e,"search-input-settle","search-input"),...r?{searchPoint:r}:{}},e.traceId);b(o,"wechat.searchConversation");const a=await It(e);if(a.opened){await S(e,"open-click-settle","search-result-click");const u=await _(e);return u.ok?(e.settleToBottom===!0&&await W({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:a.reason}):{opened:!1,reason:u.reason}}let s=a.observation;const c=y(e.binding.conversationDisplayName)&&a.reason==="search_result_not_visible";if(!y(e.binding.conversationDisplayName)){await At(e.helper,e.traceId),await S(e,"open-click-settle","search-return");const u=await _(e);if(u.ok)return e.settleToBottom===!0&&await W({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:u.reason};const h=await e.helper.request("wechat.searchConversation",{conversationName:e.binding.conversationDisplayName,waitMs:e.searchDelayMs??L(e,"search-input-settle","search-input-retry-after-return"),...r?{searchPoint:r}:{}},e.traceId);b(h,"wechat.searchConversation"),s=void 0}const d=await q({...e,...s?{observation:s}:{},allowOcrTitleRows:!c});if(!d.opened)return d;await S(e,"open-click-settle","visible-final");const l=await _(e);return l.ok?(e.settleToBottom===!0&&await W({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:d.reason}):{opened:!1,reason:l.reason}}async function we(e){const t=await q(e);if(!t.opened)return t;await S(e,"open-click-settle","visible-retry");const n=await _(e);return n.ok?(e.settleToBottom===!0&&await W({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:`${t.reason}:retry-visible-title-confirmed`}):{opened:!1,reason:n.reason}}function ln(e){const t=e.bounds,n=Number(t?.x),i=Number(t?.y),r=Number(t?.width),o=Number(t?.height);if(![n,i,r,o].every(Number.isFinite)||r<=0||o<=0)return null;const a=Math.max(520,Math.min(r-160,r*.68)),s=Math.max(48,Math.min(88,o*.08));return{x:Math.round(n+a),y:Math.round(i+o-s),coordinateSpace:"screen"}}async function It(e){let t;const n=e.searchDelayMs===0?0:L(e,"search-result-probe","search-result-probe"),i=y(e.binding.conversationDisplayName)?Pe:1;for(let r=0;r<i;r+=1){r>0&&n>0&&await ye(n);const o=await Mt(e);t=o;const{capture:a,ocr:s}=o,c=B({capture:a,ocr:s,window:e.window});if(c)return{opened:!1,reason:c,observation:o};const d=Lt(s,a,e.binding.conversationDisplayName);if(!d){if(Ot(s)||$t(s,e.binding.conversationDisplayName))break;continue}const l=A(d.bbox,a,St(a,e.window));if(!l)return{opened:!1,reason:"search_result_bbox_missing",observation:o};const u=await e.helper.request("mouse.click",{x:l.x,y:l.y,coordinateSpace:"screen",windowId:e.window.windowId},e.traceId);return b(u,"mouse.click"),{opened:!0,reason:d.reason,observation:o}}return{opened:!1,reason:"search_result_not_visible",observation:t}}async function Mt(e){if(e.window.bounds&&y(e.binding.conversationDisplayName)){const t=await e.helper.request("screen.capture",{bounds:e.window.bounds},e.traceId);if(t.ok&&t.result?.dataBase64&&t.result.width&&t.result.height){const n={...t.result,windowId:e.window.windowId,bounds:t.result.bounds??e.window.bounds},i=await ge(e.helper,n,e.traceId);return{capture:n,ocr:i}}t.errorCode&&t.errorCode!=="helper_unknown_command"&&t.errorCode!=="helper_command_unsupported"&&b(t,"screen.capture")}return M(e)}function St(e,t){return e.bounds?{...t,bounds:e.bounds}:t}async function M(e){let{capture:t,ocr:n}=await U(e.helper,e.window.windowId,e.traceId,e.window.bounds);return await _t({helper:e.helper,window:e.window,capture:t,ocr:n,traceId:e.traceId})&&(await Y("preview-dismiss-settle",`${e.traceId||""}:${e.window.windowId}:preview-dismiss`),{capture:t,ocr:n}=await U(e.helper,e.window.windowId,e.traceId,e.window.bounds)),{capture:t,ocr:n}}async function _t(e){const t=Tt(e.ocr,e.capture,e.window);if(!t)return!1;for(const n of[async()=>e.helper.request("keyboard.shortcut",{key:"escape",modifiers:[]},e.traceId),async()=>e.helper.request("mouse.click",{x:t.x,y:t.y,coordinateSpace:"screen",windowId:e.window.windowId},e.traceId)])if((await n()).ok)return!0;return!1}function Tt(e,t,n){const i=e.blocks??[],r=i.find(c=>{const d=String(c.text||""),l=c.bbox;if(!l)return!1;const u=Number(l.x),h=Number(l.y);return Number.isFinite(u)&&Number.isFinite(h)&&h>t.height*.06&&h<t.height*.22&&u>t.width*.3&&/查看原视频|查看原图|原视频|原图|视频|图片|图像/.test(d)});if(!r)return null;const o=r.bbox,a=Number(o?.y);if(!Number.isFinite(a))return null;const s=i.filter(c=>{const d=String(c.text||"").trim(),l=c.bbox;if(!l||d!=="\xD7")return!1;const u=Number(l.x),h=Number(l.y);return Number.isFinite(u)&&Number.isFinite(h)&&u>t.width*.55&&u<t.width*.9&&Math.abs(h-a)<t.height*.08}).sort((c,d)=>Number(d.bbox?.x??0)-Number(c.bbox?.x??0))[0];return A(s?.bbox,t,n)}function Wt(e,t){const{capture:n,ocr:i}=t,r=(i.blocks??[]).find(o=>{const a=String(o.text||"").trim().toLowerCase(),s=o.bbox;if(!s||!/^(搜索|search)$/.test(a))return!1;const c=Number(s.x),d=Number(s.y);return Number.isFinite(c)&&Number.isFinite(d)&&c<n.width*.22&&d<n.height*.12});return A(r?.bbox,n,e.window)}async function be(e,t,n,i){const r=await e.request("windows.capture",{windowId:t,scope:"full-window",...i?{bounds:i}:{}},n);b(r,"windows.capture");const o=r.result;if(!o?.dataBase64||!o.mimeType||!o.width||!o.height)throw new Error("helper_invalid_response: windows.capture missing screenshot data");return o}async function me(e){return e.capture.dataBase64?e.capture:be(e.helper,e.window.windowId,e.traceId,e.window.bounds)}async function U(e,t,n,i){const r=await e.request("windows.captureAndOcr",{windowId:t,scope:"full-window",...i?{bounds:i}:{}},n);if(r.ok&&r.result?.capture&&r.result?.ocr)return{capture:r.result.capture,ocr:r.result.ocr};!r.ok&&r.errorCode&&r.errorCode!=="helper_unknown_command"&&r.errorCode!=="helper_command_unsupported"&&b(r,"windows.captureAndOcr");const o=await be(e,t,n,i),a=await ge(e,o,n);return{capture:o,ocr:a}}async function ge(e,t,n){const i=await e.request("ocr.recognize",{mimeType:t.mimeType,dataBase64:t.dataBase64,width:t.width,height:t.height},n);return b(i,"ocr.recognize"),i.result??{}}function b(e,t){if(!e.ok)throw new Error(`${e.errorCode||"helper_command_failed"}: ${e.errorSummary||t}`)}function x(e){const t=`${e.appName||""} ${e.title||""} ${e.className||""}`.toLowerCase();return t.includes("wechat")||t.includes("weixin")||t.includes("\u5FAE\u4FE1")||t.includes("qwindowicon")}function ye(e){return new Promise(t=>setTimeout(t,e))}function L(e,t,n){return e.searchDelayMs===0?0:Le(t,Bt(e,n))}async function S(e,t,n){const i=L(e,t,n);i>0&&await ye(i)}function Bt(e,t){return`${e.traceId||""}:${e.binding.conversationDisplayName}:${t}`}async function At(e,t){const n=await e.request("keyboard.shortcut",{key:"return",modifiers:[]},t);b(n,"keyboard.shortcut")}async function W(e){if(!e.window.bounds)return;const t=e.observation,n=Zt(t?.ocr,t?.capture,e.window);if(n){const i=await e.helper.request("mouse.click",{x:n.x,y:n.y,coordinateSpace:"screen",windowId:e.window.windowId},e.traceId);b(i,"mouse.click"),await Y("jump-latest-settle",`${e.traceId||""}:${e.window.windowId}:jump-latest`)}}async function _(e){const{capture:t,ocr:n}=await U(e.helper,e.window.windowId,e.traceId,e.window.bounds),i=B({capture:t,ocr:n,window:e.window});return i?{ok:!1,reason:i}:$(n,t,e.binding.conversationDisplayName)?{ok:!0,reason:"target-conversation-title-confirmed"}:{ok:!1,reason:"conversation_title_not_confirmed"}}function B(e){const t=(e.ocr.blocks??[]).map(i=>Pt(i.text)).filter(Boolean),n=t.join("");return/(扫码登录|仅传输文件|重新登录|为了安全|安全验证|登录微信|微信登录|该账号已登录|帐号已登录|账号已登录|当前账号已登录|当前帐号已登录|已登录|进入微信|進入微信)/u.test(n)||Dt(e,t)?D:null}function Et(e,t,n=!0){const i=n?Ft(e.blocks??[],t):[];return i.length?i:e.visibleConversationFingerprints??[]}function Ft(e,t){const n=e.map(r=>xe(r)).filter(r=>!!r).filter(r=>r.text.length>=2).filter(r=>r.x>t.width*.08&&r.x<t.width*.38).filter(r=>r.y>t.height*.06&&r.y<t.height*.96).filter(r=>!ve(r.text)),i=new Set;return n.filter(r=>!Rt(r,n,t)).sort((r,o)=>r.y-o.y||r.x-o.x).map(r=>{const o=r.text.trim();return{title:o,bbox:{x:Math.max(0,r.x-24),y:Math.max(0,r.y-Math.max(24,r.height)),width:Math.max(120,t.width*.3),height:Math.max(48,r.height+40),coordinateSpace:"screenshotPixel"},fingerprint:o.toLowerCase()}}).filter(r=>{const o=E(r.title);return!o||i.has(o)?!1:(i.add(o),!0)})}function xe(e){const t=e.bbox,n=String(e.text||"").normalize("NFKC").trim(),i=Number(t?.x),r=Number(t?.y),o=Number(t?.width),a=Number(t?.height);return!n||![i,r,o,a].every(Number.isFinite)?null:{text:n,x:i,y:r,width:o,height:a}}function Rt(e,t,n){const i=Math.max(36,Math.min(64,n.height*.058));return t.some(r=>r!==e&&r.y<e.y&&e.y-r.y<=i&&Math.abs(r.x-e.x)<=16&&r.height>=e.height*.85)}function ve(e){const t=p(e);return/^(搜索|微信|通讯录|收藏|设置|文件传输助手|search)$/iu.test(t)||O(t)||zt(t)||/^包含[::]?/u.test(t)||/搜索网络结果/u.test(t)}function Lt(e,t,n){const i=(e.blocks??[]).map(s=>xe(s)).filter(s=>!!s),r=i.filter(s=>O(p(s.text))).sort((s,c)=>s.y-c.y||s.x-c.x),o=["\u7FA4\u804A","GroupChats","\u8054\u7CFB\u4EBA","Contacts"];for(const s of o){const c=r.findIndex(h=>p(h.text)===s);if(c<0)continue;const d=r[c],l=r[c+1]?.y??Number.POSITIVE_INFINITY,u=i.filter(h=>h.y>d.y&&h.y<l).filter(h=>h.x<t.width*.7).filter(h=>h.text.length>=2).filter(h=>!O(p(h.text))).filter(h=>!Ce(h,i)).filter(h=>G(h.text,n)).sort((h,w)=>h.y-w.y||h.x-w.x)[0];if(u)return{bbox:pe(u,t),reason:"search-result-title"}}const a=i.filter(s=>s.y>t.height*.12&&s.y<t.height*.82).filter(s=>s.x>t.width*.08&&s.x<t.width*.45).filter(s=>s.text.length>=2).filter(s=>!ve(s.text)).filter(s=>!Ce(s,i)).filter(s=>G(s.text,n)).sort((s,c)=>s.y-c.y||s.x-c.x)[0];return y(n)?null:a?{bbox:pe(a,t),reason:"search-result-title"}:null}function pe(e,t){return{x:Math.max(0,e.x-24),y:Math.max(0,e.y-12),width:Math.max(e.width+t.width*.18,160),height:Math.max(e.height+28,44),coordinateSpace:"screenshotPixel"}}function O(e){return/^(群聊|联系人|聊天记录|公众号|小程序|GroupChats|Contacts|ChatHistory|OfficialAccounts|MiniPrograms)$/u.test(e)}function Ot(e){return(e.blocks??[]).some(t=>O(p(t.text)))}function $t(e,t){return(e.visibleConversationFingerprints??[]).some(n=>G(n.title,t))}function p(e){return String(e||"").normalize("NFKC").replace(/[^\p{L}\p{N}_-]/gu,"").trim()}function Ce(e,t){return/^包含[::]?/u.test(e.text.trim())?!0:t.some(n=>n!==e&&Math.abs(n.y-e.y)<=Math.max(18,e.height)&&n.x<e.x&&/^包含[::]?/u.test(n.text.trim()))}function zt(e){const t=String(e||"").trim();return!!(/^(星期[一二三四五六日天]|周[一二三四五六日天])\s*\d{1,2}:\d{2}$/u.test(t)||/^\d{1,2}:\d{2}(?::\d{2})?$/u.test(t)||/^\d{4}[-/.年]\d{1,2}(?:[-/.月]\d{1,2}日?)?$/u.test(t)||/^\d{1,2}[-/.月]\d{1,2}日?$/u.test(t))}function Dt(e,t){if(!x(e.window)||(e.ocr.visibleConversationFingerprints??[]).length>0)return!1;const n=Number(e.window.bounds?.width||e.capture.width),i=Number(e.window.bounds?.height||e.capture.height);if(![n,i].every(Number.isFinite)||!(n>=360&&n<=720&&i>=420&&i<=840&&i>n))return!1;const o=t.filter(a=>a&&!/^[x*×]+$/iu.test(a));return o.length<=4&&!o.some(a=>/搜索|search/u.test(a))}function Pt(e){return String(e||"").normalize("NFKC").replace(/\s+/g,"").trim()}function $(e,t,n){const i=y(n)?E(n):z(n);return i?(e.blocks??[]).some(r=>{const o=y(n)?E(r.text):z(r.text);if(!o||!Ie(o,i))return!1;const a=r.bbox;if(!a)return!1;const s=Number(a.x),c=Number(a.y);if(!Number.isFinite(s)||!Number.isFinite(c))return!1;const d=s>t.width*.32&&s<t.width*.78&&c<t.height*.16,l=s<t.width*.18&&c<t.height*.12;return d||l}):!1}function j(e){if($(e.ocr,e.capture,e.binding.conversationDisplayName))return!1;const t=(e.ocr.blocks??[]).map(s=>String(s.text||"").normalize("NFKC").replace(/\s+/g," ").trim()).filter(Boolean),n=t.join(`
|
|
4
|
+
`),r=[{pattern:/UTF-?8/iu,source:n},{pattern:/Unix\s*\(LF\)/iu,source:n},{pattern:/纯文本/u,source:n},{pattern:/\d+\s*个字符/u,source:n},{pattern:/\bH1\b/u,source:n},{pattern:/^查看$/u,source:t}].reduce((s,c)=>{const d=Array.isArray(c.source)?c.source.some(l=>c.pattern.test(l)):c.pattern.test(c.source);return s+(d?1:0)},0);if(r>=2)return!0;const o=(e.ocr.blocks??[]).filter(s=>Number(s.bbox?.y)<e.capture.height*.14).map(s=>String(s.text||"")).join(`
|
|
5
|
+
`);return/\.(txt|md|rtf|docx?|pdf)\b/i.test(o)&&r>=1||/\.(png|jpe?g|gif|webp|bmp|heic|mp4|mov|m4v|avi|mkv|webm)\b/i.test(o)&&Kt(e.capture,e.ocr)?!0:((e.ocr.visibleConversationFingerprints??[]).some(s=>{const c=y(e.binding.conversationDisplayName)?E(s.title):z(s.title),d=y(e.binding.conversationDisplayName)?E(e.binding.conversationDisplayName):z(e.binding.conversationDisplayName);return!!(c&&d&&Ie(c,d))}),!1)}function Kt(e,t){const n=(t.blocks??[]).map(o=>String(o.text||"").normalize("NFKC").trim()).filter(Boolean),i=n.join(`
|
|
6
|
+
`);if(/\b\d+\s*x\s*\d+\b/i.test(i)&&/\b\d+(\.\d+)?\s*(B|KB|MB|GB)\b/i.test(i)||/\b100%\b/.test(i)&&n.length<=20)return!0;const r=n.filter(o=>!/^(□|×|\+|…|\.\.\.|100%|\d+|发送)$/u.test(o));return e.width>600&&e.height>500&&r.length<=6}function ke(e){const t=e.anchors.map(o=>({...o,normalized:v(o.expectedEchoAnchor)})).filter(o=>o.normalized);if(!t.length)return[];const n=[],i=new Set,r=Vt(e.ocr,e.screenshot).sort((o,a)=>v(a.text).length-v(o.text).length);for(const o of r){const a=v(o.text);if(!a)continue;const s=t.find(d=>qt(d.normalized,a));if(!s)continue;const c=`local-outbound-echo:${e.binding.bindingId}:${s.idempotencyKey||s.replyId||s.normalized}`;i.has(c)||(i.add(c),n.push({stableMessageKey:c,senderRole:"self",kind:"text",normalizedText:a,anchorText:a,textExcerpt:o.text.trim(),bbox:o.bbox??null,observedAt:new Date().toISOString(),deliveryStatus:"suppressed"}))}return n}function Vt(e,t){const n=(e.blocks??[]).filter(r=>Yt(r,t)&&v(r.text)).sort((r,o)=>C(r.bbox,"y")-C(o.bbox,"y")),i=n.map(r=>({text:String(r.text||"").trim(),bbox:r.bbox}));for(let r=0;r<n.length;r+=1){const o=[n[r]];for(let a=r+1;a<n.length&&o.length<5;a+=1){const s=n[a];if(!Ut(o[o.length-1],s,t))break;o.push(s),i.push({text:jt(o.map(c=>String(c.text||"").trim())),bbox:Ht(o.map(c=>c.bbox))})}}return i}function qt(e,t){if(!e||!t)return!1;if(X(e,t)>=.88)return!0;const n=e.replace(/\s+/g,""),i=t.replace(/\s+/g,"");return i.length>=8&&n.includes(i)}function Ut(e,t,n){const i=C(e.bbox,"x"),r=C(e.bbox,"y"),o=C(e.bbox,"height"),a=C(t.bbox,"x"),s=C(t.bbox,"y");if(![i,r,o,a,s].every(Number.isFinite)||i<n.width*.5||a<n.width*.5)return!1;const c=s-(r+o);return c>=-8&&c<=Math.max(64,n.height*.04)&&Math.abs(a-i)<=120}function jt(e){return e.reduce((t,n)=>t?n?`${t}${Gt(t,n)?" ":""}${n}`:t:n,"")}function Gt(e,t){return/[A-Za-z0-9]$/u.test(e)&&/^[A-Za-z0-9]/u.test(t)}function Ht(e){const t=e.filter(c=>!!c);if(!t.length)return;const n=t.map(c=>Number(c.x)).filter(Number.isFinite),i=t.map(c=>Number(c.y)).filter(Number.isFinite),r=t.map(c=>Number(c.x)+Number(c.width)).filter(Number.isFinite),o=t.map(c=>Number(c.y)+Number(c.height)).filter(Number.isFinite);if(!n.length||!i.length||!r.length||!o.length)return t[0];const a=Math.min(...n),s=Math.min(...i);return{x:a,y:s,width:Math.max(...r)-a,height:Math.max(...o)-s,coordinateSpace:t.find(c=>c.coordinateSpace)?.coordinateSpace}}function C(e,t){const n=Number(e?.[t]);return Number.isFinite(n)?n:Number.NaN}function Ne(e,t){if(!t.length)return e;const n=[...e];for(const i of t){const r=v(i.anchorText||i.normalizedText||i.textExcerpt);n.some(a=>{if(a.senderRole!=="self")return!1;const s=v(a.anchorText||a.normalizedText||a.textExcerpt);return s&&r&&X(s,r)>=.9})||n.push(i)}return n}function Yt(e,t){const n=e.bbox;if(!n)return!1;const i=Number(n.x),r=Number(n.y);return!Number.isFinite(i)||!Number.isFinite(r)?!1:i>t.width*.34&&i<t.width*.96&&r>t.height*.12&&r<t.height*.86}function Xt(e,t,n){if(!e||typeof e!="object")return null;const i=e,r=Number(i.x),o=Number(i.y),a=Number(i.width),s=Number(i.height);if(![r,o,a,s].every(Number.isFinite))return null;const c=typeof i.coordinateSpace=="string"?i.coordinateSpace:void 0,d=t.width&&n.bounds?.width?t.width/n.bounds.width:1,l=t.height&&n.bounds?.height?t.height/n.bounds.height:d,u={x:r+Math.min(a*.35,110*d),y:o+Math.min(s-8*l,Math.max(18*l,s/2))};if(c==="screen"||!n.bounds)return u;const h=Math.max(0,(t.width-n.bounds.width*d)/2),w=Math.max(0,(t.height-n.bounds.height*l)/2);return{x:n.bounds.x+(u.x-h)/d,y:n.bounds.y+(u.y-w)/l}}function A(e,t,n){if(!e||typeof e!="object")return null;const i=e,r=Number(i.x),o=Number(i.y),a=Number(i.width),s=Number(i.height);if(![r,o,a,s].every(Number.isFinite))return null;const c=typeof i.coordinateSpace=="string"?i.coordinateSpace:void 0,d={x:r+a/2,y:o+s/2};if(c==="screen"||!n.bounds)return d;const l=t.width&&n.bounds.width?t.width/n.bounds.width:1,u=t.height&&n.bounds.height?t.height/n.bounds.height:l,h=Math.max(0,(t.width-n.bounds.width*l)/2),w=Math.max(0,(t.height-n.bounds.height*u)/2);return{x:n.bounds.x+(d.x-h)/l,y:n.bounds.y+(d.y-w)/u}}function Zt(e,t,n){if(!e?.blocks?.length||!t)return null;const i=e.blocks.find(r=>{const o=String(r.text||"").trim(),a=r.bbox;if(!a||!o)return!1;const s=Number(a.x),c=Number(a.y);return!Number.isFinite(s)||!Number.isFinite(c)||!/跳转到最新消息|回到最新消息|jump\s+to\s+latest/i.test(o)?!1:s>t.width*.55&&c>t.height*.45&&c<t.height*.92});return A(i?.bbox,t,n)}function Ie(e,t){return y(t)?Se(e,t):e===t||e.includes(t)||t.includes(e)}function G(e,t){if(y(t))return Se(e,t);const n=Me(p(e)),i=Me(p(t));return!n||!i?!1:n===i?!0:n.replace(/\d+$/u,"")===i}function Me(e){return e.toLowerCase().replace(/[l1]/g,"i")}function z(e){return String(e||"").normalize("NFKC").replace(/[((]\d+[))]/g,"").replace(/\s+/g,"").replace(/[l1]/gi,"i").toLowerCase().trim()}function E(e){return String(e||"").normalize("NFKC").replace(/[((]\d+[))]/g,"").replace(/\s+/g,"").trim()}function y(e){const t=String(e||"").trim();return/^[A-Z0-9_-]{1,6}$/.test(t)&&/[A-Z]/.test(t)}function Se(e,t){const n=String(e||"").normalize("NFKC").replace(/[((]\d+[))]/g,"").replace(/\s+/g,"").trim(),i=String(t||"").normalize("NFKC").replace(/[((]\d+[))]/g,"").replace(/\s+/g,"").trim();return!!(n&&i&&(n===i||n.replace(/\d+$/u,"")===i))}export{T as WeChatChannelUserActivityAbort,U as captureAndRecognizeWeChatWindow,be as captureWeChatWindow,cn as ensureHelperPreflight,bt as ensureWeChatWindowReady,ln as fallbackWeChatMessageInputPointForWindow,kt as focusExistingWeChatWindow,Ct as focusKnownWeChatWindow,dn as focusWeChatWindow,sn as observeWeChatChannelBindingViaHelper,fe as openConversationBySearch,q as openConversationInVisibleList,ge as recognizeWeChatScreenshot};
|
|
@@ -31,6 +31,13 @@ export type WeChatChannelPreflightInput = {
|
|
|
31
31
|
duplicateInstanceBlocked?: boolean;
|
|
32
32
|
duplicateMainWindowCount?: number;
|
|
33
33
|
unsafeAuxiliaryWindowCount?: number;
|
|
34
|
+
windowsVisibleDesktopAvailable?: boolean;
|
|
35
|
+
rdpVisibleDesktopAvailable?: boolean;
|
|
36
|
+
desktopSessionVisible?: boolean;
|
|
37
|
+
screenLocked?: boolean;
|
|
38
|
+
rdpDisconnected?: boolean;
|
|
39
|
+
dpiMappingAvailable?: boolean;
|
|
40
|
+
displayTopologySupported?: boolean;
|
|
34
41
|
} | null;
|
|
35
42
|
};
|
|
36
43
|
export type WeChatChannelPreflightResult = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{requiredWeChatChannelHelperCapabilitiesForProfile as
|
|
1
|
+
import{requiredWeChatChannelHelperCapabilitiesForProfile as o,requiredWindowsWeChatChannelHelperCapabilitiesForProfile as t,validateWeChatChannelHelperReady as d}from"./helper-protocol.js";function p(e){const a=[],r=e.platform==="darwin"||e.platform==="win32";if(a.push(i(r,"platform_unsupported","\u5FAE\u4FE1 Channel \u5F53\u524D\u652F\u6301 macOS \u548C Windows \u684C\u9762\u5FAE\u4FE1\u3002")),a.push(i(e.accountTier==="enterprise","enterprise_required","\u5F53\u524D\u9636\u6BB5\u5FAE\u4FE1 Channel \u4EC5\u4F01\u4E1A\u7248\u8D26\u53F7\u53EF\u7528\u3002")),a.push(i(!!e.privacyConsentAccepted,"privacy_consent_required","\u542F\u7528\u524D\u9700\u8981\u786E\u8BA4\u5FAE\u4FE1 Channel \u6570\u636E\u4E0E\u9690\u79C1\u6388\u6743\u3002")),a.push(i(!!e.serverDecisionAvailable,"server_decision_unavailable","\u670D\u52A1\u7AEF\u5224\u65AD\u80FD\u529B\u4E0D\u53EF\u7528\uFF0C\u6682\u4E0D\u80FD\u542F\u7528\u3002")),e.currentMachineId&&e.runtime.machineId&&a.push(i(e.currentMachineId===e.runtime.machineId,"same_machine_required","\u7B2C\u4E00\u7248\u53EA\u652F\u6301\u540C\u673A\u5FAE\u4FE1\u4E0E\u540C\u673A\u795E\u5FF5\u8FD0\u884C\u65F6\u7ED1\u5B9A\u3002")),e.localAgentMachineId&&e.runtime.machineId&&a.push(i(e.localAgentMachineId===e.runtime.machineId,"same_machine_required","\u7ED1\u5B9A\u7684 Agent \u5FC5\u987B\u4E0E\u5FAE\u4FE1\u8FD0\u884C\u65F6\u5728\u540C\u4E00\u53F0\u673A\u5668\u3002")),a.push(i(e.localAgentAvailable!==!1,"local_agent_unavailable","\u672C\u673A Agent \u4E0D\u53EF\u7528\uFF0C\u65E0\u6CD5\u5B89\u5168\u63A5\u7BA1\u5FAE\u4FE1\u56DE\u590D\u3002")),!e.helperReady)a.push(i(!1,"helper_not_ready","\u5E73\u53F0 helper \u672A\u5C31\u7EEA\u3002"));else{const l=d(e.helperReady,e.expectedHelperVersion,e.requiredHelperCapabilities??c(e.platform,e.binding));l.ok?a.push(i(!0,"ok",e.platform==="win32"?"Windows helper \u7248\u672C\u4E0E\u80FD\u529B\u68C0\u67E5\u901A\u8FC7\u3002":"macOS helper \u7248\u672C\u4E0E\u80FD\u529B\u68C0\u67E5\u901A\u8FC7\u3002")):a.push(i(!1,l.errorCode,l.errorSummary))}const n=e.permissions;n&&(a.push(i(n.screenRecording!==!1,"permission_screen_recording_missing","\u7F3A\u5C11\u5C4F\u5E55\u5F55\u5236\u6743\u9650\u3002")),a.push(i(n.automation!==!1,"permission_automation_missing","\u7F3A\u5C11\u81EA\u52A8\u5316\u63A7\u5236\u6743\u9650\u3002")),a.push(i(n.wechatRunning!==!1,"wechat_not_running","\u5FAE\u4FE1\u672A\u8FD0\u884C\u3002")),a.push(i(n.wechatWindowAvailable!==!1,"wechat_window_unavailable","\u5FAE\u4FE1\u7A97\u53E3\u4E0D\u53EF\u7528\u3002")),a.push(i(n.wechatMainWindowResponsive!==!1,"wechat_window_unresponsive","\u5FAE\u4FE1\u7A97\u53E3\u65E0\u54CD\u5E94\uFF0C\u9700\u6062\u590D\u540E\u518D\u7EE7\u7EED\u3002")),a.push(i(n.windowsVisibleDesktopAvailable!==!1&&n.rdpVisibleDesktopAvailable!==!1&&n.desktopSessionVisible!==!1&&n.screenLocked!==!0&&n.rdpDisconnected!==!0,"windows_visible_desktop_unavailable","Windows \u53EF\u89C1\u684C\u9762\u4E0D\u53EF\u7528\uFF0C\u8BF7\u89E3\u9501\u6216\u4FDD\u6301 RDP \u53EF\u89C1\u684C\u9762\u4F1A\u8BDD\u540E\u518D\u542F\u7528\u3002")),a.push(i(n.dpiMappingAvailable!==!1&&n.displayTopologySupported!==!1,"dpi_mapping_failed","Windows DPI / \u591A\u5C4F\u5750\u6807\u6620\u5C04\u4E0D\u53EF\u7528\uFF0C\u8BF7\u628A\u5FAE\u4FE1\u5B8C\u6574\u653E\u5728\u4E00\u4E2A\u53EF\u89C1\u663E\u793A\u5668\u4E0A\u540E\u518D\u542F\u7528\u3002")),a.push(i(n.duplicateInstanceBlocked!==!0&&Number(n.duplicateMainWindowCount??0)===0&&Number(n.unsafeAuxiliaryWindowCount??0)===0,"wechat_single_main_window_required","\u8BF7\u53EA\u4FDD\u7559\u4E00\u4E2A\u5DF2\u767B\u5F55\u7684\u5FAE\u4FE1\u4E3B\u7A97\u53E3\uFF0C\u5173\u95ED\u91CD\u590D\u4E3B\u7A97\u53E3\u3001\u89C6\u9891\u9884\u89C8\u6216\u767B\u5F55\u63D0\u793A\u540E\u518D\u542F\u7528\u3002")));const s=a.filter(l=>!l.ok&&l.severity==="blocking");return{ok:s.length===0,canEnable:s.length===0,status:s.length===0?"ready":"blocked",checks:a}}function i(e,a,r){return{ok:e,code:e?"ok":a,severity:"blocking",message:r}}function c(e,a){const r=e==="win32"?t:o;return!a||a.allowReply!==!1?r("send"):a.downloadMedia!==!1?r("download"):r("observe")}export{p as evaluateWeChatChannelPreflight};
|
|
@@ -31,7 +31,6 @@ export declare class WeChatChannelProductRunner {
|
|
|
31
31
|
tick(): Promise<WeChatChannelSchedulerTickResult | void>;
|
|
32
32
|
stop(): Promise<void>;
|
|
33
33
|
private ensureHelperStarted;
|
|
34
|
-
private reportUnrecoverableWindowsWeChat;
|
|
35
34
|
private shouldSkipObserveForUnchangedFingerprint;
|
|
36
35
|
}
|
|
37
36
|
export declare function createWeChatChannelProductRunner(options: WeChatChannelProductRunnerOptions): WeChatChannelProductRunner;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import l from"node:path";import d from"node:fs";import{createWeChatChannelApiClient as
|
|
1
|
+
import l from"node:path";import d from"node:fs";import{createWeChatChannelApiClient as v}from"./client.js";import{WeChatChannelHelperClient as c}from"./helper-client.js";import{resolveWeChatChannelHelperAsset as y,WECHAT_CHANNEL_HELPER_VERSION as k}from"./helper-assets.js";import{requiredWindowsWeChatChannelHelperCapabilitiesForProfile as u}from"./helper-protocol.js";import{captureWeChatWindow as W,ensureWeChatWindowReady as p,fallbackWeChatMessageInputPointForWindow as S,focusKnownWeChatWindow as f,observeWeChatChannelBindingViaHelper as I,openConversationBySearch as P,recognizeWeChatScreenshot as A}from"./observer.js";import{WeChatChannelScheduler as O}from"./scheduler.js";import{loadWeChatChannelLedger as F}from"./ledger.js";import{buildWeChatConversationFingerprint as x,matchWeChatConversationFingerprints as _}from"./fingerprint.js";import{WeChatChannelOutboundSender as D}from"./outbound-sender.js";import{decideWeChatChannelActivityGate as H,nextWeChatChannelActivityRetryAt as M,normalizeWeChatChannelActivitySnapshot as T}from"./human-coordination.js";class R{options;scheduler;helper;helperStarted=!1;constructor(e){this.options=e;const n=e.api??v();this.helper=e.helper??L(e),this.scheduler=new O({runtime:e.runtime,workDir:e.workDir,api:n,ledgerPath:e.ledgerPath,outboundLedgerPath:e.outboundLedgerPath,onInboundMessages:e.onInboundMessages,activityGate:{canObserve:async(t,a)=>{if(e.runtime.foregroundPolicy==="work")return{ok:!0};const o=await this.helper.request("activity.snapshot",{});if(!o.ok)return{ok:!1,reasonCode:"user_activity_unknown",nextAttemptAt:new Date(a.getTime()+5e3)};const i=H({snapshot:T(o.result),stage:"observe"});return i.ok?{ok:!0}:{ok:!1,reasonCode:i.reasonCode,nextAttemptAt:M(a,i.waitMs)}}},shouldSkipObserve:(t,a)=>this.shouldSkipObserveForUnchangedFingerprint(t,a.foregroundMode),outboundSender:new D({helper:this.helper,platform:e.runtime.policy.platform,openConversation:async t=>{const a=await p(this.helper),o=await f(this.helper,a);return{...await P({helper:this.helper,window:o,settleToBottom:!1,binding:{bindingId:`outbound:${t}`,sessionId:"outbound",conversationDisplayName:t,enabled:!0,allowReply:!0,downloadMedia:!0}}),inputPoint:S(o)}}}),observeBinding:(t,a)=>I({runtime:e.runtime,binding:t,helper:this.helper,api:n,workDir:e.workDir,localLedgerTailAnchors:E(e.ledgerPath??U(e.workDir,e.runtime.runtimeId),e.runtime.runtimeId,t.bindingId),localOutboundEchoAnchors:a?.localOutboundEchoAnchors,foregroundMode:a?.foregroundMode,onObservationEvidence:({ocr:o})=>{N({statePath:g(e.workDir,e.runtime.runtimeId),runtimeId:e.runtime.runtimeId,binding:t,ocr:o})}})})}async start(){await this.ensureHelperStarted(),await this.scheduler.start()}async tick(){return await this.ensureHelperStarted(),this.scheduler.tick()}async stop(){this.scheduler.stop(),this.helperStarted&&this.helper.stop&&await this.helper.stop(),this.helperStarted=!1}async ensureHelperStarted(){this.helperStarted||(this.helper.start&&await this.helper.start(),this.helperStarted=!0)}async shouldSkipObserveForUnchangedFingerprint(e,n="required"){if(this.options.runtime.policy.platform!=="darwin"&&this.options.runtime.policy.platform!=="win32")return{skip:!1};const t=g(this.options.workDir,this.options.runtime.runtimeId),a=h(t,this.options.runtime.runtimeId).bindings[e.bindingId];if(!a?.fingerprint)return{skip:!1};try{const o=await p(this.helper,void 0,{foreground:n}),i=n==="background"?o:await f(this.helper,o),C=await W(this.helper,i.windowId,void 0,i.bounds),b=await A(this.helper,C),s=w({binding:e,ocr:b,prior:a});return!s?.fingerprint||s.fingerprint!==a.fingerprint?{skip:!1}:{skip:!0,reasonCode:"conversation_fingerprint_unchanged",fingerprint:s.fingerprint}}catch{return{skip:!1}}}}function re(r){return new R(r)}function E(r,e,n,t=3){return(F(r,e).bindings[n]?.recent??[]).slice(-t).map(i=>({stableMessageKey:i.stableMessageKey,senderRole:i.senderRole,kind:i.kind,anchorText:i.anchorText??i.normalizedText??i.textExcerpt??null,anchorMetadata:i.anchorMetadata??null}))}function L(r){if(r.helperClientOptions)return new c({...r.helperClientOptions,requiredCapabilities:r.helperClientOptions.requiredCapabilities??m(r.runtime),guardUnsafeWindowsCommands:r.helperClientOptions.guardUnsafeWindowsCommands??r.runtime.policy.platform==="win32",skipUserActivityGuard:r.helperClientOptions.skipUserActivityGuard??r.runtime.foregroundPolicy==="work",cleanupWindowsOverlays:r.helperClientOptions.cleanupWindowsOverlays??r.runtime.policy.platform==="win32"});const e=y();if(!e.ok)throw new Error(`${e.reasonCode}: ${e.message}`);return new c({helperPath:e.helperPath,expectedHelperVersion:e.version||k,requiredCapabilities:m(r.runtime),guardUnsafeWindowsCommands:r.runtime.policy.platform==="win32",skipUserActivityGuard:r.runtime.foregroundPolicy==="work",cleanupWindowsOverlays:r.runtime.policy.platform==="win32"})}function m(r){if(r.policy.platform==="win32")return r.bindings.some(e=>e.enabled!==!1&&e.allowReply!==!1)?u("send"):r.bindings.some(e=>e.enabled!==!1&&e.downloadMedia!==!1)?u("download"):u("observe")}function U(r,e){return l.join(r,"wechat-channel",`${e}.ledger.json`)}function g(r,e){return l.join(r,"wechat-channel",`${$(e)}.conversation-fingerprints.json`)}function h(r,e){try{const n=JSON.parse(d.readFileSync(r,"utf8"));if(n?.version===1&&n.runtimeId===e&&n.bindings&&typeof n.bindings=="object")return n}catch{}return{version:1,runtimeId:e,bindings:{}}}function q(r,e){d.mkdirSync(l.dirname(r),{recursive:!0}),d.writeFileSync(r,JSON.stringify(e,null,2))}function N(r){const e=w({binding:r.binding,ocr:r.ocr,prior:h(r.statePath,r.runtimeId).bindings[r.binding.bindingId]});if(!e?.fingerprint)return;const n=h(r.statePath,r.runtimeId);n.bindings[r.binding.bindingId]={bindingId:r.binding.bindingId,fingerprint:e.fingerprint,title:e.item.title??null,preview:e.item.preview??null,timeText:e.item.timeText??null,unreadText:e.item.unreadText??null,observedAt:new Date().toISOString()},q(r.statePath,n)}function w(r){const e=r.ocr.visibleConversationFingerprints??[],n=_({visibleItems:e,targets:[{bindingId:r.binding.bindingId,conversationDisplayName:r.binding.conversationDisplayName,lastConversationFingerprint:r.prior?.fingerprint,lastPreview:r.prior?.preview??void 0}]})[0];if(!n)return null;const t=n.item.fingerprint||x(n.item);return t?{item:n.item,fingerprint:t}:null}function $(r){return String(r||"").trim().replace(/[^a-zA-Z0-9._-]+/g,"_").replace(/^_+|_+$/g,"")||"wechat-channel-runtime"}export{R as WeChatChannelProductRunner,E as buildLocalLedgerTailAnchors,re as createWeChatChannelProductRunner};
|
|
@@ -3,7 +3,7 @@ import type { WeChatChannelApiClient, WeChatChannelObservedMessage, WeChatChanne
|
|
|
3
3
|
import { type WeChatChannelBindingLedger } from './ledger.js';
|
|
4
4
|
import { type WeChatChannelOutboundRecord } from './outbound-ledger.js';
|
|
5
5
|
import { type WeChatChannelOutboundSender } from './outbound-sender.js';
|
|
6
|
-
import type { WeChatChannelLocalOutboundEchoAnchor } from './observer.js';
|
|
6
|
+
import type { WeChatChannelForegroundMode, WeChatChannelLocalOutboundEchoAnchor } from './observer.js';
|
|
7
7
|
import type { WeChatChannelHumanActivityReasonCode } from './helper-protocol.js';
|
|
8
8
|
export type WeChatChannelSchedulerOptions = {
|
|
9
9
|
runtime: WeChatChannelRuntime;
|
|
@@ -13,16 +13,17 @@ export type WeChatChannelSchedulerOptions = {
|
|
|
13
13
|
} & Partial<Pick<WeChatChannelApiClient, 'reportOutboundStatus'>>;
|
|
14
14
|
observeBinding: (binding: WeChatChannelBindingConfig, context?: {
|
|
15
15
|
localOutboundEchoAnchors?: WeChatChannelLocalOutboundEchoAnchor[];
|
|
16
|
+
foregroundMode?: WeChatChannelForegroundMode;
|
|
16
17
|
}) => Promise<WeChatChannelObservedMessage[]>;
|
|
17
18
|
shouldSkipObserve?: (binding: WeChatChannelBindingConfig, context: {
|
|
18
19
|
ledgerBinding?: WeChatChannelBindingLedger;
|
|
19
20
|
outboundRecords: WeChatChannelOutboundRecord[];
|
|
20
21
|
now: Date;
|
|
22
|
+
foregroundMode: WeChatChannelForegroundMode;
|
|
21
23
|
}) => Promise<WeChatChannelObserveSkipDecision> | WeChatChannelObserveSkipDecision;
|
|
22
24
|
onInboundMessages?: (binding: WeChatChannelBindingConfig, messages: WeChatChannelObservedMessage[]) => Promise<void> | void;
|
|
23
25
|
outboundSender?: WeChatChannelOutboundSender;
|
|
24
26
|
activityGate?: WeChatChannelSchedulerActivityGate;
|
|
25
|
-
recoverWeChat?: (binding: WeChatChannelBindingConfig, error: unknown) => Promise<boolean>;
|
|
26
27
|
ledgerPath?: string;
|
|
27
28
|
outboundLedgerPath?: string;
|
|
28
29
|
maxUserActivityWaitMs?: number;
|
|
@@ -50,7 +51,7 @@ export type WeChatChannelSchedulerTickResult = {
|
|
|
50
51
|
}[];
|
|
51
52
|
export type WeChatChannelObserveSkipDecision = {
|
|
52
53
|
skip: true;
|
|
53
|
-
reasonCode: 'conversation_fingerprint_unchanged';
|
|
54
|
+
reasonCode: 'conversation_fingerprint_unchanged' | 'conversation_not_visible_background';
|
|
54
55
|
fingerprint?: string | null;
|
|
55
56
|
} | {
|
|
56
57
|
skip: false;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import m from"node:path";import{loadWeChatChannelLedger as C,markWeChatChannelBindingDisabled as R,saveWeChatChannelLedger as w,updateWeChatChannelBindingLedger as I}from"./ledger.js";import{classifyWeChatOutboundEchoes as _,isWeChatOutboundTerminalDeliveryStatus as y,loadWeChatChannelOutboundLedger as S,markWeChatOutboundUserActiveTimeout as O,markWeChatOutboundWaitingUserIdle as W,saveWeChatChannelOutboundLedger as A,shouldFailWeChatOutboundForUserActiveTimeout as E}from"./outbound-ledger.js";import{clearExpiredWeChatChannelCooldown as x,isWeChatChannelCooldownActive as M,noteWeChatChannelBillingPause as k,noteWeChatChannelInterruption as L,noteWeChatChannelStableRun as U}from"./cooldown.js";import{sendQueuedWeChatOutboundRecords as P}from"./outbound-sender.js";const T=600*1e3;class J{options;timer=null;running=!1;constructor(o){this.options=o}async start(){this.timer||(await this.tick(),this.timer=setInterval(()=>{this.tick().catch(()=>{})},this.options.runtime.policy.pollIntervalMs),this.timer.unref())}stop(){this.timer&&(clearInterval(this.timer),this.timer=null)}async tick(){if(!this.running){this.running=!0;try{const o=this.options.ledgerPath||v(this.options.workDir,this.options.runtime.runtimeId),i=this.options.outboundLedgerPath||F(this.options.workDir,this.options.runtime.runtimeId),t=C(o,this.options.runtime.runtimeId),s=S(i,this.options.runtime.runtimeId),u=[],c=new Date;for(const e of this.options.runtime.bindings.filter(d=>!d.enabled))t.bindings[e.bindingId]?.disabledSince||R({ledger:t,bindingId:e.bindingId});for(const e of this.options.runtime.bindings.filter(d=>d.enabled)){await this.options.api.upsertRuntime(this.options.runtime,e);const d=t.bindings[e.bindingId];if(d?.cooldown&&(d.cooldown=x(d.cooldown),M(d.cooldown))){await this.options.api.reportRunStatus(this.options.runtime,e,{status:"cooldown",reasonCode:d.cooldown.manualReviewReason||"user_interruption_cooldown"}),u.push({bindingId:e.bindingId,observedCount:0,newInboundCount:0,outboundSentCount:0,staleOutboundCount:0,failedOutboundCount:0,waitingOutboundCount:0,confirmedEchoCount:0,manualReviewCount:0,revision:d.revision});continue}const b=this.options.activityGate?await this.options.activityGate.canObserve(e,c):{ok:!0};if(b.ok===!1){const r=N({records:s.records,bindingId:e.bindingId,reasonCode:b.reasonCode,nextAttemptAt:b.nextAttemptAt,now:c,maxUserActivityWaitMs:this.options.maxUserActivityWaitMs??T});await this.reportOutboundProjections(e,[...r.waitingRecords,...r.failedRecords]),await this.options.api.reportRunStatus(this.options.runtime,e,{status:"observe_deferred_user_active",reasonCode:r.failedRecords[0]?.failureCode||b.reasonCode}),u.push({bindingId:e.bindingId,observedCount:0,newInboundCount:0,outboundSentCount:0,staleOutboundCount:0,failedOutboundCount:r.failedRecords.length,waitingOutboundCount:r.waitingRecords.length,confirmedEchoCount:0,manualReviewCount:0,revision:d?.revision??0});continue}const p=s.records.filter(r=>r.bindingId===e.bindingId);if(this.options.shouldSkipObserve&&K(d,p)){const r=await this.options.shouldSkipObserve(e,{ledgerBinding:d,outboundRecords:p,now:c});if(r.skip){await this.options.api.reportRunStatus(this.options.runtime,e,{status:"baseline_or_no_change",reasonCode:r.reasonCode}),u.push({bindingId:e.bindingId,observedCount:0,newInboundCount:0,outboundSentCount:0,staleOutboundCount:0,failedOutboundCount:0,waitingOutboundCount:0,confirmedEchoCount:0,manualReviewCount:0,revision:d?.revision??0});continue}}let f;try{f=await this.options.observeBinding(e,{localOutboundEchoAnchors:q(s.records,e.bindingId)})}catch(r){if(D(r)){const g=await this.options.recoverWeChat?.(e,r).catch(()=>!1)??!1;await this.options.api.reportRunStatus(this.options.runtime,e,{status:g?"wechat_recovered_restart_pending_login":"wechat_recovery_failed",reasonCode:"wechat_window_unresponsive",lastErrorSummary:r instanceof Error?r.message:"wechat_window_unresponsive"}),u.push({bindingId:e.bindingId,observedCount:0,newInboundCount:0,outboundSentCount:0,staleOutboundCount:0,failedOutboundCount:0,waitingOutboundCount:0,confirmedEchoCount:0,manualReviewCount:0,revision:d?.revision??0});continue}if(B(r)){const g=d??{bindingId:e.bindingId,baselineEstablished:!1,revision:0,recent:[],pendingSendKeys:[]};g.cooldown=k({state:g.cooldown,reason:"insufficient_credits",now:c}),t.bindings[e.bindingId]=g,await this.options.api.reportRunStatus(this.options.runtime,e,{status:"billing_paused",reasonCode:"insufficient_credits",lastErrorSummary:r instanceof Error?r.message:"insufficient_credits"}),u.push({bindingId:e.bindingId,observedCount:0,newInboundCount:0,outboundSentCount:0,staleOutboundCount:0,failedOutboundCount:0,waitingOutboundCount:0,confirmedEchoCount:0,manualReviewCount:0,revision:g.revision});continue}throw r}const h=_({ledger:s,bindingId:e.bindingId,messages:f}),l=I({ledger:t,bindingId:e.bindingId,observedMessages:h.remainingMessages}),a=this.options.outboundSender?await P({ledger:s,bindingId:e.bindingId,currentLastInboundRevision:l.binding.revision,sender:this.options.outboundSender,now:c,maxUserActivityWaitMs:this.options.maxUserActivityWaitMs}):{sentRecords:[],staleRecords:[],failedRecords:[],waitingRecords:[],manualReviewRecords:[]};l.binding.cooldown=U(l.binding.cooldown),l.newMessages.length>0&&(await this.options.api.ingest(this.options.runtime,e,{idempotencyKey:`${e.bindingId}:${l.binding.revision}:${$(l.newMessages)}`,messages:l.newMessages}),await this.options.onInboundMessages?.(e,l.newMessages)),await this.reportOutboundProjections(e,[...h.confirmedRecords,...h.manualReviewRecords,...a.sentRecords,...a.staleRecords,...a.failedRecords,...a.waitingRecords,...a.manualReviewRecords]),await this.options.api.reportRunStatus(this.options.runtime,e,{status:G(l.newMessages.length,h.confirmedRecords,h.manualReviewRecords,a.sentRecords,a.staleRecords,a.failedRecords,a.waitingRecords,a.manualReviewRecords),reasonCode:a.waitingRecords[0]?.deferReason}),u.push({bindingId:e.bindingId,observedCount:f.length,newInboundCount:l.newMessages.length,outboundSentCount:a.sentRecords.length,staleOutboundCount:a.staleRecords.length,failedOutboundCount:a.failedRecords.length,waitingOutboundCount:a.waitingRecords.length,confirmedEchoCount:h.confirmedRecords.length,manualReviewCount:h.manualReviewRecords.length+a.manualReviewRecords.length,revision:l.binding.revision})}return w(o,t),A(i,s),u}finally{this.running=!1}}}noteInterruption(o,i,t=new Date){const s=this.options.ledgerPath||v(this.options.workDir,this.options.runtime.runtimeId),u=C(s,this.options.runtime.runtimeId),c=u.bindings[o]??{bindingId:o,baselineEstablished:!1,revision:0,recent:[],pendingSendKeys:[]};c.cooldown=L({state:c.cooldown,reason:i,now:t}),u.bindings[o]=c,w(s,u)}async reportOutboundProjections(o,i){if(this.options.api.reportOutboundStatus)for(const t of i)await this.options.api.reportOutboundStatus(this.options.runtime,o,{replyId:t.replyId,idempotencyKey:t.idempotencyKey,status:t.sendStatus,replyBaseRevision:t.replyBaseRevision,sentAt:t.sentAt,confirmedAt:t.confirmedAt,failureCode:t.failureCode,lastErrorSummary:t.lastErrorSummary,nextAttemptAt:t.nextAttemptAt,attemptCount:t.attemptCount,commitStage:t.commitStage,copyableContentRef:t.copyableContentRef,copyableText:j(t)?t.text:void 0,agentVisible:y(t)?!1:void 0})}}function B(n){return!n||typeof n!="object"?!1:n.reasonCode==="insufficient_credits"?!0:n instanceof Error&&/\binsufficient_credits\b/.test(n.message)}function D(n){return n instanceof Error&&/\bwechat_window_unresponsive\b/i.test(n.message)}function K(n,o){return!n?.baselineEstablished||n.disabledSince||!n.recent.length?!1:!o.some(i=>i.sendStatus==="queued"||i.sendStatus==="sending"||i.sendStatus==="sent_unconfirmed")}function j(n){return(n.sendStatus==="failed"||n.sendStatus==="manual_review")&&!!n.text}function v(n,o){return m.join(n,"wechat-channel",`${o}.ledger.json`)}function F(n,o){return m.join(n,"wechat-channel",`${o}.outbound-ledger.json`)}function $(n){return n.at(-1)?.stableMessageKey||"empty"}function G(n,o,i,t=[],s=[],u=[],c=[],e=[]){return i.length>0||e.length>0?"manual_review":u.length>0?"outbound_send_failed":s.length>0?"outbound_stale":c.length>0?"send_waiting_user_idle":n>0?"new_messages_ingested":t.length>0?"outbound_sent_unconfirmed":o.length>0?"outbound_echo_confirmed":"baseline_or_no_change"}function q(n,o){return n.filter(i=>i.bindingId===o&&i.sendStatus==="sent_unconfirmed").map(i=>({replyId:i.replyId,idempotencyKey:i.idempotencyKey,expectedEchoAnchor:i.expectedEchoAnchor||i.textNormalized||i.text||""}))}function N(n){const o=n.records.filter(s=>s.bindingId===n.bindingId&&s.sendStatus==="queued"),i=[],t=[];for(const s of o)W({record:s,reasonCode:n.reasonCode,nextAttemptAt:n.nextAttemptAt,now:n.now}),E({record:s,now:n.now,maxWaitMs:n.maxUserActivityWaitMs})?(O(s,n.now),i.push(s)):t.push(s);return{waitingRecords:t,failedRecords:i}}export{J as WeChatChannelScheduler};
|
|
1
|
+
import w from"node:path";import{loadWeChatChannelLedger as C,markWeChatChannelBindingDisabled as I,saveWeChatChannelLedger as v,updateWeChatChannelBindingLedger as S}from"./ledger.js";import{classifyWeChatOutboundEchoes as y,isWeChatOutboundTerminalDeliveryStatus as O,loadWeChatChannelOutboundLedger as E,markWeChatOutboundUserActiveTimeout as W,markWeChatOutboundWaitingUserIdle as A,saveWeChatChannelOutboundLedger as k,shouldFailWeChatOutboundForUserActiveTimeout as x}from"./outbound-ledger.js";import{clearExpiredWeChatChannelCooldown as M,isWeChatChannelCooldownActive as U,noteWeChatChannelBillingPause as L,noteWeChatChannelInterruption as P,noteWeChatChannelStableRun as T}from"./cooldown.js";import{sendQueuedWeChatOutboundRecords as B}from"./outbound-sender.js";const D=600*1e3;class tn{options;timer=null;running=!1;constructor(t){this.options=t}async start(){this.timer||(await this.tick(),this.timer=setInterval(()=>{this.tick().catch(()=>{})},this.options.runtime.policy.pollIntervalMs),this.timer.unref())}stop(){this.timer&&(clearInterval(this.timer),this.timer=null)}async tick(){if(!this.running){this.running=!0;try{const t=this.options.ledgerPath||R(this.options.workDir,this.options.runtime.runtimeId),i=this.options.outboundLedgerPath||N(this.options.workDir,this.options.runtime.runtimeId),o=C(t,this.options.runtime.runtimeId),s=E(i,this.options.runtime.runtimeId),a=[],c=new Date;for(const e of this.options.runtime.bindings.filter(d=>!d.enabled))o.bindings[e.bindingId]?.disabledSince||I({ledger:o,bindingId:e.bindingId});for(const e of this.options.runtime.bindings.filter(d=>d.enabled)){await this.options.api.upsertRuntime(this.options.runtime,e);const d=o.bindings[e.bindingId],f=s.records.filter(r=>r.bindingId===e.bindingId),p=K(this.options.runtime,f);if(d?.cooldown&&(d.cooldown=M(d.cooldown),U(d.cooldown))){await this.options.api.reportRunStatus(this.options.runtime,e,{status:"cooldown",reasonCode:d.cooldown.manualReviewReason||"user_interruption_cooldown"}),a.push({bindingId:e.bindingId,observedCount:0,newInboundCount:0,outboundSentCount:0,staleOutboundCount:0,failedOutboundCount:0,waitingOutboundCount:0,confirmedEchoCount:0,manualReviewCount:0,revision:d.revision});continue}const b=this.options.activityGate?await this.options.activityGate.canObserve(e,c):{ok:!0};if(b.ok===!1){const r=Y({records:s.records,bindingId:e.bindingId,reasonCode:b.reasonCode,nextAttemptAt:b.nextAttemptAt,now:c,maxUserActivityWaitMs:this.options.maxUserActivityWaitMs??D});await this.reportOutboundProjections(e,[...r.waitingRecords,...r.failedRecords]),await this.options.api.reportRunStatus(this.options.runtime,e,{status:"observe_deferred_user_active",reasonCode:r.failedRecords[0]?.failureCode||b.reasonCode}),a.push({bindingId:e.bindingId,observedCount:0,newInboundCount:0,outboundSentCount:0,staleOutboundCount:0,failedOutboundCount:r.failedRecords.length,waitingOutboundCount:r.waitingRecords.length,confirmedEchoCount:0,manualReviewCount:0,revision:d?.revision??0});continue}if(this.options.shouldSkipObserve&&G(d,f)){const r=await this.options.shouldSkipObserve(e,{ledgerBinding:d,outboundRecords:f,now:c,foregroundMode:p});if(r.skip){await this.options.api.reportRunStatus(this.options.runtime,e,{status:F(r.reasonCode),reasonCode:r.reasonCode}),a.push({bindingId:e.bindingId,observedCount:0,newInboundCount:0,outboundSentCount:0,staleOutboundCount:0,failedOutboundCount:0,waitingOutboundCount:0,confirmedEchoCount:0,manualReviewCount:0,revision:d?.revision??0});continue}}let m;try{m=await this.options.observeBinding(e,{localOutboundEchoAnchors:H(s.records,e.bindingId),foregroundMode:p})}catch(r){if(q(r)){await this.options.api.reportRunStatus(this.options.runtime,e,{status:"observe_deferred_window_unavailable",reasonCode:_(r,"wechat_window_unavailable"),lastErrorSummary:r instanceof Error?r.message:"wechat_window_unavailable"}),a.push({bindingId:e.bindingId,observedCount:0,newInboundCount:0,outboundSentCount:0,staleOutboundCount:0,failedOutboundCount:0,waitingOutboundCount:0,confirmedEchoCount:0,manualReviewCount:0,revision:d?.revision??0});continue}if($(r)){await this.options.api.reportRunStatus(this.options.runtime,e,{status:"wechat_window_unresponsive",reasonCode:"wechat_window_unresponsive",lastErrorSummary:r instanceof Error?r.message:"wechat_window_unresponsive"}),a.push({bindingId:e.bindingId,observedCount:0,newInboundCount:0,outboundSentCount:0,staleOutboundCount:0,failedOutboundCount:0,waitingOutboundCount:0,confirmedEchoCount:0,manualReviewCount:0,revision:d?.revision??0});continue}if(j(r)){const g=d??{bindingId:e.bindingId,baselineEstablished:!1,revision:0,recent:[],pendingSendKeys:[]};g.cooldown=L({state:g.cooldown,reason:"insufficient_credits",now:c}),o.bindings[e.bindingId]=g,await this.options.api.reportRunStatus(this.options.runtime,e,{status:"billing_paused",reasonCode:"insufficient_credits",lastErrorSummary:r instanceof Error?r.message:"insufficient_credits"}),a.push({bindingId:e.bindingId,observedCount:0,newInboundCount:0,outboundSentCount:0,staleOutboundCount:0,failedOutboundCount:0,waitingOutboundCount:0,confirmedEchoCount:0,manualReviewCount:0,revision:g.revision});continue}throw r}const h=y({ledger:s,bindingId:e.bindingId,messages:m}),l=S({ledger:o,bindingId:e.bindingId,observedMessages:h.remainingMessages}),u=this.options.outboundSender?await B({ledger:s,bindingId:e.bindingId,currentLastInboundRevision:l.binding.revision,sender:this.options.outboundSender,now:c,maxUserActivityWaitMs:this.options.maxUserActivityWaitMs}):{sentRecords:[],staleRecords:[],failedRecords:[],waitingRecords:[],manualReviewRecords:[]};l.binding.cooldown=T(l.binding.cooldown),l.newMessages.length>0&&(await this.options.api.ingest(this.options.runtime,e,{idempotencyKey:`${e.bindingId}:${l.binding.revision}:${Q(l.newMessages)}`,messages:l.newMessages}),await this.options.onInboundMessages?.(e,l.newMessages)),await this.reportOutboundProjections(e,[...h.confirmedRecords,...h.manualReviewRecords,...u.sentRecords,...u.staleRecords,...u.failedRecords,...u.waitingRecords,...u.manualReviewRecords]),await this.options.api.reportRunStatus(this.options.runtime,e,{status:V(l.newMessages.length,h.confirmedRecords,h.manualReviewRecords,u.sentRecords,u.staleRecords,u.failedRecords,u.waitingRecords,u.manualReviewRecords),reasonCode:u.waitingRecords[0]?.deferReason}),a.push({bindingId:e.bindingId,observedCount:m.length,newInboundCount:l.newMessages.length,outboundSentCount:u.sentRecords.length,staleOutboundCount:u.staleRecords.length,failedOutboundCount:u.failedRecords.length,waitingOutboundCount:u.waitingRecords.length,confirmedEchoCount:h.confirmedRecords.length,manualReviewCount:h.manualReviewRecords.length+u.manualReviewRecords.length,revision:l.binding.revision})}return v(t,o),k(i,s),a}finally{this.running=!1}}}noteInterruption(t,i,o=new Date){const s=this.options.ledgerPath||R(this.options.workDir,this.options.runtime.runtimeId),a=C(s,this.options.runtime.runtimeId),c=a.bindings[t]??{bindingId:t,baselineEstablished:!1,revision:0,recent:[],pendingSendKeys:[]};c.cooldown=P({state:c.cooldown,reason:i,now:o}),a.bindings[t]=c,v(s,a)}async reportOutboundProjections(t,i){if(this.options.api.reportOutboundStatus)for(const o of i)await this.options.api.reportOutboundStatus(this.options.runtime,t,{replyId:o.replyId,idempotencyKey:o.idempotencyKey,status:o.sendStatus,replyBaseRevision:o.replyBaseRevision,sentAt:o.sentAt,confirmedAt:o.confirmedAt,failureCode:o.failureCode,lastErrorSummary:o.lastErrorSummary,nextAttemptAt:o.nextAttemptAt,attemptCount:o.attemptCount,commitStage:o.commitStage,copyableContentRef:o.copyableContentRef,copyableText:z(o)?o.text:void 0,agentVisible:O(o)?!1:void 0})}}function j(n){return!n||typeof n!="object"?!1:n.reasonCode==="insufficient_credits"?!0:n instanceof Error&&/\binsufficient_credits\b/.test(n.message)}function K(n,t){return n.foregroundPolicy==="work"||t.some(i=>i.sendStatus==="queued"||i.sendStatus==="sending"||i.sendStatus==="sent_unconfirmed")?"required":"background"}function F(n){return n==="conversation_not_visible_background"?"observe_deferred_window_unavailable":"baseline_or_no_change"}function q(n){const t=_(n);return t==="wechat_window_unavailable"||t==="wechat_window_not_found"||t==="conversation_not_visible_background"||t==="wechat_window_obstructed_background"}function _(n,t="unknown_error"){if(n&&typeof n=="object"){const s=n.reasonCode??n.errorCode;if(typeof s=="string"&&s.trim())return s.trim()}return(n instanceof Error?n.message:String(n||"")).match(/^([a-z0-9_./-]+)(?::|\b)/i)?.[1]||t}function $(n){return n instanceof Error&&/\bwechat_window_unresponsive\b/i.test(n.message)}function G(n,t){return!n?.baselineEstablished||n.disabledSince||!n.recent.length?!1:!t.some(i=>i.sendStatus==="queued"||i.sendStatus==="sending"||i.sendStatus==="sent_unconfirmed")}function z(n){return(n.sendStatus==="failed"||n.sendStatus==="manual_review")&&!!n.text}function R(n,t){return w.join(n,"wechat-channel",`${t}.ledger.json`)}function N(n,t){return w.join(n,"wechat-channel",`${t}.outbound-ledger.json`)}function Q(n){return n.at(-1)?.stableMessageKey||"empty"}function V(n,t,i,o=[],s=[],a=[],c=[],e=[]){return i.length>0||e.length>0?"manual_review":a.length>0?"outbound_send_failed":s.length>0?"outbound_stale":c.length>0?"send_waiting_user_idle":n>0?"new_messages_ingested":o.length>0?"outbound_sent_unconfirmed":t.length>0?"outbound_echo_confirmed":"baseline_or_no_change"}function H(n,t){return n.filter(i=>i.bindingId===t&&i.sendStatus==="sent_unconfirmed").map(i=>({replyId:i.replyId,idempotencyKey:i.idempotencyKey,expectedEchoAnchor:i.expectedEchoAnchor||i.textNormalized||i.text||""}))}function Y(n){const t=n.records.filter(s=>s.bindingId===n.bindingId&&s.sendStatus==="queued"),i=[],o=[];for(const s of t)A({record:s,reasonCode:n.reasonCode,nextAttemptAt:n.nextAttemptAt,now:n.now}),x({record:s,now:n.now,maxWaitMs:n.maxUserActivityWaitMs})?(W(s,n.now),i.push(s)):o.push(s);return{waitingRecords:o,failedRecords:i}}export{tn as WeChatChannelScheduler};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ExternalChannelAdapter, ExternalChannelConfig, ExternalChannelRuntimeStatus, ExternalChannelSendResult, ExternalMessageEvent, ExternalReply } from './base.js';
|
|
2
2
|
import { type WeChatRpaObservedMessage } from './wechat-rpa/normalizer.js';
|
|
3
|
+
import { type WeChatRpaProductRunnerFactory } from './wechat-rpa/product-channel.js';
|
|
3
4
|
type WeChatRpaSecret = {
|
|
4
5
|
type: 'wechat-rpa';
|
|
5
6
|
source?: 'wechat-channel' | 'macos-probe' | 'macos-flow' | 'windows-visual-flow' | 'wechat-rpa-lab' | 'fixture-jsonl';
|
|
@@ -27,10 +28,13 @@ type WeChatRpaEvent = ExternalMessageEvent & {
|
|
|
27
28
|
};
|
|
28
29
|
export declare class WeChatRpaChannelAdapter implements ExternalChannelAdapter {
|
|
29
30
|
private onMessage?;
|
|
31
|
+
private options;
|
|
30
32
|
readonly type: "wechat-rpa";
|
|
31
33
|
private secrets;
|
|
32
34
|
private connections;
|
|
33
|
-
constructor(onMessage?: ((event: WeChatRpaEvent) => ExternalMessageEvent | void) | undefined
|
|
35
|
+
constructor(onMessage?: ((event: WeChatRpaEvent) => ExternalMessageEvent | void) | undefined, options?: {
|
|
36
|
+
createProductRunner?: WeChatRpaProductRunnerFactory;
|
|
37
|
+
});
|
|
34
38
|
connect(config: ExternalChannelConfig): Promise<void>;
|
|
35
39
|
disconnect(config: ExternalChannelConfig): Promise<void>;
|
|
36
40
|
syncNow(config: ExternalChannelConfig): Promise<ExternalMessageEvent[]>;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import I from"node:crypto";import h from"node:fs";import m from"node:path";import{fileURLToPath as Y,pathToFileURL as Q}from"node:url";import{ChannelSecretRegistry as Z}from"./secret-registry.js";import{resolveShennianPath as ee}from"../config/index.js";import{probeMacWeChat as q,observedMessageFromProbe as te}from"./wechat-rpa/macos.js";import{runMacWeChatRpaFlow as D}from"./wechat-rpa/macos-flow.js";import{normalizeWeChatRpaMessage as re,WeChatRpaDeduper as F,weChatRpaConversationId as b}from"./wechat-rpa/normalizer.js";import{cancelWeChatRpaProductOutboundReply as ne,countPendingWeChatRpaProductOutbound as ae,createWeChatRpaProductSourceConnection as se,enqueueWeChatRpaProductOutboundReply as ie,isWeChatRpaProductSource as y,listWeChatRpaProductRecentMessages as oe,listPendingWeChatRpaProductOutbound as ue,weChatChannelConversationId as x}from"./wechat-rpa/product-channel.js";import{createWeChatChannelApiClient as ce}from"./wechat-channel/client.js";const de=5e3,A=5,le=8,S=Number(process.env.SHENNIAN_WECHAT_RPA_OUTBOUND_ATTACHMENT_MAX_BYTES||20*1024*1024),N=Number(process.env.SHENNIAN_WECHAT_RPA_INTERRUPTION_COOLDOWN_THRESHOLD||3),M=Number(process.env.SHENNIAN_WECHAT_RPA_INTERRUPTION_COOLDOWN_MS||300*1e3),me=10;function pe(t){return P(t,A,1,50)>=le?"scroll-read":"read-latest"}class nt{onMessage;type="wechat-rpa";secrets=new Z;connections=new Map;constructor(e){this.onMessage=e}async connect(e){if(!e.enabled)return;const r=this.readSecret(e),n=this.ensureConnection(e);if(n.stopped=!1,n.config=e,W(n,e),T(n,e),this.seedConfiguredConversations(n,r),y(r.source)){if(await this.ensureProductConnection(n,r),n.timer)return;await this.enqueueOperation(n,()=>this.pollProductOnce(n,this.readSecret(n.config))),n.timer=setInterval(()=>{this.enqueueOperation(n,()=>this.pollProductOnce(n,this.readSecret(n.config))).catch(()=>{})},n.product?.runtime.policy.pollIntervalMs??K(r.pollIntervalMs)),n.timer.unref();return}n.timer||(await this.enqueueOperation(n,()=>this.pollOnce(n,this.readSecret(n.config))),n.timer=setInterval(()=>{this.enqueueOperation(n,()=>this.pollOnce(n,this.readSecret(n.config))).catch(()=>{})},K(r.pollIntervalMs)),n.timer.unref())}async disconnect(e){const r=this.connections.get(e.id);r&&(r.stopped=!0,r.timer&&clearInterval(r.timer),r.product&&await r.product.runner.stop().catch(()=>{}),this.connections.delete(e.id))}async syncNow(e){if(!e.enabled)throw new Error("WeChat RPA channel is disabled");const r=this.readSecret(e),n=this.ensureConnection(e);return n.stopped=!1,n.config=e,W(n,e),T(n,e),this.seedConfiguredConversations(n,r),y(r.source)?(await this.ensureProductConnection(n,r),this.enqueueOperation(n,async()=>{const a=this.readSecret(n.config),i=await this.pollProductOnce(n,a),s=await this.ensureProductConnection(n,a),u=oe({config:n.config,product:s,limit:a.recentLimit});return je(u,i)})):this.enqueueOperation(n,()=>this.pollOnce(n,this.readSecret(n.config)))}async cancelOutbound(e,r){const n=this.readSecret(e);return y(n.source)?ne({config:e,idempotencyKey:r.idempotencyKey,replyId:r.replyId,reason:r.reason}):{cancelled:!1,status:"unsupported_source"}}async send(e,r){const n=this.readSecret(e);if(n.canReply===!1)throw new Error("WeChat RPA channel does not allow replies");const a=this.ensureConnection(e);if(a.config=e,W(a,e),T(a,e),this.seedConfiguredConversations(a,n),y(n.source)){const c=this.resolveConversationName(e,n,r.conversationId);if(!c)throw new Error(`Unknown WeChat RPA conversation: ${r.conversationId}`);const p=r.text.trim();if(!p&&!r.attachment)throw new Error("Reply text or attachment is required");return ie({config:e,secret:n,reply:{...r,text:p},conversationName:c}),a.runtimeState="retrying",g(a,{status:"sent",runId:a.lastRunId??null,summary:`queued ${c}`}),await this.ensureProductConnection(a,n),this.enqueueOperation(a,()=>this.pollProductOnce(a,this.readSecret(a.config))).catch(v=>{a.lastError=v instanceof Error?v.message:String(v)}),{status:"queued"}}if(!xe(n.source))throw new Error("WeChat RPA reply requires source=macos-flow, windows-visual-flow, or wechat-rpa-lab");const i=this.resolveConversationName(e,n,r.conversationId);if(!i)throw new Error(`Unknown WeChat RPA conversation: ${r.conversationId}`);const s=Ne(e,r);if(a.completedPendingReplyKeys.has(s))return{status:"sent"};const u=a.manualReviewReplies.get(s);if(u)return{status:"manual-review",reason:u.reason};const o=r.attachment?await Fe(e.workDir,r.attachment):void 0,f=r.text.trim();if(!f&&!o)throw new Error("Reply text or attachment is required");if(V(a,n)){const c="WeChat RPA is cooling down after repeated user activity";return a.runtimeState="cooldown",E(a,{key:s,conversationId:r.conversationId,conversationName:i,text:f,attachmentPath:o,reason:c}),{status:"queued",reason:c}}let d;try{a.runtimeState="syncing",d=await this.enqueueOperation(a,()=>(a.lastRunAt=new Date().toISOString(),J(e,n,i,f,o,s)))}catch(c){throw a.lastError=c instanceof Error?c.message:String(c),g(a,{status:k(a.lastError),runId:a.lastRunId??null,summary:a.lastError}),c}$(a,d);const l=X(d,{text:f,attachmentPath:o});if(l.status==="queued"){const c=l.reason;return E(a,{key:s,conversationId:r.conversationId,conversationName:i,text:l.text,attachmentPath:l.attachmentPath,reason:c,skipText:l.skipText}),a.lastError=c,{status:"queued",reason:c}}if(d.interrupted){const c=d.error||"WeChat RPA send was interrupted by user activity";return O(a,d,c),E(a,{key:s,conversationId:r.conversationId,conversationName:i,text:l.text,attachmentPath:o,reason:c,skipText:l.skipText}),{status:"queued",reason:c}}if(!d.ok){const c=d.error||"WeChat RPA send validator failed; manual review required before retry";return G(a,s,c),{status:"manual-review",reason:c}}return g(a,{status:"sent",runId:a.lastRunId??null,summary:d.rpaTraceSummary||`sent ${i}`}),C(a),a.lastError=null,{status:"sent"}}async health(e){const r=this.readSecret(e);if((r.source??"macos-probe")==="fixture-jsonl")return r.fixturePath&&h.existsSync(r.fixturePath)?{ok:!0}:{ok:!1,message:"WeChat RPA fixture file is missing"};if(r.source==="macos-flow"){if(process.platform!=="darwin")return{ok:!1,message:"WeChat RPA macOS flow requires macOS"};if(!w(r).length)return{ok:!1,message:"WeChat RPA macOS flow requires at least one group"}}if(y(r.source))return ye(r,process.platform);if(r.source==="wechat-rpa-lab"){if(process.platform!=="darwin")return{ok:!1,message:"WeChat RPA Lab source requires macOS for live runs"};if(!w(r).length)return{ok:!1,message:"WeChat RPA Lab source requires at least one group"};try{U(e.workDir)}catch(a){return{ok:!1,message:a instanceof Error?a.message:String(a)}}return{ok:!0,message:"WeChat RPA Lab source configured"}}if(r.source==="windows-visual-flow")return we(r,process.platform);const n=await q();return n.ok?{ok:!0,message:n.wechatRunning?"WeChat detected":"WeChat is not running"}:{ok:!1,message:n.message}}async defaultConversation(e){const r=this.readSecret(e),n=w(r)[0];if(!n)throw new Error("WeChat RPA channel has no configured groups");return y(r.source)?{conversationId:x(n),conversationName:n}:{conversationId:b(n),conversationName:n}}runtimeStatus(e){const r=this.connections.get(e.id);return r?{wechatRpaRuntimeState:r.runtimeState,wechatRpaLastRunAt:r.lastRunAt??null,wechatRpaLastMessageAt:r.lastMessageAt??null,wechatRpaLastInterruptedAt:r.lastInterruptedAt??null,wechatRpaLastError:r.lastError??null,wechatRpaLastRunId:r.lastRunId??null,wechatRpaLastTracePath:r.lastTracePath??null,wechatRpaLastTraceSummary:r.lastTraceSummary??null,wechatRpaPendingReplyCount:r.pendingReplies.size+ae({config:e}),wechatRpaPendingReplies:Me(r,e),wechatRpaRecentTaskSummaries:r.recentTaskSummaries}:{}}ensureConnection(e){let r=this.connections.get(e.id);return r||(r={config:e,timer:null,deduper:new F,stopped:!1,conversations:new Map,pendingReplies:new Map,manualReviewReplies:new Map,completedPendingReplyKeys:new Set,pendingStatePath:void 0,messageStatePath:void 0,operation:Promise.resolve(),recentTaskSummaries:[],runtimeState:"idle_waiting",consecutiveInterruptions:0},this.connections.set(e.id,r)),r}enqueueOperation(e,r){const n=e.operation.then(r,r);return e.operation=n.then(()=>{},()=>{}),n}readSecret(e){const r=this.secrets.get(e.secretRef);if(!r||r.type!=="wechat-rpa")throw new Error("WeChat RPA channel is not configured on this daemon");return r}async pollOnce(e,r){if(e.stopped)return[];const n=[];e.lastRunAt=new Date().toISOString();try{if(V(e,r))return e.runtimeState="cooldown",[];if(await this.drainPendingReplies(e,r))return[];let i=!1;e.runtimeState="syncing";const s=await this.readObservedMessages(e.config,r,u=>{$(e,u),u.interrupted&&(i=!0,O(e,u,u.error||"WeChat RPA poll was interrupted by user activity"))});e.lastError=null;for(const u of s){const o=re(u,{selfNicknames:B(r)});if(!o||!e.deduper.accept(o.messageId))continue;$e(e),e.conversations.set(o.conversationId,o.conversationName),e.lastMessageAt=o.receivedAt;const f={type:"external.message",managerSessionId:e.config.managerSessionId,channelId:e.config.id,channelType:"wechat-rpa",conversationId:o.conversationId,conversationName:o.conversationName,messageId:o.messageId,sender:o.sender,text:o.text,attachments:o.attachments,receivedAt:o.receivedAt,isMentioned:o.isMentioned,replyTarget:"",rawRef:o.rawRef};n.push(this.onMessage?.(f)??f),g(e,{status:"received",runId:e.lastRunId??null,summary:`received ${o.conversationName}${o.sender.name?` from ${o.sender.name}`:""}: ${L(o.text||o.attachments[0]?.name||"attachment")}`})}return i||C(e),n}catch(a){throw e.lastError=a instanceof Error?a.message:String(a),g(e,{status:k(e.lastError),runId:e.lastRunId??null,summary:e.lastError}),a}}async pollProductOnce(e,r){if(e.stopped)return[];if(!y(r.source))return[];const n=await this.ensureProductConnection(e,r),a=n.emitted.length;e.lastRunAt=new Date().toISOString(),e.runtimeState="syncing";try{await n.runner.tick();const i=n.emitted.slice(a);e.lastError=null;for(const s of i)e.conversations.set(s.conversationId,s.conversationName||""),e.lastMessageAt=s.receivedAt,g(e,{status:"received",runId:e.lastRunId??null,summary:`received ${s.conversationName||s.conversationId}${s.sender.name?` from ${s.sender.name}`:""}: ${L(s.text||s.attachments[0]?.name||"attachment")}`});return C(e),i}catch(i){throw e.lastError=i instanceof Error?i.message:String(i),e.runtimeState=k(e.lastError)==="blocked"?"interrupted":"idle_waiting",g(e,{status:k(e.lastError),runId:e.lastRunId??null,summary:e.lastError}),i}}async ensureProductConnection(e,r){const n=he(e.config,r);return e.product&&e.productConfigKey===n||(e.product&&await e.product.runner.stop().catch(()=>{}),e.product=se({config:e.config,secret:r,onMessage:this.onMessage}),Number.isFinite(r.pollIntervalMs)||await fe(e.product).catch(()=>{}),e.productConfigKey=n),e.product}async drainPendingReplies(e,r){if(!e.pendingReplies.size)return!1;for(const n of Array.from(e.pendingReplies.values())){e.runtimeState="retrying",n.attempts+=1,n.lastAttemptAt=new Date().toISOString(),R(e);const a=await J(e.config,r,n.conversationName,n.skipText?"":n.text,n.attachmentPath,n.key);$(e,a);const i=X(a,{text:n.skipText?"":n.text,attachmentPath:n.attachmentPath});if(i.status==="queued")return n.text=i.text,n.attachmentPath=i.attachmentPath,n.skipText=i.skipText,n.lastInterruptedReason=i.reason,e.lastError=i.reason,R(e),!0;if(a.interrupted){const s=a.error||"WeChat RPA pending send was interrupted by user activity";return n.text=i.text,n.attachmentPath=i.attachmentPath,n.skipText=i.skipText,n.lastInterruptedReason=s,O(e,a,s),R(e),!0}if(!a.ok){const s=a.error||"WeChat RPA pending send validator failed; manual review required before retry";return e.pendingReplies.delete(n.key),G(e,n.key,s),R(e),!0}e.pendingReplies.delete(n.key),e.completedPendingReplyKeys.add(n.key),R(e),e.lastError=null,g(e,{status:"sent",runId:e.lastRunId??null,summary:a.rpaTraceSummary||`sent ${n.conversationName}`}),C(e)}return!1}async readObservedMessages(e,r,n){if(r.source==="fixture-jsonl")return De(r.fixturePath);if(r.source==="macos-flow")return ke(e,r,n);if(r.source==="wechat-rpa-lab")return Re(e,r,n);if(r.source==="windows-visual-flow")return be(e,r,n);const a=await q(),i=te(a);return i?[i]:[]}seedConfiguredConversations(e,r){for(const n of w(r))e.conversations.set(b(n),n),e.conversations.set(x(n),n)}resolveConversationName(e,r,n){const i=this.connections.get(e.id)?.conversations.get(n);return i||(w(r).find(s=>b(s)===n||x(s)===n)??null)}}function he(t,e){return JSON.stringify({id:t.id,workDir:t.workDir,managerSessionId:t.managerSessionId,pollIntervalMs:e.pollIntervalMs,forceForeground:!!e.forceForeground,downloadAttachments:e.downloadAttachments!==!1,canReply:e.canReply!==!1,groups:w(e)})}async function fe(t){const e=await ce().getRuntimePolicy(),r=e.runtime?.scanPolicy??e.usagePolicy?.scanPolicy,n=r?.defaultPollIntervalSeconds??e.runtime?.defaultPollIntervalSeconds;if(!Number.isFinite(n))return;const a=Number.isFinite(r?.minPollIntervalSeconds)?Number(r?.minPollIntervalSeconds):e.runtime?.minPollIntervalSeconds,i=Number.isFinite(r?.maxPollIntervalSeconds)?Number(r?.maxPollIntervalSeconds):e.runtime?.maxPollIntervalSeconds,s=Number.isFinite(a)?Number(a)*1e3:t.runtime.policy.minPollIntervalMs,u=Number.isFinite(i)?Number(i)*1e3:t.runtime.policy.maxPollIntervalMs,o=Math.min(u,Math.max(s,Number(n)*1e3));t.runtime.policy.pollIntervalMs=o,t.runtime.policy.minPollIntervalMs=s,t.runtime.policy.maxPollIntervalMs=u}function K(t){return Number.isFinite(t)?Math.min(6e4,Math.max(1e3,Number(t))):de}function w(t){return Array.isArray(t.groups)?t.groups.map(e=>String(e?.name||"").trim()).filter(Boolean):[]}function B(t){return String(t.selfNickname||"").split(/[,\n,、]/).map(e=>e.trim()).filter(Boolean)}function ge(t,e){return e.length?e.some(r=>{const n=r.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");return new RegExp(`@\\s*${n}(?=$|\\s|[\uFF0C\u3002\uFF01\uFF1F,.!?:\uFF1A\uFF1B;\u3001)])`).test(t)}):!1}function we(t,e=process.platform){return e!=="win32"?{ok:!1,message:"WeChat RPA Windows visual flow requires Windows"}:{ok:!1,message:"WeChat RPA Windows visual flow is archived; redesign Windows support before enabling it"}}function ye(t,e=process.platform){return e!=="darwin"&&e!=="win32"?{ok:!1,message:"WeChat channel product source requires macOS or Windows"}:(Array.isArray(t.groups)?t.groups.map(n=>String(n?.name||"").trim()).filter(Boolean):[]).length?{ok:!0,message:"WeChat channel product source configured"}:{ok:!1,message:"WeChat channel product source requires at least one group"}}async function Re(t,e,r){const n=await H(t.workDir),a=[];for(const i of w(e)){const s=await ve(n,t,e,i);if(r?.(s),!(!s.ok||s.interrupted))for(const u of s.newMessages??[]){const o=z(i,u,e);o&&a.push(o)}}return a}async function ve(t,e,r,n){const a=pe(r.recentLimit),i={kind:a,requestId:`wechat-rpa:${n}:read:${Date.now()}`,targetGroup:n,policy:r.forceForeground?"work":"polite",limit:P(r.recentLimit,A,1,50),attachmentsDir:_(e,r)},s=await t.runWechatRpaReadLatest(i),u=Array.isArray(s.data?.structuredMessages)?s.data.structuredMessages:[],o=s.validation?.deterministic?.latestMessages??[],f=u.length?u.map((d,l)=>Se(d,s.runId||"lab-read",l)):o.map((d,l)=>({id:`${s.runId||"lab-read"}:${l}`,text:d,confidence:.8}));return{ok:!!s.ok,groupName:n,interrupted:s.status==="interrupted",reason:s.status,rpaRunId:s.runId,rpaTraceSummary:`${a} ${s.status||(s.ok?"success":"failed")} ${n}`,recentMessages:f,newMessages:f,screenshotPath:s.tracePath,error:j(s)}}function Se(t,e,r){return{id:`${e}:${t.index??r}`,text:String(t.text||t.card?.title||"").trim(),confidence:typeof t.confidence=="number"?t.confidence:.75,senderName:typeof t.senderName=="string"?t.senderName:null,attachments:Pe(t)}}function Pe(t){const e=String(t.kind||"");if(!["image","file","video-file","video-card","link-card","official-account-card","mini-program-card"].includes(e))return[];const r=typeof t.localPath=="string"?t.localPath:void 0;return[{type:e==="video-file"||e==="video-card"?"video":e==="link-card"||e==="official-account-card"||e==="mini-program-card"?"file":e,localPath:r,availability:r?"edge-local":"metadata-only"}]}async function Ie(t,e,r,n,a,i){const s=await H(t.workDir),u=Ae({groupName:r,text:n,attachmentPath:a,dedupeKey:i,requestId:i,policy:"work"}),o=[];for(const p of u){const v=await s.runWechatRpaTask(p);if(o.push(v),!v.ok)break}const f=o.find(p=>p.kind==="send-text"),d=o.find(p=>p.kind==="send-image"||p.kind==="send-video"||p.kind==="send-file"),l=u.length>0&&o.length===u.length&&o.every(p=>p.ok),c=o.some(p=>p.status==="interrupted");return{ok:l,groupName:r,interrupted:c,reason:o.at(-1)?.status,rpaRunId:o.at(-1)?.runId,rpaTraceSummary:`send ${l?"success":o.at(-1)?.status||"failed"} ${r} (${o.length}/${u.length})`,sentReply:!!n,sentReplyObserved:n?!!f?.ok:!1,sentAttachment:!!a,sentAttachmentObserved:a?!!d?.ok:!1,postSendScreenshotPath:o.at(-1)?.tracePath,error:l?void 0:j(o.at(-1))}}function Ae(t){const e=[],r=String(t.text||"").trim(),n=t.policy||"work";if(r&&e.push({kind:"send-text",requestId:t.requestId?`${t.requestId}:text`:void 0,targetGroup:t.groupName,policy:n,text:r,...t.dedupeKey?{dedupeKey:`${t.dedupeKey}:text`}:{}}),t.attachmentPath){const a=Ce(t.attachmentPath);e.push({kind:a,requestId:t.requestId?`${t.requestId}:attachment`:void 0,targetGroup:t.groupName,policy:n,filePath:t.attachmentPath,...t.dedupeKey?{dedupeKey:`${t.dedupeKey}:attachment`}:{}})}return e}function Ce(t){const e=m.extname(t).toLowerCase();return[".png",".jpg",".jpeg",".gif",".webp",".heic"].includes(e)?"send-image":[".mp4",".mov",".avi",".mkv"].includes(e)?"send-video":"send-file"}async function H(t){const e=U(t);return await import(Q(e).href)}function U(t){const e=m.dirname(Y(import.meta.url)),n=[process.env.SHENNIAN_WECHAT_RPA_LAB_INDEX,m.resolve(e,"../../../../scripts/wechat-rpa-lab/index.mjs"),t?m.join(t,"scripts/wechat-rpa-lab/index.mjs"):"",m.resolve(process.cwd(),"scripts/wechat-rpa-lab/index.mjs")].filter(Boolean).find(a=>h.existsSync(a));if(!n)throw new Error("WeChat RPA Lab API is missing; set SHENNIAN_WECHAT_RPA_LAB_INDEX or run from the repository root");return m.resolve(n)}function j(t){return t?typeof t.error=="string"?t.error:t.error?.message||(t.ok?void 0:`WeChat RPA Lab task ${t.kind||"<unknown>"} ${t.status||"failed"}`):"WeChat RPA Lab did not return a result"}async function ke(t,e,r){const n=[];for(const a of w(e)){const i=await D({groupName:a,scriptPath:e.flowScriptPath,workDir:t.workDir,forceForeground:!!e.forceForeground,noRestore:e.noRestore!==!1,idleSeconds:P(e.idleSeconds,15,0,3600),recentLimit:P(e.recentLimit,A,0,50),downloadAttachmentsDir:_(t,e)});if(r?.(i),!i.interrupted)for(const s of i.newMessages??[]){const u=z(a,s,e);u&&n.push(u)}}return n}async function be(t,e,r){const n=w(e)[0]||"<unbound>";return r?.({ok:!1,groupName:n,interrupted:!0,reason:"windows-visual-flow-archived",newMessages:[],recentMessages:[],error:"WeChat RPA Windows visual flow is archived; redesign Windows support before enabling it"}),[]}function z(t,e,r){const n=String(e.text||"").trim(),a=Array.isArray(e.attachments)?e.attachments:[];if(!n&&a.length===0)return null;const i=typeof e.senderName=="string"?e.senderName.trim():"",s=String(e.observedAt||e.timestampIso||"").trim()||new Date().toISOString();return{conversationName:t,senderName:i||null,text:n,attachments:Ue(a),observedAt:s,isMentioned:e.isMentioned===!0||ge(n,B(r)),rawId:String(e.id||`${t}:${i}:${n}:${s}`)}}async function J(t,e,r,n,a,i){return e.source==="windows-visual-flow"?{ok:!1,groupName:r,interrupted:!0,reason:"windows-visual-flow-archived",newMessages:[],recentMessages:[],error:"WeChat RPA Windows visual flow is archived; redesign Windows support before enabling it"}:e.source==="wechat-rpa-lab"?Ie(t,e,r,n,a,i):D({groupName:r,replyText:n||void 0,attachmentPath:a,scriptPath:e.flowScriptPath,workDir:t.workDir,forceForeground:!!e.forceForeground,noRestore:e.noRestore!==!1,idleSeconds:P(e.idleSeconds,15,0,3600),recentLimit:P(e.recentLimit,A,0,50),downloadAttachmentsDir:_(t,e)})}function xe(t){return t==="macos-flow"||t==="wechat-rpa-lab"}function X(t,e){return e.text&&e.attachmentPath&&t.sentReplyObserved&&!t.sentAttachmentObserved?{status:t.interrupted?"continue":"queued",text:"",attachmentPath:e.attachmentPath,skipText:!0,reason:t.error||"WeChat RPA sent the text but did not confirm the attachment; queued attachment-only retry"}:{status:"continue",text:e.text,attachmentPath:e.attachmentPath,reason:t.error||""}}function Ne(t,e){return e.idempotencyKey?e.idempotencyKey:I.createHash("sha256").update(`${t.id}
|
|
1
|
+
import I from"node:crypto";import h from"node:fs";import p from"node:path";import{fileURLToPath as Y,pathToFileURL as Q}from"node:url";import{ChannelSecretRegistry as Z}from"./secret-registry.js";import{resolveShennianPath as ee}from"../config/index.js";import{probeMacWeChat as q,observedMessageFromProbe as te}from"./wechat-rpa/macos.js";import{runMacWeChatRpaFlow as D}from"./wechat-rpa/macos-flow.js";import{normalizeWeChatRpaMessage as re,WeChatRpaDeduper as F,weChatRpaConversationId as b}from"./wechat-rpa/normalizer.js";import{cancelWeChatRpaProductOutboundReply as ne,countPendingWeChatRpaProductOutbound as ae,createWeChatRpaProductSourceConnection as se,enqueueWeChatRpaProductOutboundReply as ie,isWeChatRpaProductSource as y,listWeChatRpaProductRecentMessages as oe,listPendingWeChatRpaProductOutbound as ue,weChatChannelConversationId as x}from"./wechat-rpa/product-channel.js";import{createWeChatChannelApiClient as ce}from"./wechat-channel/client.js";const de=5e3,A=5,le=8,S=Number(process.env.SHENNIAN_WECHAT_RPA_OUTBOUND_ATTACHMENT_MAX_BYTES||20*1024*1024),N=Number(process.env.SHENNIAN_WECHAT_RPA_INTERRUPTION_COOLDOWN_THRESHOLD||3),M=Number(process.env.SHENNIAN_WECHAT_RPA_INTERRUPTION_COOLDOWN_MS||300*1e3),pe=10;function me(t){return P(t,A,1,50)>=le?"scroll-read":"read-latest"}class nt{onMessage;options;type="wechat-rpa";secrets=new Z;connections=new Map;constructor(e,r={}){this.onMessage=e,this.options=r}async connect(e){if(!e.enabled)return;const r=this.readSecret(e),n=this.ensureConnection(e);if(n.stopped=!1,n.config=e,W(n,e),T(n,e),this.seedConfiguredConversations(n,r),y(r.source)){if(await this.ensureProductConnection(n,r),n.timer)return;await this.enqueueOperation(n,()=>this.pollProductOnce(n,this.readSecret(n.config))),n.timer=setInterval(()=>{this.enqueueOperation(n,()=>this.pollProductOnce(n,this.readSecret(n.config))).catch(()=>{})},n.product?.runtime.policy.pollIntervalMs??K(r.pollIntervalMs)),n.timer.unref();return}n.timer||(await this.enqueueOperation(n,()=>this.pollOnce(n,this.readSecret(n.config))),n.timer=setInterval(()=>{this.enqueueOperation(n,()=>this.pollOnce(n,this.readSecret(n.config))).catch(()=>{})},K(r.pollIntervalMs)),n.timer.unref())}async disconnect(e){const r=this.connections.get(e.id);r&&(r.stopped=!0,r.timer&&clearInterval(r.timer),r.product&&await r.product.runner.stop().catch(()=>{}),this.connections.delete(e.id))}async syncNow(e){if(!e.enabled)throw new Error("WeChat RPA channel is disabled");const r=this.readSecret(e),n=this.ensureConnection(e);return n.stopped=!1,n.config=e,W(n,e),T(n,e),this.seedConfiguredConversations(n,r),y(r.source)?(await this.ensureProductConnection(n,r),this.enqueueOperation(n,async()=>{const a=this.readSecret(n.config),i=await this.pollProductOnce(n,a),s=await this.ensureProductConnection(n,a),u=oe({config:n.config,product:s,limit:a.recentLimit});return je(u,i)})):this.enqueueOperation(n,()=>this.pollOnce(n,this.readSecret(n.config)))}async cancelOutbound(e,r){const n=this.readSecret(e);return y(n.source)?ne({config:e,idempotencyKey:r.idempotencyKey,replyId:r.replyId,reason:r.reason}):{cancelled:!1,status:"unsupported_source"}}async send(e,r){const n=this.readSecret(e);if(n.canReply===!1)throw new Error("WeChat RPA channel does not allow replies");const a=this.ensureConnection(e);if(a.config=e,W(a,e),T(a,e),this.seedConfiguredConversations(a,n),y(n.source)){const c=this.resolveConversationName(e,n,r.conversationId);if(!c)throw new Error(`Unknown WeChat RPA conversation: ${r.conversationId}`);const m=r.text.trim();if(!m&&!r.attachment)throw new Error("Reply text or attachment is required");return ie({config:e,secret:n,reply:{...r,text:m},conversationName:c}),a.runtimeState="retrying",g(a,{status:"sent",runId:a.lastRunId??null,summary:`queued ${c}`}),await this.ensureProductConnection(a,n),this.enqueueOperation(a,()=>this.pollProductOnce(a,this.readSecret(a.config))).catch(v=>{a.lastError=v instanceof Error?v.message:String(v)}),{status:"queued"}}if(!xe(n.source))throw new Error("WeChat RPA reply requires source=macos-flow, windows-visual-flow, or wechat-rpa-lab");const i=this.resolveConversationName(e,n,r.conversationId);if(!i)throw new Error(`Unknown WeChat RPA conversation: ${r.conversationId}`);const s=Ne(e,r);if(a.completedPendingReplyKeys.has(s))return{status:"sent"};const u=a.manualReviewReplies.get(s);if(u)return{status:"manual-review",reason:u.reason};const o=r.attachment?await Fe(e.workDir,r.attachment):void 0,f=r.text.trim();if(!f&&!o)throw new Error("Reply text or attachment is required");if(V(a,n)){const c="WeChat RPA is cooling down after repeated user activity";return a.runtimeState="cooldown",E(a,{key:s,conversationId:r.conversationId,conversationName:i,text:f,attachmentPath:o,reason:c}),{status:"queued",reason:c}}let d;try{a.runtimeState="syncing",d=await this.enqueueOperation(a,()=>(a.lastRunAt=new Date().toISOString(),J(e,n,i,f,o,s)))}catch(c){throw a.lastError=c instanceof Error?c.message:String(c),g(a,{status:k(a.lastError),runId:a.lastRunId??null,summary:a.lastError}),c}$(a,d);const l=X(d,{text:f,attachmentPath:o});if(l.status==="queued"){const c=l.reason;return E(a,{key:s,conversationId:r.conversationId,conversationName:i,text:l.text,attachmentPath:l.attachmentPath,reason:c,skipText:l.skipText}),a.lastError=c,{status:"queued",reason:c}}if(d.interrupted){const c=d.error||"WeChat RPA send was interrupted by user activity";return O(a,d,c),E(a,{key:s,conversationId:r.conversationId,conversationName:i,text:l.text,attachmentPath:o,reason:c,skipText:l.skipText}),{status:"queued",reason:c}}if(!d.ok){const c=d.error||"WeChat RPA send validator failed; manual review required before retry";return G(a,s,c),{status:"manual-review",reason:c}}return g(a,{status:"sent",runId:a.lastRunId??null,summary:d.rpaTraceSummary||`sent ${i}`}),C(a),a.lastError=null,{status:"sent"}}async health(e){const r=this.readSecret(e);if((r.source??"macos-probe")==="fixture-jsonl")return r.fixturePath&&h.existsSync(r.fixturePath)?{ok:!0}:{ok:!1,message:"WeChat RPA fixture file is missing"};if(r.source==="macos-flow"){if(process.platform!=="darwin")return{ok:!1,message:"WeChat RPA macOS flow requires macOS"};if(!w(r).length)return{ok:!1,message:"WeChat RPA macOS flow requires at least one group"}}if(y(r.source))return ye(r,process.platform);if(r.source==="wechat-rpa-lab"){if(process.platform!=="darwin")return{ok:!1,message:"WeChat RPA Lab source requires macOS for live runs"};if(!w(r).length)return{ok:!1,message:"WeChat RPA Lab source requires at least one group"};try{U(e.workDir)}catch(a){return{ok:!1,message:a instanceof Error?a.message:String(a)}}return{ok:!0,message:"WeChat RPA Lab source configured"}}if(r.source==="windows-visual-flow")return we(r,process.platform);const n=await q();return n.ok?{ok:!0,message:n.wechatRunning?"WeChat detected":"WeChat is not running"}:{ok:!1,message:n.message}}async defaultConversation(e){const r=this.readSecret(e),n=w(r)[0];if(!n)throw new Error("WeChat RPA channel has no configured groups");return y(r.source)?{conversationId:x(n),conversationName:n}:{conversationId:b(n),conversationName:n}}runtimeStatus(e){const r=this.connections.get(e.id);return r?{wechatRpaRuntimeState:r.runtimeState,wechatRpaLastRunAt:r.lastRunAt??null,wechatRpaLastMessageAt:r.lastMessageAt??null,wechatRpaLastInterruptedAt:r.lastInterruptedAt??null,wechatRpaLastError:r.lastError??null,wechatRpaLastRunId:r.lastRunId??null,wechatRpaLastTracePath:r.lastTracePath??null,wechatRpaLastTraceSummary:r.lastTraceSummary??null,wechatRpaPendingReplyCount:r.pendingReplies.size+ae({config:e}),wechatRpaPendingReplies:Me(r,e),wechatRpaRecentTaskSummaries:r.recentTaskSummaries}:{}}ensureConnection(e){let r=this.connections.get(e.id);return r||(r={config:e,timer:null,deduper:new F,stopped:!1,conversations:new Map,pendingReplies:new Map,manualReviewReplies:new Map,completedPendingReplyKeys:new Set,pendingStatePath:void 0,messageStatePath:void 0,operation:Promise.resolve(),recentTaskSummaries:[],runtimeState:"idle_waiting",consecutiveInterruptions:0},this.connections.set(e.id,r)),r}enqueueOperation(e,r){const n=e.operation.then(r,r);return e.operation=n.then(()=>{},()=>{}),n}readSecret(e){const r=this.secrets.get(e.secretRef);if(!r||r.type!=="wechat-rpa")throw new Error("WeChat RPA channel is not configured on this daemon");return r}async pollOnce(e,r){if(e.stopped)return[];const n=[];e.lastRunAt=new Date().toISOString();try{if(V(e,r))return e.runtimeState="cooldown",[];if(await this.drainPendingReplies(e,r))return[];let i=!1;e.runtimeState="syncing";const s=await this.readObservedMessages(e.config,r,u=>{$(e,u),u.interrupted&&(i=!0,O(e,u,u.error||"WeChat RPA poll was interrupted by user activity"))});e.lastError=null;for(const u of s){const o=re(u,{selfNicknames:B(r)});if(!o||!e.deduper.accept(o.messageId))continue;$e(e),e.conversations.set(o.conversationId,o.conversationName),e.lastMessageAt=o.receivedAt;const f={type:"external.message",managerSessionId:e.config.managerSessionId,channelId:e.config.id,channelType:"wechat-rpa",conversationId:o.conversationId,conversationName:o.conversationName,messageId:o.messageId,sender:o.sender,text:o.text,attachments:o.attachments,receivedAt:o.receivedAt,isMentioned:o.isMentioned,replyTarget:"",rawRef:o.rawRef};n.push(this.onMessage?.(f)??f),g(e,{status:"received",runId:e.lastRunId??null,summary:`received ${o.conversationName}${o.sender.name?` from ${o.sender.name}`:""}: ${L(o.text||o.attachments[0]?.name||"attachment")}`})}return i||C(e),n}catch(a){throw e.lastError=a instanceof Error?a.message:String(a),g(e,{status:k(e.lastError),runId:e.lastRunId??null,summary:e.lastError}),a}}async pollProductOnce(e,r){if(e.stopped)return[];if(!y(r.source))return[];const n=await this.ensureProductConnection(e,r),a=n.emitted.length;e.lastRunAt=new Date().toISOString(),e.runtimeState="syncing";try{await n.runner.tick();const i=n.emitted.slice(a);e.lastError=null;for(const s of i)e.conversations.set(s.conversationId,s.conversationName||""),e.lastMessageAt=s.receivedAt,g(e,{status:"received",runId:e.lastRunId??null,summary:`received ${s.conversationName||s.conversationId}${s.sender.name?` from ${s.sender.name}`:""}: ${L(s.text||s.attachments[0]?.name||"attachment")}`});return C(e),i}catch(i){throw e.lastError=i instanceof Error?i.message:String(i),e.runtimeState=k(e.lastError)==="blocked"?"interrupted":"idle_waiting",g(e,{status:k(e.lastError),runId:e.lastRunId??null,summary:e.lastError}),i}}async ensureProductConnection(e,r){const n=he(e.config,r);return e.product&&e.productConfigKey===n||(e.product&&await e.product.runner.stop().catch(()=>{}),e.product=se({config:e.config,secret:r,createRunner:this.options.createProductRunner,onMessage:this.onMessage}),Number.isFinite(r.pollIntervalMs)||await fe(e.product).catch(()=>{}),e.productConfigKey=n),e.product}async drainPendingReplies(e,r){if(!e.pendingReplies.size)return!1;for(const n of Array.from(e.pendingReplies.values())){e.runtimeState="retrying",n.attempts+=1,n.lastAttemptAt=new Date().toISOString(),R(e);const a=await J(e.config,r,n.conversationName,n.skipText?"":n.text,n.attachmentPath,n.key);$(e,a);const i=X(a,{text:n.skipText?"":n.text,attachmentPath:n.attachmentPath});if(i.status==="queued")return n.text=i.text,n.attachmentPath=i.attachmentPath,n.skipText=i.skipText,n.lastInterruptedReason=i.reason,e.lastError=i.reason,R(e),!0;if(a.interrupted){const s=a.error||"WeChat RPA pending send was interrupted by user activity";return n.text=i.text,n.attachmentPath=i.attachmentPath,n.skipText=i.skipText,n.lastInterruptedReason=s,O(e,a,s),R(e),!0}if(!a.ok){const s=a.error||"WeChat RPA pending send validator failed; manual review required before retry";return e.pendingReplies.delete(n.key),G(e,n.key,s),R(e),!0}e.pendingReplies.delete(n.key),e.completedPendingReplyKeys.add(n.key),R(e),e.lastError=null,g(e,{status:"sent",runId:e.lastRunId??null,summary:a.rpaTraceSummary||`sent ${n.conversationName}`}),C(e)}return!1}async readObservedMessages(e,r,n){if(r.source==="fixture-jsonl")return De(r.fixturePath);if(r.source==="macos-flow")return ke(e,r,n);if(r.source==="wechat-rpa-lab")return Re(e,r,n);if(r.source==="windows-visual-flow")return be(e,r,n);const a=await q(),i=te(a);return i?[i]:[]}seedConfiguredConversations(e,r){for(const n of w(r))e.conversations.set(b(n),n),e.conversations.set(x(n),n)}resolveConversationName(e,r,n){const i=this.connections.get(e.id)?.conversations.get(n);return i||(w(r).find(s=>b(s)===n||x(s)===n)??null)}}function he(t,e){return JSON.stringify({id:t.id,workDir:t.workDir,managerSessionId:t.managerSessionId,pollIntervalMs:e.pollIntervalMs,forceForeground:!!e.forceForeground,downloadAttachments:e.downloadAttachments!==!1,canReply:e.canReply!==!1,groups:w(e)})}async function fe(t){const e=await ce().getRuntimePolicy(),r=e.runtime?.scanPolicy??e.usagePolicy?.scanPolicy,n=r?.defaultPollIntervalSeconds??e.runtime?.defaultPollIntervalSeconds;if(!Number.isFinite(n))return;const a=Number.isFinite(r?.minPollIntervalSeconds)?Number(r?.minPollIntervalSeconds):e.runtime?.minPollIntervalSeconds,i=Number.isFinite(r?.maxPollIntervalSeconds)?Number(r?.maxPollIntervalSeconds):e.runtime?.maxPollIntervalSeconds,s=Number.isFinite(a)?Number(a)*1e3:t.runtime.policy.minPollIntervalMs,u=Number.isFinite(i)?Number(i)*1e3:t.runtime.policy.maxPollIntervalMs,o=Math.min(u,Math.max(s,Number(n)*1e3));t.runtime.policy.pollIntervalMs=o,t.runtime.policy.minPollIntervalMs=s,t.runtime.policy.maxPollIntervalMs=u}function K(t){return Number.isFinite(t)?Math.min(6e4,Math.max(1e3,Number(t))):de}function w(t){return Array.isArray(t.groups)?t.groups.map(e=>String(e?.name||"").trim()).filter(Boolean):[]}function B(t){return String(t.selfNickname||"").split(/[,\n,、]/).map(e=>e.trim()).filter(Boolean)}function ge(t,e){return e.length?e.some(r=>{const n=r.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");return new RegExp(`@\\s*${n}(?=$|\\s|[\uFF0C\u3002\uFF01\uFF1F,.!?:\uFF1A\uFF1B;\u3001)])`).test(t)}):!1}function we(t,e=process.platform){return e!=="win32"?{ok:!1,message:"WeChat RPA Windows visual flow requires Windows"}:{ok:!1,message:"WeChat RPA Windows visual flow is archived; redesign Windows support before enabling it"}}function ye(t,e=process.platform){return e!=="darwin"&&e!=="win32"?{ok:!1,message:"WeChat channel product source requires macOS or Windows"}:(Array.isArray(t.groups)?t.groups.map(n=>String(n?.name||"").trim()).filter(Boolean):[]).length?{ok:!0,message:"WeChat channel product source configured"}:{ok:!1,message:"WeChat channel product source requires at least one group"}}async function Re(t,e,r){const n=await H(t.workDir),a=[];for(const i of w(e)){const s=await ve(n,t,e,i);if(r?.(s),!(!s.ok||s.interrupted))for(const u of s.newMessages??[]){const o=z(i,u,e);o&&a.push(o)}}return a}async function ve(t,e,r,n){const a=me(r.recentLimit),i={kind:a,requestId:`wechat-rpa:${n}:read:${Date.now()}`,targetGroup:n,policy:r.forceForeground?"work":"polite",limit:P(r.recentLimit,A,1,50),attachmentsDir:_(e,r)},s=await t.runWechatRpaReadLatest(i),u=Array.isArray(s.data?.structuredMessages)?s.data.structuredMessages:[],o=s.validation?.deterministic?.latestMessages??[],f=u.length?u.map((d,l)=>Se(d,s.runId||"lab-read",l)):o.map((d,l)=>({id:`${s.runId||"lab-read"}:${l}`,text:d,confidence:.8}));return{ok:!!s.ok,groupName:n,interrupted:s.status==="interrupted",reason:s.status,rpaRunId:s.runId,rpaTraceSummary:`${a} ${s.status||(s.ok?"success":"failed")} ${n}`,recentMessages:f,newMessages:f,screenshotPath:s.tracePath,error:j(s)}}function Se(t,e,r){return{id:`${e}:${t.index??r}`,text:String(t.text||t.card?.title||"").trim(),confidence:typeof t.confidence=="number"?t.confidence:.75,senderName:typeof t.senderName=="string"?t.senderName:null,attachments:Pe(t)}}function Pe(t){const e=String(t.kind||"");if(!["image","file","video-file","video-card","link-card","official-account-card","mini-program-card"].includes(e))return[];const r=typeof t.localPath=="string"?t.localPath:void 0;return[{type:e==="video-file"||e==="video-card"?"video":e==="link-card"||e==="official-account-card"||e==="mini-program-card"?"file":e,localPath:r,availability:r?"edge-local":"metadata-only"}]}async function Ie(t,e,r,n,a,i){const s=await H(t.workDir),u=Ae({groupName:r,text:n,attachmentPath:a,dedupeKey:i,requestId:i,policy:"work"}),o=[];for(const m of u){const v=await s.runWechatRpaTask(m);if(o.push(v),!v.ok)break}const f=o.find(m=>m.kind==="send-text"),d=o.find(m=>m.kind==="send-image"||m.kind==="send-video"||m.kind==="send-file"),l=u.length>0&&o.length===u.length&&o.every(m=>m.ok),c=o.some(m=>m.status==="interrupted");return{ok:l,groupName:r,interrupted:c,reason:o.at(-1)?.status,rpaRunId:o.at(-1)?.runId,rpaTraceSummary:`send ${l?"success":o.at(-1)?.status||"failed"} ${r} (${o.length}/${u.length})`,sentReply:!!n,sentReplyObserved:n?!!f?.ok:!1,sentAttachment:!!a,sentAttachmentObserved:a?!!d?.ok:!1,postSendScreenshotPath:o.at(-1)?.tracePath,error:l?void 0:j(o.at(-1))}}function Ae(t){const e=[],r=String(t.text||"").trim(),n=t.policy||"work";if(r&&e.push({kind:"send-text",requestId:t.requestId?`${t.requestId}:text`:void 0,targetGroup:t.groupName,policy:n,text:r,...t.dedupeKey?{dedupeKey:`${t.dedupeKey}:text`}:{}}),t.attachmentPath){const a=Ce(t.attachmentPath);e.push({kind:a,requestId:t.requestId?`${t.requestId}:attachment`:void 0,targetGroup:t.groupName,policy:n,filePath:t.attachmentPath,...t.dedupeKey?{dedupeKey:`${t.dedupeKey}:attachment`}:{}})}return e}function Ce(t){const e=p.extname(t).toLowerCase();return[".png",".jpg",".jpeg",".gif",".webp",".heic"].includes(e)?"send-image":[".mp4",".mov",".avi",".mkv"].includes(e)?"send-video":"send-file"}async function H(t){const e=U(t);return await import(Q(e).href)}function U(t){const e=p.dirname(Y(import.meta.url)),n=[process.env.SHENNIAN_WECHAT_RPA_LAB_INDEX,p.resolve(e,"../../../../scripts/wechat-rpa-lab/index.mjs"),t?p.join(t,"scripts/wechat-rpa-lab/index.mjs"):"",p.resolve(process.cwd(),"scripts/wechat-rpa-lab/index.mjs")].filter(Boolean).find(a=>h.existsSync(a));if(!n)throw new Error("WeChat RPA Lab API is missing; set SHENNIAN_WECHAT_RPA_LAB_INDEX or run from the repository root");return p.resolve(n)}function j(t){return t?typeof t.error=="string"?t.error:t.error?.message||(t.ok?void 0:`WeChat RPA Lab task ${t.kind||"<unknown>"} ${t.status||"failed"}`):"WeChat RPA Lab did not return a result"}async function ke(t,e,r){const n=[];for(const a of w(e)){const i=await D({groupName:a,scriptPath:e.flowScriptPath,workDir:t.workDir,forceForeground:!!e.forceForeground,noRestore:e.noRestore!==!1,idleSeconds:P(e.idleSeconds,15,0,3600),recentLimit:P(e.recentLimit,A,0,50),downloadAttachmentsDir:_(t,e)});if(r?.(i),!i.interrupted)for(const s of i.newMessages??[]){const u=z(a,s,e);u&&n.push(u)}}return n}async function be(t,e,r){const n=w(e)[0]||"<unbound>";return r?.({ok:!1,groupName:n,interrupted:!0,reason:"windows-visual-flow-archived",newMessages:[],recentMessages:[],error:"WeChat RPA Windows visual flow is archived; redesign Windows support before enabling it"}),[]}function z(t,e,r){const n=String(e.text||"").trim(),a=Array.isArray(e.attachments)?e.attachments:[];if(!n&&a.length===0)return null;const i=typeof e.senderName=="string"?e.senderName.trim():"",s=String(e.observedAt||e.timestampIso||"").trim()||new Date().toISOString();return{conversationName:t,senderName:i||null,text:n,attachments:Ue(a),observedAt:s,isMentioned:e.isMentioned===!0||ge(n,B(r)),rawId:String(e.id||`${t}:${i}:${n}:${s}`)}}async function J(t,e,r,n,a,i){return e.source==="windows-visual-flow"?{ok:!1,groupName:r,interrupted:!0,reason:"windows-visual-flow-archived",newMessages:[],recentMessages:[],error:"WeChat RPA Windows visual flow is archived; redesign Windows support before enabling it"}:e.source==="wechat-rpa-lab"?Ie(t,e,r,n,a,i):D({groupName:r,replyText:n||void 0,attachmentPath:a,scriptPath:e.flowScriptPath,workDir:t.workDir,forceForeground:!!e.forceForeground,noRestore:e.noRestore!==!1,idleSeconds:P(e.idleSeconds,15,0,3600),recentLimit:P(e.recentLimit,A,0,50),downloadAttachmentsDir:_(t,e)})}function xe(t){return t==="macos-flow"||t==="wechat-rpa-lab"}function X(t,e){return e.text&&e.attachmentPath&&t.sentReplyObserved&&!t.sentAttachmentObserved?{status:t.interrupted?"continue":"queued",text:"",attachmentPath:e.attachmentPath,skipText:!0,reason:t.error||"WeChat RPA sent the text but did not confirm the attachment; queued attachment-only retry"}:{status:"continue",text:e.text,attachmentPath:e.attachmentPath,reason:t.error||""}}function Ne(t,e){return e.idempotencyKey?e.idempotencyKey:I.createHash("sha256").update(`${t.id}
|
|
2
2
|
${e.conversationId}
|
|
3
3
|
${e.text}
|
|
4
4
|
${e.attachment?.name||""}
|
|
5
|
-
${e.attachment?.size||0}`).digest("hex").slice(0,24)}function Me(t,e){return[...Array.from(t.pendingReplies.values()).map(n=>({replyId:n.key,idempotencyKey:n.key,status:"queued",conversationName:n.conversationName,reasonCode:n.lastInterruptedReason??null,nextAttemptAt:null,queuedAt:n.queuedAt,lastAttemptAt:n.lastAttemptAt??null,attemptCount:n.attempts,commitStage:null})),...ue({config:e})].slice(0,100)}function E(t,e){const r=t.pendingReplies.get(e.key);t.pendingReplies.set(e.key,{key:e.key,conversationId:e.conversationId,conversationName:e.conversationName,text:e.text,attachmentPath:e.attachmentPath,skipText:e.skipText,queuedAt:r?.queuedAt??new Date().toISOString(),attempts:r?.attempts??0,lastAttemptAt:r?.lastAttemptAt,lastInterruptedReason:e.reason}),R(t)}function G(t,e,r){t.manualReviewReplies.set(e,{key:e,reason:r,createdAt:t.manualReviewReplies.get(e)?.createdAt??new Date().toISOString()}),t.lastError=r,g(t,{status:"failed",runId:t.lastRunId??null,summary:`manual review required: ${r}`}),R(t)}function W(t,e){const r=Oe(e);if(t.pendingStatePath===r)return;t.pendingStatePath=r;const n=Ee(r);for(const a of n.pending??[])We(a)&&(t.pendingReplies.has(a.key)||t.pendingReplies.set(a.key,a));for(const a of n.manualReview??[])Te(a)&&(t.manualReviewReplies.has(a.key)||t.manualReviewReplies.set(a.key,a));for(const a of n.completedKeys??[])typeof a=="string"&&a&&t.completedPendingReplyKeys.add(a)}function R(t){if(!t.pendingStatePath)return;const e=Array.from(t.completedPendingReplyKeys).slice(-500),r=Array.from(t.pendingReplies.values()).slice(0,500),n=Array.from(t.manualReviewReplies.values()).slice(-500);try{h.mkdirSync(
|
|
6
|
-
`).map(e=>e.trim()).filter(Boolean).map(e=>{try{return JSON.parse(e)}catch{return null}}).filter(e=>!!e)}async function Fe(t,e){if(e.localPath){if(!h.existsSync(e.localPath))throw new Error(`WeChat RPA local attachment does not exist: ${e.localPath}`);return e.localPath}if(e.url)return Ke(t,e);throw new Error("WeChat RPA attachment requires localPath or url; dataBase64 is not accepted over Manager IPC")}async function Ke(t,e){if(!e.url||!/^https?:\/\//i.test(e.url))throw new Error("WeChat RPA attachment url must be http(s)");if(Number.isFinite(e.size)&&e.size>S)throw new Error(`WeChat RPA cross-machine attachment is too large: ${e.size} bytes. Max: ${S} bytes.`);const r=await fetch(e.url);if(!r.ok)throw new Error(`WeChat RPA attachment url download failed: ${r.status}`);const n=Number(r.headers.get("content-length")||e.size||0);if(n>S)throw new Error(`WeChat RPA cross-machine attachment is too large: ${n} bytes. Max: ${S} bytes.`);const a=Buffer.from(await r.arrayBuffer());return Be(t,e,a)}function Be(t,e,r){if(!r.byteLength)throw new Error("WeChat RPA attachment is empty");if(r.byteLength>S)throw new Error(`WeChat RPA cross-machine attachment is too large: ${r.byteLength} bytes. Max: ${S} bytes.`);const n=
|
|
5
|
+
${e.attachment?.size||0}`).digest("hex").slice(0,24)}function Me(t,e){return[...Array.from(t.pendingReplies.values()).map(n=>({replyId:n.key,idempotencyKey:n.key,status:"queued",conversationName:n.conversationName,reasonCode:n.lastInterruptedReason??null,nextAttemptAt:null,queuedAt:n.queuedAt,lastAttemptAt:n.lastAttemptAt??null,attemptCount:n.attempts,commitStage:null})),...ue({config:e})].slice(0,100)}function E(t,e){const r=t.pendingReplies.get(e.key);t.pendingReplies.set(e.key,{key:e.key,conversationId:e.conversationId,conversationName:e.conversationName,text:e.text,attachmentPath:e.attachmentPath,skipText:e.skipText,queuedAt:r?.queuedAt??new Date().toISOString(),attempts:r?.attempts??0,lastAttemptAt:r?.lastAttemptAt,lastInterruptedReason:e.reason}),R(t)}function G(t,e,r){t.manualReviewReplies.set(e,{key:e,reason:r,createdAt:t.manualReviewReplies.get(e)?.createdAt??new Date().toISOString()}),t.lastError=r,g(t,{status:"failed",runId:t.lastRunId??null,summary:`manual review required: ${r}`}),R(t)}function W(t,e){const r=Oe(e);if(t.pendingStatePath===r)return;t.pendingStatePath=r;const n=Ee(r);for(const a of n.pending??[])We(a)&&(t.pendingReplies.has(a.key)||t.pendingReplies.set(a.key,a));for(const a of n.manualReview??[])Te(a)&&(t.manualReviewReplies.has(a.key)||t.manualReviewReplies.set(a.key,a));for(const a of n.completedKeys??[])typeof a=="string"&&a&&t.completedPendingReplyKeys.add(a)}function R(t){if(!t.pendingStatePath)return;const e=Array.from(t.completedPendingReplyKeys).slice(-500),r=Array.from(t.pendingReplies.values()).slice(0,500),n=Array.from(t.manualReviewReplies.values()).slice(-500);try{h.mkdirSync(p.dirname(t.pendingStatePath),{recursive:!0}),h.writeFileSync(t.pendingStatePath,JSON.stringify({version:1,pending:r,manualReview:n,completedKeys:e},null,2))}catch{}}function Ee(t){try{const e=JSON.parse(h.readFileSync(t,"utf8"));return e&&e.version===1?e:{version:1,pending:[],completedKeys:[]}}catch{return{version:1,pending:[],completedKeys:[]}}}function We(t){if(!t||typeof t!="object")return!1;const e=t;return typeof e.key=="string"&&typeof e.conversationId=="string"&&typeof e.conversationName=="string"&&typeof e.text=="string"&&typeof e.queuedAt=="string"&&typeof e.attempts=="number"&&Number.isFinite(e.attempts)&&(e.attachmentPath===void 0||typeof e.attachmentPath=="string")&&(e.skipText===void 0||typeof e.skipText=="boolean")}function Te(t){if(!t||typeof t!="object")return!1;const e=t;return typeof e.key=="string"&&typeof e.reason=="string"&&typeof e.createdAt=="string"}function Oe(t){const e=I.createHash("sha256").update(t.id).digest("hex").slice(0,16);return p.join(t.workDir,".shennian","wechat-rpa-pending-replies",`${e}.json`)}function T(t,e){const r=_e(e);if(t.messageStatePath===r)return;t.messageStatePath=r;const n=Le(r);t.deduper=new F((n.messageIds??[]).filter(a=>typeof a=="string"&&a.length>0))}function $e(t){if(t.messageStatePath)try{h.mkdirSync(p.dirname(t.messageStatePath),{recursive:!0}),h.writeFileSync(t.messageStatePath,JSON.stringify({version:1,messageIds:t.deduper.snapshot()},null,2))}catch{}}function Le(t){try{const e=JSON.parse(h.readFileSync(t,"utf8"));return e&&e.version===1?e:{version:1,messageIds:[]}}catch{return{version:1,messageIds:[]}}}function _e(t){const e=I.createHash("sha256").update(t.id).digest("hex").slice(0,16);return p.join(t.workDir,".shennian","wechat-rpa-seen-messages",`${e}.json`)}function O(t,e,r){t.lastInterruptedAt=new Date().toISOString(),t.lastError=null,t.consecutiveInterruptions+=1,t.runtimeState="interrupted",qe(t)&&(t.interruptionCooldownUntil=Date.now()+M,t.runtimeState="cooldown"),g(t,{status:"interrupted",runId:e.rpaRunId||t.lastRunId||null,summary:r})}function C(t){t.consecutiveInterruptions=0,t.interruptionCooldownUntil=void 0,t.runtimeState="idle_waiting"}function $(t,e){const r=e.postSendScreenshotPath||e.screenshotPath;e.rpaRunId&&(t.lastRunId=e.rpaRunId),r&&(t.lastTracePath=r),t.lastTraceSummary=e.rpaTraceSummary||[e.groupName?`group=${e.groupName}`:"",e.ok===!1?"failed":e.interrupted?"interrupted":"ok",e.reason?`reason=${e.reason}`:""].filter(Boolean).join(" ")}function g(t,e){const r=e.runId||t.lastRunId||`local:${Date.now().toString(36)}`;t.lastRunId=r,t.recentTaskSummaries.unshift({at:new Date().toISOString(),status:e.status,runId:r,summary:L(e.summary)}),t.recentTaskSummaries=t.recentTaskSummaries.slice(0,pe)}function k(t){const e=String(t||"").toLowerCase();return/permission|accessibility|screen recording|automation|window|foreground|target group|refusing|requires|安全|权限|窗口|目标群/.test(e)?"blocked":"failed"}function L(t){return String(t||"").replace(/\s+/g," ").trim().slice(0,180)}function V(t,e){if(e.forceForeground)return!1;const r=t.interruptionCooldownUntil;return r?Date.now()<r?!0:(t.interruptionCooldownUntil=void 0,t.consecutiveInterruptions=0,!1):!1}function qe(t){return!Number.isFinite(N)||N<=0||!Number.isFinite(M)||M<=0?!1:t.consecutiveInterruptions>=N}function De(t){return!t||!h.existsSync(t)?[]:h.readFileSync(t,"utf-8").split(`
|
|
6
|
+
`).map(e=>e.trim()).filter(Boolean).map(e=>{try{return JSON.parse(e)}catch{return null}}).filter(e=>!!e)}async function Fe(t,e){if(e.localPath){if(!h.existsSync(e.localPath))throw new Error(`WeChat RPA local attachment does not exist: ${e.localPath}`);return e.localPath}if(e.url)return Ke(t,e);throw new Error("WeChat RPA attachment requires localPath or url; dataBase64 is not accepted over Manager IPC")}async function Ke(t,e){if(!e.url||!/^https?:\/\//i.test(e.url))throw new Error("WeChat RPA attachment url must be http(s)");if(Number.isFinite(e.size)&&e.size>S)throw new Error(`WeChat RPA cross-machine attachment is too large: ${e.size} bytes. Max: ${S} bytes.`);const r=await fetch(e.url);if(!r.ok)throw new Error(`WeChat RPA attachment url download failed: ${r.status}`);const n=Number(r.headers.get("content-length")||e.size||0);if(n>S)throw new Error(`WeChat RPA cross-machine attachment is too large: ${n} bytes. Max: ${S} bytes.`);const a=Buffer.from(await r.arrayBuffer());return Be(t,e,a)}function Be(t,e,r){if(!r.byteLength)throw new Error("WeChat RPA attachment is empty");if(r.byteLength>S)throw new Error(`WeChat RPA cross-machine attachment is too large: ${r.byteLength} bytes. Max: ${S} bytes.`);const n=p.join(t,".uploads","wechat-rpa","outbound");h.mkdirSync(n,{recursive:!0});const a=He(e.name||"attachment"),i=I.createHash("sha256").update(r).digest("hex").slice(0,12),s=p.extname(a),u=s?a.slice(0,-s.length):a,o=p.join(n,`${u}-${i}${s}`);return h.existsSync(o)||h.writeFileSync(o,r),o}function He(t){return p.basename(t||"attachment").normalize("NFKC").replace(/[<>:"/\\|?*\x00-\x1F]/g,"_").replace(/\s+/g," ").replace(/^[ ._]+|[ ._]+$/g,"")||"attachment"}function _(t,e){if(e.downloadAttachments===!1)return;const r=e.downloadAttachmentsDir?.trim();if(r)return p.resolve(r);const n=I.createHash("sha256").update(t.id).digest("hex").slice(0,16);return ee("wechat-rpa","attachments","inbound",n)}function Ue(t){if(!Array.isArray(t))return[];const e=process.env.SHENNIAN_MACHINE_ID?.trim();return t.map(r=>r?.localPath?{...r,availability:r.availability||"edge-local",...r.machineId||!e?{}:{machineId:e}}:r)}function P(t,e,r,n){const a=Number(t);return Number.isFinite(a)?Math.min(n,Math.max(r,a)):e}function je(...t){const e=new Set,r=[];for(const n of t.flat()){const a=`${n.channelId}
|
|
7
7
|
${n.conversationId}
|
|
8
|
-
${n.messageId}`;e.has(a)||(e.add(a),r.push(n))}return r}export{nt as WeChatRpaChannelAdapter,Ue as annotateWeChatRpaInboundAttachments,Fe as materializeWeChatRpaOutboundAttachment,Ae as planWeChatRpaLabSendTasks,_ as resolveWeChatRpaInboundAttachmentDir,
|
|
8
|
+
${n.messageId}`;e.has(a)||(e.add(a),r.push(n))}return r}export{nt as WeChatRpaChannelAdapter,Ue as annotateWeChatRpaInboundAttachments,Fe as materializeWeChatRpaOutboundAttachment,Ae as planWeChatRpaLabSendTasks,_ as resolveWeChatRpaInboundAttachmentDir,me as selectWeChatRpaLabReadKind,Ce as weChatRpaLabTaskKindForAttachment,ye as weChatRpaProductSourceHealth,we as windowsVisualFlowHealth};
|