shennian 0.2.95 → 0.2.97
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/publish-build-manifest.json +45 -35
- package/dist/src/channels/runtime.d.ts +1 -0
- package/dist/src/channels/runtime.js +4 -4
- package/dist/src/channels/wechat-channel/fingerprint.js +1 -1
- package/dist/src/channels/wechat-channel/helper-assets.d.ts +1 -0
- package/dist/src/channels/wechat-channel/helper-assets.js +1 -1
- package/dist/src/channels/wechat-channel/helper-client.d.ts +17 -0
- package/dist/src/channels/wechat-channel/helper-client.js +3 -3
- package/dist/src/channels/wechat-channel/helper-protocol.d.ts +1 -1
- package/dist/src/channels/wechat-channel/helper-protocol.js +1 -1
- package/dist/src/channels/wechat-channel/macos-helper-app-bridge.d.ts +1 -0
- package/dist/src/channels/wechat-channel/macos-helper-app-bridge.js +2 -0
- package/dist/src/channels/wechat-channel/observer.d.ts +23 -3
- package/dist/src/channels/wechat-channel/observer.js +6 -6
- package/dist/src/channels/wechat-channel/outbound-sender.d.ts +3 -1
- package/dist/src/channels/wechat-channel/outbound-sender.js +1 -1
- package/dist/src/channels/wechat-channel/preflight.js +1 -1
- package/dist/src/channels/wechat-channel/runner.js +1 -1
- package/dist/src/commands/daemon.d.ts +3 -0
- package/dist/src/commands/daemon.js +7 -7
- package/dist/src/commands/pair.js +8 -8
- package/dist/src/commands/wechat.d.ts +26 -2
- package/dist/src/commands/wechat.js +7 -3
- package/dist/src/devtools/wechat-channel-action-smoke.d.ts +18 -0
- package/dist/src/devtools/wechat-channel-action-smoke.js +8 -7
- package/dist/src/devtools/wechat-channel-markdown-report.d.ts +6 -0
- package/dist/src/devtools/wechat-channel-markdown-report.js +5 -0
- package/dist/src/devtools/wechat-channel-product-steps.d.ts +3 -2
- package/dist/src/devtools/wechat-channel-product-steps.js +8 -4
- package/dist/src/manager/registry.js +1 -1
- package/dist/src/native-fusion/parsers.js +4 -4
- package/dist/src/session/handlers/tool-detail.d.ts +1 -1
- package/dist/src/session/handlers/tool-detail.js +1 -1
- package/node_modules/@shennian/wire/dist/machine.d.ts +25 -0
- package/node_modules/@shennian/wire/dist/machine.d.ts.map +1 -1
- package/node_modules/@shennian/wire/dist/message-payload.d.ts.map +1 -1
- package/node_modules/@shennian/wire/dist/message-payload.js +10 -2
- package/node_modules/@shennian/wire/dist/message-payload.js.map +1 -1
- package/package.json +9 -10
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import{defaultWeChatChannelAttachmentDir as ae,resolveVisibleWeChatChannelMedia as se}from"./media-resolver.js";import{updateWeChatChannelBindingLedger as ce}from"./ledger.js";import{defaultWeChatChannelVectorStorePath as de,loadWeChatChannelVectorStore as le,saveWeChatChannelVectorStore as ue,upsertWeChatChannelVectorReferences as he}from"./vector-store.js";import{normalizeWeChatChannelAttachmentAvailability as O,normalizeWeChatChannelMessageKind as we}from"./core/schema.js";import{matchWeChatConversationFingerprints as fe}from"./fingerprint.js";import{normalizeWeChatAnchorText as y,weChatTextSimilarity as P}from"./anchor.js";const q="wechat_login_required",I="wechat_duplicate_instance";async function vt(e){let t=await qe(e.helper,e.traceId);t=await Ue(e.helper,t,e.traceId);const n=await te({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 W({helper:e.helper,window:t,traceId:e.traceId});if(ne({capture:r,ocr:i,binding:e.binding})){t=await He(e.helper,e.traceId);const o=await te({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 W({helper:e.helper,window:t,traceId:e.traceId}),ne({capture:r,ocr:i,binding:e.binding}))throw new Error("wechat_window_obstructed")}return e.onObservationEvidence?.({window:t,capture:r,ocr:i}),be(e)?me({...e,window:t,capture:r,ocr:i}):V({...e,window:t,capture:r,ocr:i})}function be(e){return e.observePipeline==="edge-structure"?!0:e.observePipeline==="server-observe"?!1:!!e.api.structureWindow}async function V(e){const t=await e.api.observe(e.runtime,e.binding,{screenshots:[{captureIndex:0,mimeType:e.capture.mimeType,dataBase64:e.capture.dataBase64,width:e.capture.width,height:e.capture.height}],edgeOcrBlocks:e.ocr.blocks??[],visibleConversationFingerprints:e.ocr.visibleConversationFingerprints??[],localLedgerTailAnchors:e.localLedgerTailAnchors??[]}),n=await U({helper:e.helper,runtime:e.runtime,binding:e.binding,messages:t.observedMessages??[],workDir:e.workDir,attachmentsDir:e.attachmentsDir,window:e.window,screenshot:e.capture,windowId:e.window.windowId,traceId:e.traceId,mediaAnchorText:e.mediaAnchorText});return ie(n,re({ocr:e.ocr,screenshot:e.capture,binding:e.binding,anchors:e.localOutboundEchoAnchors??[]}))}async function me(e){if(!e.api.structureWindow)throw new Error("wechat_structure_window_unavailable");let t;try{t=await e.api.structureWindow(e.runtime,e.binding,{screenshots:[{captureIndex:0,mimeType:e.capture.mimeType,dataBase64:e.capture.dataBase64,width:e.capture.width,height:e.capture.height}],edgeOcrBlocks:e.ocr.blocks??[],visibleConversationFingerprints:e.ocr.visibleConversationFingerprints??[],localLedgerTailAnchors:e.localLedgerTailAnchors??[]})}catch(s){if(ge(s))return V(e);throw s}const n=Me(t.structuredMessages??t.observedMessages??[]),r=await xe({...e,messages:n,screenshot:e.capture}),i=await U({helper:e.helper,runtime:e.runtime,binding:e.binding,messages:r.messages,workDir:e.workDir,attachmentsDir:e.attachmentsDir,window:e.window,screenshot:e.capture,windowId:e.window.windowId,traceId:e.traceId,mediaAnchorText:e.mediaAnchorText}),o=ie(i,re({ocr:e.ocr,screenshot:e.capture,binding:e.binding,anchors:e.localOutboundEchoAnchors??[]}));return e.localLedger?ce({ledger:e.localLedger,bindingId:e.binding.bindingId,observedMessages:o,baselineOnly:e.baselineOnly,vectorReferences:r.vectorReferences}).newMessages:o}function ge(e){return e instanceof Error&&/\bedge_runtime_disabled\b/.test(e.message)}async function xe(e){const t=ye(e.messages);if(!t.length)return{messages:e.messages,vectorReferences:[]};const n=e.api.embedVisual?await pe({...e,seedBlocks:t}):t.filter(a=>d(a.vectorBase64)||d(a.signature));if(!n.length)return{messages:e.messages,vectorReferences:[]};const r=e.vectorStorePath??de(e.workDir||process.cwd(),e.runtime.runtimeId),i=le(r,e.runtime.runtimeId),o=he({store:i,bindingId:e.binding.bindingId,blocks:n});return o.length?(ue(r,i),{messages:Ie(e.messages,o),vectorReferences:o}):{messages:e.messages,vectorReferences:[]}}function ye(e){const t=[];for(const n of e)for(const[r,i]of(n.visualBlocks??[]).entries()){const o=d(i.blockId)||`${n.stableMessageKey}:visual-${r}`,a=d(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 ve(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 ke(t.visualBlocks??t.embeddedBlocks??[],e.seedBlocks)}async function pe(e){try{return await ve(e)}catch(t){if(Ce(t))throw t;return[]}}function Ce(e){return!e||typeof e!="object"?!1:e.reasonCode==="insufficient_credits"?!0:e instanceof Error&&/\binsufficient_credits\b/.test(e.message)}function ke(e,t){return e.map((n,r)=>{const i=t[r];return{...n,stableMessageKey:d(n.stableMessageKey)||d(i?.stableMessageKey),blockId:d(n.blockId)||d(i?.blockId),blockKind:d(n.blockKind)||d(i?.blockKind)||"visual",...n.bbox!==void 0?{bbox:n.bbox}:i?.bbox!==void 0?{bbox:i.bbox}:{}}}).filter(n=>d(n.stableMessageKey)&&d(n.blockId))}function Ie(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:Ne(r,i)}:r})}function Ne(e,t){const r=(e.visualBlocks??[]).map((o,a)=>{const s=d(o.blockId)||`${e.stableMessageKey}:visual-${a}`,c=t.find(l=>l.blockId===s);return c?$(c,o):j(o)}),i=new Set(r.map(o=>o.blockId));for(const o of t)i.has(o.blockId)||r.push($(o));return r}function $(e,t){return{...t?j(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 j(e){const{vectorBase64:t,...n}=e;return n}function Me(e){return e.map((t,n)=>Se(t,n)).filter(t=>t!=null)}function Se(e,t){if(!f(e))return null;const n=we(e.kind),r=Be(e.senderRole),i=m(e.normalizedText),o=m(e.anchorText)??i??m(e.textExcerpt),a=d(e.stableMessageKey)||Te({message:e,index:t,senderRole:r,kind:n,anchorText:o});return{stableMessageKey:a,senderRole:r,...m(e.senderName)!==null?{senderName:m(e.senderName)}:{},kind:n,...i!==null?{normalizedText:i}:{},...o!==null?{anchorText:o}:{},...C(e,"anchorMetadata")?{anchorMetadata:e.anchorMetadata}:{},...C(e,"neighborContext")?{neighborContext:e.neighborContext}:{},...m(e.textExcerpt)!==null?{textExcerpt:m(e.textExcerpt)}:{},...C(e,"bbox")?{bbox:e.bbox}:{},...C(e,"mediaMetadata")?{mediaMetadata:We(e.mediaMetadata)}:{},...typeof e.isBaseline=="boolean"?{isBaseline:e.isBaseline}:{},...m(e.deliveryStatus)!==null?{deliveryStatus:m(e.deliveryStatus)??void 0}:{},...m(e.observedAt)!==null?{observedAt:m(e.observedAt)??void 0}:{},...Array.isArray(e.visualBlocks)?{visualBlocks:e.visualBlocks}:{},...o?{}:{anchorText:a}}}function Te(e){const t=f(e.message.bbox)?JSON.stringify(e.message.bbox):"";return`structured-window:${[e.index,e.senderRole,e.kind,e.anchorText||d(e.message.textExcerpt)||"",t].join(":").slice(0,180)}`}function We(e){if(!f(e))return e;const t={...e};return C(t,"availability")&&(t.availability=O(t.availability)),f(t.attachment)&&(t.attachment=Y(t.attachment)),Array.isArray(t.attachments)&&(t.attachments=t.attachments.map(n=>f(n)?Y(n):n)),t}function Y(e){const t={...e};return C(t,"availability")&&(t.availability=O(t.availability)),t}function Be(e){return e==="self"||e==="contact"||e==="system"||e==="unknown"?e:"unknown"}async function U(e){if(e.binding.downloadMedia===!1)return e.messages;const t=e.messages.map((o,a)=>ze(o,e.screenshot,e.window,Ke(e.messages,a))).filter((o,a)=>_e(e.messages,a,e.mediaAnchorText)).filter(o=>o!=null);if(!t.length)return e.messages;const n=e.attachmentsDir??ae(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 B({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:Le(o.mediaMetadata,a.attachment,a.reasonCode,a.attemptReasonCodes,a.resolveTrace)}:o})}function _e(e,t,n){const r=X(n);if(!r)return!0;const i=Ee(r),o=e.map((a,s)=>X(Re(a)).includes(r)?s:-1).filter(a=>a>=0);if(!o.length)return t===Fe(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(H))return!0;return!1}function Ee(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 Fe(e,t=null){for(let n=e.length-1;n>=0;n-=1)if(H(e[n]))return Ae(e[n],t)?n:-1;return-1}function H(e){if(!e)return!1;const t=String(e.kind||"").toLowerCase(),n=f(e.mediaMetadata)?e.mediaMetadata:{},r=f(n.attachment)?n.attachment:{};return G(t,n,r)}function Ae(e,t){if(!e||!t)return!0;const n=String(e.kind||"").toLowerCase(),r=f(e.mediaMetadata)?e.mediaMetadata:{},i=f(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 Re(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 ze(e,t,n,r){const i=String(e.kind||"").toLowerCase(),o=f(e.mediaMetadata)?e.mediaMetadata:{},a=f(o.attachment)?o.attachment:{},s=d(a.availability)||d(o.availability),c=d(a.localPath)||d(o.localPath);if(s==="edge-local"&&c||!G(i,o,a))return null;const l=M(e.bbox)??M(o.bbox)??M(o.downloadActionBbox),u=M(o.downloadActionBbox),w=J(l,t,n),b=Oe(l,t,n),p=J(u,t,n);return{messageKey:e.stableMessageKey,kind:d(a.type)||d(a.kind)||d(o.messageType)||i,fileName:d(a.name)||d(a.fileName)||d(o.fileName)||null,mimeType:d(a.mimeType)||d(o.mimeType)||null,size:k(a.size)??k(o.size),mediaStatus:De(o,a),observedAt:e.observedAt??null,contextText:r??e.normalizedText??e.anchorText??e.textExcerpt??null,...w?{bbox:w}:{},...b?{screenshotBbox:b}:{},...p?{downloadActionBbox:p}:{}}}function Le(e,t,n,r,i){const o=f(e)?{...e}:{};return{...o,availability:f(t)?t.availability:o.availability,mediaStatus:f(t)&&t.availability==="edge-local"?"downloaded":o.mediaStatus,attachment:t,edgeResolveReasonCode:n,...r?.length?{edgeResolveAttempts:r}:{},...i?{edgeResolveTrace:i}:{}}}function De(e,t){const n=d(e.mediaStatus);if(n==="not_downloaded")return"not_downloaded";if(n==="downloaded"||n==="available")return"available";const r=d(t.availability)||d(e.availability);return r==="pending-download"?"not_downloaded":r==="metadata-only"?"metadata_only":n||null}function G(e,t,n){const r=`${e} ${d(t.messageType)} ${d(n.type)} ${d(n.kind)}`.toLowerCase();return/image|photo|video|file|document/.test(r)}function M(e){if(!f(e))return null;const t=k(e.x),n=k(e.y),r=k(e.width),i=k(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 J(e,t,n){if(!e)return null;if(e.coordinateSpace==="screen")return e;const r=_(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 Oe(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 f(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}function C(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function d(e){return typeof e=="string"?e.trim():""}function m(e){return d(e)||null}function k(e){const t=Number(e);return Number.isFinite(t)?t:null}async function pt(e,t){await Z(e,t)}async function Z(e,t){let n=await e.request("permissions.check",{},t);h(n,"permissions.check");const r=n.result??{};if(r.screenRecording===!1)throw new Error("permission_screen_recording_missing");if(r.accessibility===!1)throw new Error("permission_accessibility_missing");if(r.automation===!1)throw new Error("permission_automation_missing");if(r.wechatRunning===!1)throw new Error("wechat_not_running");const i=ee(r);if(i&&r.wechatWindowAvailable!==!1&&Pe(r))throw new Error(I);if(r.wechatMainWindowResponsive===!1)throw new Error("wechat_window_unresponsive");let o=null;if(r.wechatWindowAvailable===!1||i){const s=await e.request("windows.ensureReady",{restore:!0,focus:!0},t);h(s,"windows.ensureReady"),o=s.result??null,n=await e.request("permissions.check",{},t),h(n,"permissions.check")}const a=n.result??{};if(a.screenRecording===!1)throw new Error("permission_screen_recording_missing");if(a.accessibility===!1)throw new Error("permission_accessibility_missing");if(a.automation===!1)throw new Error("permission_automation_missing");if(a.wechatRunning===!1)throw new Error("wechat_not_running");if(ee(a))throw new Error(I);if(a.wechatWindowAvailable===!1)throw new Error("wechat_window_unavailable");if(a.wechatMainWindowResponsive===!1)throw new Error("wechat_window_unresponsive");if(Q([...a.captureCandidates??[],...a.restoreCandidates??[],...a.duplicateMainWindows??[],...a.unsafeAuxiliaryWindows??[]],o??void 0))throw new Error(I);return o}function Pe(e){return(e.captureCandidates?.length??0)>0||(e.restoreCandidates?.length??0)>0||(e.duplicateMainWindows?.length??0)>0||Number(e.duplicateMainWindowCount??0)>0}async function qe(e,t){const r=await Z(e,t)??await Ye(e,t);if(!r?.windowId)throw new Error("helper_invalid_response: windows.ensureReady missing window data");return r}function Q(e,t){const n=e.filter(x).filter(i=>i.visible!==!1&&i.minimized!==!0),r=t??n.slice().sort((i,o)=>T(o)-T(i))[0];return n.find(i=>Ve(i,r))??null}function ee(e){return e.duplicateInstanceBlocked===!0||Number(e.duplicateMainWindowCount??0)>0||Number(e.unsafeAuxiliaryWindowCount??0)>0||(e.duplicateMainWindows??[]).some(x)||(e.unsafeAuxiliaryWindows??[]).some(x)}function S(e){const t=e.filter(x).filter(i=>i.visible!==!1&&i.minimized!==!0),n=t.filter($e);if(n.length>1)throw new Error(I);const r=n.slice().sort((i,o)=>T(o)-T(i))[0];if(Q(t,r))throw new Error(I)}function Ve(e,t){if(!x(e)||t&&je(e,t)||e.visible===!1||e.minimized===!0)return!1;const n=Number(e.bounds?.width),r=Number(e.bounds?.height);if(!Number.isFinite(n)||!Number.isFinite(r))return!1;const i=n>=360&&n<=720&&r>=420&&r<=840&&r>n,o=n>=420&&n<=680&&r>=260&&r<=460;return i||o}function $e(e){const t=Number(e.bounds?.width),n=Number(e.bounds?.height);return Number.isFinite(t)&&Number.isFinite(n)&&t>=760&&n>=600}function T(e){const t=Number(e.bounds?.width),n=Number(e.bounds?.height);return Number.isFinite(t)&&Number.isFinite(n)?t*n:0}function je(e,t){return!!(e.windowId&&t.windowId&&e.windowId===t.windowId)}async function Ye(e,t){const n=await e.request("windows.ensureReady",{restore:!0,focus:!0},t);return h(n,"windows.ensureReady"),n.result??null}async function Ct(e,t){let n=await e.request("windows.list",{},t);h(n,"windows.list");let r=n.result?.windows??[];S(r);let i=r.find(x);if(!i){const a=await e.request("windows.ensureReady",{restore:!0,focus:!0},t);h(a,"windows.ensureReady"),n=await e.request("windows.list",{},t),h(n,"windows.list"),r=n.result?.windows??[],S(r),i=r.find(x)}if(!i)throw new Error("wechat_window_unavailable");const o=await e.request("windows.focus",{windowId:i.windowId},t);return h(o,"windows.focus"),await Xe(e,i,t)}async function Ue(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 h(r,"windows.focus"),t}async function He(e,t){const n=await e.request("windows.list",{},t);h(n,"windows.list");const r=n.result?.windows??[];S(r);const i=r.find(x);if(!i)throw new Error("wechat_window_not_found");const o=await e.request("windows.focus",{windowId:i.windowId},t);return h(o,"windows.focus"),i}async function Xe(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(x);return S(i),i.find(o=>o.windowId===t.windowId)??i[0]??t}async function A(e){const{capture:t,ocr:n}=e.observation??await W(e),r=L({capture:t,ocr:n,window:e.window});if(r)return{opened:!1,reason:r};const i=fe({visibleItems:n.visibleConversationFingerprints??[],targets:[{bindingId:e.binding.bindingId,conversationDisplayName:e.binding.conversationDisplayName}]})[0];if(!i)return{opened:!1,reason:"conversation_not_visible"};const o=lt(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 h(a,"mouse.click"),{opened:!0,reason:i.reason}}async function te(e){const t=await W(e),n=L({capture:t.capture,ocr:t.ocr,window:e.window});if(n)return{opened:!1,reason:n};if(D(t.ocr,t.capture,e.binding.conversationDisplayName))return(await A({...e,observation:t})).opened&&await g(350),await z({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:"current-conversation-title-confirmed"};const r=await A({...e,observation:t});if(r.opened&&(await g(900),(await B(e)).ok))return{opened:!0,reason:r.reason};const i=Ze(e,t),o=await e.helper.request("wechat.searchConversation",{conversationName:e.binding.conversationDisplayName,waitMs:e.searchDelayMs??700,...i?{searchPoint:i}:{}},e.traceId);h(o,"wechat.searchConversation"),await Qe(e.helper,e.traceId),await g(900);const a=await B(e);if(a.ok)return await z({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:a.reason};const s=await A(e);if(!s.opened)return s;await g(900);const c=await B(e);return c.ok?(await z({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:s.reason}):{opened:!1,reason:c.reason}}async function W(e){let t=await R(e.helper,e.window.windowId,e.traceId,e.window.bounds),n=await K(e.helper,t,e.traceId);return await Ge({helper:e.helper,window:e.window,capture:t,ocr:n,traceId:e.traceId})&&(await g(350),t=await R(e.helper,e.window.windowId,e.traceId,e.window.bounds),n=await K(e.helper,t,e.traceId)),{capture:t,ocr:n}}async function Ge(e){const t=Je(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 Je(e,t,n){const r=e.blocks??[],i=r.find(c=>{const l=String(c.text||""),u=c.bbox;if(!u)return!1;const w=Number(u.x),b=Number(u.y);return Number.isFinite(w)&&Number.isFinite(b)&&b>t.height*.06&&b<t.height*.22&&w>t.width*.3&&/查看原视频|查看原图|原视频|原图|视频|图片|图像/.test(l)});if(!i)return null;const o=i.bbox,a=Number(o?.y);if(!Number.isFinite(a))return null;const s=r.filter(c=>{const l=String(c.text||"").trim(),u=c.bbox;if(!u||l!=="\xD7")return!1;const w=Number(u.x),b=Number(u.y);return Number.isFinite(w)&&Number.isFinite(b)&&w>t.width*.55&&w<t.width*.9&&Math.abs(b-a)<t.height*.08}).sort((c,l)=>Number(l.bbox?.x??0)-Number(c.bbox?.x??0))[0];return _(s?.bbox,t,n)}function Ze(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),l=Number(s.y);return Number.isFinite(c)&&Number.isFinite(l)&&c<n.width*.22&&l<n.height*.12});return _(i?.bbox,n,e.window)}async function R(e,t,n,r){const i=await e.request("windows.capture",{windowId:t,scope:"full-window",...r?{bounds:r}:{}},n);h(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 K(e,t,n){const r=await e.request("ocr.recognize",{mimeType:t.mimeType,dataBase64:t.dataBase64,width:t.width,height:t.height},n);return h(r,"ocr.recognize"),r.result??{}}function h(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 g(e){return new Promise(t=>setTimeout(t,e))}async function Qe(e,t){const n=await e.request("keyboard.shortcut",{key:"return",modifiers:[]},t);h(n,"keyboard.shortcut")}async function z(e){if(!e.window.bounds)return;const t=e.observation,n=ut(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);h(i,"mouse.click"),await g(700)}const r=ht(e.window);if(r){const i=await e.helper.request("mouse.click",{x:r.x,y:r.y,coordinateSpace:"screen",windowId:e.window.windowId},e.traceId);h(i,"mouse.click"),await g(150)}for(let i=0;i<6;i+=1){const o=await e.helper.request("keyboard.shortcut",{key:"pagedown",modifiers:[]},e.traceId);h(o,"keyboard.shortcut"),await g(260)}for(let i=0;i<2;i+=1){const o=await e.helper.request("keyboard.shortcut",{key:"end",modifiers:[]},e.traceId);h(o,"keyboard.shortcut"),await g(180)}for(let i=0;i<3;i+=1){const o=await e.helper.request("mouse.scroll",{deltaY:-2400,windowId:e.window.windowId,...r?{point:{...r,coordinateSpace:"screen"}}:{}},e.traceId);h(o,"mouse.scroll"),await g(180)}await g(220)}async function B(e){const t=await R(e.helper,e.window.windowId,e.traceId,e.window.bounds),n=await K(e.helper,t,e.traceId),r=L({capture:t,ocr:n,window:e.window});return r?{ok:!1,reason:r}:D(n,t,e.binding.conversationDisplayName)?{ok:!0,reason:"target-conversation-title-confirmed"}:{ok:!1,reason:"conversation_title_not_confirmed"}}function L(e){const t=(e.ocr.blocks??[]).map(r=>tt(r.text)).filter(Boolean),n=t.join("");return/(扫码登录|仅传输文件|重新登录|为了安全|安全验证|登录微信|微信登录|该账号已登录|帐号已登录|账号已登录|当前账号已登录|当前帐号已登录|已登录|进入微信|進入微信)/u.test(n)||et(e,t)?q:null}function et(e,t){if(!x(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 tt(e){return String(e||"").normalize("NFKC").replace(/\s+/g,"").trim()}function D(e,t,n){const r=N(n)?F(n):E(n);return r?(e.blocks??[]).some(i=>{const o=N(n)?F(i.text):E(i.text);if(!o||!oe(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 l=s>t.width*.32&&s<t.width*.78&&c<t.height*.16,u=s<t.width*.18&&c<t.height*.12;return l||u}):!1}function ne(e){if(D(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
|
-
`),i=[{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
|
|
5
|
-
`);return/\.(txt|md|rtf|docx?|pdf)\b/i.test(o)&&i>=1||/\.(png|jpe?g|gif|webp|bmp|heic|mp4|mov|m4v|avi|mkv|webm)\b/i.test(o)&&
|
|
6
|
-
`);if(/\b\d+\s*x\s*\d+\b/i.test(r)&&/\b\d+(\.\d+)?\s*(B|KB|MB|GB)\b/i.test(r)||/\b100%\b/.test(r)&&n.length<=20)return!0;const i=n.filter(o=>!/^(□|×|\+|…|\.\.\.|100%|\d+|发送)$/u.test(o));return e.width>600&&e.height>500&&i.length<=6}function
|
|
1
|
+
import{defaultWeChatChannelAttachmentDir as pe,resolveVisibleWeChatChannelMedia as Ce}from"./media-resolver.js";import{updateWeChatChannelBindingLedger as Ne}from"./ledger.js";import{defaultWeChatChannelVectorStorePath as Ie,loadWeChatChannelVectorStore as Se,saveWeChatChannelVectorStore as Me,upsertWeChatChannelVectorReferences as ke}from"./vector-store.js";import{normalizeWeChatChannelAttachmentAvailability as $,normalizeWeChatChannelMessageKind as Te}from"./core/schema.js";import{matchWeChatConversationFingerprints as _e}from"./fingerprint.js";import{normalizeWeChatAnchorText as p,weChatTextSimilarity as V}from"./anchor.js";const Be=3,We=5,Fe=420,q="wechat_login_required",F="wechat_duplicate_instance";async function jt(e){let t=await ot(e.helper,e.traceId);t=await lt(e.helper,t,e.traceId);const n=await ae({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(be({capture:r,ocr:i,binding:e.binding})){t=await ie(e.helper,e.traceId);const o=await ae({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}),be({capture:r,ocr:i,binding:e.binding}))throw new Error("wechat_window_obstructed")}return e.onObservationEvidence?.({window:t,capture:r,ocr:i}),Ee(e)?Ae({...e,window:t,capture:r,ocr:i}):j({...e,window:t,capture:r,ocr:i})}function Ee(e){return e.observePipeline==="edge-structure"?!0:e.observePipeline==="server-observe"?!1:!!e.api.structureWindow}async function j(e){const t=await de(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 G({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 ge(r,me({ocr:e.ocr,screenshot:t,binding:e.binding,anchors:e.localOutboundEchoAnchors??[]}))}async function Ae(e){if(!e.api.structureWindow)throw new Error("wechat_structure_window_unavailable");const t=await de(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(Re(c))return j(e);throw c}const r=qe(n.structuredMessages??n.observedMessages??[]),i=await Oe({...e,messages:r,screenshot:t}),o=await G({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=ge(o,me({ocr:e.ocr,screenshot:t,binding:e.binding,anchors:e.localOutboundEchoAnchors??[]}));return e.localLedger?Ne({ledger:e.localLedger,bindingId:e.binding.bindingId,observedMessages:a,baselineOnly:e.baselineOnly,vectorReferences:i.vectorReferences}).newMessages:a}function Re(e){return e instanceof Error&&/\bedge_runtime_disabled\b/.test(e.message)}async function Oe(e){const t=Le(e.messages);if(!t.length)return{messages:e.messages,vectorReferences:[]};const n=e.api.embedVisual?await Pe({...e,seedBlocks:t}):t.filter(a=>h(a.vectorBase64)||h(a.signature));if(!n.length)return{messages:e.messages,vectorReferences:[]};const r=e.vectorStorePath??Ie(e.workDir||process.cwd(),e.runtime.runtimeId),i=Se(r,e.runtime.runtimeId),o=ke({store:i,bindingId:e.binding.bindingId,blocks:n});return o.length?(Me(r,i),{messages:$e(e.messages,o),vectorReferences:o}):{messages:e.messages,vectorReferences:[]}}function Le(e){const t=[];for(const n of e)for(const[r,i]of(n.visualBlocks??[]).entries()){const o=h(i.blockId)||`${n.stableMessageKey}:visual-${r}`,a=h(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 ze(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 De(t.visualBlocks??t.embeddedBlocks??[],e.seedBlocks)}async function Pe(e){try{return await ze(e)}catch(t){if(Ke(t))throw t;return[]}}function Ke(e){return!e||typeof e!="object"?!1:e.reasonCode==="insufficient_credits"?!0:e instanceof Error&&/\binsufficient_credits\b/.test(e.message)}function De(e,t){return e.map((n,r)=>{const i=t[r];return{...n,stableMessageKey:h(n.stableMessageKey)||h(i?.stableMessageKey),blockId:h(n.blockId)||h(i?.blockId),blockKind:h(n.blockKind)||h(i?.blockKind)||"visual",...n.bbox!==void 0?{bbox:n.bbox}:i?.bbox!==void 0?{bbox:i.bbox}:{}}}).filter(n=>h(n.stableMessageKey)&&h(n.blockId))}function $e(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:Ve(r,i)}:r})}function Ve(e,t){const r=(e.visualBlocks??[]).map((o,a)=>{const s=h(o.blockId)||`${e.stableMessageKey}:visual-${a}`,c=t.find(d=>d.blockId===s);return c?Y(c,o):H(o)}),i=new Set(r.map(o=>o.blockId));for(const o of t)i.has(o.blockId)||r.push(Y(o));return r}function Y(e,t){return{...t?H(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 H(e){const{vectorBase64:t,...n}=e;return n}function qe(e){return e.map((t,n)=>je(t,n)).filter(t=>t!=null)}function je(e,t){if(!b(e))return null;const n=Te(e.kind),r=Ue(e.senderRole),i=g(e.normalizedText),o=g(e.anchorText)??i??g(e.textExcerpt),a=h(e.stableMessageKey)||Ye({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}:{},...I(e,"anchorMetadata")?{anchorMetadata:e.anchorMetadata}:{},...I(e,"neighborContext")?{neighborContext:e.neighborContext}:{},...g(e.textExcerpt)!==null?{textExcerpt:g(e.textExcerpt)}:{},...I(e,"bbox")?{bbox:e.bbox}:{},...I(e,"mediaMetadata")?{mediaMetadata:He(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 Ye(e){const t=b(e.message.bbox)?JSON.stringify(e.message.bbox):"";return`structured-window:${[e.index,e.senderRole,e.kind,e.anchorText||h(e.message.textExcerpt)||"",t].join(":").slice(0,180)}`}function He(e){if(!b(e))return e;const t={...e};return I(t,"availability")&&(t.availability=$(t.availability)),b(t.attachment)&&(t.attachment=U(t.attachment)),Array.isArray(t.attachments)&&(t.attachments=t.attachments.map(n=>b(n)?U(n):n)),t}function U(e){const t={...e};return I(t,"availability")&&(t.availability=$(t.availability)),t}function Ue(e){return e==="self"||e==="contact"||e==="system"||e==="unknown"?e:"unknown"}async function G(e){if(e.binding.downloadMedia===!1)return e.messages;const t=e.messages.map((o,a)=>tt(o,e.screenshot,e.window,et(e.messages,a))).filter((o,a)=>Ge(e.messages,a,e.mediaAnchorText)).filter(o=>o!=null);if(!t.length)return e.messages;const n=e.attachmentsDir??pe(e.workDir||process.cwd(),e.runtime.runtimeId,e.binding.bindingId),r=await Ce({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:nt(o.mediaMetadata,a.attachment,a.reasonCode,a.attemptReasonCodes,a.resolveTrace)}:o})}function Ge(e,t,n){const r=Z(n);if(!r)return!0;const i=Xe(r),o=e.map((a,s)=>Z(Qe(a)).includes(r)?s:-1).filter(a=>a>=0);if(!o.length)return t===Ze(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(X))return!0;return!1}function Xe(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 Ze(e,t=null){for(let n=e.length-1;n>=0;n-=1)if(X(e[n]))return Je(e[n],t)?n:-1;return-1}function X(e){if(!e)return!1;const t=String(e.kind||"").toLowerCase(),n=b(e.mediaMetadata)?e.mediaMetadata:{},r=b(n.attachment)?n.attachment:{};return J(t,n,r)}function Je(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 Qe(e){if(!e)return"";const t=e;return[e.normalizedText,e.anchorText,e.textExcerpt,t.text].map(n=>String(n||"")).join(`
|
|
2
|
+
`)}function Z(e){return String(e||"").normalize("NFKC").replace(/[o]/giu,"0").replace(/[^\p{L}\p{N}]/gu,"").toLowerCase()}function et(e,t){return[e[t-1],e[t],e[t+1]].map(r=>r&&(r.normalizedText||r.anchorText||r.textExcerpt)||"").map(r=>String(r||"").trim()).filter(Boolean).join(`
|
|
3
|
+
`)||null}function tt(e,t,n,r){const i=String(e.kind||"").toLowerCase(),o=b(e.mediaMetadata)?e.mediaMetadata:{},a=b(o.attachment)?o.attachment:{},s=h(a.availability)||h(o.availability),c=h(a.localPath)||h(o.localPath);if(s==="edge-local"&&c||!J(i,o,a))return null;const d=E(e.bbox)??E(o.bbox)??E(o.downloadActionBbox),l=E(o.downloadActionBbox),f=Q(d,t,n),u=it(d,t,n),x=Q(l,t,n);return{messageKey:e.stableMessageKey,kind:h(a.type)||h(a.kind)||h(o.messageType)||i,fileName:h(a.name)||h(a.fileName)||h(o.fileName)||null,mimeType:h(a.mimeType)||h(o.mimeType)||null,size:S(a.size)??S(o.size),mediaStatus:rt(o,a),observedAt:e.observedAt??null,contextText:r??e.normalizedText??e.anchorText??e.textExcerpt??null,...f?{bbox:f}:{},...u?{screenshotBbox:u}:{},...x?{downloadActionBbox:x}:{}}}function nt(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 rt(e,t){const n=h(e.mediaStatus);if(n==="not_downloaded")return"not_downloaded";if(n==="downloaded"||n==="available")return"available";const r=h(t.availability)||h(e.availability);return r==="pending-download"?"not_downloaded":r==="metadata-only"?"metadata_only":n||null}function J(e,t,n){const r=`${e} ${h(t.messageType)} ${h(n.type)} ${h(n.kind)}`.toLowerCase();return/image|photo|video|file|document/.test(r)}function E(e){if(!b(e))return null;const t=S(e.x),n=S(e.y),r=S(e.width),i=S(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 Q(e,t,n){if(!e)return null;if(e.coordinateSpace==="screen")return e;const r=B(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 it(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 I(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function h(e){return typeof e=="string"?e.trim():""}function g(e){return h(e)||null}function S(e){const t=Number(e);return Number.isFinite(t)?t:null}async function Yt(e,t){await ee(e,t)}async function ee(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(at(r))throw new Error(F);if(r.wechatMainWindowResponsive===!1)throw new Error("wechat_window_unresponsive");if(te([...r.captureCandidates??[],...r.restoreCandidates??[],...r.hiddenRestoreCandidates??[],...r.duplicateMainWindows??[],...r.unsafeAuxiliaryWindows??[]]))throw new Error(F);if(r.wechatWindowAvailable===!1&&String(r.platform??"").toLowerCase()!=="win32")throw new Error("wechat_window_unavailable");return r}async function ot(e,t){const n=await ee(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 ie(e,t);if(!r?.windowId)throw new Error("helper_invalid_response: windows.list missing WeChat window data");return r}function te(e,t){const n=e.filter(v).filter(i=>i.visible!==!1&&i.minimized!==!0),r=t??n.slice().sort((i,o)=>M(o)-M(i))[0];return n.find(i=>st(i,r))??null}function at(e){return e.duplicateInstanceBlocked===!0||Number(e.duplicateMainWindowCount??0)>0||Number(e.unsafeAuxiliaryWindowCount??0)>0||(e.duplicateMainWindows??[]).some(v)||(e.unsafeAuxiliaryWindows??[]).some(v)}function L(e){const t=e.filter(v).filter(i=>i.visible!==!1&&i.minimized!==!0),n=t.filter(re);if(n.length>1)throw new Error(F);const r=n.slice().sort((i,o)=>M(o)-M(i))[0];if(te(t,r))throw new Error(F)}function ne(e){const t=e.filter(v),n=t.filter(o=>o.visible!==!1&&o.minimized!==!0),r=n.filter(re);return(r.length?r:n.length?n:t).slice().sort((o,a)=>M(a)-M(o))[0]??null}function st(e,t){if(!v(e)||t&&dt(e,t)||e.visible===!1||e.minimized===!0)return!1;const n=Number(e.bounds?.width),r=Number(e.bounds?.height);if(!Number.isFinite(n)||!Number.isFinite(r))return!1;const i=n>=360&&n<=720&&r>=420&&r<=840&&r>n,o=n>=420&&n<=680&&r>=260&&r<=460;return!(!i&&!o||t&&ct(e,t))}function ct(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 re(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 dt(e,t){return!!(e.windowId&&t.windowId&&e.windowId===t.windowId)}async function Ht(e,t){let n=await e.request("windows.list",{},t);w(n,"windows.list");let r=n.result?.windows??[];L(r);let i=ne(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 oe(e,i,t)}async function lt(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 ie(e,t){const n=await e.request("windows.list",{},t);w(n,"windows.list");const r=n.result?.windows??[];L(r);const i=ne(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 oe(e,i,t)}async function oe(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(v);return L(i),i.find(o=>o.windowId===t.windowId)??i[0]??t}async function z(e){const{capture:t,ocr:n}=e.observation??await T(e),r=A({capture:t,ocr:n,window:e.window});if(r)return{opened:!1,reason:r};const i=_e({visibleItems:yt(n,t),targets:[{bindingId:e.binding.bindingId,conversationDisplayName:e.binding.conversationDisplayName}]})[0];if(!i)return{opened:!1,reason:"conversation_not_visible"};const o=Ot(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 ae(e){let t={opened:!1,reason:"conversation_not_opened"};for(let r=0;r<Be;r+=1){if(t=await ut(e),t.opened||t.reason!=="conversation_title_not_confirmed")return t;await m(350)}const n=await se(e);return n.opened?{opened:!0,reason:`${n.reason}:final-visible-recovery`}:t}async function ut(e){const t=await T(e),n=A({capture:t.capture,ocr:t.ocr,window:e.window});if(n)return{opened:!1,reason:n};let r=!1;if(K(t.ocr,t.capture,e.binding.conversationDisplayName))return e.settleToBottom!==!1&&await _({helper:e.helper,window:e.window,traceId:e.traceId,observation:t}),{opened:!0,reason:"current-conversation-title-confirmed"};if(!r){const l=await z({...e,observation:t});if(l.opened){if(await m(900),(await k(e)).ok)return{opened:!0,reason:l.reason};const u=await se(e);if(u.opened)return u}}const i=gt(e,t),o=await e.helper.request("wechat.searchConversation",{conversationName:e.binding.conversationDisplayName,waitMs:e.searchDelayMs??700,...i?{searchPoint:i}:{}},e.traceId);w(o,"wechat.searchConversation");const a=await ht(e);if(a.opened){if(await m(900),(await k(e)).ok)return e.settleToBottom!==!1&&await _({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:a.reason};const f=await e.helper.request("wechat.searchConversation",{conversationName:e.binding.conversationDisplayName,waitMs:e.searchDelayMs??700,...i?{searchPoint:i}:{}},e.traceId);w(f,"wechat.searchConversation")}let s=a.observation;if(!y(e.binding.conversationDisplayName)){await xt(e.helper,e.traceId),await m(900);const l=await k(e);if(l.ok)return e.settleToBottom!==!1&&await _({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:l.reason};const f=await e.helper.request("wechat.searchConversation",{conversationName:e.binding.conversationDisplayName,waitMs:e.searchDelayMs??700,...i?{searchPoint:i}:{}},e.traceId);w(f,"wechat.searchConversation"),s=void 0}const c=await z({...e,...s?{observation:s}:{}});if(!c.opened)return c;await m(900);const d=await k(e);return d.ok?(e.settleToBottom!==!1&&await _({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:c.reason}):{opened:!1,reason:d.reason}}async function se(e){const t=await z(e);if(!t.opened)return t;await m(900);const n=await k(e);return n.ok?(e.settleToBottom!==!1&&await _({helper:e.helper,window:e.window,traceId:e.traceId}),{opened:!0,reason:`${t.reason}:retry-visible-title-confirmed`}):{opened:!1,reason:n.reason}}function Ut(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 ht(e){let t;const n=e.searchDelayMs===0?0:Fe,r=y(e.binding.conversationDisplayName)?We:1;for(let i=0;i<r;i+=1){i>0&&n>0&&await m(n);const o=await ft(e);t=o;const{capture:a,ocr:s}=o,c=A({capture:a,ocr:s,window:e.window});if(c)return{opened:!1,reason:c,observation:o};const d=Ct(s,a,e.binding.conversationDisplayName);if(!d){if(Nt(s)||It(s,e.binding.conversationDisplayName))break;continue}const l=B(d.bbox,a,wt(a,e.window));if(!l)return{opened:!1,reason:"search_result_bbox_missing",observation:o};const f=await e.helper.request("mouse.click",{x:l.x,y:l.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 ft(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},r=await le(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 wt(e,t){return e.bounds?{...t,bounds:e.bounds}:t}async function T(e){let{capture:t,ocr:n}=await P(e.helper,e.window.windowId,e.traceId,e.window.bounds);return await bt({helper:e.helper,window:e.window,capture:t,ocr:n,traceId:e.traceId})&&(await m(350),{capture:t,ocr:n}=await P(e.helper,e.window.windowId,e.traceId,e.window.bounds)),{capture:t,ocr:n}}async function bt(e){const t=mt(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 mt(e,t,n){const r=e.blocks??[],i=r.find(c=>{const d=String(c.text||""),l=c.bbox;if(!l)return!1;const f=Number(l.x),u=Number(l.y);return Number.isFinite(f)&&Number.isFinite(u)&&u>t.height*.06&&u<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(),l=c.bbox;if(!l||d!=="\xD7")return!1;const f=Number(l.x),u=Number(l.y);return Number.isFinite(f)&&Number.isFinite(u)&&f>t.width*.55&&f<t.width*.9&&Math.abs(u-a)<t.height*.08}).sort((c,d)=>Number(d.bbox?.x??0)-Number(c.bbox?.x??0))[0];return B(s?.bbox,t,n)}function gt(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 B(i?.bbox,n,e.window)}async function ce(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 de(e){return e.capture.dataBase64?e.capture:ce(e.helper,e.window.windowId,e.traceId,e.window.bounds)}async function P(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 ce(e,t,n,r),a=await le(e,o,n);return{capture:o,ocr:a}}async function le(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 v(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 m(e){return new Promise(t=>setTimeout(t,e))}async function xt(e,t){const n=await e.request("keyboard.shortcut",{key:"return",modifiers:[]},t);w(n,"keyboard.shortcut")}async function _(e){if(!e.window.bounds)return;const t=e.observation,n=Lt(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);w(i,"mouse.click"),await m(700)}const r=zt(e.window);if(r){const i=await e.helper.request("mouse.click",{x:r.x,y:r.y,coordinateSpace:"screen",windowId:e.window.windowId},e.traceId);w(i,"mouse.click"),await m(150)}for(let i=0;i<6;i+=1){const o=await e.helper.request("keyboard.shortcut",{key:"pagedown",modifiers:[]},e.traceId);w(o,"keyboard.shortcut"),await m(260)}for(let i=0;i<2;i+=1){const o=await e.helper.request("keyboard.shortcut",{key:"end",modifiers:[]},e.traceId);w(o,"keyboard.shortcut"),await m(180)}for(let i=0;i<3;i+=1){const o=await e.helper.request("mouse.scroll",{deltaY:-2400,windowId:e.window.windowId,...r?{point:{...r,coordinateSpace:"screen"}}:{}},e.traceId);w(o,"mouse.scroll"),await m(180)}await m(220)}async function k(e){const{capture:t,ocr:n}=await P(e.helper,e.window.windowId,e.traceId,e.window.bounds),r=A({capture:t,ocr:n,window:e.window});return r?{ok:!1,reason:r}:K(n,t,e.binding.conversationDisplayName)?{ok:!0,reason:"target-conversation-title-confirmed"}:{ok:!1,reason:"conversation_title_not_confirmed"}}function A(e){const t=(e.ocr.blocks??[]).map(r=>kt(r.text)).filter(Boolean),n=t.join("");return/(扫码登录|仅传输文件|重新登录|为了安全|安全验证|登录微信|微信登录|该账号已登录|帐号已登录|账号已登录|当前账号已登录|当前帐号已登录|已登录|进入微信|進入微信)/u.test(n)||Mt(e,t)?q:null}function yt(e,t){const n=vt(e.blocks??[],t);return n.length?n:e.visibleConversationFingerprints??[]}function vt(e,t){const n=e.map(i=>ue(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=>!he(i.text)),r=new Set;return n.filter(i=>!pt(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=W(i.title);return!o||r.has(o)?!1:(r.add(o),!0)})}function ue(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 pt(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 he(e){const t=C(e);return/^(搜索|微信|通讯录|收藏|设置|文件传输助手|search)$/iu.test(t)||R(t)||St(t)||/^包含[::]?/u.test(t)||/搜索网络结果/u.test(t)}function Ct(e,t,n){const r=(e.blocks??[]).map(s=>ue(s)).filter(s=>!!s),i=r.filter(s=>R(C(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(u=>C(u.text)===s);if(c<0)continue;const d=i[c],l=i[c+1]?.y??Number.POSITIVE_INFINITY,f=r.filter(u=>u.y>d.y&&u.y<l).filter(u=>u.x<t.width*.7).filter(u=>u.text.length>=2).filter(u=>!R(C(u.text))).filter(u=>!we(u,r)).filter(u=>D(u.text,n)).sort((u,x)=>u.y-x.y||u.x-x.x)[0];if(f)return{bbox:fe(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=>!he(s.text)).filter(s=>!we(s,r)).filter(s=>D(s.text,n)).sort((s,c)=>s.y-c.y||s.x-c.x)[0];return a?{bbox:fe(a,t),reason:"search-result-title"}:null}function fe(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 R(e){return/^(群聊|联系人|聊天记录|公众号|小程序|GroupChats|Contacts|ChatHistory|OfficialAccounts|MiniPrograms)$/u.test(e)}function Nt(e){return(e.blocks??[]).some(t=>R(C(t.text)))}function It(e,t){return(e.visibleConversationFingerprints??[]).some(n=>D(n.title,t))}function C(e){return String(e||"").normalize("NFKC").replace(/[^\p{L}\p{N}_-]/gu,"").trim()}function we(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 St(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 Mt(e,t){if(!v(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 kt(e){return String(e||"").normalize("NFKC").replace(/\s+/g,"").trim()}function K(e,t,n){const r=y(n)?W(n):O(n);return r?(e.blocks??[]).some(i=>{const o=y(n)?W(i.text):O(i.text);if(!o||!xe(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,l=s<t.width*.18&&c<t.height*.12;return d||l}):!1}function be(e){if(K(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
|
+
`),i=[{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(i>=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)&&i>=1||/\.(png|jpe?g|gif|webp|bmp|heic|mp4|mov|m4v|avi|mkv|webm)\b/i.test(o)&&Tt(e.capture,e.ocr)?!0:((e.ocr.visibleConversationFingerprints??[]).some(s=>{const c=y(e.binding.conversationDisplayName)?W(s.title):O(s.title),d=y(e.binding.conversationDisplayName)?W(e.binding.conversationDisplayName):O(e.binding.conversationDisplayName);return!!(c&&d&&xe(c,d))}),!1)}function Tt(e,t){const n=(t.blocks??[]).map(o=>String(o.text||"").normalize("NFKC").trim()).filter(Boolean),r=n.join(`
|
|
6
|
+
`);if(/\b\d+\s*x\s*\d+\b/i.test(r)&&/\b\d+(\.\d+)?\s*(B|KB|MB|GB)\b/i.test(r)||/\b100%\b/.test(r)&&n.length<=20)return!0;const i=n.filter(o=>!/^(□|×|\+|…|\.\.\.|100%|\d+|发送)$/u.test(o));return e.width>600&&e.height>500&&i.length<=6}function me(e){const t=e.anchors.map(o=>({...o,normalized:p(o.expectedEchoAnchor)})).filter(o=>o.normalized);if(!t.length)return[];const n=[],r=new Set,i=_t(e.ocr,e.screenshot).sort((o,a)=>p(a.text).length-p(o.text).length);for(const o of i){const a=p(o.text);if(!a)continue;const s=t.find(d=>Bt(d.normalized,a));if(!s)continue;const c=`local-outbound-echo:${e.binding.bindingId}:${s.idempotencyKey||s.replyId||s.normalized}`;r.has(c)||(r.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 _t(e,t){const n=(e.blocks??[]).filter(i=>Rt(i,t)&&p(i.text)).sort((i,o)=>N(i.bbox,"y")-N(o.bbox,"y")),r=n.map(i=>({text:String(i.text||"").trim(),bbox:i.bbox}));for(let i=0;i<n.length;i+=1){const o=[n[i]];for(let a=i+1;a<n.length&&o.length<5;a+=1){const s=n[a];if(!Wt(o[o.length-1],s,t))break;o.push(s),r.push({text:Ft(o.map(c=>String(c.text||"").trim())),bbox:At(o.map(c=>c.bbox))})}}return r}function Bt(e,t){if(!e||!t)return!1;if(V(e,t)>=.88)return!0;const n=e.replace(/\s+/g,""),r=t.replace(/\s+/g,"");return r.length>=8&&n.includes(r)}function Wt(e,t,n){const r=N(e.bbox,"x"),i=N(e.bbox,"y"),o=N(e.bbox,"height"),a=N(t.bbox,"x"),s=N(t.bbox,"y");if(![r,i,o,a,s].every(Number.isFinite)||r<n.width*.5||a<n.width*.5)return!1;const c=s-(i+o);return c>=-8&&c<=Math.max(64,n.height*.04)&&Math.abs(a-r)<=120}function Ft(e){return e.reduce((t,n)=>t?n?`${t}${Et(t,n)?" ":""}${n}`:t:n,"")}function Et(e,t){return/[A-Za-z0-9]$/u.test(e)&&/^[A-Za-z0-9]/u.test(t)}function At(e){const t=e.filter(c=>!!c);if(!t.length)return;const n=t.map(c=>Number(c.x)).filter(Number.isFinite),r=t.map(c=>Number(c.y)).filter(Number.isFinite),i=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||!r.length||!i.length||!o.length)return t[0];const a=Math.min(...n),s=Math.min(...r);return{x:a,y:s,width:Math.max(...i)-a,height:Math.max(...o)-s,coordinateSpace:t.find(c=>c.coordinateSpace)?.coordinateSpace}}function N(e,t){const n=Number(e?.[t]);return Number.isFinite(n)?n:Number.NaN}function ge(e,t){if(!t.length)return e;const n=[...e];for(const r of t){const i=p(r.anchorText||r.normalizedText||r.textExcerpt);n.some(a=>{if(a.senderRole!=="self")return!1;const s=p(a.anchorText||a.normalizedText||a.textExcerpt);return s&&i&&V(s,i)>=.9})||n.push(r)}return n}function Rt(e,t){const n=e.bbox;if(!n)return!1;const r=Number(n.x),i=Number(n.y);return!Number.isFinite(r)||!Number.isFinite(i)?!1:r>t.width*.34&&r<t.width*.96&&i>t.height*.12&&i<t.height*.86}function Ot(e,t,n){if(!e||typeof e!="object")return null;const r=e,i=Number(r.x),o=Number(r.y),a=Number(r.width),s=Number(r.height);if(![i,o,a,s].every(Number.isFinite))return null;const c=typeof r.coordinateSpace=="string"?r.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,f={x:i+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 f;const u=Math.max(0,(t.width-n.bounds.width*d)/2),x=Math.max(0,(t.height-n.bounds.height*l)/2);return{x:n.bounds.x+(f.x-u)/d,y:n.bounds.y+(f.y-x)/l}}function B(e,t,n){if(!e||typeof e!="object")return null;const r=e,i=Number(r.x),o=Number(r.y),a=Number(r.width),s=Number(r.height);if(![i,o,a,s].every(Number.isFinite))return null;const c=typeof r.coordinateSpace=="string"?r.coordinateSpace:void 0,d={x:i+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,f=t.height&&n.bounds.height?t.height/n.bounds.height:l,u=Math.max(0,(t.width-n.bounds.width*l)/2),x=Math.max(0,(t.height-n.bounds.height*f)/2);return{x:n.bounds.x+(d.x-u)/l,y:n.bounds.y+(d.y-x)/f}}function Lt(e,t,n){if(!e?.blocks?.length||!t)return null;const r=e.blocks.find(i=>{const o=String(i.text||"").trim(),a=i.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 B(r?.bbox,t,n)}function zt(e){const t=e.bounds;return!t?.width||!t?.height?null:{x:t.x+t.width*.54,y:t.y+t.height*.7}}function xe(e,t){return y(t)?ve(e,t):e===t||e.includes(t)||t.includes(e)}function D(e,t){if(y(t))return ve(e,t);const n=ye(C(e)),r=ye(C(t));return!n||!r?!1:n===r?!0:n.replace(/\d+$/u,"")===r}function ye(e){return e.toLowerCase().replace(/[l1]/g,"i")}function O(e){return String(e||"").normalize("NFKC").replace(/[((]\d+[))]/g,"").replace(/\s+/g,"").replace(/[l1]/gi,"i").toLowerCase().trim()}function W(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 ve(e,t){const n=String(e||"").normalize("NFKC").replace(/[((]\d+[))]/g,"").replace(/\s+/g,"").trim(),r=String(t||"").normalize("NFKC").replace(/[((]\d+[))]/g,"").replace(/\s+/g,"").trim();return!!(n&&r&&(n===r||n.replace(/\d+$/u,"")===r))}export{P as captureAndRecognizeWeChatWindow,ce as captureWeChatWindow,Yt as ensureHelperPreflight,ot as ensureWeChatWindowReady,Ut as fallbackWeChatMessageInputPointForWindow,ie as focusExistingWeChatWindow,lt as focusKnownWeChatWindow,Ht as focusWeChatWindow,jt as observeWeChatChannelBindingViaHelper,ae as openConversationBySearch,z as openConversationInVisibleList,le as recognizeWeChatScreenshot};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { WeChatChannelHelperTransport } from './observer.js';
|
|
1
|
+
import type { WeChatChannelHelperTransport, WeChatChannelScreenPoint } from './observer.js';
|
|
2
2
|
import { type WeChatChannelOutboundLedger, type WeChatChannelOutboundRecord, type WeChatChannelOutboundCommitStage } from './outbound-ledger.js';
|
|
3
3
|
import { type WeChatChannelActivityGatePolicy } from './human-coordination.js';
|
|
4
4
|
import type { WeChatChannelHumanActivityReasonCode } from './helper-protocol.js';
|
|
@@ -25,11 +25,13 @@ export declare class WeChatChannelOutboundSender {
|
|
|
25
25
|
openConversation: (conversationName: string) => Promise<{
|
|
26
26
|
opened: boolean;
|
|
27
27
|
reason: string;
|
|
28
|
+
inputPoint?: WeChatChannelScreenPoint | null;
|
|
28
29
|
}>;
|
|
29
30
|
traceId?: string;
|
|
30
31
|
activityGatePolicy?: Partial<WeChatChannelActivityGatePolicy>;
|
|
31
32
|
takeoverCheck?: boolean;
|
|
32
33
|
platform?: NodeJS.Platform | string;
|
|
34
|
+
postPasteSettleMs?: number;
|
|
33
35
|
});
|
|
34
36
|
canStart(_record: WeChatChannelOutboundRecord, now?: Date): Promise<{
|
|
35
37
|
ok: true;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import
|
|
1
|
+
import v from"node:fs";import A from"node:path";import{guardWeChatOutboundRevision as C,markWeChatOutboundFailed as S,markWeChatOutboundManualReview as k,markWeChatOutboundCommitStage as r,markWeChatOutboundSending as x,markWeChatOutboundSentUnconfirmed as T,markWeChatOutboundUserActiveTimeout as h,markWeChatOutboundWaitingUserIdle as w,shouldFailWeChatOutboundForUserActiveTimeout as d}from"./outbound-ledger.js";import{decideWeChatChannelActivityGate as _,nextWeChatChannelActivityRetryAt as I,normalizeWeChatChannelActivitySnapshot as R}from"./human-coordination.js";const W=600*1e3,b=450,y=520;async function j(e){const t={sentRecords:[],staleRecords:[],failedRecords:[],waitingRecords:[],manualReviewRecords:[]},s=e.now??new Date,n=e.maxUserActivityWaitMs??W,i=e.ledger.records.filter(a=>a.bindingId===e.bindingId&&a.sendStatus==="queued");for(const a of i){if(d({record:a,now:s,maxWaitMs:n})){h(a,s),t.failedRecords.push(a);continue}if(!C({record:a,currentLastInboundRevision:e.currentLastInboundRevision,now:s}).ok){t.staleRecords.push(a);continue}try{const o=await e.sender.canStart(a,s);if(o.ok===!1){w({record:a,reasonCode:o.reasonCode,nextAttemptAt:o.nextAttemptAt,now:s}),d({record:a,now:s,maxWaitMs:n})?(h(a,s),t.failedRecords.push(a)):t.waitingRecords.push(a);continue}x(a),await e.sender.send(a),T(a,s),t.sentRecords.push(a)}catch(o){if(o instanceof f){if(D(o)){k(a,"user_takeover_before_return","User activity was detected after paste and before Return; manual WeChat inspection is required",o.stage),t.manualReviewRecords.push(a);continue}w({record:a,reasonCode:o.reasonCode,nextAttemptAt:o.nextAttemptAt,now:s}),d({record:a,now:s,maxWaitMs:n})?(h(a,s),t.failedRecords.push(a)):t.waitingRecords.push(a);continue}S(a,q(o),o instanceof Error?o.message:String(o)),t.failedRecords.push(a)}}return t}class B{options;activeAutomationLeaseId=null;constructor(t){this.options=t}async canStart(t,s=new Date){const n=await this.activitySnapshot();U(n,this.options.platform);const i=_({snapshot:n,stage:"send_start",policy:this.options.activityGatePolicy});return i.ok===!1?{ok:!1,reasonCode:i.reasonCode,nextAttemptAt:I(s,i.waitMs)}:{ok:!0}}async send(t){await this.withAutomationLease(t,()=>this.sendWithLease(t))}async sendWithLease(t){await this.ensureStageIdle(t,"open_conversation"),r(t,"open_conversation");const s=await this.options.openConversation(t.conversationName);if(!s.opened)throw new Error(s.reason||"conversation_not_opened");r(t,"focus_input"),await L(this.options.helper,this.options.traceId,s.inputPoint??void 0),await this.ensureStageIdle(t,"focus_input"),await this.ensureStageIdle(t,"clipboard_snapshot"),r(t,"clipboard_snapshot");const n=await this.options.helper.request("clipboard.snapshot",{},this.options.traceId);c(n,"clipboard.snapshot");let i=!1;try{await this.sendTextIfPresent(t,()=>{i=!0}),await this.sendAttachmentsIfPresent(t,()=>{i=!0})}finally{const a=n.result&&typeof n.result=="object"?n.result:{},p=await this.options.helper.request("clipboard.restore",a,this.options.traceId);i||c(p,"clipboard.restore")}}async sendTextIfPresent(t,s){if(!t.text)return;await this.ensureStageIdle(t,"clipboard_set"),r(t,"clipboard_set"),await u(this.options.helper,"a",m(this.options.platform),this.options.traceId),await u(this.options.helper,"backspace",[],this.options.traceId);const n=await this.options.helper.request("clipboard.setText",{text:t.text},this.options.traceId);c(n,"clipboard.setText"),await this.ensureStageIdle(t,"paste"),r(t,"paste"),await u(this.options.helper,"v",m(this.options.platform),this.options.traceId),await l(this.options.postPasteSettleMs??b),await this.ensureStageIdle(t,"before_return"),r(t,"before_return"),await u(this.options.helper,"return",[],this.options.traceId),s(),r(t,"after_return")}async sendAttachmentsIfPresent(t,s){for(const n of t.attachmentLocalRefs??[]){O(n),await this.ensureStageIdle(t,"clipboard_set"),r(t,"clipboard_set");const i=E(n),a=i==="image"&&g(this.options.platform)?await this.options.helper.request("clipboard.setImage",{filePath:n},this.options.traceId):await this.options.helper.request("clipboard.setFiles",{filePaths:[n]},this.options.traceId);c(a,i==="image"&&g(this.options.platform)?"clipboard.setImage":"clipboard.setFiles"),await this.ensureStageIdle(t,"paste"),r(t,"paste"),await u(this.options.helper,"v",m(this.options.platform),this.options.traceId),await l(this.options.postPasteSettleMs??b),await this.ensureStageIdle(t,"before_return"),r(t,"before_return"),await u(this.options.helper,"return",[],this.options.traceId),s(),r(t,"after_return")}}async withAutomationLease(t,s){const n=await this.options.helper.request("automation.lease.acquire",{owner:"wechat-channel",purpose:`send:${t.bindingId}`,ttlMs:6e4},this.options.traceId);c(n,"automation.lease.acquire");const i=typeof n.result?.leaseId=="string"?n.result.leaseId:"";try{this.activeAutomationLeaseId=i||null,await s()}finally{this.activeAutomationLeaseId=null,i&&await this.options.helper.request("automation.lease.release",{leaseId:i},this.options.traceId)}}async ensureStageIdle(t,s){if(this.options.takeoverCheck===!1)return;if(this.activeAutomationLeaseId){const a=await this.automationLeaseDecision();if(a.ok===!1)throw new f({reasonCode:a.reasonCode,nextAttemptAt:a.nextAttemptAt,stage:s,replyId:t.replyId});return}const n=await this.activitySnapshot(),i=_({snapshot:n,stage:M(s),policy:this.options.activityGatePolicy});if(!i.ok)throw new f({reasonCode:i.reasonCode,nextAttemptAt:I(new Date,i.waitMs),stage:s,replyId:t.replyId})}async activitySnapshot(){const t=await this.options.helper.request("activity.snapshot",{},this.options.traceId);return c(t,"activity.snapshot"),R(t.result)}async automationLeaseDecision(){if(!this.activeAutomationLeaseId)return{ok:!0};const t=await this.options.helper.request("automation.lease.status",{},this.options.traceId);c(t,"automation.lease.status");const s=t.result??{};return s.active===!1||s.leaseId!==this.activeAutomationLeaseId||!s.interrupted?{ok:!0}:{ok:!1,reasonCode:P(s.interruptReason),nextAttemptAt:new Date(Date.now()+1e3)}}}class f extends Error{reasonCode;nextAttemptAt;stage;replyId;constructor(t){super(`user_takeover_aborted:${t.reasonCode}:${t.stage}`),this.name="WeChatChannelUserTakeoverAbort",this.reasonCode=t.reasonCode,this.nextAttemptAt=t.nextAttemptAt,this.stage=t.stage,this.replyId=t.replyId}}function M(e){return e==="open_conversation"?"open_conversation":e==="queued"?"send_start":"dangerous_action"}async function L(e,t,s){const n={waitMs:y,...s?{inputPoint:s}:{}};if((await e.request("wechat.focusMessageInput",n,t)).ok){await l(120);const p=await e.request("wechat.focusMessageInput",n,t);c(p,"wechat.focusMessageInput");return}await l(350);const a=await e.request("wechat.focusMessageInput",{...n,waitMs:Math.max(y,700)},t);c(a,"wechat.focusMessageInput")}async function u(e,t,s,n){const i=await e.request("keyboard.shortcut",{key:t,modifiers:s},n);c(i,"keyboard.shortcut")}function O(e){if(!v.statSync(e).isFile())throw new Error(`wechat_channel_attachment_not_file:${e}`)}function E(e){const t=A.extname(e).toLowerCase();return[".png",".jpg",".jpeg",".gif",".webp",".heic",".tiff",".bmp"].includes(t)?"image":[".mp4",".mov",".m4v",".avi",".mkv",".webm"].includes(t)?"video":"file"}function g(e){return(e??process.platform)==="win32"}function m(e){return(e??process.platform)==="win32"?["control"]:["command"]}function U(e,t){if((t??process.platform)!=="darwin")return;const s=e?.permissions;if(!s)return;const n=[s.accessibilityTrusted===!1?"accessibility":null,s.iohidPostGranted===!1?"input-monitoring":null].filter(Boolean);if(n.length)throw new Error(`permission_missing:mac_input:${n.join(",")}`)}function l(e){return new Promise(t=>setTimeout(t,e))}function q(e){const t=e instanceof Error?e.message:String(e);return/conversation_not|conversation.*visible|conversation.*open/i.test(t)?"conversation_not_opened":/permission|accessibility|screen|automation/i.test(t)?"permission_missing":/clipboard/i.test(t)?"clipboard_failed":/attachment|not_file|ENOENT|no such file/i.test(t)?"attachment_unavailable":"send_failed"}function P(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 D(e){return e.stage==="before_return"}function c(e,t){if(!e.ok)throw new Error(`${e.errorCode||"helper_command_failed"}: ${e.errorSummary||t}`)}export{B as WeChatChannelOutboundSender,f as WeChatChannelUserTakeoverAbort,j as sendQueuedWeChatOutboundRecords};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{requiredWeChatChannelHelperCapabilitiesForProfile as
|
|
1
|
+
import{requiredWeChatChannelHelperCapabilitiesForProfile as s,requiredWindowsWeChatChannelHelperCapabilitiesForProfile as t,validateWeChatChannelHelperReady as c}from"./helper-protocol.js";function u(e){const a=[],i=e.platform==="darwin"||e.platform==="win32";if(a.push(r(i,"platform_unsupported","\u5FAE\u4FE1 Channel \u5F53\u524D\u652F\u6301 macOS \u548C Windows \u684C\u9762\u5FAE\u4FE1\u3002")),a.push(r(e.accountTier==="enterprise","enterprise_required","\u5F53\u524D\u9636\u6BB5\u5FAE\u4FE1 Channel \u4EC5\u4F01\u4E1A\u7248\u8D26\u53F7\u53EF\u7528\u3002")),a.push(r(!!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(r(!!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(r(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(r(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(r(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(r(!1,"helper_not_ready","\u5E73\u53F0 helper \u672A\u5C31\u7EEA\u3002"));else{const l=c(e.helperReady,e.expectedHelperVersion,e.requiredHelperCapabilities??h(e.platform,e.binding));l.ok?a.push(r(!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(r(!1,l.errorCode,l.errorSummary))}const n=e.permissions;n&&(a.push(r(n.screenRecording!==!1,"permission_screen_recording_missing","\u7F3A\u5C11\u5C4F\u5E55\u5F55\u5236\u6743\u9650\u3002")),a.push(r(n.automation!==!1,"permission_automation_missing","\u7F3A\u5C11\u81EA\u52A8\u5316\u63A7\u5236\u6743\u9650\u3002")),a.push(r(n.wechatRunning!==!1,"wechat_not_running","\u5FAE\u4FE1\u672A\u8FD0\u884C\u3002")),a.push(r(n.wechatWindowAvailable!==!1,"wechat_window_unavailable","\u5FAE\u4FE1\u7A97\u53E3\u4E0D\u53EF\u7528\u3002")),a.push(r(n.wechatMainWindowResponsive!==!1,"wechat_window_unresponsive","\u5FAE\u4FE1\u7A97\u53E3\u65E0\u54CD\u5E94\uFF0C\u9700\u6062\u590D\u540E\u518D\u7EE7\u7EED\u3002")),a.push(r(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 o=a.filter(l=>!l.ok&&l.severity==="blocking");return{ok:o.length===0,canEnable:o.length===0,status:o.length===0?"ready":"blocked",checks:a}}function r(e,a,i){return{ok:e,code:e?"ok":a,severity:"blocking",message:i}}function h(e,a){const i=e==="win32"?t:s;return!a||a.allowReply!==!1?i("send"):a.downloadMedia!==!1?i("download"):i("observe")}export{u as evaluateWeChatChannelPreflight};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import l from"node:path";import d from"node:fs";import{createWeChatChannelApiClient as b}from"./client.js";import{WeChatChannelHelperClient as c}from"./helper-client.js";import{resolveWeChatChannelHelperAsset as v,WECHAT_CHANNEL_HELPER_VERSION as y}from"./helper-assets.js";import{requiredWindowsWeChatChannelHelperCapabilitiesForProfile as h}from"./helper-protocol.js";import{captureWeChatWindow as W,ensureWeChatWindowReady as p,focusKnownWeChatWindow as f,observeWeChatChannelBindingViaHelper as
|
|
1
|
+
import l from"node:path";import d from"node:fs";import{createWeChatChannelApiClient as b}from"./client.js";import{WeChatChannelHelperClient as c}from"./helper-client.js";import{resolveWeChatChannelHelperAsset as v,WECHAT_CHANNEL_HELPER_VERSION as y}from"./helper-assets.js";import{requiredWindowsWeChatChannelHelperCapabilitiesForProfile as h}from"./helper-protocol.js";import{captureWeChatWindow as W,ensureWeChatWindowReady as p,fallbackWeChatMessageInputPointForWindow as k,focusKnownWeChatWindow as f,observeWeChatChannelBindingViaHelper as S,openConversationBySearch as I,recognizeWeChatScreenshot as P}from"./observer.js";import{WeChatChannelScheduler as A}from"./scheduler.js";import{loadWeChatChannelLedger as O}from"./ledger.js";import{buildWeChatConversationFingerprint as F,matchWeChatConversationFingerprints as x}from"./fingerprint.js";import{WeChatChannelOutboundSender as _}from"./outbound-sender.js";import{decideWeChatChannelActivityGate as D,nextWeChatChannelActivityRetryAt as H,normalizeWeChatChannelActivitySnapshot as T}from"./human-coordination.js";class R{options;scheduler;helper;helperStarted=!1;constructor(e){this.options=e;const n=e.api??b();this.helper=e.helper??M(e),this.scheduler=new A({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=D({snapshot:T(o.result),stage:"observe"});return i.ok?{ok:!0}:{ok:!1,reasonCode:i.reasonCode,nextAttemptAt:H(a,i.waitMs)}}},shouldSkipObserve:t=>this.shouldSkipObserveForUnchangedFingerprint(t),outboundSender:new _({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 I({helper:this.helper,window:o,settleToBottom:!1,binding:{bindingId:`outbound:${t}`,sessionId:"outbound",conversationDisplayName:t,enabled:!0,allowReply:!0,downloadMedia:!0}}),inputPoint:k(o)}}}),recoverWeChat:(t,a)=>this.reportUnrecoverableWindowsWeChat(t,a),observeBinding:(t,a)=>S({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,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 reportUnrecoverableWindowsWeChat(e,n){return this.options.runtime.policy.platform!=="win32",!1}async shouldSkipObserveForUnchangedFingerprint(e){if(this.options.runtime.policy.platform!=="darwin"&&this.options.runtime.policy.platform!=="win32")return{skip:!1};const n=g(this.options.workDir,this.options.runtime.runtimeId),t=u(n,this.options.runtime.runtimeId).bindings[e.bindingId];if(!t?.fingerprint)return{skip:!1};try{const a=await p(this.helper),o=await f(this.helper,a),i=await W(this.helper,o.windowId,void 0,o.bounds),C=await P(this.helper,i),s=w({binding:e,ocr:C,prior:t});return!s?.fingerprint||s.fingerprint!==t.fingerprint?{skip:!1}:{skip:!0,reasonCode:"conversation_fingerprint_unchanged",fingerprint:s.fingerprint}}catch{return{skip:!1}}}}function ee(r){return new R(r)}function E(r,e,n,t=3){return(O(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 M(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=v();if(!e.ok)throw new Error(`${e.reasonCode}: ${e.message}`);return new c({helperPath:e.helperPath,expectedHelperVersion:e.version||y,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)?h("send"):r.bindings.some(e=>e.enabled!==!1&&e.downloadMedia!==!1)?h("download"):h("observe")}function U(r,e){return l.join(r,"wechat-channel",`${e}.ledger.json`)}function g(r,e){return l.join(r,"wechat-channel",`${q(e)}.conversation-fingerprints.json`)}function u(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 L(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:u(r.statePath,r.runtimeId).bindings[r.binding.bindingId]});if(!e?.fingerprint)return;const n=u(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()},L(r.statePath,n)}function w(r){const e=r.ocr.visibleConversationFingerprints??[],n=x({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||F(n.item);return t?{item:n.item,fingerprint:t}:null}function q(r){return String(r||"").trim().replace(/[^a-zA-Z0-9._-]+/g,"_").replace(/^_+|_+$/g,"")||"wechat-channel-runtime"}export{R as WeChatChannelProductRunner,E as buildLocalLedgerTailAnchors,ee as createWeChatChannelProductRunner};
|
|
@@ -58,6 +58,9 @@ export declare function getDaemonStatus(opts?: {
|
|
|
58
58
|
}): DaemonStatus;
|
|
59
59
|
export declare function buildDaemonSpawnOptions(launch: DetachedLaunchSpec, logFd: number, env?: NodeJS.ProcessEnv): Parameters<typeof spawn>[2];
|
|
60
60
|
export declare function captureEnvForService(): Record<string, string>;
|
|
61
|
+
type LaunchdCommandExecutor = (command: string) => void;
|
|
62
|
+
export declare function getLaunchdServiceTarget(uid?: number | string): string;
|
|
63
|
+
export declare function reloadLaunchdAgent(execCommand?: LaunchdCommandExecutor): boolean;
|
|
61
64
|
/**
|
|
62
65
|
* Save current env vars so the daemon can load them at startup.
|
|
63
66
|
* This is critical for launchd/systemd which start with minimal env.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import s from"chalk";import i from"node:fs";import d from"node:path";import a from"node:os";import{execSync as c,spawn as
|
|
2
|
-
`)}function
|
|
1
|
+
import s from"chalk";import i from"node:fs";import d from"node:path";import a from"node:os";import{execSync as c,spawn as se}from"node:child_process";import{randomUUID as ce}from"node:crypto";import{fileURLToPath as ae}from"node:url";import{getShennianDir as ue,loadConfig as C,resolveShennianPath as h,saveConfig as de}from"../config/index.js";import{buildAugmentedPath as le}from"../env-path.js";import{buildWindowsLauncherCommand as pe,buildWindowsScheduledTaskXml as me,buildWindowsStartupVbs as fe,escapeXml as N,isWindowsCmdScript as Se,quoteCmdArg as j,quoteSystemdArg as he}from"./daemon-windows.js";import{buildWindowsLauncherCommand as dn,buildWindowsScheduledTaskXml as ln,buildWindowsStartupVbs as pn}from"./daemon-windows.js";const ge=d.dirname(ae(import.meta.url)),y=ue(),p=h("daemon.pid"),u=h("daemon.log"),b=h("remote-access.disabled"),I=h("daemon-launcher.json"),F=d.resolve(ge,"../../bin/shennian.js"),W=process.execPath,B=new Set(["PATH","HOME","USERPROFILE","HOMEDRIVE","HOMEPATH","USERNAME","USERDOMAIN","COMPUTERNAME","USER","LOGNAME","SHELL","TMPDIR","LANG","LC_ALL","LC_CTYPE","SSH_AUTH_SOCK","XDG_CONFIG_HOME","XDG_DATA_HOME","TEMP","TMP","APPDATA","LOCALAPPDATA","SHENNIAN_DESKTOP_SERVER_URL","SHENNIAN_WECHAT_CHANNEL_HELPER_DIR","SHENNIAN_HOME","SHENNIAN_NATIVE_FUSION_DISABLED"]);function ye(e){return B.has(e)}function l(){const e=a.platform();return e==="darwin"||e==="linux"||e==="win32"?e:"linux"}function L(e){const n=e.replace(/\\/g,"/").toLowerCase(),t=a.tmpdir().replace(/\\/g,"/").toLowerCase();return n.includes("/_npx/")||n.includes("/npm-cache/_npx/")||n.includes("/pnpm/dlx/")||n.startsWith(t.endsWith("/")?t:`${t}/`)}function we(e){return i.existsSync(e.scriptPath)&&!L(e.scriptPath)?{command:e.nodeExec,args:[e.scriptPath,"run-service"],mode:"direct"}:e.shennianCommandPath&&!L(e.shennianCommandPath)?{command:e.shennianCommandPath,args:["run-service"],mode:"global-shim"}:e.npxPath?{command:e.npxPath,args:["--yes","shennian","run-service"],mode:"npx"}:{command:e.nodeExec,args:[e.scriptPath,"run-service"],mode:"direct"}}function ve(e){const n=e.trim();if(!n)return null;if(n.startsWith("{"))try{const r=JSON.parse(n),o=Number(r.pid);return!Number.isInteger(o)||o<=0?null:{pid:o,...typeof r.instanceId=="string"?{instanceId:r.instanceId}:{},...typeof r.version=="string"?{version:r.version}:{},...r.launcher==="desktop-managed"||r.launcher==="global-cli"||r.launcher==="unknown"?{launcher:r.launcher}:{},...typeof r.startedAt=="string"?{startedAt:r.startedAt}:{},...r.adopted===!0?{adopted:!0}:{}}}catch{return null}const t=parseInt(n,10);return isNaN(t)?null:{pid:t}}function R(){try{return ve(i.readFileSync(p,"utf-8"))}catch{return null}}function Ee(){return R()?.pid??null}function w(e){try{return process.kill(e,0),!0}catch{return!1}}function ke(e,n=3e3){const t=Date.now();for(;Date.now()-t<n;){if(!w(e))return!0;Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,100)}return!w(e)}function Pe(){return i.existsSync(b)}function Ae(){try{i.unlinkSync(b)}catch{}}function V(){return"global-cli"}function Ie(){return`${process.pid}-${Date.now()}-${ce()}`}function De(e,n,t={}){i.mkdirSync(y,{recursive:!0}),i.writeFileSync(p,JSON.stringify({pid:e,instanceId:n,...t.version?{version:t.version}:{},launcher:t.launcher??V(),...t.adopted?{adopted:!0}:{},startedAt:new Date().toISOString()},null,2))}function sn(e,n){const t=R();if(!(t?.pid!==e||t.instanceId!==n))try{i.unlinkSync(p)}catch{}}function J(e,n=V(),t){i.mkdirSync(y,{recursive:!0}),i.writeFileSync(I,JSON.stringify({pid:e,launcher:n,...t?{instanceId:t}:{},updatedAt:new Date().toISOString()},null,2))}function O(e){if(e)try{if(JSON.parse(i.readFileSync(I,"utf-8")).instanceId!==e)return}catch{return}try{i.unlinkSync(I)}catch{}}function Ne(e){e&&J(e)}function be(e,n){if(!e||!n)return"unknown";try{const t=JSON.parse(i.readFileSync(I,"utf-8"));if(t.pid!==e)return"unknown";if(t.launcher==="desktop-managed"||t.launcher==="global-cli")return t.launcher}catch{}return $(e)}function $(e){if(l()==="win32")return"unknown";try{const n=c(`ps -p ${e} -o command=`,{encoding:"utf-8",stdio:["ignore","pipe","ignore"],timeout:1e3,windowsHide:!0}).replace(/\\/g,"/");if(n.includes("/node_modules/shennian/")||n.includes("/bin/shennian")||n.includes(" shennian run-service")||n.includes(" shennian.js run-service"))return"global-cli"}catch{}return"unknown"}function Le(e){const n=e.replace(/\\/g,"/").trim();return/^(?:\S*\/)?node\s+\S*(?:\/node_modules\/shennian\/|\/dist\/bin\/shennian\.js|\/bin\/shennian)\s+run-service(?:\s|$)/.test(n)||/^(?:\S*\/)?shennian(?:\.cmd)?\s+run-service(?:\s|$)/.test(n)||/^(?:\S*\/)?npx(?:\.cmd)?\s+(?:--yes\s+)?shennian\s+run-service(?:\s|$)/.test(n)}function G(e=process.pid){if(l()==="win32")return[];try{return c("ps -axo pid=,command=",{encoding:"utf-8",stdio:["ignore","pipe","ignore"],timeout:1e3,windowsHide:!0}).split(/\r?\n/).map(t=>{const r=t.trim().match(/^(\d+)\s+(.+)$/);if(!r)return null;const o=Number(r[1]),S=r[2];return!Number.isInteger(o)||o===e?null:Le(S)?o:null}).filter(t=>typeof t=="number")}catch{return[]}}function f(e={}){let n=R(),t=n?.pid??null,r=t!==null&&w(t),o=t!==null&&!r,S=n?.adopted===!0;const v=C();if(o&&e.cleanupStale){try{i.unlinkSync(p)}catch{}n=null,t=null,r=!1,o=!1}if(!r){const g=G()[0];if(g&&(t=g,r=!0,o=!1,S=!0,e.cleanupStale)){const M=Ie();De(g,M,{launcher:$(g),adopted:!0}),J(g,$(g)),n={pid:g,instanceId:M,adopted:!0}}}return{running:r,pid:t,stale:o,remoteAccessDisabled:Pe(),launcher:be(t,r),platform:l(),shennianDir:y,pidFile:p,logFile:u,...n?.instanceId?{instanceId:n.instanceId}:{},...S?{adopted:!0}:{},...v.machineId?{machineId:v.machineId}:{},paired:!!(v.machineToken&&v.machineId),...v.serverUrl?{serverUrl:v.serverUrl}:{}}}function E(e){process.stdout.write(`${JSON.stringify(e,null,2)}
|
|
2
|
+
`)}function K(e){const n=e?.trim();if(!n)return;const t=C();t.serverUrl=n,de(t)}function X(e){return e?.trim()||process.env.SHENNIAN_DESKTOP_SERVER_URL?.trim()||void 0}const x="com.shennian.agent",m=d.join(a.homedir(),"Library/LaunchAgents",`${x}.plist`),Re=d.join(a.homedir(),"AppData","Roaming","Microsoft","Windows","Start Menu","Programs","Startup"),T=h("autostart.cmd"),H=h("autostart.vbs"),_=h("autostart.xml"),Oe=d.join(Re,"ShennianAgent.vbs");function q(e){const n=l()==="win32"?`where ${e}`:`command -v ${e}`;try{return c(n,{stdio:["ignore","pipe","pipe"],encoding:"utf-8",windowsHide:!0}).split(/\r?\n/).map(o=>o.trim()).find(Boolean)??null}catch{return null}}function $e(){const e=l()==="win32"?"npx.cmd":"npx",n=d.join(d.dirname(W),e);return i.existsSync(n)?n:q("npx")}function k(){return we({nodeExec:W,scriptPath:F,shennianCommandPath:q("shennian"),npxPath:$e()})}function Y(){try{c(`schtasks /delete /tn "${P}" /f`,{stdio:"pipe",windowsHide:!0})}catch{}}function z(){try{i.unlinkSync(Oe)}catch{}}function xe(){const e=process.env.USERDOMAIN?.trim(),n=process.env.COMPUTERNAME?.trim(),t=process.env.USERNAME?.trim()||a.userInfo().username;return e&&e.toUpperCase()!=="WORKGROUP"?`${e}\\${t}`:n?`${n}\\${t}`:t}function Te(e){if(l()==="win32"&&Se(e.command)){const n=[e.command,...e.args].map(j).join(" ");return{command:process.env.ComSpec||"cmd.exe",args:["/d","/s","/c",`"${n}"`],windowsVerbatimArguments:!0}}return{command:e.command,args:e.args}}function He(e,n,t=process.env){return{detached:!0,stdio:["ignore",n,n],env:t,windowsHide:!0,...e.windowsVerbatimArguments?{windowsVerbatimArguments:!0}:{}}}function _e(){const e=k();i.writeFileSync(T,pe(e,u)),i.writeFileSync(H,fe(T)),z(),Y();const n=me({userId:xe(),command:d.join(process.env.SystemRoot||"C:\\Windows","System32","wscript.exe"),arguments:[j(H)]});i.writeFileSync(_,n,"utf8");try{return c(`schtasks /create /tn "${P}" /xml "${_}" /f`,{stdio:"pipe",windowsHide:!0}),c(`schtasks /run /tn "${P}"`,{stdio:"pipe",windowsHide:!0}),!0}catch{return!1}}function Q(){const e={};for(const n of B)process.env[n]&&(e[n]=process.env[n]);if(e.HOME??=a.homedir(),e.PATH=le({pathValue:e.PATH,env:e}),e.USER??=a.userInfo().username,l()==="win32"){const n=process.env.TEMP||process.env.TMP||a.tmpdir(),t=a.homedir();e.USERPROFILE??=t;const r=d.win32.parse(t);e.HOMEDRIVE??=r.root.replace(/[\\/]$/,"")||process.env.HOMEDRIVE||"",e.HOMEPATH??=t.slice((e.HOMEDRIVE||"").length)||process.env.HOMEPATH||"",e.USERNAME??=process.env.USERNAME||a.userInfo().username,e.COMPUTERNAME??=process.env.COMPUTERNAME||"",e.TMPDIR??=n,e.TEMP??=n,e.TMP??=n}else e.TMPDIR??="/tmp";return e}function Ue(){const e=Q(),n=k(),t=Object.entries(e).map(([o,S])=>` <key>${N(o)}</key>
|
|
3
3
|
<string>${N(S)}</string>`).join(`
|
|
4
4
|
`),r=[n.command,...n.args].map(o=>` <string>${N(o)}</string>`).join(`
|
|
5
5
|
`);return`<?xml version="1.0" encoding="UTF-8"?>
|
|
@@ -7,7 +7,7 @@ import s from"chalk";import i from"node:fs";import d from"node:path";import a fr
|
|
|
7
7
|
<plist version="1.0">
|
|
8
8
|
<dict>
|
|
9
9
|
<key>Label</key>
|
|
10
|
-
<string>${
|
|
10
|
+
<string>${x}</string>
|
|
11
11
|
<key>ProgramArguments</key>
|
|
12
12
|
<array>
|
|
13
13
|
${r}
|
|
@@ -27,8 +27,8 @@ ${t}
|
|
|
27
27
|
<key>StandardErrorPath</key>
|
|
28
28
|
<string>${u}</string>
|
|
29
29
|
</dict>
|
|
30
|
-
</plist>`}const D=d.join(a.homedir(),".config/systemd/user/shennian.service");function
|
|
31
|
-
`),r=[n.command,...n.args].map(
|
|
30
|
+
</plist>`}const D=d.join(a.homedir(),".config/systemd/user/shennian.service");function Me(){const e=Q(),n=k(),t=Object.entries(e).map(([o,S])=>`Environment=${o}=${S}`).join(`
|
|
31
|
+
`),r=[n.command,...n.args].map(he).join(" ");return`[Unit]
|
|
32
32
|
Description=Shennian Agent Daemon
|
|
33
33
|
After=network.target
|
|
34
34
|
|
|
@@ -41,6 +41,6 @@ StandardOutput=append:${u}
|
|
|
41
41
|
StandardError=append:${u}
|
|
42
42
|
|
|
43
43
|
[Install]
|
|
44
|
-
WantedBy=default.target`}const
|
|
44
|
+
WantedBy=default.target`}const P="ShennianAgent";function Ce(e){c(e,{stdio:"pipe",windowsHide:!0})}function je(){const e=process.getuid?.();if(typeof e=="number")return e;const n=a.userInfo();return typeof n.uid=="number"?n.uid:n.username}function Z(e=je()){return`gui/${e}/${x}`}function Fe(e=Ce){try{return e(`launchctl unload "${m}" 2>/dev/null; launchctl load -w "${m}"`),e(`launchctl kickstart -k "${Z()}"`),!0}catch{try{return e(`launchctl load -w "${m}"`),e(`launchctl kickstart -k "${Z()}"`),!0}catch{return!1}}}function We(){i.mkdirSync(y,{recursive:!0});const e={};for(const[n,t]of Object.entries(process.env))t!==void 0&&ye(n)&&(e[n]=t);i.writeFileSync(h("env.json"),JSON.stringify(e,null,2))}function ee(e={}){i.mkdirSync(y,{recursive:!0});const n=f({cleanupStale:!0});if(n.pid!==null&&n.running&&!n.adopted){e.quiet||console.log(s.green(`\u2713 Background service already running (PID ${n.pid})`));return}ne(n);const t=i.openSync(u,"a"),r=Te(k()),o=se(r.command,r.args,He(r,t));o.unref(),i.closeSync(t),Ne(o.pid),e.quiet||(console.log(s.green(`\u2713 Background service started (PID ${o.pid})`)),console.log(s.gray(` Logs: ${u}`)))}function ne(e=f({cleanupStale:!0})){if(!(e.pid===null||!e.running||!e.adopted))try{process.kill(e.pid,"SIGTERM"),ke(e.pid);try{i.unlinkSync(p)}catch{}}catch{}}function te(){i.mkdirSync(y,{recursive:!0});const e=l();switch(k().mode==="direct"&&L(F)&&console.warn(s.yellow("\u26A0 Warning: Current CLI path is temporary (npx). Auto-start may not work after reboot.\n Run `npm install -g shennian@latest` for reliable auto-start.")),e){case"darwin":{const t=d.dirname(m);return i.mkdirSync(t,{recursive:!0}),i.writeFileSync(m,Ue()),Fe()}case"linux":{const t=d.dirname(D);i.mkdirSync(t,{recursive:!0}),i.writeFileSync(D,Me());try{c("systemctl --user daemon-reload && systemctl --user enable shennian",{stdio:"pipe",windowsHide:!0})}catch{return!1}try{c(`loginctl enable-linger ${a.userInfo().username}`,{stdio:"pipe",windowsHide:!0})}catch{}try{return c("systemctl --user restart shennian",{stdio:"pipe",windowsHide:!0}),!0}catch{return!1}}case"win32":return _e()}}function Be(){const n=f({cleanupStale:!0}).pid;if(n===null)return{};if(!w(n)){try{i.unlinkSync(p)}catch{}return{stalePid:n}}try{return process.kill(n,"SIGTERM"),{stoppedPid:n}}catch(t){return{error:t instanceof Error?t.message:String(t)}}}async function Ve(e=5e3){const n=Be();if(!n.stoppedPid)return n;if(!await A(n.stoppedPid,e))try{process.kill(n.stoppedPid,"SIGKILL"),await A(n.stoppedPid,2e3)}catch(r){return{...n,error:r instanceof Error?r.message:String(r)}}try{i.unlinkSync(p)}catch{}O();for(const r of G())if(r!==n.stoppedPid)try{process.kill(r,"SIGTERM"),await A(r,2e3)}catch{}return n}function Je(e={}){K(X(e.api)),Ae(),ne(),te()||ee({quiet:e.json}),e.json?E(f()):console.log(s.green("\u2713 Background service started"))}async function Ge(e={}){switch(i.mkdirSync(y,{recursive:!0}),i.writeFileSync(b,new Date().toISOString()),l()){case"darwin":{if(i.existsSync(m))try{c(`launchctl unload -w "${m}"`,{stdio:"pipe",windowsHide:!0})}catch{}break}case"linux":{try{c("systemctl --user disable --now shennian",{stdio:"pipe",windowsHide:!0})}catch{}break}case"win32":{try{c(`schtasks /change /tn "${P}" /disable`,{stdio:"pipe",windowsHide:!0})}catch{}try{c(`schtasks /end /tn "${P}"`,{stdio:"pipe",windowsHide:!0})}catch{}break}}const t=await Ve(),r=f({cleanupStale:!0});if(e.json){E(t.error?{...r,error:t.error}:r);return}t.error?console.error(s.red(`\u2717 Failed to stop: ${t.error}`)):console.log(s.green("\u2713 Background service stopped"))}function re(e){We(),Je(e)}async function U(e={}){await Ge(e)}async function A(e,n=5e3){const t=Date.now();for(;Date.now()-t<n;){if(!w(e))return!0;await new Promise(r=>setTimeout(r,100))}return!w(e)}async function Ke(e={}){K(X(e.api));const n=Ee();if(n!==null&&w(n))try{process.kill(n,"SIGTERM"),await A(n)||(process.kill(n,"SIGKILL"),await A(n,2e3));try{i.unlinkSync(p)}catch{}O(),e.json||console.log(s.green(`\u2713 Background service stopped (PID ${n})`))}catch(t){const r=t instanceof Error?t.message:String(t);e.json?E({...f(),error:r}):console.error(s.red(`\u2717 Failed to stop: ${r}`));return}else if(n!==null){try{i.unlinkSync(p)}catch{}O(),e.json||console.log(s.yellow(`\u26A0 Service process no longer exists, cleaned up stale PID ${n}`))}else e.json||console.log(s.gray("\u25CF Background service not running, starting it now"));if(l()==="linux"&&te()){e.json&&E(f());return}ee({quiet:e.json}),e.json&&E(f())}function ie(e={}){const n=f({cleanupStale:!0});if(e.json){E(n);return}if(n.pid===null){console.log(s.gray("\u25CF Background service not running"));return}n.running?(console.log(s.green(`\u25CF Background service running (PID ${n.pid})`)),console.log(s.gray(` Logs: ${u}`))):console.log(s.yellow(`\u25CF Background service stopped (PID ${n.pid} is stale)`))}function oe(e){if(!i.existsSync(u)){console.log(s.gray("No logs yet"));return}try{const n=c(a.platform()==="win32"?`powershell Get-Content -Tail ${e.lines} "${u}"`:`tail -n ${e.lines} "${u}"`,{encoding:"utf-8",windowsHide:!0});process.stdout.write(n)}catch{const n=i.readFileSync(u,"utf-8").split(`
|
|
45
45
|
`).slice(-e.lines);console.log(n.join(`
|
|
46
|
-
`))}}async function
|
|
46
|
+
`))}}async function Xe(){switch(await U(),l()){case"darwin":{if(i.existsSync(m)){try{c(`launchctl unload -w "${m}"`,{stdio:"pipe",windowsHide:!0})}catch{}i.unlinkSync(m),console.log(s.green("\u2713 Auto-start uninstalled (launchd)"))}break}case"linux":{try{c("systemctl --user disable --now shennian",{stdio:"pipe",windowsHide:!0})}catch{}if(i.existsSync(D)){i.unlinkSync(D);try{c("systemctl --user daemon-reload",{stdio:"pipe",windowsHide:!0})}catch{}}console.log(s.green("\u2713 Auto-start uninstalled (systemd)"));break}case"win32":{Y();try{i.unlinkSync(T)}catch{}try{i.unlinkSync(H)}catch{}try{i.unlinkSync(_)}catch{}z(),console.log(s.green("\u2713 Auto-start uninstalled (Task Scheduler)"));break}}}function cn(e){e.command("start").description("Start the background service").option("--json","Print machine-readable status JSON").option("--api <url>","Server URL override").action(r=>re(r)),e.command("stop").description("Stop the background service").option("--json","Print machine-readable status JSON").action(r=>U(r)),e.command("status").description("Show background service status").option("--json","Print machine-readable status JSON").action(r=>ie(r)),e.command("logs").description("Show recent logs").option("-n, --lines <n>","Number of lines","50").action(r=>oe({lines:parseInt(r.lines,10)}));const n=e.command("daemon",{hidden:!0}).description("Deprecated: use top-level start/stop/status/logs"),t=(r,o)=>{o||console.error(s.yellow(`\u26A0 Deprecated command. Use \`shennian ${r}\` instead.`))};n.command("start").description("Start the background service").option("--json","Print machine-readable status JSON").option("--api <url>","Server URL override").action(r=>{t("start",r.json),re(r)}),n.command("stop").description("Stop the background service").option("--json","Print machine-readable status JSON").action(async r=>{t("stop",r.json),await U(r)}),n.command("restart").description("Restart the background service").option("--json","Print machine-readable status JSON").option("--api <url>","Server URL override").action(r=>{t("stop && shennian start",r.json),Ke(r)}),n.command("status").description("Show background service status").option("--json","Print machine-readable status JSON").action(r=>{t("status",r.json),ie(r)}),n.command("logs").description("Show recent logs").option("-n, --lines <n>","Number of lines","50").action(r=>{t("logs"),oe({lines:parseInt(r.lines,10)})}),n.command("uninstall").description("Uninstall auto-start service").action(Xe)}export{He as buildDaemonSpawnOptions,dn as buildWindowsLauncherCommand,ln as buildWindowsScheduledTaskXml,pn as buildWindowsStartupVbs,Q as captureEnvForService,O as clearDaemonLauncher,sn as clearDaemonPidIfOwner,Ae as clearRemoteAccessDisabled,Ie as createDaemonInstanceId,G as findRunningDaemonProcessIds,V as getCurrentProcessDaemonLauncher,f as getDaemonStatus,Z as getLaunchdServiceTarget,te as installService,L as isEphemeralCliPath,Pe as isRemoteAccessDisabled,ye as isSafeSnapshotEnvKey,Le as isShennianRunServiceCommand,Ne as recordStartedDaemon,cn as registerDaemonCommand,Fe as reloadLaunchdAgent,we as resolveServiceLaunchSpec,We as saveEnvSnapshot,ee as startDaemonProcess,J as writeDaemonLauncher,De as writeDaemonPid};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import
|
|
2
|
-
`)}function
|
|
3
|
-
\u2717 Pairing code expired. Please re-run: shennian pair`)),process.exit(1))),!i.ok)continue;const
|
|
1
|
+
import a from"chalk";import y from"node:os";import U from"node:readline";import{loadConfig as d,saveConfig as T}from"../config/index.js";import{detectAgents as S}from"../agents/detect.js";import{clearRemoteAccessDisabled as v,startDaemonProcess as P,installService as N,saveEnvSnapshot as R}from"./daemon.js";import{detectAndChooseServer as I}from"../region.js";import{buildPairQrPayload as O,PAIR_QR_RENDER_OPTIONS as $}from"./pair-qr.js";const j=3e3,E=600*1e3;function s(o){process.stdout.write(`${JSON.stringify(o)}
|
|
2
|
+
`)}function w(o,e){s({type:"error",code:o,message:e}),process.exit(1)}function _(o){const e="\u2500".repeat(o.length+4);console.log(a.cyan(`\u250C${e}\u2510`)),console.log(a.cyan("\u2502 ")+a.bold.yellow(o)+a.cyan(" \u2502")),console.log(a.cyan(`\u2514${e}\u2518`))}async function C(o){const e=(await import("qrcode-terminal")).default;return new Promise(n=>{e.generate(o,$,r=>{console.log(r),n()})})}async function D(o,e,n={}){const r=Date.now()+E;for(n.json&&s({type:"pair.waiting"});Date.now()<r;){await new Promise(m=>setTimeout(m,j));let i;try{i=await fetch(`${o}/api/machines/pair/status?token=${encodeURIComponent(e)}`)}catch{continue}if(i.status===410&&(n.json?w("PAIR_EXPIRED","Pairing code expired"):(console.error(a.red(`
|
|
3
|
+
\u2717 Pairing code expired. Please re-run: shennian pair`)),process.exit(1))),!i.ok)continue;const t=await i.json();if(t.status==="claimed"&&t.machineToken&&t.machineId)return n.json&&s({type:"pair.claimed"}),{machineToken:t.machineToken,machineId:t.machineId,machineName:t.machineName??""}}return null}function L(o){const e=U.createInterface({input:process.stdin,output:process.stdout});return new Promise(n=>{e.question(o,r=>{e.close(),n(r.trim().toLowerCase()==="y")})})}async function A(o){const{serverUrl:e,machineName:n,force:r,json:i}=o,t=d();if(t.machineToken&&t.machineId&&!r){i?s({type:"done",status:"already-paired",machineId:t.machineId}):(console.log(a.green(`\u2713 Already paired (machine ID: ${t.machineId})`)),console.log(a.gray(' Run "shennian pair" to re-pair to another account')));return}const c=S().map(g=>g.type),l=y.platform(),u=l==="win32"?"windows":l==="darwin"?"macos":"linux";i||(console.log(a.bold(`
|
|
4
4
|
Shennian CLI \u2014 Pairing
|
|
5
|
-
`)),console.log(
|
|
6
|
-
\u2717 Timed out. Pairing code expired, please try again.`)),process.exit(1)),
|
|
7
|
-
\u2713 Paired successfully! Machine "${f.machineName}" is now linked.`))}async function b(o){const e=
|
|
8
|
-
Starting background service...`)),N()||P()}function z(o){o.command("desktop-register",{hidden:!0}).description("Register this desktop-managed machine to the authenticated account").option("--api <url>","Server URL").option("--name <name>","Machine name",
|
|
9
|
-
Starting background service...`)),N()||P({quiet:!!e.json}),e.json&&s({type:"done",status:"paired",machineId:
|
|
5
|
+
`)),console.log(a.gray(` Machine: ${n} | OS: ${u} | Agents: ${c.join(", ")||"none"}`)),console.log());let h;try{h=await fetch(`${e}/api/machines/pair/create`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:n,os:u,agentList:c})})}catch{const g=`Network error: cannot connect to ${e}`;i&&w("NETWORK_ERROR",g),console.error(a.red(`\u2717 ${g}`)),process.exit(1)}if(!h.ok){const k=`Failed to create machine: ${(await h.json().catch(()=>({}))).error??h.statusText}`;i&&w("PAIR_CREATE_FAILED",k),console.error(a.red(`\u2717 ${k}`)),process.exit(1)}const p=await h.json(),x=O(p.pairToken);i?s({type:"pair.created",code:p.pairToken,machineId:p.machineId,expiresAt:new Date(Date.now()+E).toISOString()}):(console.log(a.white("Scan QR code or enter the pairing code in the Shennian app:")),console.log(),await C(x),_(p.pairToken),console.log(),console.log(a.gray("Waiting for pairing... (Ctrl+C to cancel)")));const f=await D(e,p.pairToken,{json:i});f||(i&&w("PAIR_TIMEOUT","Timed out waiting for pairing"),console.error(a.red(`
|
|
6
|
+
\u2717 Timed out. Pairing code expired, please try again.`)),process.exit(1)),T({...t,machineToken:f.machineToken,machineId:f.machineId,serverUrl:e}),i&&s({type:"pair.saved",machineId:f.machineId}),i||console.log(a.green(`
|
|
7
|
+
\u2713 Paired successfully! Machine "${f.machineName}" is now linked.`))}async function b(o){const e=d(),r=S().map(l=>l.type),i=y.platform(),t=i==="win32"?"windows":i==="darwin"?"macos":"linux",m=await fetch(`${o.serverUrl}/api/machines/desktop/register`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${o.authToken}`},body:JSON.stringify({machineId:e.machineId,name:o.machineName,os:t,agentList:r})});if(!m.ok){const u=(await m.json().catch(()=>({}))).error??m.statusText;throw new Error(`Failed to register desktop machine: ${u}`)}const c=await m.json();if(!c.machine?.id||!c.machineToken)throw new Error("Failed to register desktop machine: missing machine token");return e.machineId=c.machine.id,e.machineToken=c.machineToken,e.serverUrl=o.serverUrl,T(e),{machineId:c.machine.id,machineName:c.machine.name}}async function M(o){const e=o?.trim();if(e)return e;const n=process.env.SHENNIAN_DESKTOP_SERVER_URL?.trim();if(n)return n;const r=d().serverUrl?.trim();return r||I()}async function W(o,e){const n=d();if(!n.machineToken||!n.machineId){const i=o??await I();await A({serverUrl:i,machineName:e,force:!1})}else console.log(a.green(`\u2713 Already paired (machine ID: ${n.machineId})`));R(),v(),console.log(a.gray(`
|
|
8
|
+
Starting background service...`)),N()||P()}function z(o){o.command("desktop-register",{hidden:!0}).description("Register this desktop-managed machine to the authenticated account").option("--api <url>","Server URL").option("--name <name>","Machine name",y.hostname()).action(async e=>{try{const n=process.env.SHENNIAN_DESKTOP_AUTH_TOKEN;if(!n)throw new Error("Missing desktop auth token");const r=await M(e.api),i=await b({serverUrl:r,authToken:n,machineName:e.name});console.log(JSON.stringify({ok:!0,...i}))}catch(n){console.error(n instanceof Error?n.message:String(n)),process.exit(1)}}),o.command("pair").description("Pair this machine to a Shennian account (re-pair if already paired)").option("--api <url>","Server URL override").option("--server <url>","Server URL override").option("--name <name>","Machine name",y.hostname()).option("--json","Emit machine-readable JSONL events").option("--force","Force re-pair even when a machine token exists").action(async e=>{const n=d();if(n.machineToken&&n.machineId&&!e.force){if(e.json){s({type:"done",status:"already-paired",machineId:n.machineId});return}if(console.log(a.yellow(`Currently paired (machine ID: ${n.machineId})`)),!await L("Re-pairing will unlink the current account. Continue? (y/N) ")){console.log(a.gray("Cancelled"));return}}const r=e.server??e.api??await I(),i=!!(e.force||n.machineToken&&n.machineId);await A({serverUrl:r,machineName:e.name,force:i,json:!!e.json}),R(),v(),e.json?s({type:"daemon.starting"}):console.log(a.gray(`
|
|
9
|
+
Starting background service...`)),N()||P({quiet:!!e.json}),e.json&&s({type:"done",status:"paired",machineId:d().machineId})})}export{b as registerDesktopMachine,z as registerPairCommand,A as runPairFlow,W as runSmartStart};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Command } from 'commander';
|
|
2
|
-
import type { ExternalMessageEvent } from '../channels/base.js';
|
|
2
|
+
import type { ExternalMessageEvent, ExternalMessageAttachment, ExternalReplyAttachment } from '../channels/base.js';
|
|
3
3
|
export type WeChatDoctorCheck = {
|
|
4
4
|
id: string;
|
|
5
5
|
ok: boolean;
|
|
@@ -35,6 +35,9 @@ export type WeChatToolOptions = {
|
|
|
35
35
|
sessionId?: string;
|
|
36
36
|
workDir?: string;
|
|
37
37
|
source?: string;
|
|
38
|
+
download?: 'auto' | 'never';
|
|
39
|
+
traceId?: string;
|
|
40
|
+
timeoutMs?: number;
|
|
38
41
|
recentLimit?: number;
|
|
39
42
|
pollIntervalMs?: number;
|
|
40
43
|
fetchImpl?: typeof fetch;
|
|
@@ -50,31 +53,52 @@ export type WeChatDirectToolRuntime = {
|
|
|
50
53
|
};
|
|
51
54
|
export type WeChatSendOptions = WeChatToolOptions & {
|
|
52
55
|
text: string;
|
|
56
|
+
attachment?: ExternalReplyAttachment;
|
|
53
57
|
idempotencyKey?: string;
|
|
58
|
+
dryRun?: boolean;
|
|
54
59
|
};
|
|
55
60
|
export type WeChatReadLatestOptions = WeChatToolOptions & {
|
|
56
61
|
limit?: number;
|
|
57
62
|
};
|
|
58
63
|
export type WeChatSendResult = {
|
|
59
64
|
ok: true;
|
|
65
|
+
operation: 'write';
|
|
66
|
+
conversation: string;
|
|
60
67
|
conversationName: string;
|
|
61
68
|
conversationId: string;
|
|
62
69
|
sessionId: string;
|
|
63
70
|
channelId: string;
|
|
64
|
-
|
|
71
|
+
traceId?: string;
|
|
72
|
+
outDir: string;
|
|
73
|
+
helperTracePath?: string | null;
|
|
74
|
+
sendStatus: 'dry_run' | 'queued' | 'sent' | 'sent_unconfirmed' | 'confirmed_echo' | 'manual_review' | 'pending' | 'ok';
|
|
65
75
|
pending?: boolean;
|
|
76
|
+
attachment?: Pick<ExternalReplyAttachment, 'kind' | 'name' | 'mimeType' | 'size' | 'localPath'>;
|
|
77
|
+
attachments: Array<Pick<ExternalReplyAttachment, 'kind' | 'name' | 'mimeType' | 'size' | 'localPath'>>;
|
|
78
|
+
dryRun?: boolean;
|
|
79
|
+
reasonCode?: null;
|
|
80
|
+
message?: string;
|
|
66
81
|
};
|
|
67
82
|
export type WeChatReadLatestResult = {
|
|
68
83
|
ok: true;
|
|
84
|
+
operation: 'read';
|
|
85
|
+
conversation: string;
|
|
69
86
|
conversationName: string;
|
|
70
87
|
conversationId: string;
|
|
71
88
|
sessionId: string;
|
|
72
89
|
channelId: string;
|
|
90
|
+
traceId?: string;
|
|
91
|
+
outDir: string;
|
|
92
|
+
helperTracePath?: string | null;
|
|
73
93
|
messages: ExternalMessageEvent[];
|
|
94
|
+
attachments: ExternalMessageAttachment[];
|
|
74
95
|
count: number;
|
|
96
|
+
reasonCode?: null;
|
|
97
|
+
message?: string;
|
|
75
98
|
};
|
|
76
99
|
export declare function runWeChatDoctor(options?: WeChatDoctorOptions): Promise<WeChatDoctorResult>;
|
|
77
100
|
export declare function runWeChatSend(options: WeChatSendOptions): Promise<WeChatSendResult>;
|
|
78
101
|
export declare function runWeChatReadLatest(options: WeChatReadLatestOptions): Promise<WeChatReadLatestResult>;
|
|
79
102
|
export declare function registerWeChatCommand(program: Command): void;
|
|
103
|
+
export declare function buildWeChatReadMarkdown(result: WeChatReadLatestResult): string;
|
|
80
104
|
export {};
|