#!/usr/bin/env node import { execFileSync } from 'node:child_process'; const DEFAULT_CDP = '/Users/inmanx/.agents/skills/chrome-cdp/scripts/cdp.mjs'; const DEFAULT_PLATFORM_TARGET = 'DA600CC0'; const EXTENSION_ID = 'kafggjjlhebccdgkechmbgflkaifkccf'; function parseArgs(argv) { const args = { expectedRunId: '', identifier: '', ddid: '', closeArrangementTid: '', platformTarget: DEFAULT_PLATFORM_TARGET, cdp: process.env.CDP_CLI || DEFAULT_CDP, timeoutMs: 30_000 }; for (let index = 0; index < argv.length; index += 1) { const value = argv[index]; if (value === '--expected-run-id') args.expectedRunId = argv[++index] || ''; else if (value === '--identifier') args.identifier = argv[++index] || ''; else if (value === '--ddid') args.ddid = argv[++index] || ''; else if (value === '--close-arrangement-tid') args.closeArrangementTid = argv[++index] || ''; else if (value === '--platform-target') args.platformTarget = argv[++index] || ''; else if (value === '--cdp') args.cdp = argv[++index] || ''; else if (value === '--timeout-ms') args.timeoutMs = Number(argv[++index]); else throw new Error(`unknown argument: ${value}`); } if (!args.expectedRunId) throw new Error('--expected-run-id is required'); if (!args.identifier.includes('TEST-202609')) throw new Error('--identifier must be a TEST-202609 object'); if (args.ddid && !/^\d+$/.test(args.ddid)) throw new Error('--ddid must be numeric'); if (args.closeArrangementTid && !/^\d+$/.test(args.closeArrangementTid)) throw new Error('--close-arrangement-tid must be numeric'); if (!Number.isFinite(args.timeoutMs) || args.timeoutMs < 5_000) throw new Error('--timeout-ms must be at least 5000'); return args; } function cdp(args, command, target, ...rest) { return execFileSync(process.execPath, [args.cdp, command, target, ...rest], { encoding: 'utf8', maxBuffer: 32 * 1024 * 1024 }).trim(); } function parseJson(value, label) { try { return JSON.parse(String(value || '').trim()); } catch { throw new Error(`${label} returned non-JSON output: ${String(value || '').slice(0, 500)}`); } } function wait(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } const args = parseArgs(process.argv.slice(2)); parseJson(cdp(args, 'eval', args.platformTarget, `(async()=>JSON.stringify(await sendToExtension('PING',{},3000)))()`), 'extension ping'); const targetsResult = parseJson(cdp(args, 'evalraw', args.platformTarget, 'Target.getTargets', '{}'), 'target list'); const serviceWorker = targetsResult.targetInfos?.find((target) => ( target.type === 'service_worker' && target.url === `chrome-extension://${EXTENSION_ID}/background.js` )); if (!serviceWorker?.targetId) throw new Error('extension_service_worker_missing'); const attached = parseJson(cdp(args, 'evalraw', args.platformTarget, 'Target.attachToTarget', JSON.stringify({ targetId: serviceWorker.targetId, flatten: false })), 'service worker attach'); if (!attached.sessionId) throw new Error('extension_service_worker_attach_failed'); const probeKey = `READONLY-BUSINESS-FRAME-${args.expectedRunId}-${Date.now()}`; const expression = `(async()=>{ const tabs=await chrome.tabs.query({url:"https://lwlt.hisy.cc/*"}); const tab=tabs.find((candidate)=>/\\/System\\/Mainlt\\.asp/i.test(candidate.url||""))||tabs[0]; if(!tab?.id)throw new Error("erp_tab_missing"); const injected=await chrome.scripting.executeScript({ target:{tabId:tab.id,allFrames:true},world:"MAIN", func:async(identifier,expectedDdid,closeArrangementTid)=>{ const pathname=String(location.pathname||""); if(!/^\\/system\\/business\\//i.test(pathname))return null; const documentText=String(document.body?.innerText||document.body?.textContent||""); const forms=[...document.forms].map((form)=>({ id:String(form.id||""),name:String(form.name||""),method:String(form.method||""), action_path:(()=>{try{return new URL(form.action||location.href,location.href).pathname}catch{return ""}})(), element_count:form.elements.length, field_names:[...new Set([...form.elements].map((element)=>String(element.name||"")).filter(Boolean))].sort() })); const functionNames=["SubmitInfoForm","CheckData","DoInfo","SubmitForm"]; const functions={}; for(const name of functionNames){ try{ const source=typeof window[name]==="function"?String(window[name]):""; if(source)functions[name]=source.slice(0,20000); }catch{} } try{ const source=typeof window.Vidy?.CheckMastInput==="function"?String(window.Vidy.CheckMastInput):""; if(source)functions["Vidy.CheckMastInput"]=source.slice(0,20000); }catch{} try{ const source=typeof window.Vidy?.ClearInput==="function"?String(window.Vidy.ClearInput):""; if(source)functions["Vidy.ClearInput"]=source.slice(0,20000); }catch{} const relevantScripts=[...document.scripts].map((script)=>String(script.textContent||"")) .filter((source)=>/SubmitInfoForm|DoInfoJH|xiadanbeizhu/.test(source)) .map((source)=>source.slice(0,30000)); const trackedControls={}; for(const name of ["xiadanbeizhu","zhusuanpai","da","xiao","ddid","tdid"]){ const control=document.querySelector('[name="'+name+'"],#'+name); if(!control)continue; trackedControls[name]={ tag:String(control.tagName||""),type:String(control.type||""), value_length:String(control.value||"").length,max_length:Number(control.maxLength), disabled:Boolean(control.disabled),read_only:Boolean(control.readOnly) }; } let validationCapture=null; const listForm=document.querySelector('#ListForm,form[name="ListForm"]'); const actualDdid=String(listForm?.querySelector('[name="ddid"]')?.value||""); const noteControl=listForm?.querySelector('[name="xiadanbeizhu"]'); const receivableSlots=[]; const activeReceivables=[]; const generatedZeroReceivables=[]; const receivableInspectionScope={ ddid_matched:Boolean(expectedDdid&&actualDdid===expectedDdid), marker_in_note:String(noteControl?.value||"").includes("TEST-202609"), value_redacted:true }; if(expectedDdid&&actualDdid===expectedDdid&&String(noteControl?.value||"").includes("TEST-202609")){ const rowIds=[...listForm.elements].filter((control)=>/^ys_id\\d+$/.test(String(control.name||""))); for(const rowIdControl of rowIds){ const match=String(rowIdControl.name||"").match(/^ys_id(\\d+)$/); if(!match)continue; const index=match[1]; const value=(name)=>String(listForm.elements.namedItem(name)?.value||""); const name=value("ys_xiangmu"+index); const amount=Number(value("ys_jine"+index)||0); const row={ row_id:String(rowIdControl.value),name, quantity:Number(value("ys_shuliang"+index)||0), unit_price:Number(value("ys_danjia"+index)||0), amount, currency:value("ys_bizhong"+index)||"CNY", paid:Number(value("ys_yishoufu"+index)||0)!==0, settled:Boolean(value("ys_shenheren"+index)), value_redacted:true }; receivableSlots.push(row); if(!/^\\d+$/.test(String(rowIdControl.value||""))||String(rowIdControl.value||"")==="0")continue; activeReceivables.push(row); if(["成人团费","单人房差"].includes(name)&&amount===0)generatedZeroReceivables.push(row); } } if(expectedDdid&&actualDdid===expectedDdid&&String(noteControl?.value||"").includes("TEST-202609")){ const jq=window.jQuery||window.$; const sha256=async(value)=>{ const digest=await crypto.subtle.digest("SHA-256",new TextEncoder().encode(value)); return [...new Uint8Array(digest)].map((byte)=>byte.toString(16).padStart(2,"0")).join(""); }; const beforeBody="Act=DoInfoJH&"+jq(listForm).serialize(); const alerts=[]; const originalAlert=window.alert; const originalDialogAlert=jq?.dialog?.alert; window.alert=(message)=>{alerts.push(String(message||"").length)}; if(jq?.dialog&&typeof originalDialogAlert==="function")jq.dialog.alert=(message)=>{alerts.push(String(message||"").length)}; let validationResult=false; let thrown=""; try{validationResult=window.Vidy?.CheckMastInput?.("ListForm")===true}catch(error){thrown=String(error?.message||error)} window.alert=originalAlert; if(jq?.dialog&&typeof originalDialogAlert==="function")jq.dialog.alert=originalDialogAlert; const afterBody="Act=DoInfoJH&"+jq(listForm).serialize(); validationCapture={ ddid_matched:true,marker_in_note:true,validation_result:validationResult, alert_count:alerts.length,thrown:Boolean(thrown), before_sha256:await sha256(beforeBody),after_sha256:await sha256(afterBody), body_changed:beforeBody!==afterBody, before_field_count:[...new URLSearchParams(beforeBody.slice("Act=DoInfoJH&".length)).keys()].length, after_field_count:[...new URLSearchParams(afterBody.slice("Act=DoInfoJH&".length)).keys()].length, note_length_after_validation:String(noteControl?.value||"").length, no_erp_write:true }; } let uiCleanup=null; if(closeArrangementTid&&/\\/system\\/business\\/teams_(daoyou|cheliang|jiudian|piao|qita)\\.asp$/i.test(pathname)){ const arrangementForm=document.querySelector('#InfoForm1,form[name="InfoForm1"]'); const arrangementTid=String(arrangementForm?.elements?.namedItem?.('tdID')?.value||""); const arrangementNote=String(arrangementForm?.elements?.namedItem?.('beizhu0')?.value||""); const dialogApi=window.frameElement?.api; const exact=arrangementTid===closeArrangementTid&&arrangementNote.includes("TEST-202609"); if(exact&&typeof dialogApi?.close==="function"){ window.setTimeout(()=>dialogApi.close(),0); uiCleanup={scheduled:true,tid_matched:true,marker_matched:true,no_erp_write:true}; }else{ uiCleanup={scheduled:false,tid_matched:arrangementTid===closeArrangementTid, marker_matched:arrangementNote.includes("TEST-202609"),close_available:typeof dialogApi?.close==="function",no_erp_write:true}; } } return { pathname,title:document.title,identifier_present:documentText.includes(identifier), marker_present:documentText.includes("TEST-202609"),account_present:documentText.includes("测试ai员工账号"), frame_api:{present:Boolean(window.frameElement?.api),close:typeof window.frameElement?.api?.close==="function"}, forms,functions,relevant_scripts:relevantScripts,tracked_controls:trackedControls, validation_capture:validationCapture,receivable_inspection_scope:receivableInspectionScope, receivable_slots:receivableSlots, active_receivables:activeReceivables, generated_zero_receivables:generatedZeroReceivables, ui_cleanup:uiCleanup,no_erp_write:true }; },args:[${JSON.stringify(args.identifier)},${JSON.stringify(args.ddid)},${JSON.stringify(args.closeArrangementTid)}] }); if(${JSON.stringify(Boolean(args.closeArrangementTid))})await new Promise((resolve)=>setTimeout(resolve,500)); const frames=injected.map((item)=>item.result).filter(Boolean); const saved=await chrome.storage.local.get("businessTaskResults"); const resultMap=saved.businessTaskResults||{}; resultMap[${JSON.stringify(probeKey)}]={ status:"completed",stage:"read_only_business_frame_inspection", message:"business frame source inspection completed",write_attempted:false,no_erp_write:true, report:{frames} }; await chrome.storage.local.set({businessTaskResults:resultMap}); return true; })()`; const message = JSON.stringify({ id: 1, method: 'Runtime.evaluate', params: { expression, awaitPromise: true, returnByValue: true } }); cdp(args, 'evalraw', args.platformTarget, 'Target.sendMessageToTarget', JSON.stringify({ sessionId: attached.sessionId, message })); const startedAt = Date.now(); let result = null; while (Date.now() - startedAt < args.timeoutMs) { try { const response = parseJson(cdp( args, 'eval', args.platformTarget, `(async()=>JSON.stringify(await sendToExtension('GET_TASK_RESULT',{task_id:${JSON.stringify(probeKey)}},3000)))()` ), 'probe result'); result = response?.result || null; if (result?.status === 'completed') break; } catch { // The bounded read-only inspection is still running. } await wait(500); } try { cdp(args, 'evalraw', args.platformTarget, 'Target.detachFromTarget', JSON.stringify({ sessionId: attached.sessionId })); } catch { // Detachment is cleanup only. } if (!result) throw new Error(`read-only business frame inspection timed out after ${args.timeoutMs}ms`); console.log(JSON.stringify({ probe_key: probeKey, result }, null, 2));