agentic-os/dashboard/pages/plugins.js

182 lines
7.7 KiB
JavaScript

async function renderPlugins() {
const content = document.getElementById('pageContent');
content.innerHTML = `
<div class="page-header">
<div class="page-header-left">
<h1 class="page-title">Plugin Registry</h1>
<p class="page-subtitle">Install skills from GitHub or create your own</p>
</div>
<button class="btn btn-primary" onclick="showInstallPlugin()">+ Install from GitHub</button>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-bottom:24px">
<div class="card">
<div class="card-title" style="margin-bottom:12px">Install a Plugin</div>
<div class="form-group">
<label class="form-label">GitHub Repo URL</label>
<input id="pluginRepoInput" class="form-input" placeholder="https://github.com/you/your-skill-repo">
<div class="form-hint">Paste a GitHub URL to clone a skill from a repo</div>
</div>
<div class="form-group">
<label class="form-label">Plugin Name (optional)</label>
<input id="pluginNameInput" class="form-input" placeholder="auto-detect from repo URL">
</div>
<button class="btn btn-primary" onclick="installPluginFromRepo()">Install</button>
<div id="installStatus" style="margin-top:8px;font-size:12px"></div>
</div>
<div class="card">
<div class="card-title" style="margin-bottom:12px">Connect External App</div>
<div class="form-group">
<label class="form-label">App Name</label>
<input id="integrationNameInput" class="form-input" placeholder="e.g., Slack, Zapier, n8n">
</div>
<div class="form-group">
<label class="form-label">Webhook / URL</label>
<input id="integrationUrlInput" class="form-input" placeholder="https://hooks.slack.com/...">
</div>
<div class="form-group">
<label class="form-label">Type</label>
<select id="integrationType" class="form-input">
<option value="webhook">Webhook</option>
<option value="api">REST API</option>
<option value="zapier">Zapier</option>
<option value="n8n">n8n</option>
</select>
</div>
<button class="btn btn-primary" onclick="addIntegration()">Connect</button>
<div id="integrationStatus" style="margin-top:8px;font-size:12px"></div>
</div>
</div>
<div class="card" style="margin-bottom:16px">
<div class="card-title" style="margin-bottom:12px">Connected Apps</div>
<div id="integrationsList"></div>
</div>
<div id="pluginList"><div class="loading"><div class="loading-spinner"></div></div></div>
`;
try {
const [pluginsData, integrationsData] = await Promise.all([
api.getPlugins(),
api.getIntegrations(),
]);
renderPluginTable(pluginsData.plugins || []);
renderIntegrations(integrationsData.integrations || []);
} catch (err) {
document.getElementById('pluginList').innerHTML = `<div class="empty-state"><div class="empty-state-icon">⚠</div><div class="empty-state-title">${escapeHtml(err.message)}</div></div>`;
}
}
function renderPluginTable(plugins) {
const container = document.getElementById('pluginList');
if (plugins.length === 0) {
container.innerHTML = '<div class="empty-state"><div class="empty-state-icon">🔌</div><div class="empty-state-title">No plugins installed</div><div class="empty-state-desc">Install plugins from GitHub or create your own</div></div>';
return;
}
container.innerHTML = `
<div class="card">
<div class="card-title">Installed Plugins (${plugins.length})</div>
<div class="table-wrapper">
<table>
<thead><tr><th>Plugin</th><th>Version</th><th>Type</th><th>Source</th><th>Installed</th><th></th></tr></thead>
<tbody>
${plugins.map(p => `
<tr>
<td><strong>${escapeHtml(p.name)}</strong></td>
<td><code>${p.version || '1.0.0'}</code></td>
<td><span class="badge badge-info">${p.type || 'skill'}</span></td>
<td style="font-size:11px;max-width:200px;overflow:hidden;text-overflow:ellipsis" title="${escapeHtml(p.source || '')}">${escapeHtml(p.source || 'built-in')}</td>
<td style="font-size:12px;color:var(--text-muted)">${formatDate(p.installed)}</td>
<td>${p.source ? `<button class="btn btn-ghost" style="padding:4px 8px;font-size:11px" onclick="uninstallPlugin('${escapeHtml(p.name)}')">Remove</button>` : '—'}</td>
</tr>
`).join('')}
</tbody>
</table>
</div>
</div>
`;
}
function renderIntegrations(integrations) {
const container = document.getElementById('integrationsList');
if (integrations.length === 0) {
container.innerHTML = '<div style="font-size:12px;color:var(--text-muted)">No external apps connected yet</div>';
return;
}
container.innerHTML = `<div style="display:flex;flex-direction:column;gap:8px">
${integrations.map(i => `
<div style="display:flex;align-items:center;justify-content:space-between;padding:8px 12px;background:var(--bg-secondary);border-radius:6px">
<div>
<strong style="font-size:13px">${escapeHtml(i.name)}</strong>
<span class="badge badge-info" style="margin-left:8px">${escapeHtml(i.type)}</span>
<div style="font-size:11px;color:var(--text-muted);margin-top:2px">${escapeHtml(i.url)}</div>
</div>
<span style="font-size:11px;color:var(--accent)">● connected</span>
</div>
`).join('')}
</div>`;
}
function showInstallPlugin() {
document.getElementById('pluginRepoInput').focus();
}
async function installPluginFromRepo() {
const repoUrl = document.getElementById('pluginRepoInput').value.trim();
const name = document.getElementById('pluginNameInput').value.trim();
const statusEl = document.getElementById('installStatus');
if (!repoUrl) {
statusEl.innerHTML = '<span style="color:#ef4444">Please enter a GitHub repo URL</span>';
return;
}
statusEl.innerHTML = '<span style="color:var(--accent)">Installing...</span>';
try {
const r = await api.installPlugin(name || repoUrl, repoUrl);
statusEl.innerHTML = `<span style="color:#22c55e">✓ ${escapeHtml(r.plugin)} installed</span>`;
document.getElementById('pluginRepoInput').value = '';
document.getElementById('pluginNameInput').value = '';
setTimeout(() => renderPlugins(), 2000);
} catch (err) {
statusEl.innerHTML = `<span style="color:#ef4444">Error: ${escapeHtml(err.message)}</span>`;
}
}
async function uninstallPlugin(name) {
if (!confirm(`Remove "${name}"?`)) return;
try {
await api.uninstallPlugin(name);
showToast(`${name} removed`, 'success');
renderPlugins();
} catch (err) {
showToast(`Error: ${err.message}`, 'error');
}
}
async function addIntegration() {
const name = document.getElementById('integrationNameInput').value.trim();
const url = document.getElementById('integrationUrlInput').value.trim();
const type = document.getElementById('integrationType').value;
const statusEl = document.getElementById('integrationStatus');
if (!name || !url) {
statusEl.innerHTML = '<span style="color:#ef4444">Name and URL required</span>';
return;
}
statusEl.innerHTML = '<span style="color:var(--accent)">Connecting...</span>';
try {
const r = await api.addIntegration(name, url, type);
statusEl.innerHTML = `<span style="color:#22c55e">✓ ${escapeHtml(r.name)} connected</span>`;
document.getElementById('integrationNameInput').value = '';
document.getElementById('integrationUrlInput').value = '';
setTimeout(() => renderPlugins(), 2000);
} catch (err) {
statusEl.innerHTML = `<span style="color:#ef4444">Error: ${escapeHtml(err.message)}</span>`;
}
}