feat: move plugin loading to dedicated hidden BrowserWindow (Phase 1)#9889
feat: move plugin loading to dedicated hidden BrowserWindow (Phase 1)#9889jackkav wants to merge 30 commits intoKong:developfrom
Conversation
2223c13 to
6efa211
Compare
There was a problem hiding this comment.
Pull request overview
This PR moves plugin enumeration/loading for the UI renderer behind a dedicated hidden Electron BrowserWindow (with Node integration) and routes renderer-side plugin interactions through a typed IPC bridge, as the first phase toward renderer hardening.
Changes:
- Added a hidden “plugin window” (HTML + entry + preload) and a main-process manager that proxies plugin API calls over IPC.
- Updated UI codepaths (themes, plugin reload, settings plugins list) to call
window.main.plugins.*instead of importing the plugin runtime directly. - Introduced serializable bridge types plus new unit tests covering plugin behaviors and plugin hook handling; updated build/esbuild entrypoints to ship the new window scripts.
Reviewed changes
Copilot reviewed 24 out of 24 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/insomnia/src/ui/renderer-listeners.ts | Route plugin reload through window.main.plugins.reloadPlugins() |
| packages/insomnia/src/ui/hooks/use-global-keyboard-shortcuts.ts | Route plugin reload shortcut through the plugins bridge |
| packages/insomnia/src/ui/hooks/theme.ts | Fetch themes via plugins bridge instead of direct plugin API call |
| packages/insomnia/src/ui/components/settings/plugins.tsx | Use SerializablePlugin and fetch plugin list via bridge |
| packages/insomnia/src/root.tsx | Reload plugins via bridge during theme install/apply flow |
| packages/insomnia/src/plugins/index.ts | Add test helper to control in-memory plugin list |
| packages/insomnia/src/plugins/bridge-types.ts | Define serializable plugin/theme/action metadata boundary + bridge API |
| packages/insomnia/src/plugins/tests/themes.test.ts | Add baseline tests for built-in theme list + plugin theme merge behavior |
| packages/insomnia/src/plugins/tests/index.test.ts | Add broad unit tests for plugin exports (hooks/actions/tags/themes) and errors |
| packages/insomnia/src/plugin-window.html | Add HTML host for hidden plugin window script |
| packages/insomnia/src/network/network.ts | Export internal hook-application helpers for unit testing |
| packages/insomnia/src/network/tests/plugin-hooks.test.ts | Add unit tests around request/response plugin hook behavior |
| packages/insomnia/src/main/window-utils.ts | Create plugin window during window initialization; export destroy helper |
| packages/insomnia/src/main/plugin-window.ts | Implement plugin window lifecycle + IPC proxying + pending request map |
| packages/insomnia/src/main/ipc/main.ts | Add plugins bridge type to main IPC surface; register plugin IPC handlers |
| packages/insomnia/src/main/ipc/electron.ts | Extend IPC channel type unions for new plugin channels |
| packages/insomnia/src/entry.preload.ts | Expose window.main.plugins.* bridge methods in renderer preload |
| packages/insomnia/src/entry.plugin-window.ts | Implement plugin-window-side dispatcher and result serialization |
| packages/insomnia/src/entry.plugin-window-preload.ts | Provide minimal window.app shim to support plugin path resolution |
| packages/insomnia/scripts/build.ts | Copy plugin-window HTML into production build output |
| packages/insomnia/esbuild.entrypoints.ts | Add esbuild entrypoints/watchers for plugin window + preload bundles |
| packages/insomnia/PLUGIN_SYSTEM_POC.md | Add architecture/plan documentation for the plugin system POC |
| packages/insomnia-smoke-test/playwright.config.ts | Change local reporter configuration |
| AGENTS.md | Add guidance on minimizing command output and cx navigation |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
d01ff18 to
2835bce
Compare
There was a problem hiding this comment.
I'd prefer to keep this in Confluence
There was a problem hiding this comment.
Its easier for the llm to reference here. I'll get rid of it once the work is done though and can add it to confluence for posterity.
|
Some plugin APIs that rely on the main window (e.g., showAlert, showWrapper) will trigger errors following this change. These APIs may require bridging to the main window to maintain functionality. And if the previous plugins depend on some of the window APIs, like height and width, they also break. As well as if the plugins are trying to append DOMs. Is this acceptable? |
|
feedback concern: templating now runs in main, which means templating has access to nodejs again, temporarily. injecting custom UI, in to the DOM won't work anymore, because it was using ambiant renderer libraries like React. We could add these to the plugin context and build bridges but this would be securable. |
- Set npm loglevel=warn to suppress install/run progress noise - Switch Playwright local reporter from list to dot (less output per test, CI unchanged) - Add scripts/setup.sh for one-time local git config (compact log, short status) - Document setup script in AGENTS.md Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
- Revert loglevel=warn from .npmrc — too broad, suppresses CI output - Remove shell alias suggestions from setup.sh — out of scope for a repo script Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Removes setup.sh in favour of explicit quiet-command guidance that benefits all agents (Claude, Copilot, Codex) without requiring a one-time setup step. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
cx gives agents a cost ladder (overview → symbols → definition → read) that reduces file reads for all agents that read AGENTS.md — complementary to CodeGraph which is Claude Code-specific. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
All plugin API calls (getThemes, getPlugins, getActivePlugins, reloadPlugins, getRequestActions, getRequestGroupActions, getWorkspaceActions, getDocumentActions) are now routed through a dedicated hidden BrowserWindow with nodeIntegration:true instead of running directly in the renderer. IPC relay: renderer → ipcMain.handle → plugin window webContents → ipcRenderer.send back to main → resolve renderer promise via pending-request map with 30s timeout. Renderer-side callers updated to use window.main.plugins.* bridge. Two new esbuild entry points added (plugin-window, plugin-window-preload). Dev build threshold updated from 3 to 6 to account for all contexts. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Add executeAction IPC method so all four plugin action dropdowns (request, requestGroup, workspace, document) dispatch through the plugin window instead of running context modules directly in the renderer. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
…bridge Bridge getTemplateTags() and runTemplateTagAction() so code-editor, one-line-editor, and tag-editor no longer import from plugins/index or plugins/context/store in the renderer. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
…rowserWindow Bridge template tags (getTemplateTags, runTemplateTagAction), bundle plugin listing (getBundlePlugins), and elevated plugin actions (executePluginMainAction) so no renderer code calls plugin index or context modules directly for execution. Remaining renderer plugin imports are intentional: applyColorScheme/getColorScheme (DOM utilities) and createPlugin (filesystem scaffolding), neither of which is plugin execution. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
- Create database.plugin-window.ts IPC proxy so the plugin window reads from the main process NeDB connection instead of opening a second one - Initialize database + services in entry.plugin-window.ts before sending plugin-window-ready, fixing the silent "Service not initialized" crash that was masked by unawaited promises - Add isMainWindow fallback to the page fixture so firstWindow() racing to return the hidden plugin window doesn't break other tests - Add plugin-bridge.test.ts: E2E test that writes a requestAction plugin, reloads via the bridge, and verifies the action appears in the request dropdown Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Co-authored-by: Copilot <[email protected]>
Moves requestHooks and responseHooks execution into the hidden plugin window via the IPC bridge. The default-headers built-in runs in the renderer (no IPC). A cached hasRequestHooks/hasResponseHooks check in the main process avoids any plugin window round-trip per request when no user plugins have hooks registered. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
_applyRequestPluginHooks and _applyResponsePluginHooks now use window.main.plugins.* IPC only in the Electron renderer. In the main process (OAuth2 token exchange via get-token.ts) and Node.js CLI (insomnia-inso), they fall back to loading plugins directly via plugins.getRequestHooks/getResponseHooks. This fixes: - inso CLI: "window is not defined" in all run collection/test commands - Electron: OAuth2 token exchange failing with "no access token provided" Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Playwright's firstWindow() was racing with the plugin window and sometimes returning it instead of the main app window. By deferring createPluginWindow() to did-finish-load on the main window, the plugin window is guaranteed to not exist yet when firstWindow() resolves. Removes the findMainWindow polling fallback from the test fixture. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
…textMenuTag type Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
…dler Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
…gin window to main renderer Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
f985e6b to
d472861
Compare
Summary
Moves all plugin loading and execution out of the UI renderer into a dedicated hidden `BrowserWindow` with `nodeIntegration: true`. The renderer communicates with the plugin window exclusively over IPC.
What changed
Architecture
What's bridged (Phase 1 complete)
Hook bridging design
Request and response hooks mutate their context objects by reference, which only works within a single process. To cross the IPC boundary the plugin window receives a serialized copy of the request/response, runs all registered hooks against it, and returns the fully mutated object — matching the same pattern used by pre-request scripts.
The built-in default-headers hook runs in the renderer without any IPC (it has no plugin dependency). A `hasRequestHooks`/`hasResponseHooks` result is cached in the main process after the first check and cleared on `reloadPlugins`, so requests incur zero plugin-window round-trips when no user plugins have hooks registered.
Non-renderer process handling
`_applyRequestPluginHooks` and `_applyResponsePluginHooks` guard on `process.type !== 'renderer'` before using the IPC bridge. In the Electron main process (OAuth token exchange) and in the inso CLI (Node.js), hooks are applied directly via `plugins.getRequestHooks()` / `plugins.getResponseHooks()` without any IPC.
Intentionally remaining in renderer
Security fixes (from review)
Playwright fixture
The plugin window is created after the main window's `did-finish-load` event, so Playwright's `firstWindow()` always returns the main app window. No fixture fallback or polling is needed.
Test plan
🤖 Generated with Claude Code
closes INS-2466