Skip to content

Instantly share code, notes, and snippets.

@lumpsoid
Created April 18, 2026 17:41
Show Gist options
  • Select an option

  • Save lumpsoid/201e51a0759f64f5b04a68bd80fd398c to your computer and use it in GitHub Desktop.

Select an option

Save lumpsoid/201e51a0759f64f5b04a68bd80fd398c to your computer and use it in GitHub Desktop.
openrouter-full-backup.js
async function fullBackup() {
// --- find db ---
const dbs = await indexedDB.databases();
const dbEntry = dbs.find(db => db.name.includes(':org_'))
?? dbs.find(db => db.name.startsWith('openrouter:playground:'));
if (!dbEntry) {
console.error('No OpenRouter database found. Make sure you are on openrouter.ai');
return;
}
// --- open ---
const db = await new Promise((res, rej) => {
const r = indexedDB.open(dbEntry.name);
r.onsuccess = e => res(e.target.result);
r.onerror = e => rej(e.target.error);
});
// the object store name matches the db name
const storeName = dbEntry.name;
if (![...db.objectStoreNames].includes(storeName)) {
console.error(`Object store "${storeName}" not found. Stores available:`, [...db.objectStoreNames]);
db.close();
return;
}
// --- dump all records ---
const all = await new Promise((res, rej) => {
const tx = db.transaction(storeName, 'readonly');
const req = tx.objectStore(storeName).getAll();
req.onsuccess = () => res(req.result);
req.onerror = e => rej(e.target.error);
});
db.close();
// --- bucket by prefix ---
const rooms = {};
const manifests = {};
const characters = {};
const messages = {};
const items = {};
let master = null;
for (const { key, value } of all) {
if (key.startsWith('v3:room:')) rooms[value.id] = value;
else if (key.startsWith('v3:manifest:')) manifests[key.replace('v3:manifest:', '')] = value;
else if (key.startsWith('v3:character:')) characters[value.id] = value;
else if (key.startsWith('v3:message:')) messages[value.id] = value;
else if (key.startsWith('v3:item:')) items[value.id] = value;
else if (key.startsWith('v3:master:')) master = value;
}
console.log(`Loaded — rooms:${Object.keys(rooms).length} manifests:${Object.keys(manifests).length} chars:${Object.keys(characters).length} msgs:${Object.keys(messages).length} items:${Object.keys(items).length}`);
// --- helpers ---
function itemToText(item) {
const content = item?.data?.content;
if (!Array.isArray(content)) return null;
return content.map(b => b.text ?? b.content ?? '').filter(Boolean).join('\n') || null;
}
// --- assemble ---
const roomIds = master?.roomIds ?? Object.keys(rooms);
const exportedRooms = roomIds.map(roomId => {
const room = rooms[roomId];
const manifest = manifests[roomId];
if (!room || !manifest) return null;
const roomChars = (manifest.characterIds ?? [])
.map(cid => characters[cid])
.filter(Boolean)
.map(c => ({
id: c.id,
model: c.model,
samplingParameters: c.samplingParameters,
reasoning: c.reasoning ?? null,
isRemoved: c.isRemoved ?? false,
isDisabled: c.isDisabled ?? false,
}));
const charModelMap = Object.fromEntries(roomChars.map(c => [c.id, c.model]));
const roomMessages = (manifest.messageIds ?? [])
.map(mid => messages[mid])
.filter(Boolean)
.map(msg => {
const isUser = msg.characterId === 'USER';
const role = isUser ? 'user' : 'assistant';
const model = isUser ? null : (charModelMap[msg.characterId] ?? msg.characterId);
const resolvedItems = (msg.items ?? []).map(ref => ({
type: ref.type,
id: ref.id,
text: itemToText(items[ref.id]),
}));
return {
id: msg.id,
role,
model,
createdAt: msg.createdAt,
updatedAt: msg.updatedAt,
parentMessageId: msg.parentMessageId ?? null,
isEdited: msg.isEdited ?? false,
context: msg.context ?? null,
text: resolvedItems.filter(i => i.type === 'message') .map(i => i.text).filter(Boolean).join('\n') || null,
reasoning: resolvedItems.filter(i => i.type === 'reasoning').map(i => i.text).filter(Boolean).join('\n') || null,
metadata: msg.metadata ?? null,
};
});
return {
id: room.id,
title: room.title,
createdAt: room.createdAt,
updatedAt: room.updatedAt,
lastActivityAt: room.lastActivityAt ?? null,
isPinned: room.isPinned ?? false,
characters: roomChars,
messages: roomMessages,
};
}).filter(Boolean);
// --- download ---
const payload = {
exportedAt: new Date().toISOString(),
dbName,
totalRooms: exportedRooms.length,
totalMessages: exportedRooms.reduce((n, r) => n + r.messages.length, 0),
rooms: exportedRooms,
};
const blob = new Blob([JSON.stringify(payload, null, 2)], { type: 'application/json' });
const a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = `openrouter-backup-${Date.now()}.json`;
a.click();
URL.revokeObjectURL(a.href);
console.log(`✅ Exported ${payload.totalRooms} rooms, ${payload.totalMessages} messages`);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment