diff --git a/bundled-addons/hermes/addon.json b/bundled-addons/hermes/addon.json index 92b8a711..2dc30d83 100644 --- a/bundled-addons/hermes/addon.json +++ b/bundled-addons/hermes/addon.json @@ -1,14 +1,14 @@ { "id": "hermes", "name": "Hermes", - "version": "0.1.0", + "version": "0.2.0", "description": "Message anyone by their .bch name. End-to-end encrypted DMs over Nostr (NIP-17), identity derived from your wallet seed.", "author": "Silent Mode", - "icon": "data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgNjQgNjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGcgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMzhCREY4IiBzdHJva2Utd2lkdGg9IjUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgdHJhbnNmb3JtPSJyb3RhdGUoLTIyIDMyIDMyKSI+PHJlY3QgeD0iMjAiIHk9IjI2IiB3aWR0aD0iMzAiIGhlaWdodD0iMTgiIHJ4PSIxLjUiLz48cGF0aCBkPSJNMjAgMjcgTDM1IDM3IEw1MCAyNyIvPjxwYXRoIGQ9Ik0yMCAzNiBMNCAzNiIvPjwvZz48L3N2Zz4K", + "icon": "data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgNjQgNjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGcgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMzhCREY4IiBzdHJva2Utd2lkdGg9IjQiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgdHJhbnNmb3JtPSJyb3RhdGUoLTIwIDMyIDMyKSI+PHJlY3QgeD0iMTgiIHk9IjI0IiB3aWR0aD0iMzQiIGhlaWdodD0iMjIiIHJ4PSIxLjUiLz48cGF0aCBkPSJNMTggMjUgTDM1IDM5IEw1MiAyNSIvPjxwYXRoIGQ9Ik0xOCAyOCBMLTQgMjQiLz48cGF0aCBkPSJNMTggMzYgTC02IDM2Ii8+PHBhdGggZD0iTTE4IDQ0IEwwIDQ2Ii8+PC9nPjwvc3ZnPg==", "main": "index.js", "activation": "on-demand", "panels": [ - { "id": "main", "title": "Hermes", "icon": "data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgNjQgNjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGcgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMzhCREY4IiBzdHJva2Utd2lkdGg9IjUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgdHJhbnNmb3JtPSJyb3RhdGUoLTIyIDMyIDMyKSI+PHJlY3QgeD0iMjAiIHk9IjI2IiB3aWR0aD0iMzAiIGhlaWdodD0iMTgiIHJ4PSIxLjUiLz48cGF0aCBkPSJNMjAgMjcgTDM1IDM3IEw1MCAyNyIvPjxwYXRoIGQ9Ik0yMCAzNiBMNCAzNiIvPjwvZz48L3N2Zz4K", "page": "panel.html", "side": "left" } + { "id": "main", "title": "Hermes", "icon": "data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgNjQgNjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGcgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMzhCREY4IiBzdHJva2Utd2lkdGg9IjQiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgdHJhbnNmb3JtPSJyb3RhdGUoLTIwIDMyIDMyKSI+PHJlY3QgeD0iMTgiIHk9IjI0IiB3aWR0aD0iMzQiIGhlaWdodD0iMjIiIHJ4PSIxLjUiLz48cGF0aCBkPSJNMTggMjUgTDM1IDM5IEw1MiAyNSIvPjxwYXRoIGQ9Ik0xOCAyOCBMLTQgMjQiLz48cGF0aCBkPSJNMTggMzYgTC02IDM2Ii8+PHBhdGggZD0iTTE4IDQ0IEwwIDQ2Ii8+PC9nPjwvc3ZnPg==", "page": "panel.html", "side": "left" } ], "capabilities": ["sidebar-panel"] } diff --git a/bundled-addons/hermes/index.js b/bundled-addons/hermes/index.js index 2d9598cb..16b736ed 100644 --- a/bundled-addons/hermes/index.js +++ b/bundled-addons/hermes/index.js @@ -1,19 +1,28 @@ -// Hermes bundled add-on — thin launcher. +// Hermes bundled add-on — thin proxy to the host's panel API. // -// 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. This add-on exists for ONE reason: register a -// left-strip sidebar entry so the Messages panel is discoverable without -// pressing Ctrl+Shift+E. +// 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. // -// The panel (panel.html) is a small chrome: mark, "Open Messages" button, -// shortcut hint. Click → forwarded through api.onMessage → an in-process -// ipcMain.emit that main.js listens for and resolves to openHermesWindow(). -// Keeping the forwarding tiny: nothing crypto-sensitive flows through this -// add-on, so a compromise of this one-handler surface can at worst open an -// otherwise-openable window. +// 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", "inbox", "send", + "markThreadRead", "contactsList", "contactsAdd", "contactsUpdate", + "contactsRemove", +]; + module.exports = { activate(api) { api.registerSidebarPanel({ @@ -26,13 +35,47 @@ module.exports = { // 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", () => { - // In-process emit: main.js listens on "hermes-open-request" and calls - // openHermesWindow(). Using emit (not .send) because this is a - // same-process signal, not an IPC to a renderer. ipcMain.emit("hermes-open-request"); return { ok: true }; }); - api.log("registered hermes sidebar panel"); + + // 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)"); }, }; diff --git a/bundled-addons/hermes/panel.html b/bundled-addons/hermes/panel.html index acbc4fbc..30dd0e4d 100644 --- a/bundled-addons/hermes/panel.html +++ b/bundled-addons/hermes/panel.html @@ -5,63 +5,593 @@ Hermes -