auth: cut over community machine access to agent keys

This commit is contained in:
a.tolmachev
2026-05-03 15:42:07 +00:00
parent c7b33930db
commit 6fd62df2a8
18 changed files with 768 additions and 302 deletions
+101 -8
View File
@@ -1,5 +1,7 @@
var KEYS = [];
var AGENTS = [];
var currentWorkspaceId = null;
var currentAgentId = null;
var selectedScopes = new Set(['read']);
var search = '';
@@ -32,6 +34,7 @@ function mapKeyRecord(record) {
var apiKey = record.api_key || {};
return {
id: apiKey.id,
agentId: apiKey.agent_id || null,
name: apiKey.name,
prefix: apiKey.prefix || '',
scopes: apiKey.scopes || [],
@@ -41,6 +44,15 @@ function mapKeyRecord(record) {
};
}
function mapAgentRecord(record) {
return {
id: record.id,
slug: record.slug,
displayName: record.display_name || record.slug || record.id,
mcpEndpoint: record.mcp_endpoint || '',
};
}
function currentWorkspace() {
return window.getCurrentWorkspace ? window.getCurrentWorkspace() : null;
}
@@ -52,19 +64,46 @@ async function loadKeys() {
setTableLoading(true);
if (!currentWorkspaceId || !window.CrankApi) {
AGENTS = [];
KEYS = [];
currentAgentId = null;
renderAgentPicker();
setCreateButtonState();
setTableLoading(false);
renderTable(tKey('apikeys.error.api'));
return;
}
try {
var response = await window.CrankApi.listPlatformApiKeys(currentWorkspaceId);
KEYS = ((response && response.items) || []).map(mapKeyRecord);
var response = await window.CrankApi.listAgents(currentWorkspaceId);
AGENTS = ((response && response.items) || []).map(mapAgentRecord);
if (!AGENTS.length) {
currentAgentId = null;
KEYS = [];
renderAgentPicker();
setCreateButtonState();
setTableLoading(false);
renderTable();
return;
}
if (!currentAgentId || !AGENTS.some(function(agent) { return agent.id === currentAgentId; })) {
currentAgentId = AGENTS[0].id;
}
renderAgentPicker();
var keysResponse = await window.CrankApi.listAgentPlatformApiKeys(
currentWorkspaceId,
currentAgentId
);
KEYS = ((keysResponse && keysResponse.items) || []).map(mapKeyRecord);
setCreateButtonState();
setTableLoading(false);
renderTable();
} catch (error) {
AGENTS = [];
KEYS = [];
currentAgentId = null;
renderAgentPicker();
setCreateButtonState();
setTableLoading(false);
renderTable(error.message || tKey('apikeys.error.load'));
}
@@ -85,10 +124,11 @@ function setTableLoading(on) {
async function revokeKey(id) {
if (!confirm(tKey('apikeys.confirm.revoke'))) return;
if (!currentAgentId) return;
try {
var key = KEYS.find(function(item) { return item.id === id; });
await window.CrankApi.revokePlatformApiKey(currentWorkspaceId, id);
await window.CrankApi.revokeAgentPlatformApiKey(currentWorkspaceId, currentAgentId, id);
await loadKeys();
if (window.CrankUi) {
window.CrankUi.success(
@@ -108,10 +148,11 @@ async function revokeKey(id) {
async function deleteKey(id) {
if (!confirm(tKey('apikeys.confirm.delete'))) return;
if (!currentAgentId) return;
try {
var key = KEYS.find(function(item) { return item.id === id; });
await window.CrankApi.deletePlatformApiKey(currentWorkspaceId, id);
await window.CrankApi.deleteAgentPlatformApiKey(currentWorkspaceId, currentAgentId, id);
await loadKeys();
if (window.CrankUi) {
window.CrankUi.success(
@@ -130,7 +171,7 @@ async function deleteKey(id) {
}
async function createKey(name, scopes) {
var created = await window.CrankApi.createPlatformApiKey(currentWorkspaceId, {
var created = await window.CrankApi.createAgentPlatformApiKey(currentWorkspaceId, currentAgentId, {
name: name,
scopes: scopes,
});
@@ -140,6 +181,48 @@ async function createKey(name, scopes) {
};
}
function currentAgent() {
return AGENTS.find(function(agent) { return agent.id === currentAgentId; }) || null;
}
function renderAgentPicker() {
var select = document.getElementById('agent-select');
var hint = document.getElementById('agent-endpoint-hint');
if (!select || !hint) return;
select.innerHTML = '';
if (!AGENTS.length) {
var emptyOption = document.createElement('option');
emptyOption.value = '';
emptyOption.textContent = tKey('apikeys.agent.empty');
select.appendChild(emptyOption);
select.disabled = true;
hint.textContent = tKey('apikeys.agent.empty_hint');
return;
}
AGENTS.forEach(function(agent) {
var option = document.createElement('option');
option.value = agent.id;
option.textContent = agent.displayName;
if (agent.id === currentAgentId) option.selected = true;
select.appendChild(option);
});
select.disabled = false;
var agent = currentAgent();
hint.textContent = agent && agent.mcpEndpoint
? tfKey('apikeys.agent.endpoint', { endpoint: agent.mcpEndpoint })
: tKey('apikeys.agent.endpoint_missing');
}
function setCreateButtonState() {
var button = document.getElementById('btn-create-key');
if (!button) return;
button.disabled = !currentAgentId;
}
function renderScopes() {
var el = document.getElementById('scope-checkboxes');
var tmpl = document.getElementById('tmpl-scope-checkbox');
@@ -165,14 +248,16 @@ function renderTable(errorMessage) {
var rows = KEYS.filter(function(key) {
return !q || key.name.toLowerCase().includes(q) || key.prefix.toLowerCase().includes(q);
});
var subtitle = document.querySelector('.section-card-subtitle');
var subtitle = document.getElementById('keys-summary-subtitle');
tbody.innerHTML = '';
if (subtitle) {
var active = KEYS.filter(function(key) { return key.status === 'active'; }).length;
var revoked = KEYS.filter(function(key) { return key.status === 'revoked'; }).length;
subtitle.textContent = tfKey('apikeys.active.subtitle', { active: active, revoked: revoked });
subtitle.textContent = currentAgentId
? tfKey('apikeys.active.subtitle', { active: active, revoked: revoked })
: tKey('apikeys.agent.empty_hint');
}
if (errorMessage) {
@@ -191,7 +276,9 @@ function renderTable(errorMessage) {
var td = document.createElement('td');
td.colSpan = 7;
td.style.cssText = 'text-align:center;padding:36px;color:var(--text-muted);';
td.textContent = KEYS.length ? tKey('apikeys.empty.search') : tKey('apikeys.empty.none');
td.textContent = currentAgentId
? (KEYS.length ? tKey('apikeys.empty.search') : tKey('apikeys.empty.none'))
: tKey('apikeys.agent.empty_hint');
empty.appendChild(td);
tbody.appendChild(empty);
return;
@@ -242,6 +329,7 @@ function renderTable(errorMessage) {
var modal = document.getElementById('modal-create');
function openModal() {
if (!currentAgentId) return;
document.getElementById('modal-form-body').hidden = false;
document.getElementById('modal-reveal-body').hidden = true;
document.getElementById('modal-footer-create').hidden = false;
@@ -340,6 +428,11 @@ document.getElementById('key-search').addEventListener('input', function() {
renderTable();
});
document.getElementById('agent-select').addEventListener('change', async function() {
currentAgentId = this.value || null;
await loadKeys();
});
function copyPrefix(prefix) {
if (navigator.clipboard) {
navigator.clipboard.writeText(prefix).catch(function() {});