801 lines
29 KiB
JavaScript
801 lines
29 KiB
JavaScript
function buildToolDescription() {
|
|
var snapshot = wizardCurrentVersion && wizardCurrentVersion.snapshot
|
|
? wizardCurrentVersion.snapshot
|
|
: wizardCurrentVersion;
|
|
var existing = snapshot && snapshot.tool_description ? snapshot.tool_description : {};
|
|
|
|
return {
|
|
title: textValue('tool-title') || textValue('tool-display-name') || textValue('tool-name'),
|
|
description: textValue('tool-description'),
|
|
tags: Array.isArray(existing.tags) ? existing.tags.slice() : [],
|
|
examples: Array.isArray(existing.examples) ? existing.examples.slice() : [],
|
|
};
|
|
}
|
|
|
|
function buildWizardState() {
|
|
var snapshot = wizardCurrentVersion && wizardCurrentVersion.snapshot
|
|
? wizardCurrentVersion.snapshot
|
|
: wizardCurrentVersion;
|
|
var existingState = snapshot && snapshot.wizard_state ? snapshot.wizard_state : {};
|
|
return {
|
|
input_sample: parseStructuredText(textValue('wizard-input-sample')),
|
|
output_sample: parseStructuredText(textValue('wizard-output-sample')),
|
|
test_input: parseStructuredText(textValue('wizard-test-input')),
|
|
import_findings: Array.isArray(existingState.import_findings)
|
|
? existingState.import_findings.slice()
|
|
: [],
|
|
};
|
|
}
|
|
|
|
function checkedValue(id) {
|
|
var element = document.getElementById(id);
|
|
return !!(element && element.checked);
|
|
}
|
|
|
|
function normalizeApprovalTtlSeconds(value) {
|
|
var ttl = Number(value || 300);
|
|
if (!Number.isFinite(ttl)) return 300;
|
|
return Math.max(1, Math.min(300, Math.round(ttl)));
|
|
}
|
|
|
|
function buildApprovalPolicy() {
|
|
if (!checkedValue('approval-required')) return null;
|
|
|
|
var title = textValue('approval-title') || tKey('wizard.approval.default_title');
|
|
var body = textValue('approval-body') || tKey('wizard.approval.default_body');
|
|
|
|
return {
|
|
required: true,
|
|
risk_level: textValue('approval-risk-level') || 'normal',
|
|
confirmation_title: title,
|
|
confirmation_body_template: body,
|
|
ttl_seconds: normalizeApprovalTtlSeconds(textValue('approval-ttl-seconds')),
|
|
show_payload_preview: checkedValue('approval-show-payload-preview'),
|
|
payload_preview_mode: textValue('approval-payload-preview-mode') || 'summary',
|
|
};
|
|
}
|
|
|
|
function applyApprovalPolicyToExecutionConfig(config) {
|
|
var next = config || {};
|
|
var policy = buildApprovalPolicy();
|
|
if (policy) {
|
|
next.approval_policy = policy;
|
|
} else {
|
|
next.approval_policy = null;
|
|
}
|
|
return next;
|
|
}
|
|
|
|
function collectWizardPayload() {
|
|
var name = textValue('tool-name');
|
|
if (!name) throw new Error(tKey('wizard.error.tool_name'));
|
|
|
|
if (window.CrankWizardMapping && typeof window.CrankWizardMapping.sync === 'function') {
|
|
window.CrankWizardMapping.sync();
|
|
}
|
|
|
|
var inputSchemaValue = parseStructuredText(textValue('tool-input-schema'));
|
|
var outputSchemaValue = parseStructuredText(textValue('tool-output-schema'));
|
|
var inputMappingValue = parseStructuredText(textValue('tool-input-mapping'));
|
|
var outputMappingValue = parseStructuredText(textValue('tool-output-mapping'));
|
|
|
|
return {
|
|
name: name,
|
|
display_name: textValue('tool-display-name') || name,
|
|
category: 'general',
|
|
protocol: wizardProtocol,
|
|
target: buildTarget(),
|
|
input_schema: convertJsonSchemaToCrankSchema(inputSchemaValue, []),
|
|
output_schema: convertJsonSchemaToCrankSchema(outputSchemaValue, []),
|
|
input_mapping: buildMappingSet(inputMappingValue, 'input'),
|
|
output_mapping: buildMappingSet(outputMappingValue, 'output'),
|
|
execution_config: applyApprovalPolicyToExecutionConfig(parseExecutionConfig(textValue('tool-exec-config'))),
|
|
tool_description: buildToolDescription(),
|
|
wizard_state: buildWizardState(),
|
|
};
|
|
}
|
|
|
|
async function saveOperation(stayOnPage) {
|
|
try {
|
|
await persistCurrentDraft(stayOnPage);
|
|
} catch (error) {
|
|
if (window.CrankUi) {
|
|
window.CrankUi.error(error.message || tKey('wizard.error.save'), tKey('wizard.error.save_title'));
|
|
}
|
|
}
|
|
}
|
|
|
|
async function loadOperationForEdit() {
|
|
if (!wizardWorkspaceId || !wizardEditId) return;
|
|
var detail = await window.CrankApi.getOperation(wizardWorkspaceId, wizardEditId);
|
|
wizardProtocol = detail.protocol || 'rest';
|
|
await loadWizardPanels([3]);
|
|
var draftVersion = await window.CrankApi.getOperationVersion(
|
|
wizardWorkspaceId,
|
|
wizardEditId,
|
|
detail.draft_version_ref.version
|
|
);
|
|
wizardCurrentOperation = detail;
|
|
wizardCurrentVersion = draftVersion;
|
|
prefillWizardFromEdit(detail, draftVersion);
|
|
renderAgentFacingPreview();
|
|
renderImportQualityFindings(draftVersion);
|
|
}
|
|
|
|
function setEditModePresentation() {
|
|
var progressLabel = document.querySelector('.progress-label');
|
|
if (progressLabel) progressLabel.textContent = tKey('wizard.progress.edit');
|
|
renderSidebarBrand('edit');
|
|
}
|
|
|
|
function selectProtocol(protocol) {
|
|
wizardProtocol = protocol || 'rest';
|
|
document.querySelectorAll('.protocol-card').forEach(function(card) {
|
|
var proto = card.dataset.protocol || 'rest';
|
|
var selected = proto === wizardProtocol;
|
|
card.classList.toggle('selected', selected);
|
|
card.setAttribute('aria-checked', selected ? 'true' : 'false');
|
|
});
|
|
var s3name = document.getElementById('sidebar-step-3-name');
|
|
if (s3name) {
|
|
var step3Labels = window.CrankWizardState.step3Labels();
|
|
s3name.textContent = step3Labels[wizardProtocol] || tKey('wizard.step3.rest.label');
|
|
}
|
|
updateWizardProtocolVisibility();
|
|
}
|
|
|
|
function bindWizardLiveActions() {
|
|
bindLiveAction('wizard-upload-input-sample', tKey('wizard.busy.input_sample'), uploadInputSampleFromWizard);
|
|
bindLiveAction('wizard-upload-output-sample', tKey('wizard.busy.output_sample'), uploadOutputSampleFromWizard);
|
|
bindLiveAction('wizard-generate-draft', tKey('wizard.busy.generate'), generateDraftFromWizard);
|
|
bindLiveAction('wizard-run-test', tKey('wizard.busy.test'), runWizardTest);
|
|
bindLiveAction('wizard-run-quality', tKey('wizard.busy.quality'), analyzeWizardQuality);
|
|
bindClick('wizard-copy-test-response', copyTestResponseToOutputSample);
|
|
bindClick('wizard-copy-agent-preview', copyAgentFacingPreview);
|
|
bindLiveAction('wizard-export-yaml', tKey('wizard.busy.export_yaml'), exportWizardYaml);
|
|
bindLiveAction('wizard-import-yaml', tKey('wizard.busy.import_yaml'), importWizardYaml);
|
|
bindLiveAction('wizard-publish-operation', tKey('wizard.busy.publish'), publishWizardOperation);
|
|
bindClick('wizard-import-yaml-file-trigger', function() {
|
|
var input = document.getElementById('wizard-import-yaml-file');
|
|
if (input) input.click();
|
|
});
|
|
|
|
var yamlInput = document.getElementById('wizard-import-yaml-file');
|
|
if (yamlInput) yamlInput.addEventListener('change', handleImportYamlFileSelection);
|
|
if (window.CrankWizardMapping && typeof window.CrankWizardMapping.initialize === 'function') {
|
|
window.CrankWizardMapping.initialize();
|
|
}
|
|
bindApprovalPolicyControls();
|
|
bindAgentFacingPreview();
|
|
}
|
|
|
|
function bindClick(id, handler) {
|
|
var element = document.getElementById(id);
|
|
if (!element) return;
|
|
element.addEventListener('click', function(event) {
|
|
event.preventDefault();
|
|
handler();
|
|
});
|
|
}
|
|
|
|
function bindLiveAction(id, busyLabel, handler) {
|
|
var element = document.getElementById(id);
|
|
if (!element) return;
|
|
element.addEventListener('click', function(event) {
|
|
event.preventDefault();
|
|
runWizardLiveAction(element, busyLabel, handler);
|
|
});
|
|
}
|
|
|
|
function setApprovalPolicyEditor(policy) {
|
|
var enabled = !!(policy && policy.required);
|
|
var required = document.getElementById('approval-required');
|
|
if (required) required.checked = enabled;
|
|
setValue('approval-risk-level', policy && policy.risk_level ? policy.risk_level : 'normal');
|
|
setValue('approval-ttl-seconds', policy && policy.ttl_seconds ? String(policy.ttl_seconds) : '300');
|
|
setValue('approval-title', policy && policy.confirmation_title ? policy.confirmation_title : tKey('wizard.approval.default_title'));
|
|
setValue('approval-body', policy && policy.confirmation_body_template ? policy.confirmation_body_template : tKey('wizard.approval.default_body'));
|
|
var showPayload = document.getElementById('approval-show-payload-preview');
|
|
if (showPayload) {
|
|
showPayload.checked = !policy || policy.show_payload_preview !== false;
|
|
}
|
|
setValue('approval-payload-preview-mode', policy && policy.payload_preview_mode ? policy.payload_preview_mode : 'summary');
|
|
updateApprovalPolicyUi();
|
|
}
|
|
|
|
function updateApprovalPolicyUi() {
|
|
var enabled = checkedValue('approval-required');
|
|
var toggle = document.getElementById('approval-required-toggle');
|
|
var fields = document.getElementById('approval-config-fields');
|
|
if (toggle) toggle.classList.toggle('on', enabled);
|
|
if (fields) fields.hidden = !enabled;
|
|
|
|
var title = textValue('approval-title') || tKey('wizard.approval.default_title');
|
|
var body = textValue('approval-body') || tKey('wizard.approval.default_body');
|
|
setTextContent('approval-preview-title', title);
|
|
setTextContent('approval-preview-body', body);
|
|
}
|
|
|
|
function bindApprovalPolicyControls() {
|
|
[
|
|
'approval-required',
|
|
'approval-risk-level',
|
|
'approval-ttl-seconds',
|
|
'approval-title',
|
|
'approval-body',
|
|
'approval-show-payload-preview',
|
|
'approval-payload-preview-mode',
|
|
].forEach(function(id) {
|
|
var element = document.getElementById(id);
|
|
if (!element || element.dataset.approvalBound === 'true') return;
|
|
element.dataset.approvalBound = 'true';
|
|
element.addEventListener('input', updateApprovalPolicyUi);
|
|
element.addEventListener('change', updateApprovalPolicyUi);
|
|
});
|
|
updateApprovalPolicyUi();
|
|
}
|
|
|
|
async function runWizardLiveAction(button, busyLabel, handler) {
|
|
if (!button || button.dataset.busy === 'true') {
|
|
return;
|
|
}
|
|
|
|
var originalLabel = button.textContent;
|
|
button.dataset.busy = 'true';
|
|
button.disabled = true;
|
|
button.classList.add('is-busy');
|
|
button.textContent = busyLabel;
|
|
|
|
try {
|
|
await handler();
|
|
} catch (error) {
|
|
showWizardLiveStatus(
|
|
tKey('wizard.live.failed_title'),
|
|
error && error.message ? error.message : tKey('wizard.live.failed_body'),
|
|
true
|
|
);
|
|
if (window.CrankUi) {
|
|
window.CrankUi.error(
|
|
error && error.message ? error.message : tKey('wizard.live.failed_body'),
|
|
tKey('wizard.live.failed_title')
|
|
);
|
|
}
|
|
} finally {
|
|
button.dataset.busy = 'false';
|
|
button.disabled = false;
|
|
button.classList.remove('is-busy');
|
|
button.textContent = originalLabel;
|
|
}
|
|
}
|
|
|
|
function updateWizardProtocolVisibility() {
|
|
if (typeof window.renderEditionCapabilityHints === 'function'
|
|
&& window.CrankWizardState
|
|
&& typeof window.CrankWizardState.currentEditionCapabilities === 'function') {
|
|
window.renderEditionCapabilityHints(window.CrankWizardState.currentEditionCapabilities());
|
|
}
|
|
}
|
|
|
|
function showWizardLiveStatus(title, text, isError) {
|
|
var root = document.getElementById('wizard-live-status');
|
|
var titleEl = document.getElementById('wizard-live-status-title');
|
|
var textEl = document.getElementById('wizard-live-status-text');
|
|
if (!root || !titleEl || !textEl) return;
|
|
titleEl.textContent = title;
|
|
textEl.textContent = text;
|
|
root.hidden = false;
|
|
root.classList.toggle('is-error', !!isError);
|
|
root.classList.toggle('is-success', !isError);
|
|
}
|
|
|
|
function showPendingImportGuidance() {
|
|
var raw = sessionStorage.getItem('crank_import_guidance');
|
|
if (!raw) return;
|
|
sessionStorage.removeItem('crank_import_guidance');
|
|
try {
|
|
var warnings = JSON.parse(raw);
|
|
if (!Array.isArray(warnings) || warnings.length === 0) return;
|
|
showWizardLiveStatus(
|
|
tKey('wizard.yaml.imported_with_warnings'),
|
|
warnings.map(function(warning) { return '• ' + warning; }).join('\n')
|
|
);
|
|
} catch (_) {
|
|
// Ignore invalid stale session data.
|
|
}
|
|
}
|
|
|
|
function currentDraftVersion() {
|
|
if (wizardCurrentOperation && wizardCurrentOperation.draft_version_ref) {
|
|
return wizardCurrentOperation.draft_version_ref.version;
|
|
}
|
|
if (wizardCurrentOperation && wizardCurrentOperation.current_draft_version) {
|
|
return wizardCurrentOperation.current_draft_version;
|
|
}
|
|
if (wizardCurrentVersion && wizardCurrentVersion.version) {
|
|
return wizardCurrentVersion.version;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
function updateOperationPayload(payload) {
|
|
return {
|
|
display_name: payload.display_name,
|
|
category: payload.category,
|
|
target: payload.target,
|
|
input_schema: payload.input_schema,
|
|
output_schema: payload.output_schema,
|
|
input_mapping: payload.input_mapping,
|
|
output_mapping: payload.output_mapping,
|
|
execution_config: payload.execution_config,
|
|
tool_description: payload.tool_description,
|
|
wizard_state: payload.wizard_state,
|
|
};
|
|
}
|
|
|
|
async function refreshCurrentOperationState() {
|
|
if (!wizardWorkspaceId || !wizardEditId) return null;
|
|
var detail = await window.CrankApi.getOperation(wizardWorkspaceId, wizardEditId);
|
|
var versionNumber = detail.draft_version_ref
|
|
? detail.draft_version_ref.version
|
|
: detail.current_draft_version;
|
|
var versionDocument = await window.CrankApi.getOperationVersion(
|
|
wizardWorkspaceId,
|
|
wizardEditId,
|
|
versionNumber
|
|
);
|
|
wizardCurrentOperation = detail;
|
|
wizardCurrentVersion = versionDocument;
|
|
updateWizardProtocolVisibility();
|
|
return {
|
|
detail: detail,
|
|
version: versionDocument,
|
|
};
|
|
}
|
|
|
|
async function persistCurrentDraft(stayOnPage) {
|
|
if (!wizardWorkspaceId) {
|
|
throw new Error(tKey('wizard.error.no_workspace'));
|
|
}
|
|
|
|
var payload = collectWizardPayload();
|
|
var response;
|
|
var createdNow = false;
|
|
|
|
if (wizardMode === 'edit' && wizardEditId) {
|
|
response = await window.CrankApi.updateOperation(
|
|
wizardWorkspaceId,
|
|
wizardEditId,
|
|
updateOperationPayload(payload)
|
|
);
|
|
} else {
|
|
response = await window.CrankApi.createOperation(wizardWorkspaceId, payload);
|
|
createdNow = true;
|
|
wizardEditId = response.operation_id;
|
|
wizardMode = 'edit';
|
|
history.replaceState(
|
|
{},
|
|
'',
|
|
window.location.pathname + '?mode=edit&operationId=' + encodeURIComponent(wizardEditId)
|
|
);
|
|
setEditModePresentation();
|
|
_doGoToStep(currentStep);
|
|
var toolNameInput = document.getElementById('tool-name');
|
|
if (toolNameInput) toolNameInput.disabled = true;
|
|
}
|
|
|
|
await refreshCurrentOperationState();
|
|
|
|
if (stayOnPage) {
|
|
var saveDraftBtn = document.querySelector('.btn-save-draft');
|
|
if (saveDraftBtn) {
|
|
var label = saveDraftBtn.textContent;
|
|
saveDraftBtn.textContent = tKey('wizard.save.saved');
|
|
setTimeout(function() {
|
|
saveDraftBtn.textContent = label;
|
|
}, 1400);
|
|
}
|
|
}
|
|
|
|
showWizardLiveStatus(
|
|
createdNow ? tKey('wizard.save.created') : tKey('wizard.save.updated'),
|
|
tKey('wizard.save.body')
|
|
);
|
|
return response;
|
|
}
|
|
|
|
function safeStringify(value) {
|
|
if (value === null || value === undefined) return '';
|
|
if (typeof value === 'string') return value;
|
|
return JSON.stringify(value, null, 2);
|
|
}
|
|
|
|
function downloadTextFile(fileName, content, mimeType) {
|
|
var blob = new Blob([content], { type: mimeType || 'text/plain;charset=utf-8' });
|
|
var url = URL.createObjectURL(blob);
|
|
var link = document.createElement('a');
|
|
link.href = url;
|
|
link.download = fileName;
|
|
link.click();
|
|
URL.revokeObjectURL(url);
|
|
}
|
|
|
|
function setTextareaValue(id, value) {
|
|
var element = document.getElementById(id);
|
|
if (!element) return;
|
|
element.value = safeStringify(value);
|
|
}
|
|
|
|
function setTextContent(id, value) {
|
|
var element = document.getElementById(id);
|
|
if (!element) return;
|
|
element.textContent = value || '';
|
|
}
|
|
|
|
function structuredTextOrFallback(id, fallback) {
|
|
try {
|
|
var value = parseStructuredText(textValue(id));
|
|
if (value === null || value === undefined || value === '') return fallback;
|
|
return value;
|
|
} catch (_error) {
|
|
return fallback;
|
|
}
|
|
}
|
|
|
|
function buildAgentFacingPreview() {
|
|
var inputSchema = structuredTextOrFallback('tool-input-schema', {});
|
|
var inputSample = structuredTextOrFallback('wizard-input-sample', {});
|
|
var outputSample = structuredTextOrFallback('wizard-output-sample', {});
|
|
var toolName = textValue('tool-name') || 'unnamed_tool';
|
|
var title = textValue('tool-title') || textValue('tool-display-name') || toolName;
|
|
var description = textValue('tool-description');
|
|
|
|
return {
|
|
manifest: {
|
|
name: toolName,
|
|
title: title,
|
|
description: description,
|
|
inputSchema: inputSchema,
|
|
},
|
|
toolCall: {
|
|
name: toolName,
|
|
arguments: inputSample,
|
|
},
|
|
successResponse: outputSample,
|
|
errorResponse: {
|
|
isError: true,
|
|
content: [
|
|
{
|
|
type: 'text',
|
|
text: 'Инструмент вернул ошибку. Проверьте входные параметры или состояние внешнего API.',
|
|
},
|
|
],
|
|
},
|
|
};
|
|
}
|
|
|
|
function renderAgentFacingPreview() {
|
|
var root = document.getElementById('agent-facing-preview-card');
|
|
if (!root) return;
|
|
|
|
var preview = buildAgentFacingPreview();
|
|
setTextContent('agent-preview-tool-name', preview.manifest.name);
|
|
setTextContent('agent-preview-tool-title', preview.manifest.title);
|
|
setTextContent('agent-preview-tool-description', preview.manifest.description);
|
|
setTextareaValue('agent-preview-input-schema', preview.manifest.inputSchema);
|
|
setTextareaValue('agent-preview-tool-call', preview.toolCall);
|
|
setTextareaValue('agent-preview-success-response', preview.successResponse);
|
|
setTextareaValue('agent-preview-error-response', preview.errorResponse);
|
|
}
|
|
|
|
function bindAgentFacingPreview() {
|
|
[
|
|
'tool-name',
|
|
'tool-title',
|
|
'tool-display-name',
|
|
'tool-description',
|
|
'tool-input-schema',
|
|
'wizard-input-sample',
|
|
'wizard-output-sample',
|
|
].forEach(function(id) {
|
|
var element = document.getElementById(id);
|
|
if (!element || element.dataset.agentPreviewBound === 'true') return;
|
|
element.dataset.agentPreviewBound = 'true';
|
|
element.addEventListener('input', renderAgentFacingPreview);
|
|
element.addEventListener('change', renderAgentFacingPreview);
|
|
});
|
|
renderAgentFacingPreview();
|
|
}
|
|
|
|
function copyAgentFacingPreview() {
|
|
var preview = buildAgentFacingPreview();
|
|
var content = JSON.stringify(preview.manifest, null, 2);
|
|
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
navigator.clipboard.writeText(content).then(function() {
|
|
showWizardLiveStatus(tKey('wizard.agent_preview.copied'), tKey('wizard.agent_preview.copied_body'));
|
|
}).catch(function() {
|
|
setTextareaValue('agent-preview-input-schema', preview.manifest.inputSchema);
|
|
});
|
|
}
|
|
}
|
|
|
|
function severityLabel(severity) {
|
|
if (severity === 'error') return tKey('wizard.quality.severity_error');
|
|
if (severity === 'warning') return tKey('wizard.quality.severity_warning');
|
|
return tKey('wizard.quality.severity_info');
|
|
}
|
|
|
|
function qualityTargetForFinding(finding) {
|
|
var code = String(finding && finding.code || '');
|
|
var path = String(finding && finding.field_path || '');
|
|
|
|
if (code.indexOf('tool_name') >= 0 || code.indexOf('weak_tool_name') >= 0) {
|
|
return { step: 4, selector: '#tool-name', label: 'Перейти к имени инструмента' };
|
|
}
|
|
if (code.indexOf('description') >= 0 || path.indexOf('tool_description') >= 0) {
|
|
return { step: 4, selector: '#tool-description', label: 'Перейти к описанию' };
|
|
}
|
|
if (code.indexOf('input') >= 0 || code.indexOf('parameter') >= 0 || path.indexOf('input_schema') >= 0) {
|
|
return { step: 4, selector: '#tool-input-schema', label: 'Перейти к входной схеме' };
|
|
}
|
|
if (code.indexOf('output') >= 0 || code.indexOf('response_schema') >= 0 || path.indexOf('output_schema') >= 0) {
|
|
return { step: 4, selector: '#tool-output-schema', label: 'Перейти к схеме ответа' };
|
|
}
|
|
if (code.indexOf('response_projection') >= 0 || code.indexOf('mapping') >= 0 || path.indexOf('output_mapping') >= 0) {
|
|
return { step: 5, selector: '#tool-output-mapping', label: 'Перейти к маппингу ответа' };
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function focusWizardQualityTarget(target) {
|
|
if (!target || !window.CrankWizardShell) return;
|
|
var load = typeof window.CrankWizardShell.loadWizardPanels === 'function'
|
|
? window.CrankWizardShell.loadWizardPanels([target.step])
|
|
: Promise.resolve();
|
|
load.then(function() {
|
|
if (typeof window.CrankWizardShell.doGoToStep === 'function') {
|
|
window.CrankWizardShell.doGoToStep(target.step);
|
|
}
|
|
window.setTimeout(function() {
|
|
var element = document.querySelector(target.selector);
|
|
if (!element) return;
|
|
element.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
if (typeof element.focus === 'function') element.focus();
|
|
element.classList.add('quality-focus-target');
|
|
window.setTimeout(function() {
|
|
element.classList.remove('quality-focus-target');
|
|
}, 1600);
|
|
}, 80);
|
|
});
|
|
}
|
|
|
|
function renderQualityFindings(report) {
|
|
var empty = document.getElementById('wizard-quality-empty');
|
|
var list = document.getElementById('wizard-quality-findings');
|
|
if (!empty || !list) return;
|
|
|
|
list.replaceChildren();
|
|
var findings = report && Array.isArray(report.findings) ? report.findings : [];
|
|
if (!findings.length) {
|
|
empty.textContent = tKey('wizard.quality.no_findings');
|
|
empty.hidden = false;
|
|
list.hidden = true;
|
|
return;
|
|
}
|
|
|
|
empty.hidden = true;
|
|
list.hidden = false;
|
|
findings.forEach(function(finding) {
|
|
var item = document.createElement('div');
|
|
item.className = 'quality-finding ' + (finding.severity || 'info');
|
|
item.dataset.code = finding.code || '';
|
|
|
|
var title = document.createElement('div');
|
|
title.className = 'quality-finding-title';
|
|
title.textContent = severityLabel(finding.severity) + ': ' + (finding.message || finding.code || '');
|
|
item.appendChild(title);
|
|
|
|
if (finding.suggested_action) {
|
|
var action = document.createElement('div');
|
|
action.className = 'quality-finding-action';
|
|
action.textContent = finding.suggested_action;
|
|
item.appendChild(action);
|
|
}
|
|
|
|
var target = qualityTargetForFinding(finding);
|
|
if (target) {
|
|
var button = document.createElement('button');
|
|
button.type = 'button';
|
|
button.className = 'btn-ghost-sm quality-finding-jump';
|
|
button.textContent = target.label;
|
|
button.addEventListener('click', function(event) {
|
|
event.preventDefault();
|
|
focusWizardQualityTarget(target);
|
|
});
|
|
item.appendChild(button);
|
|
}
|
|
|
|
if (finding.field_path) {
|
|
var path = document.createElement('div');
|
|
path.className = 'quality-finding-path';
|
|
path.textContent = finding.field_path;
|
|
item.appendChild(path);
|
|
}
|
|
|
|
list.appendChild(item);
|
|
});
|
|
}
|
|
|
|
function renderImportQualityFindings(versionDocument) {
|
|
var snapshot = versionDocument && versionDocument.snapshot
|
|
? versionDocument.snapshot
|
|
: versionDocument;
|
|
var state = snapshot && snapshot.wizard_state ? snapshot.wizard_state : {};
|
|
var findings = Array.isArray(state.import_findings) ? state.import_findings : [];
|
|
if (!findings.length) return;
|
|
|
|
renderQualityFindings({
|
|
blocking: findings.some(function(finding) { return finding.severity === 'error'; }),
|
|
findings: findings,
|
|
});
|
|
|
|
var empty = document.getElementById('wizard-quality-empty');
|
|
if (empty) {
|
|
empty.textContent = 'Рекомендации из OpenAPI import. Запустите проверку качества, чтобы пересчитать их по текущему черновику.';
|
|
empty.hidden = false;
|
|
}
|
|
}
|
|
|
|
async function analyzeWizardQuality() {
|
|
if (!wizardWorkspaceId) {
|
|
throw new Error(tKey('wizard.error.no_workspace'));
|
|
}
|
|
|
|
var report = await window.CrankApi.analyzeOperationQuality(
|
|
wizardWorkspaceId,
|
|
collectWizardPayload()
|
|
);
|
|
renderQualityFindings(report);
|
|
showWizardLiveStatus(
|
|
report.blocking ? tKey('wizard.quality.blocked_title') : tKey('wizard.quality.checked_title'),
|
|
report.blocking ? tKey('wizard.quality.blocked_body') : tKey('wizard.quality.checked_body'),
|
|
!!report.blocking
|
|
);
|
|
return report;
|
|
}
|
|
|
|
async function exportWizardYaml() {
|
|
await persistCurrentDraft(true);
|
|
var yaml = await window.CrankApi.exportOperation(wizardWorkspaceId, wizardEditId, {
|
|
mode: 'portable',
|
|
version: currentDraftVersion(),
|
|
});
|
|
downloadTextFile((textValue('tool-name') || 'operation') + '.yaml', yaml, 'application/yaml');
|
|
showWizardLiveStatus(tKey('wizard.yaml.exported'), tKey('wizard.yaml.exported_body'));
|
|
}
|
|
|
|
async function importWizardYaml() {
|
|
if (!wizardWorkspaceId) {
|
|
throw new Error(tKey('wizard.error.no_workspace'));
|
|
}
|
|
var yamlDocument = textValue('wizard-import-yaml-text');
|
|
if (!yamlDocument) {
|
|
showWizardLiveStatus(tKey('wizard.yaml.none'), tKey('wizard.yaml.none_body'), true);
|
|
return;
|
|
}
|
|
var imported = await window.CrankApi.importOperation(wizardWorkspaceId, yamlDocument, 'upsert');
|
|
if (Array.isArray(imported.warnings) && imported.warnings.length > 0) {
|
|
sessionStorage.setItem('crank_import_guidance', JSON.stringify(imported.warnings));
|
|
}
|
|
showWizardLiveStatus(tKey('wizard.yaml.imported'), tKey('wizard.yaml.imported_body'));
|
|
window.location.href = window.location.pathname + '?mode=edit&operationId=' + encodeURIComponent(imported.operation_id);
|
|
}
|
|
|
|
async function publishWizardOperation() {
|
|
var report = await analyzeWizardQuality();
|
|
if (report.blocking) {
|
|
throw new Error(tKey('wizard.quality.blocking_error'));
|
|
}
|
|
await persistCurrentDraft(true);
|
|
var published = await window.CrankApi.publishOperation(
|
|
wizardWorkspaceId,
|
|
wizardEditId,
|
|
currentDraftVersion()
|
|
);
|
|
await refreshCurrentOperationState();
|
|
showWizardLiveStatus(
|
|
tKey('wizard.publish.done'),
|
|
tfKey('wizard.publish.done_body', { version: published.published_version })
|
|
);
|
|
}
|
|
|
|
function handleImportYamlFileSelection(event) {
|
|
var file = event.target.files && event.target.files[0];
|
|
if (!file) return;
|
|
var reader = new FileReader();
|
|
reader.onload = function(loadEvent) {
|
|
setValue('wizard-import-yaml-text', loadEvent.target.result || '');
|
|
showWizardLiveStatus(tKey('wizard.yaml.loaded'), tKey('wizard.yaml.loaded_body'));
|
|
};
|
|
reader.readAsText(file);
|
|
}
|
|
|
|
function describeWizardTestResult(result) {
|
|
return {
|
|
title: result.ok ? tKey('wizard.test.completed') : tKey('wizard.test.failed'),
|
|
body: result.ok ? tKey('wizard.test.completed_body') : tKey('wizard.test.failed_body'),
|
|
isError: !result.ok,
|
|
};
|
|
}
|
|
|
|
async function uploadInputSampleFromWizard() {
|
|
await persistCurrentDraft(true);
|
|
await window.CrankApi.uploadInputSample(
|
|
wizardWorkspaceId,
|
|
wizardEditId,
|
|
parseStructuredText(textValue('wizard-input-sample'))
|
|
);
|
|
showWizardLiveStatus(tKey('wizard.sample.input_saved'), tKey('wizard.sample.input_saved_body'));
|
|
}
|
|
|
|
async function uploadOutputSampleFromWizard() {
|
|
await persistCurrentDraft(true);
|
|
await window.CrankApi.uploadOutputSample(
|
|
wizardWorkspaceId,
|
|
wizardEditId,
|
|
parseStructuredText(textValue('wizard-output-sample'))
|
|
);
|
|
showWizardLiveStatus(tKey('wizard.sample.output_saved'), tKey('wizard.sample.output_saved_body'));
|
|
}
|
|
|
|
async function generateDraftFromWizard() {
|
|
await persistCurrentDraft(true);
|
|
var generated = await window.CrankApi.generateDraft(wizardWorkspaceId, wizardEditId, {});
|
|
setValue('tool-input-schema', JSON.stringify(crankSchemaToJsonSchema(generated.input_schema), null, 2));
|
|
setValue('tool-output-schema', JSON.stringify(crankSchemaToJsonSchema(generated.output_schema), null, 2));
|
|
setValue('tool-input-mapping', mappingSetToEditorValue(generated.input_mapping, 'input', wizardProtocol));
|
|
setValue('tool-output-mapping', mappingSetToEditorValue(generated.output_mapping, 'output', wizardProtocol));
|
|
if (window.CrankWizardMapping && typeof window.CrankWizardMapping.renderFromEditors === 'function') {
|
|
window.CrankWizardMapping.renderFromEditors();
|
|
}
|
|
renderAgentFacingPreview();
|
|
showWizardLiveStatus(tKey('wizard.sample.generated'), tKey('wizard.sample.generated_body'));
|
|
}
|
|
|
|
async function runWizardTest() {
|
|
await persistCurrentDraft(true);
|
|
var result = await window.CrankApi.runOperationTest(wizardWorkspaceId, wizardEditId, {
|
|
version: currentDraftVersion(),
|
|
input: parseStructuredText(textValue('wizard-test-input')),
|
|
});
|
|
wizardTestResponsePreview = result.response_preview;
|
|
setTextareaValue('wizard-test-request-preview', result.request_preview);
|
|
setTextareaValue('wizard-test-response-preview', result.response_preview);
|
|
setTextareaValue('wizard-test-errors', result.errors && result.errors.length ? result.errors : []);
|
|
var status = describeWizardTestResult(result);
|
|
showWizardLiveStatus(
|
|
status.title,
|
|
status.body,
|
|
status.isError
|
|
);
|
|
}
|
|
|
|
function copyTestResponseToOutputSample() {
|
|
if (!wizardTestResponsePreview) {
|
|
showWizardLiveStatus(tKey('wizard.test.no_response'), tKey('wizard.test.no_response_body'), true);
|
|
return;
|
|
}
|
|
setTextareaValue('wizard-output-sample', wizardTestResponsePreview);
|
|
renderAgentFacingPreview();
|
|
showWizardLiveStatus(tKey('wizard.test.response_copied'), tKey('wizard.test.response_copied_body'));
|
|
}
|
|
|
|
window.CrankWizardLive = {
|
|
saveOperation: saveOperation,
|
|
loadOperationForEdit: loadOperationForEdit,
|
|
showPendingImportGuidance: showPendingImportGuidance,
|
|
bindWizardLiveActions: bindWizardLiveActions,
|
|
updateWizardProtocolVisibility: updateWizardProtocolVisibility,
|
|
renderAgentFacingPreview: renderAgentFacingPreview,
|
|
setApprovalPolicyEditor: setApprovalPolicyEditor,
|
|
};
|