agen-vektor 0.3.28 → 0.3.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/tools/e2b.js +84 -0
- package/dist/tui/app.js +7 -1
- package/package.json +1 -1
package/dist/tools/e2b.js
CHANGED
|
@@ -335,6 +335,90 @@ ${lines.join('\n')}`,
|
|
|
335
335
|
};
|
|
336
336
|
},
|
|
337
337
|
},
|
|
338
|
+
{
|
|
339
|
+
definition: {
|
|
340
|
+
name: 'e2b_screenshot',
|
|
341
|
+
description: 'Take a screenshot of a URL with headless Chromium INSIDE a persistent e2b cloud session (from e2b_cloud create) and get it back as a downloadable gateway link (render_ui button opens it). Requires Playwright+Chromium installed in the session (npx playwright install --with-deps chromium). The target URL is fetched FROM INSIDE the sandbox — use http://localhost:<port> for dev servers running in this session. The sandbox needs internet unless the target is localhost.',
|
|
342
|
+
parameters: {
|
|
343
|
+
type: 'object',
|
|
344
|
+
properties: {
|
|
345
|
+
id: { type: 'string', description: 'Session id from e2b_cloud create' },
|
|
346
|
+
url: { type: 'string', description: 'URL to capture, e.g. http://localhost:3000 (default)' },
|
|
347
|
+
wait_ms: { type: 'number', description: 'Milliseconds to wait for page load before capturing (default 1500)' },
|
|
348
|
+
full_page: { type: 'boolean', description: 'Capture full scrollable page (default false)' },
|
|
349
|
+
},
|
|
350
|
+
required: ['id'],
|
|
351
|
+
},
|
|
352
|
+
},
|
|
353
|
+
async execute(args, ctx) {
|
|
354
|
+
const id = String(args.id || '');
|
|
355
|
+
const target = String(args.url || 'http://localhost:3000').trim();
|
|
356
|
+
if (!/^[A-Za-z0-9-]{8,80}$/.test(id))
|
|
357
|
+
return { output: 'ERROR: invalid session id' };
|
|
358
|
+
if (!/^https?:\/\//.test(target) || target.includes("'"))
|
|
359
|
+
return { output: 'ERROR: url harus http(s) tanpa quote' };
|
|
360
|
+
const waitMs = Math.min(15_000, Math.max(0, Number(args.wait_ms) || 1500));
|
|
361
|
+
const fullPage = args.full_page === true;
|
|
362
|
+
// Dua langkah: (1) exec script Playwright → simpan PNG di /home/user/, (2) beri
|
|
363
|
+
// link download via route /v1/e2b/file (gateway stream PNG → tombol render_ui
|
|
364
|
+
// di TUI membukanya di browser). Script memakai flag jitless WAJIB (gotcha V8
|
|
365
|
+
// OOM di sandbox kecil) dan hanya setelah marker __VECTOR_DONE_ hasil poll.
|
|
366
|
+
// PENTING: script DITULIS ke /home/user (bukan /tmp) — ESM resolve
|
|
367
|
+
// `import 'playwright'` relatif thd lokasi file, dan package terinstall
|
|
368
|
+
// di /home/user/node_modules (npm i tanpa -g).
|
|
369
|
+
const script = [
|
|
370
|
+
'mkdir -p /home/user',
|
|
371
|
+
`cat > /home/user/vector-shot.mjs <<\'EOF\'`,
|
|
372
|
+
`import { chromium } from 'playwright';`,
|
|
373
|
+
`const b = await chromium.launch({ args: ['--no-sandbox','--disable-dev-shm-usage','--disable-gpu','--js-flags=--jitless'] });`,
|
|
374
|
+
`const p = await b.newPage();`,
|
|
375
|
+
`try {`,
|
|
376
|
+
` await p.goto(${JSON.stringify(target)}, { waitUntil: 'load', timeout: 20000 });`,
|
|
377
|
+
` await p.waitForTimeout(${waitMs});`,
|
|
378
|
+
` await p.screenshot({ path: '/home/user/vector-preview.png', fullPage: ${fullPage ? 'true' : 'false'} });`,
|
|
379
|
+
` console.log('SHOT_OK');`,
|
|
380
|
+
`} catch (e) { console.log('SHOT_ERR ' + e.message); await b.close(); process.exit(3); }`,
|
|
381
|
+
`await b.close();`,
|
|
382
|
+
`EOF`,
|
|
383
|
+
`cd /home/user && node /home/user/vector-shot.mjs`,
|
|
384
|
+
].join('\n');
|
|
385
|
+
ctx.onActivity?.('e2b', `screenshot ${target}`);
|
|
386
|
+
// Poll exec manual di sini (bukan e2bExec blocking): kita butuh exit code.
|
|
387
|
+
const started = await fetch(GW_BASE + '/v1/e2b/exec?id=' + encodeURIComponent(id), {
|
|
388
|
+
method: 'POST',
|
|
389
|
+
headers: { 'Content-Type': 'application/json', 'User-Agent': 'VectorHead-AI-Agent/0.1' },
|
|
390
|
+
body: JSON.stringify({ command: script }),
|
|
391
|
+
signal: AbortSignal.timeout(30_000),
|
|
392
|
+
});
|
|
393
|
+
const sdata = (await started.json().catch(() => null));
|
|
394
|
+
if (!started.ok || !sdata?.jobId) {
|
|
395
|
+
return { output: `ERROR: e2b exec gagal — ${(0, credentials_1.redact)(sdata?.error || 'HTTP ' + started.status)}`, summary: 'screenshot exec error' };
|
|
396
|
+
}
|
|
397
|
+
const t0 = Date.now();
|
|
398
|
+
while (Date.now() - t0 < 90_000) {
|
|
399
|
+
await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
|
|
400
|
+
const jr = await fetch(GW_BASE + '/v1/e2b/job?id=' + encodeURIComponent(id) + '&job=' + sdata.jobId, {
|
|
401
|
+
signal: AbortSignal.timeout(15_000),
|
|
402
|
+
});
|
|
403
|
+
const j = (await jr.json().catch(() => null));
|
|
404
|
+
if (j?.status === 'done') {
|
|
405
|
+
if (j.exitCode !== 0 || !(j.stdout || '').includes('SHOT_OK')) {
|
|
406
|
+
const why = (j.stderr || j.stdout || '').trim().slice(0, 300);
|
|
407
|
+
return { output: `ERROR: screenshot gagal (exit ${j.exitCode})\n${(0, credentials_1.redact)(why)}` + (why.includes('Cannot find package') ? '\n→ Playwright belum terpasang di sesi ini: e2b_exec {command: "npm i playwright && npx playwright install --with-deps chromium"}' : ''), summary: 'screenshot error' };
|
|
408
|
+
}
|
|
409
|
+
const link = `${GW_BASE}/v1/e2b/file?id=${encodeURIComponent(id)}&path=%2Fhome%2Fuser%2Fvector-preview.png`;
|
|
410
|
+
return {
|
|
411
|
+
output: `screenshot tersimpan: /home/user/vector-preview.png (${target})\nlink: ${link}`,
|
|
412
|
+
summary: `e2b screenshot ${target}`,
|
|
413
|
+
data: { widget: { type: 'button', text: '🖼 Lihat screenshot', link }, screenshot: link },
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
if (j?.status === 'error')
|
|
417
|
+
break;
|
|
418
|
+
}
|
|
419
|
+
return { output: 'ERROR: screenshot timeout (job tidak selesai dalam 90s)', summary: 'screenshot timeout' };
|
|
420
|
+
},
|
|
421
|
+
},
|
|
338
422
|
{
|
|
339
423
|
definition: {
|
|
340
424
|
name: 'e2b_download',
|
package/dist/tui/app.js
CHANGED
|
@@ -1763,7 +1763,13 @@ class App {
|
|
|
1763
1763
|
this.runFollowupMsgs.push(followupMsg);
|
|
1764
1764
|
}
|
|
1765
1765
|
}
|
|
1766
|
-
|
|
1766
|
+
// Tombol UI: tool render_ui — dan kini juga tool e2b (screenshot/
|
|
1767
|
+
// preview) — bisa mengembalikan data.widget {type:'button',text,link}.
|
|
1768
|
+
// Whitelist supaya tool pihak ketiga (MCP) tidak bisa memicu tombol
|
|
1769
|
+
// (surface prompt-injection: deskripsi/hasil tool = untrusted data).
|
|
1770
|
+
if ((tool === 'render_ui' || tool.startsWith('e2b_')) &&
|
|
1771
|
+
data &&
|
|
1772
|
+
data.widget) {
|
|
1767
1773
|
const w = data.widget;
|
|
1768
1774
|
this.activeUiButton = w;
|
|
1769
1775
|
this.messages.push({ kind: 'ui', content: w.text, ui: w, ts: Date.now() });
|
package/package.json
CHANGED