Add operation approval policy editor

This commit is contained in:
github-ops
2026-06-24 10:45:09 +00:00
parent 5922aea68f
commit d83ab541d9
20 changed files with 451 additions and 11 deletions
+42
View File
@@ -572,6 +572,27 @@ var TRANSLATIONS = {
'wizard.step5.execution': 'Execution settings',
'wizard.step5.exec_title': 'Request execution',
'wizard.step5.exec_subtitle': 'Timeout, retry count and authorization profile',
'wizard.approval.title': 'Human confirmation',
'wizard.approval.subtitle': 'Enable this for actions that must not run without an explicit user decision.',
'wizard.approval.required_label': 'Require confirmation before execution',
'wizard.approval.required_desc': 'The MCP client receives a pending request, and the action runs only after confirmation through a separate approval endpoint.',
'wizard.approval.risk_level': 'Risk level',
'wizard.approval.risk.normal': 'Normal action',
'wizard.approval.risk.dangerous': 'Dangerous action',
'wizard.approval.risk.financial': 'Financial action',
'wizard.approval.risk.irreversible': 'Irreversible action',
'wizard.approval.ttl': 'How long to wait for confirmation',
'wizard.approval.confirmation_title': 'Confirmation title',
'wizard.approval.confirmation_title_hint': 'This text will be shown by the external confirmation interface.',
'wizard.approval.confirmation_body': 'Description for the user',
'wizard.approval.confirmation_body_hint': 'Explain briefly what will happen after confirmation.',
'wizard.approval.show_payload': 'Show request parameters',
'wizard.approval.payload_mode': 'How to show parameters',
'wizard.approval.payload.summary': 'Short summary',
'wizard.approval.payload.masked_json': 'JSON with masked secrets',
'wizard.approval.preview_label': 'Preview for confirmation UI',
'wizard.approval.default_title': 'Confirm operation execution',
'wizard.approval.default_body': 'Review operation parameters and confirm execution.',
'wizard.step5.security_level_title': 'Operation security',
'wizard.step5.community_security_note': '',
'wizard.step5.live_title': 'Check and publish',
@@ -1396,6 +1417,27 @@ var TRANSLATIONS = {
'wizard.step5.execution': 'Параметры выполнения',
'wizard.step5.exec_title': 'Выполнение запроса',
'wizard.step5.exec_subtitle': 'Время ожидания, повторные попытки и профиль авторизации',
'wizard.approval.title': 'Подтверждение человеком',
'wizard.approval.subtitle': 'Включайте для действий, которые нельзя выполнять без явного решения пользователя.',
'wizard.approval.required_label': 'Требовать подтверждение перед выполнением',
'wizard.approval.required_desc': 'MCP клиент получит ожидающий запрос, а действие выполнится только после подтверждения через отдельный эндпоинт подтверждения.',
'wizard.approval.risk_level': 'Уровень риска',
'wizard.approval.risk.normal': 'Обычное действие',
'wizard.approval.risk.dangerous': 'Опасное действие',
'wizard.approval.risk.financial': 'Финансовое действие',
'wizard.approval.risk.irreversible': 'Необратимое действие',
'wizard.approval.ttl': 'Сколько ждать подтверждение',
'wizard.approval.confirmation_title': 'Заголовок подтверждения',
'wizard.approval.confirmation_title_hint': 'Этот текст увидит внешний интерфейс подтверждения.',
'wizard.approval.confirmation_body': 'Описание для пользователя',
'wizard.approval.confirmation_body_hint': 'Коротко объясните, что произойдет после подтверждения.',
'wizard.approval.show_payload': 'Показывать параметры запроса',
'wizard.approval.payload_mode': 'Как показывать параметры',
'wizard.approval.payload.summary': 'Краткое описание',
'wizard.approval.payload.masked_json': 'JSON с маскированием секретов',
'wizard.approval.preview_label': 'Предпросмотр для интерфейса подтверждения',
'wizard.approval.default_title': 'Подтвердите выполнение операции',
'wizard.approval.default_body': 'Проверьте параметры операции и подтвердите выполнение.',
'wizard.step5.security_level_title': 'Защита операции',
'wizard.step5.community_security_note': '',
'wizard.step5.live_title': 'Проверка и публикация',
+90 -1
View File
@@ -27,6 +27,45 @@ function buildWizardState() {
};
}
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'));
@@ -50,7 +89,7 @@ function collectWizardPayload() {
output_schema: convertJsonSchemaToCrankSchema(outputSchemaValue, []),
input_mapping: buildMappingSet(inputMappingValue, 'input'),
output_mapping: buildMappingSet(outputMappingValue, 'output'),
execution_config: parseExecutionConfig(textValue('tool-exec-config')),
execution_config: applyApprovalPolicyToExecutionConfig(parseExecutionConfig(textValue('tool-exec-config'))),
tool_description: buildToolDescription(),
wizard_state: buildWizardState(),
};
@@ -126,6 +165,7 @@ function bindWizardLiveActions() {
if (window.CrankWizardMapping && typeof window.CrankWizardMapping.initialize === 'function') {
window.CrankWizardMapping.initialize();
}
bindApprovalPolicyControls();
bindAgentFacingPreview();
}
@@ -147,6 +187,54 @@ function bindLiveAction(id, 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;
@@ -708,4 +796,5 @@ function copyTestResponseToOutputSample() {
bindWizardLiveActions: bindWizardLiveActions,
updateWizardProtocolVisibility: updateWizardProtocolVisibility,
renderAgentFacingPreview: renderAgentFacingPreview,
setApprovalPolicyEditor: setApprovalPolicyEditor,
};
+8
View File
@@ -429,6 +429,13 @@ function executionConfigToEditorValue(config) {
return window.jsyaml ? window.jsyaml.dump(value, { lineWidth: -1 }) : JSON.stringify(value, null, 2);
}
function setApprovalPolicyFromSnapshot(config) {
if (!window.CrankWizardLive || typeof window.CrankWizardLive.setApprovalPolicyEditor !== 'function') {
return;
}
window.CrankWizardLive.setApprovalPolicyEditor(config && config.approval_policy ? config.approval_policy : null);
}
function operationSnapshot(versionDocument) {
if (!versionDocument) return {};
return versionDocument.snapshot || versionDocument;
@@ -487,6 +494,7 @@ function prefillWizardFromEdit(detail, versionDocument) {
setValue('tool-input-mapping', mappingSetToEditorValue(snapshot.input_mapping, 'input', snapshot.protocol || detail.protocol));
setValue('tool-output-mapping', mappingSetToEditorValue(snapshot.output_mapping, 'output', snapshot.protocol || detail.protocol));
setValue('tool-exec-config', executionConfigToEditorValue(snapshot.execution_config || {}));
setApprovalPolicyFromSnapshot(snapshot.execution_config || {});
prefillWizardSamples(snapshot);
if (window.CrankWizardMapping && typeof window.CrankWizardMapping.renderFromEditors === 'function') {
window.CrankWizardMapping.renderFromEditors();