Files
crank/apps/ui/js/agents.js
T
github-ops ba29ac7b94
Deploy / deploy (push) Successful in 2m44s
CI / Rust Checks (push) Successful in 5m31s
CI / UI Checks (push) Successful in 5s
CI / Deployment Manifests (push) Successful in 2s
CI / Frontend E2E (push) Successful in 4m24s
chore: publish clean community baseline
2026-06-19 16:45:51 +00:00

500 lines
16 KiB
JavaScript

function agentUiStatus(status) {
if (status === 'published') return 'published';
if (status === 'archived') return 'archived';
return status || 'draft';
}
function mapAgent(agent) {
var mapped = {
id: agent.id,
slug: agent.slug,
display_name: agent.display_name,
description: agent.description || '',
status: agentUiStatus(agent.status),
raw_status: agent.status,
operation_count: agent.operation_count || 0,
operation_ids: agent.operation_ids || [],
key_count: agent.key_count || 0,
calls_today: agent.calls_today || 0,
created_at: agent.created_at,
current_draft_version: agent.current_draft_version || 1,
latest_published_version: agent.latest_published_version,
mcp_endpoint: agent.mcp_endpoint || '',
};
return window.localizeDemoAgent ? window.localizeDemoAgent(mapped) : mapped;
}
function mapOperation(operation) {
var mapped = {
id: operation.id,
name: operation.name,
display_name: operation.display_name,
protocol: operation.protocol,
status: operation.status,
current_draft_version: operation.current_draft_version || 1,
latest_published_version: operation.latest_published_version,
};
return window.localizeDemoOperation ? window.localizeDemoOperation(mapped) : mapped;
}
function currentLocale() {
return localStorage.getItem('crank_lang') === 'ru' ? 'ru-RU' : 'en-US';
}
document.addEventListener('alpine:init', function() {
Alpine.data('agents', function() {
return {
agents: [],
operations: [],
capabilities: null,
loading: true,
loadError: '',
workspaceId: null,
agentSearch: '',
openDropdown: null,
drawerOpen: false,
drawerMode: 'create',
editingId: null,
saving: false,
form: {
display_name: '',
slug: '',
description: '',
status: 'published',
selectedOps: [],
},
opSearch: '',
slugManuallyEdited: false,
async init() {
var self = this;
await (window.whenWorkspacesReady ? window.whenWorkspacesReady() : Promise.resolve());
var workspace = window.getCurrentWorkspace ? window.getCurrentWorkspace() : null;
this.workspaceId = workspace ? workspace.id : null;
await this.loadCapabilities();
await this.reload();
document.addEventListener('keydown', function(event) {
if (event.key === 'Escape' && self.drawerOpen) {
self.closeDrawer();
}
});
document.addEventListener('click', function() {
self.openDropdown = null;
});
window.addEventListener('crank:workspacechange', async function(event) {
self.workspaceId = event.detail ? event.detail.id : null;
await self.loadCapabilities();
await self.reload();
});
},
async loadCapabilities() {
if (!window.CrankApi || typeof window.CrankApi.getCapabilities !== 'function') {
this.capabilities = null;
return;
}
try {
this.capabilities = await window.CrankApi.getCapabilities();
} catch (_error) {
this.capabilities = null;
}
},
async reload() {
this.loading = true;
this.loadError = '';
if (!this.workspaceId || !window.CrankApi) {
this.agents = [];
this.operations = [];
this.loading = false;
this.loadError = this.tKey('agents.error.api');
return;
}
try {
var responses = await Promise.all([
window.CrankApi.listAgents(this.workspaceId),
window.CrankApi.listOperations(this.workspaceId),
]);
this.agents = ((responses[0] && responses[0].items) || []).map(mapAgent);
this.operations = ((responses[1] && responses[1].items) || []).map(mapOperation);
} catch (error) {
this.agents = [];
this.operations = [];
this.loadError = error.message || this.tKey('agents.error.load');
}
this.loading = false;
},
get filteredAgents() {
var query = this.agentSearch.toLowerCase().trim();
if (!query) return this.agents;
return this.agents.filter(function(agent) {
return agent.display_name.toLowerCase().includes(query)
|| agent.slug.toLowerCase().includes(query)
|| (agent.description || '').toLowerCase().includes(query);
});
},
get totalCalls() {
return this.agents.reduce(function(sum, agent) {
return sum + (agent.calls_today || 0);
}, 0);
},
get activeCount() {
return this.agents.filter(function(agent) {
return agent.status === 'published';
}).length;
},
get totalOpsExposed() {
return this.agents.reduce(function(sum, agent) {
return sum + (agent.operation_count || 0);
}, 0);
},
get isCommunityBuild() {
return !!(this.capabilities && this.capabilities.edition === 'community');
},
agentCalloutText() {
if (this.isCommunityBuild) {
return this.tfKey('agents.callout.community', { count: this.operations.length });
}
return this.tfKey('agents.callout.default', { count: this.operations.length });
},
subtitleText() {
return this.tKey(this.isCommunityBuild ? 'agents.subtitle.community' : 'agents.subtitle');
},
emptyStateText() {
return this.tKey(this.isCommunityBuild ? 'agents.empty.initial.sub_community' : 'agents.empty.initial.sub');
},
drawerSubText() {
if (this.drawerMode === 'edit') {
return this.tKey('agents.drawer.edit_sub');
}
return this.tKey(this.isCommunityBuild ? 'agents.drawer.new_sub_community' : 'agents.drawer.new_sub');
},
operationsSubText() {
return this.tKey(this.isCommunityBuild ? 'agents.drawer.operations_sub_community' : 'agents.drawer.operations_sub');
},
get filteredOps() {
var query = this.opSearch.toLowerCase().trim();
if (!query) return this.operations;
return this.operations.filter(function(operation) {
return operation.name.toLowerCase().includes(query)
|| operation.display_name.toLowerCase().includes(query);
});
},
openCreate() {
this.drawerMode = 'create';
this.editingId = null;
this.form = {
display_name: '',
slug: '',
description: '',
status: 'published',
selectedOps: [],
};
this.opSearch = '';
this.slugManuallyEdited = false;
this.drawerOpen = true;
},
openEdit(agent) {
this.drawerMode = 'edit';
this.editingId = agent.id;
this.form = {
display_name: agent.display_name,
slug: agent.slug,
description: agent.description,
status: agent.raw_status || agent.status || 'draft',
selectedOps: [].concat(agent.operation_ids || []),
};
this.opSearch = '';
this.slugManuallyEdited = true;
this.drawerOpen = true;
},
closeDrawer() {
this.drawerOpen = false;
this.saving = false;
},
onNameInput(value) {
this.form.display_name = value;
if (!this.slugManuallyEdited) {
this.form.slug = value
.toLowerCase()
.replace(/\s+/g, '-')
.replace(/[^a-z0-9-]/g, '');
}
},
onSlugInput(value) {
this.form.slug = value.toLowerCase().replace(/[^a-z0-9-]/g, '');
this.slugManuallyEdited = true;
},
toggleOp(operationId) {
var index = this.form.selectedOps.indexOf(operationId);
if (index === -1) {
this.form.selectedOps.push(operationId);
} else {
this.form.selectedOps.splice(index, 1);
}
},
isOpSelected(operationId) {
return this.form.selectedOps.includes(operationId);
},
async saveAgent() {
var self = this;
if (
this.saving
|| !this.workspaceId
|| !this.form.display_name.trim()
|| !this.form.slug.trim()
) {
return;
}
this.saving = true;
try {
var agentId = this.editingId;
var currentVersion = 1;
var existingAgent = this.drawerMode === 'edit'
? this.agents.find(function(item) { return item.id === agentId; })
: null;
var previousStatus = existingAgent ? (existingAgent.raw_status || 'draft') : 'draft';
if (this.drawerMode === 'create') {
var created = await window.CrankApi.createAgent(this.workspaceId, {
slug: this.form.slug,
display_name: this.form.display_name,
description: this.form.description,
instructions: {},
tool_selection_policy: {},
});
agentId = created.agent_id;
currentVersion = created.version || 1;
} else {
await window.CrankApi.updateAgent(this.workspaceId, this.editingId, {
slug: this.form.slug,
display_name: this.form.display_name,
description: this.form.description,
});
var agent = await window.CrankApi.getAgent(this.workspaceId, this.editingId);
currentVersion = agent.current_draft_version || 1;
}
await window.CrankApi.saveAgentBindings(
this.workspaceId,
agentId,
this.form.selectedOps.map(function(operationId) {
var operation = self.operations.find(function(item) {
return item.id === operationId;
});
return {
operation_id: operationId,
operation_version: operation && operation.latest_published_version
? operation.latest_published_version
: operation && operation.current_draft_version
? operation.current_draft_version
: 1,
tool_name: operation ? operation.name : operationId,
tool_title: operation ? (operation.display_name || operation.name) : operationId,
tool_description_override: null,
enabled: true,
};
}),
);
if (this.form.status === 'published') {
await window.CrankApi.publishAgent(this.workspaceId, agentId, {
version: currentVersion,
});
} else if (this.form.status === 'archived') {
await window.CrankApi.archiveAgent(this.workspaceId, agentId);
} else if (this.drawerMode === 'edit' && previousStatus !== 'draft') {
await window.CrankApi.unpublishAgent(this.workspaceId, agentId);
}
await this.reload();
if (window.CrankUi) {
window.CrankUi.success(
this.tfKey('agents.toast.saved_message', {
name: this.form.display_name,
count: this.form.selectedOps.length
}),
this.drawerMode === 'create'
? this.tKey('agents.toast.saved_title_create')
: this.tKey('agents.toast.saved_title_update')
);
}
this.closeDrawer();
} catch (error) {
if (window.CrankUi) {
window.CrankUi.error(
error.message || this.tKey('agents.toast.save_error_message'),
this.tKey('agents.toast.save_error_title')
);
}
this.saving = false;
}
},
async deleteAgent(id) {
if (!confirm(this.tKey('agents.toast.delete_confirm'))) return;
try {
var agent = this.agents.find(function(item) { return item.id === id; });
await window.CrankApi.deleteAgent(this.workspaceId, id);
await this.reload();
if (window.CrankUi) {
window.CrankUi.success(
this.tfKey('agents.toast.delete_message', { name: agent ? agent.display_name : '' }),
this.tKey('agents.toast.delete_title')
);
}
} catch (error) {
if (window.CrankUi) {
window.CrankUi.error(
error.message || this.tKey('agents.toast.delete_error_message'),
this.tKey('agents.toast.delete_error_title')
);
}
}
},
async applyLifecycle(agent, action) {
if (!this.workspaceId || !window.CrankApi) {
return;
}
try {
if (action === 'publish') {
await window.CrankApi.publishAgent(this.workspaceId, agent.id, {
version: agent.current_draft_version || 1,
});
} else if (action === 'unpublish') {
await window.CrankApi.unpublishAgent(this.workspaceId, agent.id);
} else if (action === 'archive') {
await window.CrankApi.archiveAgent(this.workspaceId, agent.id);
}
await this.reload();
if (window.CrankUi) {
window.CrankUi.success(
action === 'publish'
? this.tfKey('agents.toast.lifecycle_publish', { name: agent.display_name })
: action === 'unpublish'
? this.tfKey('agents.toast.lifecycle_unpublish', { name: agent.display_name })
: this.tfKey('agents.toast.lifecycle_archive', { name: agent.display_name }),
this.tKey('agents.toast.lifecycle_title')
);
}
} catch (error) {
if (window.CrankUi) {
window.CrankUi.error(
error.message || this.tKey('agents.toast.lifecycle_error_message'),
this.tKey('agents.toast.lifecycle_error_title')
);
}
}
},
lifecycleAction(agent) {
if (agent.raw_status === 'published') {
return { key: 'unpublish', label: this.tKey('agents.lifecycle.unpublish') };
}
if (agent.raw_status === 'archived') {
return { key: 'unpublish', label: this.tKey('agents.lifecycle.restore_draft') };
}
return { key: 'publish', label: this.tKey('agents.lifecycle.publish') };
},
lifecycleLabel(agent) {
if (agent.raw_status === 'published') return this.tKey('agents.lifecycle.published');
if (agent.raw_status === 'archived') return this.tKey('agents.lifecycle.archived');
return this.tKey('agents.lifecycle.draft');
},
mcpEndpoint(agent) {
if (agent.mcp_endpoint) return agent.mcp_endpoint;
var workspace = window.getCurrentWorkspace ? window.getCurrentWorkspace() : null;
var workspaceSlug = workspace ? workspace.slug : 'default';
return '/mcp/v1/' + workspaceSlug + '/' + agent.slug;
},
endpointHelpText(agent) {
return this.tfKey(
this.isCommunityBuild ? 'agents.card.endpoint_help_community' : 'agents.card.endpoint_help',
{ count: agent.key_count || 0 }
);
},
copyEndpoint(agent) {
var endpoint = this.mcpEndpoint(agent);
navigator.clipboard.writeText(endpoint).catch(function() {});
if (window.CrankUi) {
window.CrankUi.info(endpoint, this.tKey('agents.toast.endpoint_title'));
}
},
statusClass(status) {
return 'agent-status-badge agent-status-' + status;
},
protocolBadge(operation) {
return 'badge badge-rest';
},
protocolLabel(operation) {
return 'REST';
},
formatDate(dateStr) {
if (!dateStr) return '—';
return new Date(dateStr).toLocaleDateString(currentLocale(), {
month: 'short',
day: 'numeric',
year: 'numeric',
});
},
formatCalls(value) {
if (!value) return '0';
return value >= 1000 ? (value / 1000).toFixed(1) + 'k' : String(value);
},
tKey(key) {
return window.t ? t(key) : key;
},
tfKey(key, vars) {
return window.tf ? tf(key, vars) : this.tKey(key);
},
};
});
});