oracle7-chameleon / scripts /capture_showcase.mjs
psiloceyeben's picture
Upload folder using huggingface_hub
09aeaeb verified
Raw History Blame Contribute Delete
5.87 kB
/** Actual CLI -> API -> worker -> counterpart runs, then screenshots of their UI. */
import {chromium} from '@playwright/test';
import {execFileSync} from 'node:child_process';
import {createHash} from 'node:crypto';
import {mkdir,readFile,writeFile} from 'node:fs/promises';
import {fileURLToPath} from 'node:url';
import path from 'node:path';
const root=fileURLToPath(new URL('..',import.meta.url));
const out=path.join(root,'apps/web/public/showcase');
const evidence=path.join(root,'docs/evidence-showcase-cli');
const home=path.join(root,'.local/showcase-cli');
const base='http://127.0.0.1:8120';
const python=path.join(root,'.venv/Scripts/python.exe');
await mkdir(out,{recursive:true});await mkdir(evidence,{recursive:true});
function cli(args){return execFileSync(python,['-m','chameleon_cli','--home',home,...args],{cwd:root,encoding:'utf8',timeout:90000,windowsHide:true});}
const cards=(await (await fetch(base+'/api/v1/examples')).json()).filter(e=>e.origin!=='synthetic');
const browser=await chromium.launch();const context=await browser.newContext({viewport:{width:1300,height:900}});const page=await context.newPage();
const errors=[];page.on('pageerror',e=>errors.push(e.message));
const examples=[];let transcript='';
try{
for(const [index,card] of cards.entries()){
let job;
if(index===0){
transcript='$ chameleon point '+card.source+' --wait 45\n'+cli(['point',card.source,'--wait','45']);
const state=JSON.parse(await readFile(path.join(home,createHash('sha256').update(base).digest('hex').slice(0,24)+'.json'),'utf8'));
job=JSON.parse(cli(['--json','status',state.last_job]));
await context.addCookies(state.cookies.map(c=>({...c,secure:false,httpOnly:c.name==='chameleon_session',sameSite:'Strict'})));
}else job=JSON.parse(cli(['--json','point',card.source,'--objective',card.objective,'--wait','45']));
if(job.status!=='VERIFIED'||job.counterpart?.status!=='READY')throw Error('Counterpart not ready: '+card.label);
await page.goto(base);await page.getByText('YOUR PRIVATE GUEST SESSION').waitFor();
const response=await context.request.get(base+'/api/v1/twins/'+job.counterpart.twin_id);if(!response.ok())throw Error('Missing owned counterpart');
const twin=await response.json();const website=twin.manifest.kind==='local-website-reader-v1';
const region=page.getByRole('region',{name:website?'Website counterpart':'Local working twin'});await region.waitFor();
let checked;
if(website){
const token=twin.pages[0].blocks.find(b=>b.text.length>30)?.text.split(/\s+/).find(w=>w.length>5);
if(!token)throw Error('No searchable evidence');
await region.getByLabel('Search captured pages').fill(token);
if(!await region.getByTestId('website-content').locator('p,h3,pre,blockquote').count())throw Error('Search returned no evidence');
await region.getByLabel('Search captured pages').fill('');
checked=['Captured content opens from saved state','Search returns matching source content','Observed links retained for bounded navigation'];
}else{
const answer=JSON.parse(cli(['--json','query',twin.id,'count rows']));
const count=Object.values(twin.tables).reduce((n,t)=>n+t.rows.length,0);
if(answer.result.answer!==count)throw Error('Copied-table count mismatch');
checked=['Live public CSV captured into an isolated table','Row count independently checked through the CLI','Local query, edit and export surface available'];
}
await region.scrollIntoViewIfNeeded();
const bounds=await region.boundingBox();await page.screenshot({path:path.join(out,'example-'+index+'.png'),clip:{x:bounds.x,y:bounds.y,width:bounds.width,height:Math.min(bounds.height,740)}});
examples.push({source:card.source,label:card.label,image:'showcase/example-'+index+'.png',kind:website?'Website reader':'Editable data copy',provider:job.form.provider,captured_at:job.evidence.captured_at,evidence_digest:job.evidence.content_digest,checked,pages:twin.pages.length,rows:Object.values(twin.tables).reduce((n,t)=>n+t.rows.length,0)});
console.log(JSON.stringify({example:card.label,status:'VERIFIED',provider:job.form.provider}));
}
await writeFile(path.join(out,'cli-transcript.txt'),transcript);
const terminal=await context.newPage();
const escape=s=>s.replaceAll('&','&amp;').replaceAll('<','&lt;').replaceAll('>','&gt;');
await terminal.setViewportSize({width:1100,height:890});
await terminal.setContent('<!doctype html><html><head><style>body{margin:0;padding:28px;background:#e9eddc;color:#375c40;font-family:monospace}.frame{border:3px solid #8eab68;box-shadow:7px 7px 0 #c0cea7;background:#f4f8e7}.bar{background:#d5e3bc;border-bottom:3px solid #8eab68;padding:13px 20px;letter-spacing:2px;font-size:12px}.led{color:#769951}pre{font:15px/1.45 Consolas,monospace;white-space:pre-wrap;overflow-wrap:anywhere;margin:22px 28px}.foot{padding:14px 24px;border-top:2px solid #cfddba;font-size:10px;color:#75885c}</style></head><body><div class="frame"><div class="bar"><span class="led">■ ■ ■</span>  CHAMELEON / LOCAL TERMINAL</div><pre>'+escape(transcript)+'</pre><div class="foot">ACTUAL CLI OUTPUT / CHAMELEON PROVIDER / NO SOURCE WRITES</div></div></body></html>');
await terminal.locator('.frame').screenshot({path:path.join(out,'cli.png')});
await terminal.close();
if(errors.length)throw Error('Browser errors: '+errors.join(', '));
const manifest={captured_at:new Date().toISOString(),examples,cli:{image:'showcase/cli.png',transcript:'showcase/cli-transcript.txt',provider:'chameleon-local-v1'}};
await writeFile(path.join(out,'manifest.json'),JSON.stringify(manifest,null,2));
await writeFile(path.join(evidence,'capture-results.json'),JSON.stringify({recorded_at:manifest.captured_at,actual_sources:examples.length,provider:'chameleon-local-v1',entry:'installed CLI harness',browser_errors:errors,public_deployment:false},null,2));
}finally{await browser.close();}