From 2201eb17590e454d6c0b436ed415cfeca1d09d43 Mon Sep 17 00:00:00 2001 From: Lyon <88232613+pikasTech@users.noreply.github.com> Date: Mon, 22 Jun 2026 22:58:16 +0800 Subject: [PATCH] fix: summarize web-probe collected json files (#690) Co-authored-by: Codex --- scripts/src/hwlab-node-impl.ts | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/scripts/src/hwlab-node-impl.ts b/scripts/src/hwlab-node-impl.ts index 87b278b5..3bcb002d 100644 --- a/scripts/src/hwlab-node-impl.ts +++ b/scripts/src/hwlab-node-impl.ts @@ -9236,6 +9236,19 @@ const dir=process.argv[1]; const selected=process.argv[2]||''; const maxFiles=${ const shaFile=(file)=>'sha256:'+crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex'); const safeRel=(value)=>Boolean(value)&&!path.isAbsolute(value)&&!value.includes('..')&&!value.includes('\\\\')&&value.split('/').every((part)=>part&&part!=='.'&&part!=='..'); const short=(value,max=180)=>value===undefined||value===null?undefined:String(value).replace(/\\s+/g,' ').slice(0,max); +const slimObject=(value,maxKeys=24)=>{ + if(!value||typeof value!=='object'||Array.isArray(value)) return value??null; + const out={}; + for(const key of Object.keys(value).slice(0,maxKeys)){ + const item=value[key]; + if(item===null||item===undefined||typeof item==='number'||typeof item==='boolean'||typeof item==='string') out[key]=short(item,160); + else if(Array.isArray(item)) out[key]={arrayLength:item.length}; + else out[key]={keys:Object.keys(item).slice(0,12)}; + } + return out; +}; +const slimFinding=(item)=>item&&typeof item==='object'?{kind:short(item.kind||item.code||item.id,80),severity:short(item.severity||item.level,24),count:item.count??item.sampleCount??null,summary:short(item.summary||item.message,240)}:null; +const slimRunnerError=(item)=>item&&typeof item==='object'?{ts:short(item.ts||item.at,32),type:short(item.type,48),retry:item.retry||item.lastRetryLabel||null,retryExhausted:item.retryExhausted===true,attemptCount:item.attemptCount??(Array.isArray(item.attempts)?item.attempts.length:null),lastFailureKind:short(item.lastFailureKind||item.failureKind,80),lastReadinessReason:short(item.lastReadinessReason,80),message:short(item.message||item.lastError,240)}:null; const slimJsonl=(value)=>{ if(!value||typeof value!=='object'||Array.isArray(value)) return {value:short(value)}; const readiness=value.readiness&&typeof value.readiness==='object'?value.readiness:null; @@ -9268,11 +9281,11 @@ if(selected){ if(relative.startsWith('..')||path.isAbsolute(relative)){console.log(JSON.stringify({ok:false,command:'web-probe-observe collect',stateDir:dir,mode:'file',reason:'file-outside-state-dir',file:selected,valuesRedacted:true},null,2)); process.exit(2);} if(!fs.existsSync(file)||!fs.statSync(file).isFile()){console.log(JSON.stringify({ok:false,command:'web-probe-observe collect',stateDir:dir,mode:'file',reason:'file-not-found',file:selected,valuesRedacted:true},null,2)); process.exit(1);} const st=fs.statSync(file); const raw=fs.readFileSync(file); const text=raw.slice(0,Math.min(raw.length,maxReadBytes)).toString('utf8'); const truncated=raw.length>maxReadBytes; - const lines=text.split(/\\r?\\n/).filter(Boolean); const isJsonl=selected.endsWith('.jsonl'); + const lines=text.split(/\\r?\\n/).filter(Boolean); const isJsonl=selected.endsWith('.jsonl'); const isJson=selected.endsWith('.json'); let jsonSummary=null; - if(selected.endsWith('.json')&&!truncated){try{const parsed=JSON.parse(text); jsonSummary={topLevelKeys:parsed&&typeof parsed==='object'&&!Array.isArray(parsed)?Object.keys(parsed).slice(0,40):[],counts:parsed&&parsed.counts||null,runnerErrors:Array.isArray(parsed&&parsed.runnerErrors)?parsed.runnerErrors.slice(-8):[],findings:Array.isArray(parsed&&parsed.findings)?parsed.findings.slice(0,8):[]}}catch(error){jsonSummary={parseError:String(error&&error.message||error).slice(0,160)}}} + if(isJson){try{const parsed=JSON.parse(raw.toString('utf8')); jsonSummary={topLevelKeys:parsed&&typeof parsed==='object'&&!Array.isArray(parsed)?Object.keys(parsed).slice(0,40):[],counts:slimObject(parsed&&parsed.counts),sampleMetrics:slimObject(parsed&&parsed.sampleMetrics),runtimeAlerts:slimObject(parsed&&parsed.runtimeAlerts),pagePerformance:slimObject(parsed&&parsed.pagePerformance),runnerErrors:Array.isArray(parsed&&parsed.runnerErrors)?parsed.runnerErrors.slice(-8).map(slimRunnerError).filter(Boolean):[],findings:Array.isArray(parsed&&parsed.findings)?parsed.findings.slice(0,8).map(slimFinding).filter(Boolean):[]}}catch(error){jsonSummary={parseError:String(error&&error.message||error).slice(0,160)}}} const jsonlTail=isJsonl?lines.slice(-20).map((line,index)=>{try{return slimJsonl(JSON.parse(line))}catch(error){return {parseError:true,index,lineTail:line.slice(-240),error:String(error&&error.message||error).slice(0,160)}}}):null; - console.log(JSON.stringify({ok:true,command:'web-probe-observe collect',stateDir:dir,mode:'file',file:{path:file,relative,byteCount:st.size,sha256:shaFile(file),truncated,content:isJsonl||jsonSummary?undefined:text,jsonlTail,jsonSummary,lineCount:lines.length},valuesRedacted:true},null,2)); + console.log(JSON.stringify({ok:true,command:'web-probe-observe collect',stateDir:dir,mode:'file',file:{path:file,relative,byteCount:st.size,sha256:shaFile(file),truncated,content:isJsonl||isJson||jsonSummary?undefined:text,jsonlTail,jsonSummary,lineCount:lines.length},valuesRedacted:true},null,2)); process.exit(0); } const out=[]; const walk=(p)=>{for(const ent of fs.readdirSync(p,{withFileTypes:true})){const full=path.join(p,ent.name); if(ent.isDirectory()) walk(full); else out.push(full); if(out.length>=maxFiles) return;}};