chore: establish YCode replication baseline and TODO

This commit is contained in:
xiaohan.yuan committed 2026-10-17 02:43:49 +08:00
commit 6dc59f89ba
1118 files changed
+196002

No files matched your search

+88
View File
@@ -0,0 +1,88 @@
// Real isolated production App/preload/IPC, native focus/keys + controlled Monaco paste event.
import fs from 'node:fs/promises'
import path from 'node:path'
import { spawn } from 'node:child_process'
import { createRequire } from 'node:module'
import assert from 'node:assert/strict'
const directory = await fs.mkdtemp(path.resolve('.tmp/mcp-native-ui-'))
await Promise.all(['data', 'sessions', 'home'].map(name => fs.mkdir(path.join(directory, name))))
const bootstrap = path.join(directory, 'bootstrap.cjs')
await fs.writeFile(bootstrap, '(' + nativeQa.toString() + ')()')
const env = { ...process.env, YCODE_MCP_UI_DIRECTORY: directory, YCODE_QA_NODE: process.execPath, YCODE_USER_DATA_DIR: path.join(directory, 'data'),
YCODE_OMP_HOME: path.join(directory, 'home'), YCODE_QA_OFFSCREEN: '1', YCODE_DISABLE_GPU: '1' }
delete env.ELECTRON_RUN_AS_NODE; delete env.ELECTRON_RENDERER_URL; delete env.YCODE_MODEL_API_KEY
const code = await new Promise(resolve => {
const child = spawn(createRequire(import.meta.url)('electron'), ['--no-sandbox', '--disable-gpu-sandbox', bootstrap], { env, windowsHide: true, stdio: ['ignore', 'pipe', 'pipe'] })
const timer = setTimeout(() => child.kill(), 50000)
child.stdout.on('data', () => {}); child.stderr.on('data', () => {})
child.on('exit', code => { clearTimeout(timer); resolve(code ?? 1) })
})
assert.equal(code, 0, 'Native UI failed: ' + directory)
const report = JSON.parse(await fs.readFile(path.join(directory, 'report.json'), 'utf8'))
assert.equal(report.result, 'PASS', 'Native UI report failed: ' + directory)
console.log('MCP_NATIVE_UI_PASS ' + report.checks.length + ' ' + directory)
function nativeQa() {
const { app, BrowserWindow } = require('electron')
const fs = require('node:fs/promises'), path = require('node:path'), { pathToFileURL } = require('node:url'), { createHash } = require('node:crypto')
const directory = process.env.YCODE_MCP_UI_DIRECTORY
app.disableHardwareAcceleration(); app.setPath('userData', path.join(directory, 'data')); app.setPath('sessionData', path.join(directory, 'sessions')); app.setPath('home', path.join(directory, 'home'))
const production = import(pathToFileURL(path.resolve('out/main/index.mjs')).href)
const checks = [], styles = {}
let main, phase = 'boot'
const pause = ms => new Promise(resolve => setTimeout(resolve, ms))
const check = (label, actual, expected) => { const pass = JSON.stringify(actual) === JSON.stringify(expected); checks.push({ label, actual, expected, pass }); if (!pass) throw Error(label) }
const read = expression => main.webContents.executeJavaScript(expression)
async function wait(expression, label) { for (let i = 0; i < 500; i++) { const value = await read(expression); if (value) return value; await pause(30) } throw Error('Timeout: ' + label) }
async function click(text, selector = 'button') { await read(`(()=>{const e=[...document.querySelectorAll(${JSON.stringify(selector)})].find(e=>e.textContent.trim()===${JSON.stringify(text)});if(!e||e.disabled)throw Error('Missing enabled button');e.click()})()`); await pause(70) }
async function shot(name) { const png = (await main.webContents.capturePage(undefined, { stayHidden: true, stayAwake: true })).toPNG(); await fs.writeFile(path.join(directory, name + '.png'), png) }
;(async () => {
await production; await app.whenReady()
for (let i = 0; i < 300; i++) { main = BrowserWindow.getAllWindows().find(window => window.getTitle() === 'YCode'); if (main && !main.webContents.isLoading()) break; await pause(30) }
main.setContentSize(1336, 901)
await wait('!!window.ycode&&!!document.querySelector(".sidebar__tab")', 'actual App')
check('real App configDir isolated', path.resolve((await read('window.ycode.app.info()')).configDir), path.join(directory, 'data'))
phase = 'open-real-mcp-manager'
await click('专家·技能·连接器', '.sidebar__tab'); await click('连接器', '.um-tab'); await click('自定义连接器')
await wait('!!document.querySelector(".mcp-modal")', 'source MCP modal')
await read('Promise.all([...document.querySelector(".mcp-modal").getAnimations(),...document.querySelector(".mcp-modal-mask").getAnimations()].map(a=>a.finished.catch(()=>undefined))).then(()=>true)')
styles.light = await read(`(()=>{const e=document.querySelector('.mcp-modal'),b=e.querySelector('.wb-button'),s=getComputedStyle(e),r=e.getBoundingClientRect();return{portalHasThemeScope:!!e.closest('.expert-center-page'),bounds:[r.x,r.y,r.width,r.height],bg:s.backgroundColor,color:s.color,buttonColor:getComputedStyle(b).color,buttonHeight:b.getBoundingClientRect().height}})()`)
check('portal retains source theme scope', styles.light.portalHasThemeScope, true)
check('light modal opaque source background', styles.light.bg, 'rgb(255, 255, 255)')
check('source modal dimensions', styles.light.bounds.slice(2), [640, 700])
check('source button height', styles.light.buttonHeight, 32)
check('source secondary button foreground', styles.light.buttonColor, 'rgba(0, 0, 0, 0.9)')
await shot('light-manager')
await read('document.documentElement.dataset.theme="dark"'); await pause(120)
styles.dark = await read(`(()=>{const e=document.querySelector('.mcp-modal'),b=e.querySelector('.wb-button'),probe=document.createElement('span');probe.style.background='var(--wb-bg-primary)';e.append(probe);const bg=getComputedStyle(probe).backgroundColor;probe.remove();return{bg:getComputedStyle(e).backgroundColor,expectedBg:bg,color:getComputedStyle(e).color,buttonHeight:b.getBoundingClientRect().height}})()`)
check('dark portal resolves inherited background token', styles.dark.bg, styles.dark.expectedBg)
check('dark background changes with source theme', styles.dark.bg !== styles.light.bg, true)
check('dark button retains 32px height', styles.dark.buttonHeight, 32)
await shot('dark-manager'); await read('document.documentElement.dataset.theme="light"'); await pause(80)
phase = 'native-monaco-paste'
await click('添加 MCP')
await wait('!!document.querySelector(".mcp-config-editor .monaco-editor textarea")', 'actual editable source Monaco')
const input = { mcpServers: { 'Native paste fixture': { command: process.env.YCODE_QA_NODE, args: [path.resolve('scripts/test-mcp-server.mjs'), 'stdio', path.join(directory, 'events.jsonl')] } } }
const text = JSON.stringify(input, null, 2)
const point = await read(`(()=>{const e=document.querySelector('.mcp-config-editor .monaco-editor .view-lines'),r=e.getBoundingClientRect();return{x:Math.round(r.x+60),y:Math.round(r.y+12)}})()`)
main.focus(); main.webContents.focus()
main.webContents.sendInputEvent({ type: 'mouseDown', button: 'left', clickCount: 1, ...point }); main.webContents.sendInputEvent({ type: 'mouseUp', button: 'left', clickCount: 1, ...point }); await pause(40)
main.webContents.sendInputEvent({ type: 'keyDown', keyCode: 'A', modifiers: ['control'] }); main.webContents.sendInputEvent({ type: 'keyUp', keyCode: 'A', modifiers: ['control'] }); await pause(30)
// Exercise Monaco's paste handler, not its typing/IME path or direct model setters.
// Controlled DataTransfer leaves the operating-system clipboard untouched.
await read(`(()=>{const data=new DataTransfer();data.setData('text/plain',${JSON.stringify(text)});return document.activeElement.dispatchEvent(new ClipboardEvent('paste',{clipboardData:data,bubbles:true,cancelable:true}))})()`)
await pause(160)
await click('保存')
const saved = await wait('window.ycode.mcp.list().then(s=>s.find(x=>x.name==="Native paste fixture"))', 'native paste saved through real preload IPC')
check('native pasted JSON preserves full command', saved.command, process.env.YCODE_QA_NODE)
check('native pasted JSON preserves args without auto-inserted braces', saved.args, input.mcpServers['Native paste fixture'].args)
check('new config never auto-approved', saved.trusted, false)
check('new config never auto-enabled', saved.enabled, false)
await shot('saved-untrusted')
phase = 'complete'
const bytes = await fs.readFile('out/main/index.mjs')
await fs.writeFile(path.join(directory, 'report.json'), JSON.stringify({ result: 'PASS', phase, checks, styles,
buildSha256: createHash('sha256').update(bytes).digest('hex'), scope: 'Real isolated production App/preload/IPC; native source Monaco focus/keys plus controlled renderer ClipboardEvent paste and save. No system clipboard access or model/upstream calls. Navigation buttons use DOM click. No original runtime actions.' }, null, 2))
app.quit()
})().catch(async error => { await shot('failure').catch(() => undefined); await fs.writeFile(path.join(directory, 'report.json'), JSON.stringify({ result: 'FAIL', phase, checks, styles, error: String(error) }, null, 2)); process.exitCode = 1; app.quit() })
}