mirror of
https://github.com/siteboon/claudecodeui.git
synced 2026-03-15 10:57:25 +00:00
feat(plugins): add backend subprocess support via RPC bridge
Extend the plugin system so plugins can optionally declare a entry in their manifest. The host spawns a Node.js subprocess for that script, assigns it a random local port, and exposes an RPC proxy route () that the sandboxed iframe can call via postMessage — avoiding the need for the iframe to hold auth tokens. Changes: - Add plugin process manager to spawn/stop server subprocesses - Wire subprocess lifecycle into enable/disable and uninstall routes - Add RPC proxy route on the host server - Extend PluginsContext and PluginTabContent to handle ccui:rpc and ccui:rpc-response postMessage events - Add hello-world server.js as a reference subprocess implementation - Update manifest.json and README with server field documentation
This commit is contained in:
@@ -14,9 +14,32 @@
|
||||
body.dark { background: #1a1a2e; color: #e0e0e0; }
|
||||
body.light { background: #ffffff; color: #1a1a2e; }
|
||||
h1 { font-size: 1.4rem; margin-bottom: 8px; }
|
||||
h2 { font-size: 1.1rem; margin-top: 24px; margin-bottom: 8px; opacity: 0.8; }
|
||||
.context { font-size: 0.85rem; opacity: 0.7; margin-top: 16px; }
|
||||
.context dt { font-weight: 600; margin-top: 8px; }
|
||||
.context dd { margin-left: 12px; }
|
||||
button {
|
||||
margin-top: 12px;
|
||||
padding: 8px 16px;
|
||||
border: 1px solid currentColor;
|
||||
border-radius: 6px;
|
||||
background: transparent;
|
||||
color: inherit;
|
||||
cursor: pointer;
|
||||
font-size: 0.85rem;
|
||||
opacity: 0.8;
|
||||
}
|
||||
button:hover { opacity: 1; }
|
||||
pre {
|
||||
margin-top: 8px;
|
||||
padding: 12px;
|
||||
border-radius: 6px;
|
||||
font-size: 0.8rem;
|
||||
overflow-x: auto;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
body.dark pre { background: #2a2a3e; }
|
||||
body.light pre { background: #f0f0f4; }
|
||||
</style>
|
||||
</head>
|
||||
<body class="light">
|
||||
@@ -29,24 +52,63 @@
|
||||
<dt>Session</dt><dd id="ctx-session">—</dd>
|
||||
</dl>
|
||||
|
||||
<h2>Server RPC</h2>
|
||||
<p style="font-size: 0.85rem; opacity: 0.7;">This calls the plugin's own Node.js server subprocess via the postMessage RPC bridge.</p>
|
||||
<button id="btn-hello">Call GET /hello</button>
|
||||
<button id="btn-echo">Call POST /echo</button>
|
||||
<pre id="rpc-result">Click a button to make an RPC call...</pre>
|
||||
|
||||
<script>
|
||||
// Listen for context updates from the host app.
|
||||
// ── RPC helper ──────────────────────────────────────────────────
|
||||
// Sends a request through the host's postMessage bridge, which
|
||||
// proxies it to this plugin's server subprocess.
|
||||
function callBackend(method, path, body) {
|
||||
return new Promise((resolve) => {
|
||||
const requestId = Math.random().toString(36).slice(2);
|
||||
|
||||
function handler(event) {
|
||||
if (event.data?.type === 'ccui:rpc-response' && event.data.requestId === requestId) {
|
||||
window.removeEventListener('message', handler);
|
||||
resolve(event.data);
|
||||
}
|
||||
}
|
||||
window.addEventListener('message', handler);
|
||||
|
||||
window.parent.postMessage({
|
||||
type: 'ccui:rpc',
|
||||
requestId,
|
||||
method,
|
||||
path,
|
||||
body: body || undefined,
|
||||
}, '*');
|
||||
});
|
||||
}
|
||||
|
||||
// ── Context listener ────────────────────────────────────────────
|
||||
window.addEventListener('message', (event) => {
|
||||
if (!event.data || event.data.type !== 'ccui:context') return;
|
||||
|
||||
const { theme, project, session } = event.data;
|
||||
|
||||
// Apply theme
|
||||
document.body.className = theme || 'light';
|
||||
|
||||
// Display context
|
||||
document.getElementById('ctx-theme').textContent = theme || '—';
|
||||
document.getElementById('ctx-project').textContent = project ? project.name : '(none)';
|
||||
document.getElementById('ctx-session').textContent = session ? session.title || session.id : '(none)';
|
||||
});
|
||||
|
||||
// Ask the host for the current context on load.
|
||||
// Request context on load
|
||||
window.parent.postMessage({ type: 'ccui:request-context' }, '*');
|
||||
|
||||
// ── RPC demo buttons ────────────────────────────────────────────
|
||||
document.getElementById('btn-hello').addEventListener('click', async () => {
|
||||
const result = await callBackend('GET', '/hello');
|
||||
document.getElementById('rpc-result').textContent = JSON.stringify(result, null, 2);
|
||||
});
|
||||
|
||||
document.getElementById('btn-echo').addEventListener('click', async () => {
|
||||
const result = await callBackend('POST', '/echo', { greeting: 'Hello from the iframe!' });
|
||||
document.getElementById('rpc-result').textContent = JSON.stringify(result, null, 2);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user