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 selectedOperations() { var selected = this.form.selectedOps; return this.operations.filter(function(operation) { return selected.includes(operation.id); }); }, get agentToolFindings() { var findings = []; var selected = this.selectedOperations; if (selected.length > 8) { findings.push(this.tKey('agents.drawer.finding.too_many_tools')); } for (var leftIndex = 0; leftIndex < selected.length; leftIndex += 1) { for (var rightIndex = leftIndex + 1; rightIndex < selected.length; rightIndex += 1) { if (this.operationsLookSimilar(selected[leftIndex], selected[rightIndex])) { findings.push(this.tfKey('agents.drawer.finding.similar_tools', { left: selected[leftIndex].display_name || selected[leftIndex].name, right: selected[rightIndex].display_name || selected[rightIndex].name, })); return findings; } } } return findings; }, 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); }, operationsLookSimilar(left, right) { var leftTokens = this.operationTokens(left); var rightTokens = this.operationTokens(right); if (!leftTokens.length || !rightTokens.length) return false; var shared = leftTokens.filter(function(token) { return rightTokens.includes(token); }).length; var smaller = Math.min(leftTokens.length, rightTokens.length); return shared >= 2 && (shared / smaller) >= 0.6; }, operationTokens(operation) { var text = [ operation.name || '', operation.display_name || '', ].join(' ').toLowerCase(); var tokens = []; text.split(/[^a-z0-9а-яё]+/i).forEach(function(token) { if (token.length < 3 || tokens.includes(token)) return; tokens.push(token); }); return tokens; }, 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); }, }; }); });