auth: cut over community machine access to agent keys
This commit is contained in:
+101
-8
@@ -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() {});
|
||||
|
||||
Reference in New Issue
Block a user