Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion bin/browse.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,16 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
const BASE_DATA_DIR = resolve(process.env.VERDICT_DATA_DIR || resolve(__dirname, '..', '.verdict-data'));
const SERVER_SCRIPT = resolve(__dirname, '..', 'src', 'server.mjs');

// Parse --session <name> and --json from argv early
// Parse --session <name>, --storage-state <path>, and --json from argv early
const rawArgs = process.argv.slice(2);
const jsonFlag = rawArgs.includes('--json');
let sessionName = null;
let storageStatePath = null;
const filtered = [];
for (let i = 0; i < rawArgs.length; i++) {
if (rawArgs[i] === '--json') continue;
if (rawArgs[i] === '--session' && rawArgs[i + 1]) { sessionName = rawArgs[++i]; continue; }
if (rawArgs[i] === '--storage-state' && rawArgs[i + 1]) { storageStatePath = rawArgs[++i]; continue; }
filtered.push(rawArgs[i]);
}
const [command, ...args] = filtered;
Expand Down Expand Up @@ -117,6 +119,10 @@ Auth Profiles:
handoff Switch to visible browser for manual login
resume Return to headless after login
goto-auth <url> --profile <name> Navigate with auto-loaded auth
storage-state-load <path> Load Playwright storageState JSON (cookies + localStorage)

Flags (global):
--storage-state <path> Load Playwright storageState JSON before running command

Cookies:
cookies [url] List cookie-set <n> <v> <dom> Set manually
Expand Down Expand Up @@ -191,11 +197,21 @@ if (!state || !isAlive(state.pid)) {
}

try {
if (storageStatePath) {
const loadResult = await send(state, 'storage-state-load', [storageStatePath]);
if (loadResult.startsWith('Error')) { outputError(loadResult); }
process.stderr.write(`${loadResult}\n`);
}
output(await send(state, command, args));
} catch (e) {
if (e.cause?.code === 'ECONNREFUSED' || e.cause?.code === 'UND_ERR_SOCKET' || e.message?.includes('fetch failed')) {
process.stderr.write('Server down, restarting...\n');
state = await startServer();
if (storageStatePath) {
const loadResult = await send(state, 'storage-state-load', [storageStatePath]);
if (loadResult.startsWith('Error')) { outputError(loadResult); }
process.stderr.write(`${loadResult}\n`);
}
output(await send(state, command, args));
} else if (command === "stop") { output("Server stopped."); } else { outputError(e.message); }
}
16 changes: 16 additions & 0 deletions src/server.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,22 @@ const commands = {
const p = profilePath(name); if (!existsSync(p)) return `Profile "${name}" not found.`;
unlinkSync(p); return `Profile "${name}" deleted.`;
},
async 'storage-state-load'(args) {
const filePath = args[0]; if (!filePath) return 'Error: path required. Usage: storage-state-load <path>';
if (!existsSync(filePath)) return `Error: file not found: ${filePath}`;
try {
const state = JSON.parse(readFileSync(filePath, 'utf8'));
await context.clearCookies();
if (state.cookies?.length) await context.addCookies(state.cookies);
const origins = state.origins || [];
for (const { origin, localStorage: items } of origins) {
if (!items?.length) continue;
await page.goto(origin, { waitUntil: 'domcontentloaded', timeout: 15000 }).catch(() => {});
await page.evaluate((ls) => { for (const { name, value } of ls) localStorage.setItem(name, value); }, items);
}
return `Loaded storageState from ${filePath}: ${state.cookies?.length || 0} cookies, ${origins.length} origin(s)`;
} catch (e) { return `Error loading storageState: ${e.message}`; }
},
async handoff() {
const cookies = await context.cookies();
const currentUrl = page.url();
Expand Down