Files
sovereign_browser/www/agents/config.js
T

290 lines
9.8 KiB
JavaScript

/* Agent config page — sovereign://agents
*
* Fetches the current agent config from sovereign://agents/config (JSON)
* on page load and populates the form. Saves use sovereign://agents/set.
* Model refresh uses sovereign://agents/models.
*/
var savedModel = '';
function sovereignGet(url) {
return new Promise(function(resolve, reject) {
var x = new XMLHttpRequest();
x.open('GET', url, true);
x.onreadystatechange = function() {
if (x.readyState !== 4) return;
try { resolve(JSON.parse(x.responseText)); }
catch (e) { reject(e); }
};
x.onerror = function() { reject(new Error('XHR error')); };
x.send();
});
}
function showStatus(cls, msg) {
var s = document.getElementById('status');
s.className = 'status ' + cls + ' show';
s.textContent = msg;
}
function toggleKeyHide() {
var cb = document.getElementById('api-key-hide');
var ak = document.getElementById('agent.llm_api_key');
ak.type = cb.checked ? 'password' : 'text';
}
function applyConfig(d) {
/* Provider dropdown. */
var sel = document.getElementById('agent.provider');
sel.innerHTML = '';
(d.providers || []).forEach(function(name, i) {
var opt = document.createElement('option');
opt.value = name;
opt.textContent = name;
if (i === d.active_provider) opt.selected = true;
sel.appendChild(opt);
});
/* Active provider fields. */
document.getElementById('provider_name').value = d.provider_name || '';
document.getElementById('agent.llm_base_url').value = d.base_url || '';
document.getElementById('agent.llm_api_key').value = d.api_key || '';
/* Model dropdown. */
savedModel = d.model || '';
populateModels(d.models || []);
/* Max iterations + Sovereign Browser Skill fields. */
document.getElementById('agent.max_iterations').value =
d.max_iterations != null ? d.max_iterations : 100;
document.getElementById('agent.skill_name').value =
d.skill_name || 'Sovereign Browser Default';
document.getElementById('agent.skill_description').value =
d.skill_description || 'Default agent skill for sovereign_browser';
document.getElementById('agent.skill_template').value =
d.skill_template || d.system_prompt || '';
document.getElementById('agent.skill_requires_tools').value =
d.skill_requires_tools || 'browser, fs, shell';
updateRefreshBtn();
}
function save(key) {
var el = document.getElementById(key);
var val = el ? el.value : '';
return sovereignGet('sovereign://agents/set?key=' + encodeURIComponent(key) +
'&value=' + encodeURIComponent(val))
.then(function(d) {
if (d.error) showStatus('err', d.message);
else showStatus('ok', d.key + ' saved');
return d;
})
.catch(function(e) {
showStatus('err', 'Error: ' + e.message);
throw e;
});
}
function saveAndRefresh(key) {
save(key).then(function() {
var bu = document.getElementById('agent.llm_base_url');
var ak = document.getElementById('agent.llm_api_key');
if (bu && bu.value && bu.value.trim() && ak && ak.value && ak.value.trim()) {
refreshModels();
}
}).catch(function() {});
}
function updateRefreshBtn() {
var bu = document.getElementById('agent.llm_base_url');
var ak = document.getElementById('agent.llm_api_key');
var btn = document.getElementById('refresh-models-btn');
var enabled = bu && bu.value && bu.value.trim() &&
ak && ak.value && ak.value.trim();
btn.disabled = !enabled;
}
function onProviderChange() {
save('agent.provider').then(function() { window.location.reload(); });
}
function saveProviderName() {
var el = document.getElementById('provider_name');
if (el && el.value && el.value.trim()) {
save('agent.provider_name').then(function() { window.location.reload(); });
}
}
function addProvider() {
var el = document.getElementById('new_provider_name');
if (el && el.value && el.value.trim()) {
sovereignGet('sovereign://agents/set?key=agent.provider_add&value=' +
encodeURIComponent(el.value.trim()))
.then(function(d) {
if (d.error) showStatus('err', d.message);
else { showStatus('ok', 'Provider added'); window.location.reload(); }
})
.catch(function(e) { showStatus('err', 'Error: ' + e.message); });
}
}
function removeProvider() {
var sel = document.getElementById('agent.provider');
if (sel && sel.value) {
if (!confirm('Remove provider "' + sel.value + '"?')) return;
sovereignGet('sovereign://agents/set?key=agent.provider_remove&value=' +
encodeURIComponent(sel.value))
.then(function(d) {
if (d.error) showStatus('err', d.message);
else { showStatus('ok', 'Provider removed'); window.location.reload(); }
})
.catch(function(e) { showStatus('err', 'Error: ' + e.message); });
}
}
function onModelChange() {
var sel = document.getElementById('agent.llm_model');
var txt = document.getElementById('agent.llm_model_text');
if (sel.value === '__custom__') {
txt.style.display = '';
txt.value = savedModel;
txt.focus();
} else {
txt.style.display = 'none';
savedModel = sel.value;
save('agent.llm_model');
}
}
function onModelTextInput() {
var txt = document.getElementById('agent.llm_model_text');
savedModel = txt.value;
}
function saveCustomModel() {
var txt = document.getElementById('agent.llm_model_text');
if (txt && txt.value && txt.value.trim()) {
savedModel = txt.value.trim();
save('agent.llm_model');
}
}
function populateModels(models) {
var sel = document.getElementById('agent.llm_model');
var txt = document.getElementById('agent.llm_model_text');
sel.innerHTML = '';
models = models.slice().sort(function(a, b) {
return a.toLowerCase().localeCompare(b.toLowerCase());
});
var found = false;
for (var i = 0; i < models.length; i++) {
var opt = document.createElement('option');
opt.value = models[i];
opt.textContent = models[i];
if (models[i] === savedModel) { opt.selected = true; found = true; }
sel.appendChild(opt);
}
var copt = document.createElement('option');
copt.value = '__custom__';
copt.textContent = '(custom...)';
if (!found) { copt.selected = true; sel.value = '__custom__'; }
sel.appendChild(copt);
if (!found) {
txt.style.display = '';
txt.value = savedModel;
} else {
txt.style.display = 'none';
}
}
function refreshModels() {
var bu = document.getElementById('agent.llm_base_url');
var ak = document.getElementById('agent.llm_api_key');
if (!bu || !bu.value || !bu.value.trim()) return;
var btn = document.getElementById('refresh-models-btn');
btn.disabled = true;
btn.textContent = 'Loading...';
var url = 'sovereign://agents/models?base_url=' +
encodeURIComponent(bu.value.trim()) +
'&api_key=' + encodeURIComponent(ak ? ak.value : '');
sovereignGet(url)
.then(function(d) {
if (d && d.error) {
showStatus('err', 'Models: ' + d.error);
} else if (d && d.length) {
populateModels(d);
showStatus('ok', 'Loaded ' + d.length + ' models');
} else {
showStatus('err', 'No models returned');
}
})
.catch(function(e) { showStatus('err', 'Models error: ' + e.message); })
.then(function() {
btn.textContent = 'Refresh Models';
updateRefreshBtn();
});
}
/* ── Save as Skill (publish to Nostr as kind 31123) ──────────── */
function saveAsSkill() {
var name = document.getElementById('agent.skill_name').value.trim();
var desc = document.getElementById('agent.skill_description').value.trim();
var content = document.getElementById('agent.skill_template').value.trim();
var toolsRaw = document.getElementById('agent.skill_requires_tools').value.trim();
if (!name || !content) {
showStatus('err', 'Name and template are required');
return;
}
var tools = [];
if (toolsRaw) {
tools = toolsRaw.split(',').map(function(t) {
return t.trim();
}).filter(function(t) { return t.length > 0; });
}
/* Save the local fields first, then publish to Nostr. */
save('agent.skill_name').then(function() {
return save('agent.skill_description');
}).then(function() {
return save('agent.skill_template');
}).then(function() {
return save('agent.skill_requires_tools');
}).then(function() {
var url = 'sovereign://agents/skills/create?name='
+ encodeURIComponent(name)
+ '&description=' + encodeURIComponent(desc)
+ '&content=' + encodeURIComponent(content)
+ '&requires_tools=' + encodeURIComponent(JSON.stringify(tools));
return sovereignGet(url);
}).then(function(d) {
if (d.error) {
showStatus('err', 'Publish failed: ' + d.message);
} else {
showStatus('ok', 'Skill published to Nostr (d: ' + d.d + ')');
}
}).catch(function(e) {
showStatus('err', 'Error: ' + e.message);
});
}
/* ── Init ───────────────────────────────────────────────────────── */
sovereignGet('sovereign://agents/config?_=' + Date.now())
.then(function(d) { applyConfig(d); })
.catch(function(e) { showStatus('err', 'Failed to load config: ' + e.message); });
document.addEventListener('DOMContentLoaded', function() {
var bu = document.getElementById('agent.llm_base_url');
var ak = document.getElementById('agent.llm_api_key');
var txt = document.getElementById('agent.llm_model_text');
if (bu) bu.addEventListener('input', updateRefreshBtn);
if (ak) ak.addEventListener('input', updateRefreshBtn);
if (txt) txt.addEventListener('change', saveCustomModel);
});
/* After config loads, auto-refresh models if base URL + API key are set. */
sovereignGet('sovereign://agents/config?_=' + Date.now())
.then(function(d) {
if (d.base_url && d.base_url.trim() && d.api_key && d.api_key.trim()) {
refreshModels();
}
})
.catch(function() {});