290 lines
9.8 KiB
JavaScript
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() {});
|