feat: connect wizard live flow to admin api
This commit is contained in:
@@ -47,6 +47,41 @@
|
||||
return payload;
|
||||
}
|
||||
|
||||
async function requestText(path, options) {
|
||||
var response = await fetch(path, Object.assign({
|
||||
credentials: 'same-origin',
|
||||
headers: headers(),
|
||||
}, options || {}));
|
||||
|
||||
var text = await response.text();
|
||||
var payload = null;
|
||||
|
||||
if (text) {
|
||||
try {
|
||||
payload = JSON.parse(text);
|
||||
} catch (_error) {}
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
if (response.status === 401 && window.CrankAuth && typeof window.CrankAuth.handleUnauthorized === 'function') {
|
||||
window.CrankAuth.handleUnauthorized();
|
||||
}
|
||||
var message = payload && payload.error
|
||||
? payload.error.message
|
||||
: payload && payload.message
|
||||
? payload.message
|
||||
: text
|
||||
? text
|
||||
: ('HTTP ' + response.status);
|
||||
var error = new Error(message);
|
||||
error.status = response.status;
|
||||
error.payload = payload;
|
||||
throw error;
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
function get(path) {
|
||||
return request(API_BASE + path);
|
||||
}
|
||||
@@ -155,9 +190,23 @@
|
||||
archiveOperation: function(workspaceId, operationId) {
|
||||
return post('/workspaces/' + encodeURIComponent(workspaceId) + '/operations/' + encodeURIComponent(operationId) + '/archive', {});
|
||||
},
|
||||
publishOperation: function(workspaceId, operationId, version) {
|
||||
return post('/workspaces/' + encodeURIComponent(workspaceId) + '/operations/' + encodeURIComponent(operationId) + '/publish', {
|
||||
version: version,
|
||||
});
|
||||
},
|
||||
createOperationVersion: function(workspaceId, operationId, payload) {
|
||||
return post('/workspaces/' + encodeURIComponent(workspaceId) + '/operations/' + encodeURIComponent(operationId) + '/versions', payload);
|
||||
},
|
||||
runOperationTest: function(workspaceId, operationId, payload) {
|
||||
return post('/workspaces/' + encodeURIComponent(workspaceId) + '/operations/' + encodeURIComponent(operationId) + '/test-runs', payload);
|
||||
},
|
||||
uploadInputSample: function(workspaceId, operationId, sample) {
|
||||
return post('/workspaces/' + encodeURIComponent(workspaceId) + '/operations/' + encodeURIComponent(operationId) + '/samples/input-json', sample);
|
||||
},
|
||||
uploadOutputSample: function(workspaceId, operationId, sample) {
|
||||
return post('/workspaces/' + encodeURIComponent(workspaceId) + '/operations/' + encodeURIComponent(operationId) + '/samples/output-json', sample);
|
||||
},
|
||||
uploadProtoFile: function(workspaceId, operationId, content, fileName) {
|
||||
return postBytes(
|
||||
'/workspaces/' + encodeURIComponent(workspaceId) + '/operations/' + encodeURIComponent(operationId) + '/descriptors/proto',
|
||||
@@ -165,6 +214,40 @@
|
||||
fileName
|
||||
);
|
||||
},
|
||||
uploadDescriptorSet: function(workspaceId, operationId, content, fileName) {
|
||||
return postBytes(
|
||||
'/workspaces/' + encodeURIComponent(workspaceId) + '/operations/' + encodeURIComponent(operationId) + '/descriptors/descriptor-set',
|
||||
content,
|
||||
fileName
|
||||
);
|
||||
},
|
||||
listGrpcServices: function(workspaceId, operationId, version) {
|
||||
return get(
|
||||
'/workspaces/' + encodeURIComponent(workspaceId) + '/operations/' + encodeURIComponent(operationId) + '/grpc/services'
|
||||
+ query({ version: version })
|
||||
);
|
||||
},
|
||||
generateDraft: function(workspaceId, operationId, payload) {
|
||||
return post('/workspaces/' + encodeURIComponent(workspaceId) + '/operations/' + encodeURIComponent(operationId) + '/drafts/generate', payload || {});
|
||||
},
|
||||
exportOperation: function(workspaceId, operationId, params) {
|
||||
return requestText(
|
||||
API_BASE + '/workspaces/' + encodeURIComponent(workspaceId) + '/operations/' + encodeURIComponent(operationId) + '/export' + query(params),
|
||||
{
|
||||
headers: headers({ 'Accept': 'application/yaml' }),
|
||||
}
|
||||
);
|
||||
},
|
||||
importOperation: function(workspaceId, yamlDocument, mode) {
|
||||
return request(
|
||||
API_BASE + '/workspaces/' + encodeURIComponent(workspaceId) + '/operations/import' + query({ mode: mode }),
|
||||
{
|
||||
method: 'POST',
|
||||
headers: headers({ 'Content-Type': 'application/yaml' }),
|
||||
body: yamlDocument,
|
||||
}
|
||||
);
|
||||
},
|
||||
listAgents: function(workspaceId) {
|
||||
return get('/workspaces/' + encodeURIComponent(workspaceId) + '/agents');
|
||||
},
|
||||
|
||||
+2
File diff suppressed because one or more lines are too long
+434
-89
@@ -7,6 +7,9 @@ var wizardWorkspaceId = null;
|
||||
var wizardCurrentOperation = null;
|
||||
var wizardCurrentVersion = null;
|
||||
var wizardProtoUpload = null;
|
||||
var wizardDescriptorSetUpload = null;
|
||||
var wizardTestResponsePreview = null;
|
||||
var grpcDescriptorServices = [];
|
||||
|
||||
/* ── Dynamic step loading ── */
|
||||
function _stepFile(n) {
|
||||
@@ -210,6 +213,7 @@ document.addEventListener('DOMContentLoaded', async function() {
|
||||
|
||||
await loadWizardPanels([1, 2, 3, 4, 5]);
|
||||
bindProtocolCards();
|
||||
bindWizardLiveActions();
|
||||
|
||||
var params = new URLSearchParams(window.location.search);
|
||||
if (params.get('mode') === 'edit' && params.get('operationId')) {
|
||||
@@ -219,6 +223,7 @@ document.addEventListener('DOMContentLoaded', async function() {
|
||||
await loadOperationForEdit();
|
||||
}
|
||||
|
||||
updateWizardProtocolVisibility();
|
||||
_doGoToStep(1);
|
||||
});
|
||||
|
||||
@@ -824,30 +829,11 @@ var REFLECTION_MOCK = {
|
||||
};
|
||||
|
||||
function startReflection() {
|
||||
var idle = document.getElementById('grpc-reflect-idle');
|
||||
var loading = document.getElementById('grpc-reflect-loading');
|
||||
var error = document.getElementById('grpc-reflect-error');
|
||||
var view = document.getElementById('reflect-parsed-view');
|
||||
|
||||
if (idle) idle.style.display = 'none';
|
||||
if (loading) loading.style.display = '';
|
||||
if (error) error.style.display = 'none';
|
||||
if (view) view.style.display = 'none';
|
||||
document.getElementById('grpc-method-detail').style.display = 'none';
|
||||
|
||||
var statusEl = document.getElementById('grpc-reflect-status-text');
|
||||
var steps = ['Connecting…', 'Negotiating TLS…', 'Sending reflection request…', 'Parsing service descriptors…'];
|
||||
var i = 0;
|
||||
var iv = setInterval(function() {
|
||||
if (statusEl && i < steps.length) { statusEl.textContent = steps[i++]; }
|
||||
}, 380);
|
||||
|
||||
setTimeout(function() {
|
||||
clearInterval(iv);
|
||||
if (loading) loading.style.display = 'none';
|
||||
if (idle) idle.style.display = '';
|
||||
renderReflectionResult(REFLECTION_MOCK);
|
||||
}, 1700);
|
||||
goToStep(5);
|
||||
showWizardLiveStatus(
|
||||
'gRPC discovery moved to live descriptor flow',
|
||||
'Server reflection is not wired in this deployment. Save the draft, then use the descriptor-set tools in Step 5 to upload a real descriptor set and discover unary methods from the backend.'
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1191,71 +1177,7 @@ function collectWizardPayload() {
|
||||
|
||||
async function saveOperation(stayOnPage) {
|
||||
try {
|
||||
var payload = collectWizardPayload();
|
||||
var response;
|
||||
|
||||
if (!wizardWorkspaceId) {
|
||||
throw new Error('No workspace selected');
|
||||
}
|
||||
|
||||
if (wizardMode === 'edit' && wizardEditId) {
|
||||
response = await window.CrankApi.updateOperation(wizardWorkspaceId, wizardEditId, {
|
||||
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,
|
||||
});
|
||||
} else {
|
||||
response = await window.CrankApi.createOperation(wizardWorkspaceId, payload);
|
||||
wizardEditId = response.operation_id;
|
||||
wizardMode = 'edit';
|
||||
history.replaceState({}, '', window.location.pathname + '?mode=edit&operationId=' + encodeURIComponent(wizardEditId));
|
||||
setEditModePresentation();
|
||||
var toolNameInput = document.getElementById('tool-name');
|
||||
if (toolNameInput) toolNameInput.disabled = true;
|
||||
}
|
||||
|
||||
if (wizardProtocol === 'grpc' && wizardProtoUpload && wizardEditId) {
|
||||
var descriptor = await window.CrankApi.uploadProtoFile(
|
||||
wizardWorkspaceId,
|
||||
wizardEditId,
|
||||
new TextEncoder().encode(wizardProtoUpload.content),
|
||||
wizardProtoUpload.fileName
|
||||
);
|
||||
payload.target.descriptor_ref = descriptor.descriptor_id;
|
||||
wizardCurrentVersion = wizardCurrentVersion || {};
|
||||
wizardCurrentVersion.target = payload.target;
|
||||
await window.CrankApi.updateOperation(wizardWorkspaceId, wizardEditId, {
|
||||
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,
|
||||
});
|
||||
}
|
||||
|
||||
if (stayOnPage) {
|
||||
var saveDraftBtn = document.querySelector('.btn-save-draft');
|
||||
if (saveDraftBtn) {
|
||||
var label = saveDraftBtn.textContent;
|
||||
saveDraftBtn.textContent = 'Saved ✓';
|
||||
setTimeout(function() {
|
||||
saveDraftBtn.textContent = label;
|
||||
}, 1400);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
window.location.href = (window.APP_BASE || '') + 'index.html';
|
||||
await persistCurrentDraft(stayOnPage);
|
||||
} catch (error) {
|
||||
alert(error.message || 'Failed to save operation');
|
||||
}
|
||||
@@ -1294,6 +1216,7 @@ function selectProtocol(protocol) {
|
||||
});
|
||||
var s3name = document.getElementById('sidebar-step-3-name');
|
||||
if (s3name) s3name.textContent = STEP3_LABELS[wizardProtocol] || 'Protocol config';
|
||||
updateWizardProtocolVisibility();
|
||||
}
|
||||
|
||||
function prefillUpstream(baseUrl, headers) {
|
||||
@@ -1423,4 +1346,426 @@ function prefillWizardFromEdit(detail, versionDocument) {
|
||||
|
||||
var toolNameInput = document.getElementById('tool-name');
|
||||
if (toolNameInput) toolNameInput.disabled = true;
|
||||
updateWizardProtocolVisibility();
|
||||
}
|
||||
|
||||
function bindWizardLiveActions() {
|
||||
bindClick('wizard-upload-input-sample', uploadInputSampleFromWizard);
|
||||
bindClick('wizard-upload-output-sample', uploadOutputSampleFromWizard);
|
||||
bindClick('wizard-generate-draft', generateDraftFromWizard);
|
||||
bindClick('wizard-run-test', runWizardTest);
|
||||
bindClick('wizard-copy-test-response', copyTestResponseToOutputSample);
|
||||
bindClick('wizard-export-yaml', exportWizardYaml);
|
||||
bindClick('wizard-import-yaml', importWizardYaml);
|
||||
bindClick('wizard-publish-operation', publishWizardOperation);
|
||||
bindClick('wizard-select-descriptor-set', function() {
|
||||
var input = document.getElementById('wizard-descriptor-set-input');
|
||||
if (input) input.click();
|
||||
});
|
||||
bindClick('wizard-upload-descriptor-set', uploadDescriptorSetAndDiscover);
|
||||
bindClick('wizard-import-yaml-file-trigger', function() {
|
||||
var input = document.getElementById('wizard-import-yaml-file');
|
||||
if (input) input.click();
|
||||
});
|
||||
|
||||
var descriptorInput = document.getElementById('wizard-descriptor-set-input');
|
||||
if (descriptorInput) descriptorInput.addEventListener('change', handleDescriptorSetSelection);
|
||||
|
||||
var yamlInput = document.getElementById('wizard-import-yaml-file');
|
||||
if (yamlInput) yamlInput.addEventListener('change', handleImportYamlFileSelection);
|
||||
}
|
||||
|
||||
function bindClick(id, handler) {
|
||||
var element = document.getElementById(id);
|
||||
if (!element) return;
|
||||
element.addEventListener('click', function(event) {
|
||||
event.preventDefault();
|
||||
handler();
|
||||
});
|
||||
}
|
||||
|
||||
function updateWizardProtocolVisibility() {
|
||||
var grpcTools = document.getElementById('wizard-grpc-live-tools');
|
||||
if (grpcTools) grpcTools.style.display = wizardProtocol === 'grpc' ? '' : 'none';
|
||||
}
|
||||
|
||||
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.style.display = '';
|
||||
root.style.borderColor = isError ? 'rgba(248, 113, 113, 0.35)' : '';
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
}
|
||||
|
||||
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 uploadPendingGrpcArtifacts(payload) {
|
||||
if (wizardProtocol !== 'grpc' || !wizardEditId) return;
|
||||
|
||||
var descriptorResponse = null;
|
||||
|
||||
if (wizardProtoUpload) {
|
||||
descriptorResponse = await window.CrankApi.uploadProtoFile(
|
||||
wizardWorkspaceId,
|
||||
wizardEditId,
|
||||
new TextEncoder().encode(wizardProtoUpload.content),
|
||||
wizardProtoUpload.fileName
|
||||
);
|
||||
wizardProtoUpload = null;
|
||||
}
|
||||
|
||||
if (wizardDescriptorSetUpload) {
|
||||
descriptorResponse = await window.CrankApi.uploadDescriptorSet(
|
||||
wizardWorkspaceId,
|
||||
wizardEditId,
|
||||
wizardDescriptorSetUpload.bytes,
|
||||
wizardDescriptorSetUpload.fileName
|
||||
);
|
||||
wizardDescriptorSetUpload = null;
|
||||
var descriptorName = document.getElementById('wizard-descriptor-set-name');
|
||||
if (descriptorName) descriptorName.textContent = 'Descriptor set uploaded';
|
||||
}
|
||||
|
||||
if (!descriptorResponse) return;
|
||||
|
||||
payload.target.descriptor_ref = descriptorResponse.descriptor_id;
|
||||
payload.target.descriptor_set_b64 = '';
|
||||
await window.CrankApi.updateOperation(
|
||||
wizardWorkspaceId,
|
||||
wizardEditId,
|
||||
updateOperationPayload(payload)
|
||||
);
|
||||
}
|
||||
|
||||
async function persistCurrentDraft(stayOnPage) {
|
||||
if (!wizardWorkspaceId) {
|
||||
throw new Error('No workspace selected');
|
||||
}
|
||||
|
||||
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 uploadPendingGrpcArtifacts(payload);
|
||||
await refreshCurrentOperationState();
|
||||
|
||||
if (stayOnPage) {
|
||||
var saveDraftBtn = document.querySelector('.btn-save-draft');
|
||||
if (saveDraftBtn) {
|
||||
var label = saveDraftBtn.textContent;
|
||||
saveDraftBtn.textContent = 'Saved ✓';
|
||||
setTimeout(function() {
|
||||
saveDraftBtn.textContent = label;
|
||||
}, 1400);
|
||||
}
|
||||
}
|
||||
|
||||
showWizardLiveStatus(
|
||||
createdNow ? 'Draft created' : 'Draft updated',
|
||||
'The current draft is saved on the backend and ready for live actions in this wizard.'
|
||||
);
|
||||
return response;
|
||||
}
|
||||
|
||||
function safeStringify(value) {
|
||||
if (value === null || value === undefined) return '';
|
||||
if (typeof value === 'string') return value;
|
||||
return JSON.stringify(value, null, 2);
|
||||
}
|
||||
|
||||
function setTextareaValue(id, value) {
|
||||
var element = document.getElementById(id);
|
||||
if (!element) return;
|
||||
element.value = safeStringify(value);
|
||||
}
|
||||
|
||||
async function uploadInputSampleFromWizard() {
|
||||
await persistCurrentDraft(true);
|
||||
await window.CrankApi.uploadInputSample(
|
||||
wizardWorkspaceId,
|
||||
wizardEditId,
|
||||
parseStructuredText(textValue('wizard-input-sample'))
|
||||
);
|
||||
showWizardLiveStatus('Input sample saved', 'The input sample is now stored against the current draft version.');
|
||||
}
|
||||
|
||||
async function uploadOutputSampleFromWizard() {
|
||||
await persistCurrentDraft(true);
|
||||
await window.CrankApi.uploadOutputSample(
|
||||
wizardWorkspaceId,
|
||||
wizardEditId,
|
||||
parseStructuredText(textValue('wizard-output-sample'))
|
||||
);
|
||||
showWizardLiveStatus('Output sample saved', 'The output sample is now stored against the current draft version.');
|
||||
}
|
||||
|
||||
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));
|
||||
showWizardLiveStatus('Draft generated', 'Schemas and mappings were regenerated from the saved samples.');
|
||||
}
|
||||
|
||||
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 : []);
|
||||
showWizardLiveStatus(
|
||||
result.ok ? 'Test run completed' : 'Test run returned errors',
|
||||
result.ok
|
||||
? 'The current draft executed successfully against the upstream.'
|
||||
: 'The draft executed, but the backend returned validation or runtime errors.',
|
||||
!result.ok
|
||||
);
|
||||
}
|
||||
|
||||
function copyTestResponseToOutputSample() {
|
||||
if (!wizardTestResponsePreview) {
|
||||
showWizardLiveStatus('No test response yet', 'Run a test first, then copy the response preview into the output sample editor.', true);
|
||||
return;
|
||||
}
|
||||
setTextareaValue('wizard-output-sample', wizardTestResponsePreview);
|
||||
showWizardLiveStatus('Response copied', 'The latest test response was copied into the output sample editor.');
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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('YAML exported', 'The current draft YAML was downloaded from the backend.');
|
||||
}
|
||||
|
||||
async function importWizardYaml() {
|
||||
if (!wizardWorkspaceId) {
|
||||
throw new Error('No workspace selected');
|
||||
}
|
||||
var yamlDocument = textValue('wizard-import-yaml-text');
|
||||
if (!yamlDocument) {
|
||||
showWizardLiveStatus('No YAML provided', 'Paste a YAML document or load it from a file before importing.', true);
|
||||
return;
|
||||
}
|
||||
var imported = await window.CrankApi.importOperation(wizardWorkspaceId, yamlDocument, 'upsert');
|
||||
showWizardLiveStatus('YAML imported', 'The imported operation draft was stored. The wizard will reopen it now.');
|
||||
window.location.href = window.location.pathname + '?mode=edit&operationId=' + encodeURIComponent(imported.operation_id);
|
||||
}
|
||||
|
||||
async function publishWizardOperation() {
|
||||
await persistCurrentDraft(true);
|
||||
var published = await window.CrankApi.publishOperation(
|
||||
wizardWorkspaceId,
|
||||
wizardEditId,
|
||||
currentDraftVersion()
|
||||
);
|
||||
await refreshCurrentOperationState();
|
||||
showWizardLiveStatus(
|
||||
'Operation published',
|
||||
'Version ' + published.published_version + ' is now published and can be bound into agents.'
|
||||
);
|
||||
}
|
||||
|
||||
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('YAML loaded', 'The YAML document was loaded into the import editor.');
|
||||
};
|
||||
reader.readAsText(file);
|
||||
}
|
||||
|
||||
function handleDescriptorSetSelection(event) {
|
||||
var file = event.target.files && event.target.files[0];
|
||||
if (!file) return;
|
||||
var reader = new FileReader();
|
||||
reader.onload = function(loadEvent) {
|
||||
wizardDescriptorSetUpload = {
|
||||
fileName: file.name,
|
||||
bytes: new Uint8Array(loadEvent.target.result),
|
||||
};
|
||||
var fileNameEl = document.getElementById('wizard-descriptor-set-name');
|
||||
if (fileNameEl) fileNameEl.textContent = file.name;
|
||||
showWizardLiveStatus('Descriptor set selected', 'Upload the descriptor set to discover unary gRPC methods from the backend.');
|
||||
};
|
||||
reader.readAsArrayBuffer(file);
|
||||
}
|
||||
|
||||
function renderDescriptorServiceList(services) {
|
||||
grpcDescriptorServices = services || [];
|
||||
var summaryEl = document.getElementById('wizard-descriptor-services-summary');
|
||||
var listEl = document.getElementById('wizard-descriptor-services-list');
|
||||
if (!summaryEl || !listEl) return;
|
||||
|
||||
var methodCount = grpcDescriptorServices.reduce(function(total, service) {
|
||||
return total + ((service.methods || []).length);
|
||||
}, 0);
|
||||
summaryEl.textContent = grpcDescriptorServices.length
|
||||
? (grpcDescriptorServices.length + ' services · ' + methodCount + ' unary methods discovered')
|
||||
: 'No unary methods discovered yet';
|
||||
listEl.innerHTML = '';
|
||||
|
||||
grpcDescriptorServices.forEach(function(service, serviceIndex) {
|
||||
var group = document.createElement('div');
|
||||
group.className = 'config-card';
|
||||
group.style.marginBottom = '0';
|
||||
|
||||
var body = document.createElement('div');
|
||||
body.className = 'config-card-body';
|
||||
body.style.padding = '16px';
|
||||
body.style.gap = '10px';
|
||||
|
||||
var title = document.createElement('div');
|
||||
title.className = 'config-card-title';
|
||||
title.textContent = (service.package ? service.package + '.' : '') + service.service;
|
||||
body.appendChild(title);
|
||||
|
||||
(service.methods || []).forEach(function(method, methodIndex) {
|
||||
var button = document.createElement('button');
|
||||
button.type = 'button';
|
||||
button.className = 'btn-ghost-sm';
|
||||
button.style.width = '100%';
|
||||
button.style.justifyContent = 'space-between';
|
||||
button.textContent = method.name;
|
||||
button.addEventListener('click', function() {
|
||||
applyDiscoveredGrpcMethod(serviceIndex, methodIndex);
|
||||
});
|
||||
body.appendChild(button);
|
||||
});
|
||||
|
||||
group.appendChild(body);
|
||||
listEl.appendChild(group);
|
||||
});
|
||||
}
|
||||
|
||||
function applyDiscoveredGrpcMethod(serviceIndex, methodIndex) {
|
||||
var service = grpcDescriptorServices[serviceIndex];
|
||||
var method = service && service.methods ? service.methods[methodIndex] : null;
|
||||
if (!service || !method) return;
|
||||
|
||||
var route = '/' + (service.package ? service.package + '.' : '') + service.service + '/' + method.name;
|
||||
selectGrpcSource('manual');
|
||||
setValue('grpc-manual-route', route);
|
||||
setValue('grpc-manual-req-type', method.name + 'Request');
|
||||
setValue('grpc-manual-res-type', method.name + 'Response');
|
||||
grpcManualRouteInput(route);
|
||||
grpcManualTypeInput();
|
||||
setValue('tool-input-schema', JSON.stringify(crankSchemaToJsonSchema(method.input_schema), null, 2));
|
||||
setValue('tool-output-schema', JSON.stringify(crankSchemaToJsonSchema(method.output_schema), null, 2));
|
||||
showWizardLiveStatus('gRPC method applied', 'The route and schemas were filled from the uploaded descriptor set.');
|
||||
}
|
||||
|
||||
async function uploadDescriptorSetAndDiscover() {
|
||||
var hasSavedDescriptor = !!(
|
||||
wizardCurrentVersion
|
||||
&& wizardCurrentVersion.target
|
||||
&& wizardCurrentVersion.target.kind === 'grpc'
|
||||
&& wizardCurrentVersion.target.descriptor_ref
|
||||
);
|
||||
|
||||
if (!wizardDescriptorSetUpload && !hasSavedDescriptor) {
|
||||
showWizardLiveStatus('No descriptor set selected', 'Choose a descriptor-set file before uploading it, or save a draft that already has one attached.', true);
|
||||
return;
|
||||
}
|
||||
|
||||
await persistCurrentDraft(true);
|
||||
|
||||
var services = await window.CrankApi.listGrpcServices(
|
||||
wizardWorkspaceId,
|
||||
wizardEditId,
|
||||
currentDraftVersion()
|
||||
);
|
||||
renderDescriptorServiceList(services.services || []);
|
||||
showWizardLiveStatus('Descriptor services discovered', 'The backend parsed the descriptor set. Pick a method to apply its route and schemas.');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user