88 lines
6.2 KiB
JavaScript
88 lines
6.2 KiB
JavaScript
/** All original access uses public REA. Local reads are retained evidence and recovery outputs only. */
|
|
import fs from 'node:fs'
|
|
import path from 'node:path'
|
|
import crypto from 'node:crypto'
|
|
import assert from 'node:assert/strict'
|
|
import {spawnSync} from 'node:child_process'
|
|
|
|
const root='C:/Users/Administrator/.workbuddy/plugins/marketplaces/experts/plugins/senior-project-manager'
|
|
const rootSha256='304805c85b87e8266294fb3aaf283d45fc71d7977af064fcb1eed1199e9389e6'
|
|
const inventoryPath='.tmp/rea-project-manager-inventory-20261016.json'
|
|
const recovered='.tmp/rea-project-manager-recovered-20261016'
|
|
const manifestPath=path.join(recovered,'evidence-manifest.json')
|
|
const hash=bytes=>crypto.createHash('sha256').update(bytes).digest('hex')
|
|
const marker=file=>/(^|\/)(?:\.downloaded_at|\.created-by-session)$/.test(file)
|
|
function rea(operation,args){
|
|
const result=spawnSync(process.execPath,['.tmp/rea-css-runtime/scripts/rea.mjs',operation,...args],{encoding:'utf8',maxBuffer:48*1024*1024})
|
|
if(result.status!==0)throw Error('REA '+operation+' failed: '+(result.stderr||result.stdout).slice(0,2000))
|
|
const evidence=JSON.parse(result.stdout)
|
|
assert.equal(evidence.normalized_result.root_sha256,rootSha256,'Pinned REA root identity')
|
|
return evidence
|
|
}
|
|
if(process.argv.includes('--inventory')){
|
|
const inventory=rea('inspect-selected-directory-text-root',[root])
|
|
fs.writeFileSync(inventoryPath,JSON.stringify(inventory,null,2)+'\n')
|
|
}
|
|
const inventory=JSON.parse(fs.readFileSync(inventoryPath,'utf8'))
|
|
assert.equal(inventory.normalized_result.root_sha256,rootSha256)
|
|
assert.equal(inventory.normalized_result.coverage.status,'complete')
|
|
const files=inventory.normalized_result.members.filter(member=>member.kind==='file')
|
|
assert.equal(files.length,82)
|
|
assert.equal(files.reduce((n,file)=>n+file.size_bytes,0),1879348)
|
|
const wanted=files.filter(file=>!marker(file.path)),known=new Map(wanted.map(file=>[file.path,file]))
|
|
function localPath(file){
|
|
assert.equal(path.posix.normalize(file),file)
|
|
assert.ok(!file.startsWith('/')&&!file.includes('\\')&&!file.split('/').includes('..'))
|
|
const target=path.resolve(recovered,file),base=path.resolve(recovered)+path.sep
|
|
assert.ok(target.startsWith(base),'Recovery output stays in task directory')
|
|
return target
|
|
}
|
|
function verifyItem(item){
|
|
const member=known.get(item.path)
|
|
assert.ok(member&&!marker(item.path))
|
|
assert.equal(item.sha256,member.sha256)
|
|
const bytes=fs.readFileSync(localPath(item.path))
|
|
assert.equal(bytes.length,member.size_bytes)
|
|
assert.equal(hash(bytes),member.sha256)
|
|
const retained=JSON.parse(fs.readFileSync(item.retainedExport,'utf8'))
|
|
assert.equal(retained.evidence_id,item.evidenceId)
|
|
assert.equal(retained.normalized_result.root_sha256,rootSha256)
|
|
const original=retained.normalized_result.files.find(file=>file.path===item.path)
|
|
assert.ok(original)
|
|
const originalBytes=original.encoding==='base64'?Buffer.from(original.data_base64,'base64'):Buffer.from(original.text,'utf8')
|
|
assert.equal(hash(originalBytes),member.sha256)
|
|
assert.equal(original.sha256,member.sha256)
|
|
assert.equal(originalBytes.length,member.size_bytes)
|
|
}
|
|
if(process.argv.includes('--check')){
|
|
const manifest=JSON.parse(fs.readFileSync(manifestPath,'utf8'))
|
|
assert.equal(manifest.rootSha256,rootSha256)
|
|
manifest.files.forEach(verifyItem)
|
|
assert.equal(new Set(manifest.files.map(file=>file.path)).size,manifest.files.length)
|
|
assert.equal(manifest.files.length,wanted.length,'All functional/license/package files recovered')
|
|
assert.ok(!fs.existsSync(path.join(recovered,'.downloaded_at')))
|
|
console.log(JSON.stringify({status:'PASS',rootSha256,files:manifest.files.length,bytes:manifest.files.reduce((n,file)=>n+file.bytes,0),coverage:manifest.coverage}))
|
|
process.exit(0)
|
|
}
|
|
const supportedOnly=process.argv.includes('--supported-only')
|
|
const textFiles=wanted.filter(file=>!file.path.endsWith('.png')&&(!supportedOnly||/\.(js|css|json|md|txt|yaml|yml|py)$/i.test(file.path)))
|
|
const binaryFiles=wanted.filter(file=>file.path.endsWith('.png'))
|
|
const manifest={rootSha256,inventoryEvidenceId:inventory.evidence_id,retainedInventory:inventoryPath,files:[],coverage:{inventory:inventory.normalized_result.coverage,totalSourceFiles:files.length,selectedFunctionalFiles:wanted.length,recoveredFiles:0,excludedHostMarkers:files.filter(file=>marker(file.path)).map(file=>({path:file.path,bytes:file.size_bytes,sha256:file.sha256})),pending:[]}}
|
|
fs.mkdirSync(recovered,{recursive:true})
|
|
function retain(evidence,kind){
|
|
const retainedExport='.tmp/rea-project-manager-'+kind+'-'+evidence.evidence_id+'.json'
|
|
fs.writeFileSync(retainedExport,JSON.stringify(evidence)+'\n')
|
|
for(const file of evidence.normalized_result.files){
|
|
const member=known.get(file.path),bytes=file.encoding==='base64'?Buffer.from(file.data_base64,'base64'):Buffer.from(file.text,'utf8')
|
|
assert.ok(member&&!marker(file.path));assert.equal(file.sha256,member.sha256);assert.equal(hash(bytes),member.sha256);assert.equal(bytes.length,member.size_bytes)
|
|
const target=localPath(file.path);fs.mkdirSync(path.dirname(target),{recursive:true});fs.writeFileSync(target,bytes)
|
|
const item={path:file.path,bytes:bytes.length,sha256:file.sha256,evidenceId:evidence.evidence_id,retainedExport};verifyItem(item);manifest.files.push(item)
|
|
}
|
|
manifest.files.sort((a,b)=>a.path.localeCompare(b.path));manifest.coverage.recoveredFiles=manifest.files.length
|
|
manifest.coverage.pending=wanted.filter(member=>!manifest.files.some(file=>file.path===member.path)).map(file=>({path:file.path,bytes:file.size_bytes,sha256:file.sha256}))
|
|
fs.writeFileSync(manifestPath,JSON.stringify(manifest,null,2)+'\n')
|
|
}
|
|
for(let start=0;start<textFiles.length;start+=16)retain(rea('export-selected-directory-text',[root,rootSha256,JSON.stringify(textFiles.slice(start,start+16).map(file=>file.path))]),'text')
|
|
for(let start=0;start<binaryFiles.length;start+=16)retain(rea('export-selected-directory-binary',[root,rootSha256,JSON.stringify(binaryFiles.slice(start,start+16).map(file=>({path:file.path,sha256:file.sha256})))]),'binary')
|
|
console.log(JSON.stringify({status:manifest.coverage.pending.length?'PARTIAL':'PASS',rootSha256,recoveredFiles:manifest.files.length,bytes:manifest.files.reduce((n,file)=>n+file.bytes,0),pendingFiles:manifest.coverage.pending.length,manifestPath}))
|