// Hermes bundled add-on — thin proxy to the host's panel API. // // All the real messaging code (NIP-17 wrap/unwrap, relay subscriptions, // persistent inbox, contacts) lives in Theseus main.js. Too much raw network // + crypto to run under the add-on sandbox, and the vault seed must never // cross the host/add-on boundary. // // This file does two things: // 1. Register the left-strip sidebar entry (so Hermes is discoverable // without pressing Ctrl+Shift+E). // 2. Proxy a small, allow-listed set of panel operations between the // panel's silentmode.invoke() calls and main.js's `global._hermesAPI`. // The panel never sees ipcRenderer — only this named handler surface. // // Events coming the other way (inbox updates, relay status changes) are // fanned out via a shared ipcMain event bus `__hermes_bus`: main.js's // hermesBusEmit publishes, this file forwards each payload to the panel. const { ipcMain } = require("electron"); const PANEL_METHODS = [ "status", "canUseVault", "close", "init", "signOut", "getBackup", "hasBackup", "publishCloudBackup", "fetchCloudBackup", "hasCloudBackup", "removeCloudBackup", "getProfile", "updateProfile", "getPhone", "setPhone", "clearPhone", "findByPhones", "inbox", "send", "markThreadRead", "contactsList", "contactsAdd", "contactsUpdate", "contactsRemove", ]; module.exports = { activate(api) { api.registerSidebarPanel({ id: "main", title: "Hermes", page: "panel.html", // Dock on the LEFT strip (alongside mail/pithos/telegram etc.), not // the right-hand add-on dock. The host checks `side` on both the // manifest's panels[] entry and the registerSidebarPanel call — // declaring it in both keeps dormant vs active renders consistent. side: "left", }); // Legacy launcher behavior: panel.html can still ask to open the // standalone Messages window. Kept because Ctrl+Shift+E users have // muscle memory, and because a few flows (debug, multi-window) // benefit from a free-floating window. api.onMessage("open-messages", () => { ipcMain.emit("hermes-open-request"); return { ok: true }; }); // Panel API proxies. Every method listed in PANEL_METHODS invokes the // same-named function on global._hermesAPI. The main.js side defines // that object once hermes is initialised; before that, status/canUseVault // are the only safe methods and will return a stub-ok. for (const name of PANEL_METHODS) { api.onMessage(name, async (payload) => { const hermes = global._hermesAPI; if (!hermes || typeof hermes[name] !== "function") { // Host not ready. Treat status/canUseVault as "no identity yet" // so the panel can render its signin screen without a toast. if (name === "status") return { ok: true, ready: false }; if (name === "canUseVault") return { ok: true, available: false }; return { ok: false, error: "hermes host not ready" }; } return await hermes[name](payload); }); } // Main-side → panel event bus. hermesBusEmit in main.js fires events on // ipcMain under __hermes_bus; forward each to the panel via api.emit so // panel-side `silentmode.on("hermes-message", cb)` works identically to // the ipcRenderer path used by the standalone window. const busForward = (channel, payload) => { try { api.emit(channel, payload); } catch {} }; ipcMain.on("__hermes_bus", busForward); api.onDeactivate && api.onDeactivate(() => { try { ipcMain.off("__hermes_bus", busForward); } catch {} }); api.log("hermes sidebar panel registered (proxy mode)"); }, };