Files
ycode/docs/协作/evidence/B01-feedback-01/mode-visual-probe.cjs
T

96 lines
10 KiB
JavaScript

const fs = require('node:fs/promises')
const path = require('node:path')
const { pathToFileURL } = require('node:url')
if (!process.versions.electron) {
;(async () => {
const dir = await fs.mkdtemp(path.resolve('.tmp/alignment-window-'))
const data = path.join(dir, 'data'), home = path.join(dir, 'home'), cwd = path.join(dir, 'workspace')
for (const folder of [data, home, cwd]) await fs.mkdir(folder)
const now = Date.now()
const model = { id:'audit-model', name:'Audit Model', providerId:'custom', model:'fixture', baseUrl:'http://127.0.0.1:1', authMode:'none', protocol:'anthropic', supportsToolCall:true }
await fs.writeFile(path.join(data,'models.json'), JSON.stringify({version:1, models:[model], selectedModelId:model.id}))
await fs.writeFile(path.join(data,'workspaces.json'),JSON.stringify({selectedWorkspaceId:'audit-space',workspaces:[{id:'audit-space',name:'Audit Workspace',root:cwd,createdAt:now}]}))
const question = { id:'q1', question:'Which target should be inspected?', answers:['Local fixture'] }
const messages = [
{id:'u1',role:'user',content:'Inspect the local fixture.',createdAt:now-10000,status:'done'},
{id:'a1',role:'assistant',content:'The local fixture is ready.',createdAt:now-9000,status:'done',turnStartedAt:now-10000,turnFinishedAt:now,turnDurationMs:10000,
tools:[{id:'q-tool',name:'AskUserQuestion',args:{questions:[question]},argsRaw:'{}',status:'ok',output:'ANSWER_RAW_SENTINEL',metadata:{questionAnswer:{questions:[question]}}}],
parts:[{type:'reasoning',id:'r1',text:'I need the target.',finished:true},{type:'tool',toolId:'q-tool'},{type:'reasoning',id:'r2',text:'The target is selected.',finished:true},{type:'text',text:'The local fixture is ready.'}]}
]
const session = (id,agentMode,ms) => ({id,title:`Audit ${agentMode}`,cwd,workspaceId:'audit-space',modelId:model.id,agentMode,permissionMode:'full-access',createdAt:ms,updatedAt:ms,messages})
await fs.writeFile(path.join(data,'sessions.json'),JSON.stringify({sessions:[session('audit-plan','plan',now+1),session('audit-ask','ask',now)]}))
const env={...process.env,YCODE_USER_DATA_DIR:data,YCODE_QA_DIRECTORY:dir,YCODE_QA_OFFSCREEN:'1',YCODE_DISABLE_GPU:'1',YCODE_DISABLE_SYSTEM_INTEGRATION:'1',AUDIT_WINDOW_DIR:dir}
delete env.ELECTRON_RUN_AS_NODE; delete env.ELECTRON_RENDERER_URL
const child=require('node:child_process').spawn(require('electron'),['--no-sandbox','--disable-gpu-sandbox',__filename],{env,windowsHide:true,stdio:['ignore','pipe','pipe']})
let log='';child.stdout.on('data',x=>{log+=x});child.stderr.on('data',x=>{log+=x})
const timeout=setTimeout(()=>child.kill(),90000)
child.on('exit',async code=>{clearTimeout(timeout);await fs.writeFile(path.join(dir,'process.log'),log);console.log(JSON.stringify({directory:dir,exitCode:code}));if(code)console.error(log.slice(-7000));process.exitCode=code??1})
})().catch(e=>{console.error(e);process.exitCode=1})
} else {
const {app,BrowserWindow,ipcMain}=require('electron')
const dir=process.env.AUDIT_WINDOW_DIR
app.disableHardwareAcceleration();app.setPath('home',path.join(dir,'home'))
const originalHandle=ipcMain.handle.bind(ipcMain)
ipcMain.handle=(channel,listener)=>originalHandle(channel,async(...args)=>{if(channel==='session:messages')await new Promise(r=>setTimeout(r,1200));return listener(...args)})
let win
const pause=ms=>new Promise(r=>setTimeout(r,ms))
const read=code=>win.webContents.executeJavaScript(code)
async function wait(code){for(let i=0;i<250;i++){const x=await read(code);if(x)return x;await pause(40)}throw new Error('Timeout '+code)}
async function shot(name){await read('document.fonts.ready');await pause(80);await fs.writeFile(path.join(dir,name+'.png'),(await win.webContents.capturePage()).toPNG())}
const styleProbe=`(selector)=>{const e=document.querySelector(selector);if(!e)return null;const c=getComputedStyle(e),r=e.getBoundingClientRect();return {selector,text:e.textContent,width:r.width,height:r.height,fontSize:c.fontSize,fontWeight:c.fontWeight,lineHeight:c.lineHeight,padding:c.padding,borderRadius:c.borderRadius,background:c.backgroundColor,color:c.color,display:c.display,opacity:c.opacity}}`
const styles=selectors=>read(`(()=>{const p=${styleProbe};return ${JSON.stringify(selectors)}.map(p)})()`)
;(async()=>{
await import(pathToFileURL(path.resolve('out/main/index.mjs')).href);await app.whenReady();win=BrowserWindow.getAllWindows()[0];win.setSize(1336,902);win.showInactive()
const errors=[];win.webContents.on('console-message',(_e,level,message)=>{if(level>=3)errors.push(message)})
await wait('!!document.querySelector(".yc-session-skeleton")')
const skeleton=await styles(['.yc-session-skeleton','.yc-session-skeleton__avatar','.yc-session-skeleton__name','.yc-session-skeleton__user','.yc-session-skeleton__line','.yc-session-skeleton__line--short'])
await shot('loading')
await wait('!!document.querySelector(".yc-mode-chip") && !!document.querySelector(".msg--assistant")')
await pause(350)
const mode=await styles(['.yc-mode-chip','.yc-mode-chip > svg','.yc-mode-chip__clear'])
await shot('plan')
await read('document.querySelector(".yc-mode-chip").focus()')
const focusedMode=await styles(['.yc-mode-chip','.yc-mode-chip > svg','.yc-mode-chip__clear'])
await shot('plan-focus')
await read('document.querySelector(".cr-collapse--completed > header").click()')
await wait('!!document.querySelector(".ask-user-question--completed")')
await read('document.querySelector(".ask-user-question__header .cr-tool-head__main").click()')
await wait('!!document.querySelector(".question-answer-transcript__question")')
const question=await styles(['.ask-user-question--completed','.ask-user-question__header','.ask-user-question__content','.question-answer-transcript__question','.question-answer-transcript__answer'])
await shot('answered-expanded')
await read('document.querySelector("[data-session-id=\\"audit-ask\\"]").click()')
await wait('!!document.querySelector(".yc-session-skeleton")')
const switching=await read('({hasHome:!!document.querySelector(".wb-home-route"),hasSkeleton:!!document.querySelector(".yc-session-skeleton"),hasComposer:!!document.querySelector(".wb-composer"),hasHeader:!!document.querySelector(".source-chat-header"),text:document.querySelector(".yc-mode-chip")?.textContent})')
await shot('switch-loading')
await wait('!!document.querySelector(".msg--assistant") && !document.querySelector(".yc-session-skeleton")')
const ask=await styles(['.yc-mode-chip','[data-permission-trigger]','.yc-source-permission button'])
await shot('ask')
await read('document.querySelector("[data-session-id=\\"audit-ask\\"] .agent-card-more-button").click()')
await wait('!!document.querySelector(".cb-context-menu-item")')
await read('[...document.querySelectorAll(".cb-context-menu-item")].find(e=>e.textContent.includes("归档")).click()')
await wait('!!document.querySelector(".task-popconfirm") && Number(getComputedStyle(document.querySelector(".task-popconfirm")).opacity)>=0.99')
await pause(500)
const popconfirm=await styles(['.task-popconfirm','.task-popconfirm__title','.task-popconfirm__description','.task-popconfirm__confirm','.task-popconfirm__cancel'])
const popconfirmAncestors=await read('(()=>{let e=document.querySelector(".task-popconfirm"),a=[];while(e){const c=getComputedStyle(e);a.push({className:e.className,opacity:c.opacity,visibility:c.visibility});e=e.parentElement}return a})()')
await shot('archive-settled')
await read('document.querySelector(".task-popconfirm__cancel").click()')
await read('document.querySelector(".yc-mode-chip__clear").click()')
await wait('!document.querySelector(".yc-mode-chip")')
const fixtureTime=Date.now(), runId='audit-approval-run'
const userMessage={id:'audit-approval-user',requestId:runId,role:'user',content:'Render a local approval fixture.',createdAt:fixtureTime,status:'done'}
const approval={id:'audit-approval',requestId:'audit-approval',runId,toolCallId:'audit-command',kind:'approval',title:'执行命令命中询问规则,需要确认。',status:'pending',options:[{optionId:'once',name:'允许',kind:'allow_once'},{optionId:'session',name:'本次会话内始终允许',kind:'allow_always'},{optionId:'deny',name:'拒绝',kind:'reject_once'}],details:{requestKind:'rule-intercept',toolName:'Bash',command:'echo audit',reason:'fixture rule',rule:{id:'fixture-command-rule',description:'Local fixture only'},allowScope:'session'}}
const assistant={id:'audit-approval-assistant',requestId:runId,role:'assistant',content:'',createdAt:fixtureTime,status:'streaming',turnStartedAt:fixtureTime,parts:[],tools:[],interactions:[approval],runtimeProgress:{source:'agent',phase:'waiting-approval',toolName:'Bash'}}
win.webContents.send('chat:run-started',{sessionId:'audit-ask',runId,assistantMessageId:assistant.id,userMessage,modelId:'audit-model',modelName:'Audit Model',sequence:0,messages:[userMessage,assistant]})
await wait('!!document.querySelector(".conversation-intercept-card")')
await pause(150)
const permission=await styles(['.conversation-intercept-card','.conversation-intercept-card__title','.conversation-intercept-card__rule-action','.conversation-intercept-card__description','.conversation-intercept-card__option','.conversation-intercept-card__option-num'])
await shot('approval')
await read('document.querySelector(".conversation-intercept-card__rule-action").click()')
const permissionRule=await styles(['.conversation-intercept-card__rule'])
await shot('approval-rule')
await fs.writeFile(path.join(dir,'report.json'),JSON.stringify({head:'51531dcae8d8ade85db9e203bc8c0f712805b2ae',scope:'Real isolated YCode main/preload/renderer; delayed local IPC and injected approval snapshot; fixture model only; no model calls or original app access.',skeleton,mode,focusedMode,question,switching,ask,popconfirm,popconfirmAncestors,permission,permissionRule,errors},null,2))
console.log('WINDOW_AUDIT_DONE');app.exit(0)
})().catch(async e=>{console.error(e);if(win)await shot('failure').catch(()=>{});await fs.writeFile(path.join(dir,'failure.txt'),String(e));app.exit(1)})
}