Menu
popagent
publicLatest change 7f0ff66d6d9fb6468416c58bee46bd3d08169501 - Checkpoint browser channels and memory work by AkurAI Build
import type { BrowserProfile } from "./api-types";
import { retryableInit } from "./retryable-init";
import { secrets } from "./secrets";
import { storage } from "./storage";
const RESOURCE_ID = "popagent-browser";
const PROFILE_COLUMNS = `id,name,enabled,created_at::text AS "createdAt",updated_at::text AS "updatedAt"`;
function validStorageState(value: unknown): value is Record<string, unknown> {
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
const state = value as { cookies?: unknown; origins?: unknown };
return Array.isArray(state.cookies) && Array.isArray(state.origins);
}
export class BrowserProfileStore {
readonly storage = storage;
private readonly initializeOnce = retryableInit(() => this.initialize());
init() { return this.initializeOnce(); }
private async initialize() {
await this.storage.init();
await this.storage.db.none(await Bun.file(new URL("./browser-profiles.sql", import.meta.url)).text());
}
async list(): Promise<BrowserProfile[]> {
await this.init();
return this.storage.db.any<BrowserProfile>(`SELECT ${PROFILE_COLUMNS} FROM popagent_browser_profiles ORDER BY name`);
}
async create(input: { name: string; enabled: boolean; state: unknown }): Promise<BrowserProfile> {
if (!validStorageState(input.state)) throw new Error("Invalid Playwright storage state");
await this.init();
const id = crypto.randomUUID();
const profile = await this.storage.db.tx(async (db) => {
if (input.enabled) await db.none("UPDATE popagent_browser_profiles SET enabled=FALSE,updated_at=NOW() WHERE enabled");
return db.one<BrowserProfile>(
`INSERT INTO popagent_browser_profiles (id,name,enabled) VALUES ($1,$2,$3) RETURNING ${PROFILE_COLUMNS}`,
[id, input.name, input.enabled],
);
});
try {
if (!await secrets.store(RESOURCE_ID, `profile:${id}`, JSON.stringify(input.state))) throw new Error("Unable to store browser profile secret");
return profile;
} catch (error) {
await this.storage.db.none("DELETE FROM popagent_browser_profiles WHERE id=$1", [id]);
throw error;
}
}
async update(id: string, input: { name: string; enabled: boolean; state?: unknown }): Promise<BrowserProfile | undefined> {
if (input.state !== undefined && !validStorageState(input.state)) throw new Error("Invalid Playwright storage state");
await this.init();
const profile = await this.storage.db.tx(async (db) => {
if (input.enabled) await db.none("UPDATE popagent_browser_profiles SET enabled=FALSE,updated_at=NOW() WHERE id<>$1 AND enabled", [id]);
return (await db.oneOrNone<BrowserProfile>(
`UPDATE popagent_browser_profiles SET name=$2,enabled=$3,updated_at=NOW() WHERE id=$1 RETURNING ${PROFILE_COLUMNS}`,
[id, input.name, input.enabled],
)) ?? undefined;
});
if (profile && input.state !== undefined && !await secrets.update(RESOURCE_ID, `profile:${id}`, JSON.stringify(input.state))) {
throw new Error("Browser profile secret is missing");
}
return profile;
}
async recallState(id: string): Promise<Record<string, unknown> | undefined> {
const value = await secrets.recall(RESOURCE_ID, `profile:${id}`);
if (!value) return undefined;
const parsed: unknown = JSON.parse(value);
if (!validStorageState(parsed)) throw new Error("Stored browser profile has invalid storage state");
return parsed;
}
async active(): Promise<{ profile: BrowserProfile; state: Record<string, unknown> } | undefined> {
await this.init();
const profile = await this.storage.db.oneOrNone<BrowserProfile>(
`SELECT ${PROFILE_COLUMNS} FROM popagent_browser_profiles WHERE enabled ORDER BY updated_at DESC LIMIT 1`,
);
if (!profile) return undefined;
const state = await this.recallState(profile.id);
if (!state) throw new Error("Enabled browser profile secret is missing");
return { profile, state };
}
async delete(id: string): Promise<boolean> {
await this.init();
const removed = Boolean(await this.storage.db.oneOrNone("DELETE FROM popagent_browser_profiles WHERE id=$1 RETURNING id", [id]));
if (removed) await secrets.delete(RESOURCE_ID, `profile:${id}`);
return removed;
}
}
export const browserProfiles = new BrowserProfileStore();