mirror of
https://github.com/paperclipai/paperclip
synced 2026-04-25 17:25:15 +02:00
## Thinking Path > - Paperclip orchestrates AI agents for zero-human companies > - Agents sometimes need to capture UI screenshots for visual verification of fixes > - The Paperclip UI requires authentication, so headless browser screenshots fail without auth > - The CLI already stores a board token in `~/.paperclip/auth.json` > - This pull request adds a Playwright-based screenshot script that reads the board token and injects it as a Bearer header > - The benefit is agents can now take authenticated screenshots of any Paperclip UI page without storing email/password credentials ## What Changed - Added `scripts/screenshot.cjs` — a Node.js script that: - Reads the board token from `~/.paperclip/auth.json` - Launches Chromium via Playwright with the token as an `Authorization` header - Navigates to the specified URL and saves a screenshot - Supports `--width`, `--height`, and `--wait` flags - Accepts both full URLs and path-only shortcuts (e.g., `/PAPA/agents/cto/instructions`) ## Verification ```bash node scripts/screenshot.cjs /PAPA/agents/cto/instructions /tmp/test.png --width 1920 ``` Should produce an authenticated screenshot of the agent instructions page. ## Risks - Low risk — standalone utility script with no impact on the main application. Requires Playwright (already a dev dependency) and a valid board token in `~/.paperclip/auth.json`. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [ ] I have run tests locally and they pass - [ ] I have added or updated tests where applicable - [ ] If this change affects the UI, I have included before/after screenshots - [ ] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing>
93 lines
2.9 KiB
JavaScript
93 lines
2.9 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Screenshot utility for Paperclip UI.
|
|
*
|
|
* Reads the board token from ~/.paperclip/auth.json and injects it as a
|
|
* Bearer header so Playwright can access authenticated pages.
|
|
*
|
|
* Usage:
|
|
* node scripts/screenshot.cjs <url-or-path> [output.png] [--width 1280] [--height 800] [--wait 2000]
|
|
*
|
|
* Examples:
|
|
* node scripts/screenshot.cjs /PAPA/agents/cto/instructions /tmp/shot.png
|
|
* node scripts/screenshot.cjs http://localhost:5173/PAPA/agents/cto/instructions
|
|
*/
|
|
|
|
const fs = require("fs");
|
|
const path = require("path");
|
|
const os = require("os");
|
|
|
|
// --- CLI args -----------------------------------------------------------
|
|
const args = process.argv.slice(2);
|
|
function flag(name, fallback) {
|
|
const i = args.indexOf(`--${name}`);
|
|
if (i === -1) return fallback;
|
|
const val = args.splice(i, 2)[1];
|
|
return Number.isNaN(Number(val)) ? fallback : Number(val);
|
|
}
|
|
const width = flag("width", 1280);
|
|
const height = flag("height", 800);
|
|
const waitMs = flag("wait", 2000);
|
|
|
|
const rawUrl = args[0];
|
|
const outPath = args[1] || "/tmp/paperclip-screenshot.png";
|
|
|
|
if (!rawUrl) {
|
|
console.error("Usage: node scripts/screenshot.cjs <url-or-path> [output.png]");
|
|
process.exit(1);
|
|
}
|
|
|
|
// --- Auth ----------------------------------------------------------------
|
|
function loadBoardToken() {
|
|
const authPath = path.resolve(os.homedir(), ".paperclip/auth.json");
|
|
try {
|
|
const auth = JSON.parse(fs.readFileSync(authPath, "utf-8"));
|
|
const creds = auth.credentials || {};
|
|
const entry = Object.values(creds)[0];
|
|
if (entry && entry.token && entry.apiBase) return { token: entry.token, apiBase: entry.apiBase };
|
|
} catch (_) {
|
|
// ignore
|
|
}
|
|
return null;
|
|
}
|
|
|
|
const cred = loadBoardToken();
|
|
if (!cred) {
|
|
console.error("No board token found in ~/.paperclip/auth.json");
|
|
process.exit(1);
|
|
}
|
|
|
|
// Resolve URL — if it starts with / treat as path relative to apiBase
|
|
const url = rawUrl.startsWith("http") ? rawUrl : `${cred.apiBase}${rawUrl}`;
|
|
|
|
// Validate URL before launching browser
|
|
const origin = new URL(url).origin;
|
|
|
|
// --- Screenshot ----------------------------------------------------------
|
|
(async () => {
|
|
const { chromium } = require("playwright");
|
|
const browser = await chromium.launch();
|
|
try {
|
|
const context = await browser.newContext({
|
|
viewport: { width, height },
|
|
});
|
|
|
|
const page = await context.newPage();
|
|
// Scope the auth header to the Paperclip origin only
|
|
await page.route(`${origin}/**`, async (route) => {
|
|
await route.continue({
|
|
headers: { ...route.request().headers(), Authorization: `Bearer ${cred.token}` },
|
|
});
|
|
});
|
|
await page.goto(url, { waitUntil: "networkidle", timeout: 20000 });
|
|
await page.waitForTimeout(waitMs);
|
|
await page.screenshot({ path: outPath, fullPage: false });
|
|
console.log(`Saved: ${outPath}`);
|
|
} catch (err) {
|
|
console.error(`Screenshot failed: ${err.message}`);
|
|
process.exitCode = 1;
|
|
} finally {
|
|
await browser.close();
|
|
}
|
|
})();
|