File size: 4,641 Bytes
da0d085
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
import { chromium } from 'playwright';
import assert from 'node:assert/strict';
import fs from 'node:fs/promises';
const root = new URL('../..', import.meta.url).pathname;
const url = process.argv[2] || 'https://mike0021-minicpm5-2b-webgpu-pi-http.static.hf.space/';
const result = { date: new Date().toISOString(), url, errors: [], prompt: 'Read app.js and change the exported greeting from Hello to Bonjour. Leave other files unchanged and verify the change.', handle: 'Real OPFS directory handle passed through the native worker protocol; model and tools are not mocked.' };
const context = await chromium.launchPersistentContext(root + '/validation/http-browser-profile', {
  executablePath: '/usr/bin/chromium', headless: true, viewport: { width: 1440, height: 1000 },
  args: ['--enable-unsafe-webgpu', '--use-angle=vulkan', '--enable-features=Vulkan', '--disable-vulkan-surface'],
});
const page = await context.newPage(); page.setDefaultTimeout(120_000);
page.on('pageerror', e => result.errors.push(e.message));
const idle = () => page.waitForFunction(() => window.browserPi && !window.browserPi.busy);
let timer;
try {
  await page.goto(url); await page.waitForFunction(() => window.browserPi); await page.evaluate(() => window.browserPi.ready); await idle();
  await page.evaluate(async () => {
    const root = await navigator.storage.getDirectory();
    try { await root.removeEntry('local-agent-check', { recursive: true }); } catch (e) { if (e.name !== 'NotFoundError') throw e; }
    window.testFolder = await root.getDirectoryHandle('local-agent-check', { create: true });
    for (const [name, content] of [['README.md', '# Example JavaScript project\n'], ['app.js', 'export const greeting = "Hello";\n']]) {
      const stream = await (await window.testFolder.getFileHandle(name, { create: true })).createWritable(); await stream.write(content); await stream.close();
    }
    window.showDirectoryPicker = async () => window.testFolder;
  });
  await page.locator('#open-folder').click(); await page.locator('#local-folder-bar').waitFor({ state: 'visible' }); await idle(); await page.waitForTimeout(300);
  const startLoad = Date.now(); await page.evaluate(() => window.browserPi.load()); result.loadMs = Date.now() - startLoad; await idle(); await page.waitForTimeout(300);
  const started = Date.now();
  const pending = page.evaluate(prompt => window.browserPi.prompt(prompt), result.prompt);
  try { await Promise.race([pending, new Promise((_, reject) => { timer = setTimeout(() => reject(Error('Model trial exceeded 120 seconds')), 120000); })]); }
  catch (error) { await page.evaluate(() => window.browserPi.rpc('stop')); await pending.catch(() => {}); throw error; }
  finally { clearTimeout(timer); }
  result.elapsedMs = Date.now() - started; await idle();
  const state = await page.evaluate(() => window.browserPi.rpc('snapshot')); result.messages = state.messages;
  result.patches = await page.evaluate(() => window.browserPi.rpc('local_changes'));
  assert.equal(result.patches.length, 1); assert.equal(result.patches[0].path, '/app.js');
  assert.match(Buffer.from(result.patches[0].after, 'base64').toString(), /greeting\s*=\s*["']Bonjour["']/);
  result.diskBeforeApply = await page.evaluate(async () => (await (await window.testFolder.getFileHandle('app.js')).getFile()).text());
  assert.equal(result.diskBeforeApply, 'export const greeting = "Hello";\n');
  await page.locator('#review-changes').click(); await page.locator('#local-review').waitFor({ state: 'visible' });
  await page.screenshot({ path: root + '/validation/local-folder-agent-review.png' });
  await page.locator('#review-apply').click(); await page.locator('#local-review').waitFor({ state: 'hidden' }); await idle();
  result.diskAfterApply = await page.evaluate(async () => (await (await window.testFolder.getFileHandle('app.js')).getFile()).text());
  assert.equal(result.diskAfterApply, Buffer.from(result.patches[0].after, 'base64').toString());
  result.final = state.messages.filter(m => m.role === 'assistant').at(-1)?.content.filter(c => c.type === 'text').map(c => c.text).join('\n');
  assert.deepEqual(result.errors, []); result.passed = true;
  await page.locator('#disconnect-folder').click(); await idle();
  console.log(JSON.stringify({ passed: true, loadMs: result.loadMs, elapsedMs: result.elapsedMs, final: result.final, changes: result.patches.length }));
} catch (error) { result.failure = error.stack; console.error(error); process.exitCode = 1; }
finally { clearTimeout(timer); await fs.writeFile(root + '/validation/local-folder-agent-live.json', JSON.stringify(result, null, 2)); await context.close(); }