chore: publish clean community baseline
This commit is contained in:
@@ -0,0 +1,267 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Crank — New Operation</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="../../css/variables.css">
|
||||
<link rel="stylesheet" href="../../css/layout.css">
|
||||
<link rel="stylesheet" href="../../css/wizard.css">
|
||||
<link rel="stylesheet" href="../../css/pages.css">
|
||||
<script src="%CRANK_BUNDLE_PROTECTED_CORE%"></script>
|
||||
</head>
|
||||
<body class="wizard-page">
|
||||
|
||||
<!-- ═══════════════════ NAVBAR ═══════════════════ -->
|
||||
<nav class="navbar">
|
||||
<a href="/" class="nav-logo">
|
||||
<div class="nav-logo-mark"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 28 28" fill="white"><path fill-rule="evenodd" d="M 10.000,1.800 L 12.287,3.962 L 15.408,3.557 L 15.987,6.650 L 18.750,8.157 L 17.400,11.000 L 18.750,13.843 L 15.987,15.350 L 15.408,18.443 L 12.287,18.038 L 10.000,20.200 L 7.713,18.038 L 4.592,18.443 L 4.013,15.350 L 1.250,13.843 L 2.600,11.000 L 1.250,8.157 L 4.013,6.650 L 4.592,3.557 L 7.713,3.962 Z M 12.800,11.000 A 2.8 2.8 0 1 0 7.200,11.000 A 2.8 2.8 0 1 0 12.800,11.000 Z"/><path fill-rule="evenodd" d="M 11.791,15.604 Q 16.083,17.179 21.375,21.154 A 2.00 2.00 0 1 1 23.625,17.846 Q 19.782,15.610 14.940,10.973 Z M 24.300,19.500 A 1.8 1.8 0 1 0 20.700,19.500 A 1.8 1.8 0 1 0 24.300,19.500 Z"/><path d="M 10.0,9.7 L 11.3,11.0 L 10.0,12.3 L 8.7,11.0 Z" fill="rgba(0,0,0,0.3)"/></svg></div>
|
||||
<span class="nav-logo-text">Crank</span>
|
||||
</a>
|
||||
|
||||
<!-- Workspace switcher -->
|
||||
<div class="ws-switcher" id="ws-switcher">
|
||||
<button class="ws-switcher-trigger" onclick="toggleWsSwitcher(event)">
|
||||
<div class="ws-dot" id="ws-dot">A</div>
|
||||
<span class="ws-current-name" id="ws-current-name" data-testid="shell-workspace-name">acme-workspace</span>
|
||||
<svg width="11" height="11" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 6l4 4 4-4"/></svg>
|
||||
</button>
|
||||
<div class="ws-dropdown" id="ws-dropdown" hidden>
|
||||
<div class="ws-dropdown-label" data-i18n="nav.workspaces">Your workspaces</div>
|
||||
<div id="ws-dropdown-list"></div>
|
||||
<div class="ws-dropdown-footer">
|
||||
<button class="ws-dropdown-create" onclick="window.location.href='/workspace-setup?mode=create'">
|
||||
<svg width="12" height="12" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"><path d="M8 3v10M3 8h10"/></svg>
|
||||
<span data-i18n="nav.create_workspace">Create workspace</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="nav-links">
|
||||
<a href="/" class="nav-link active" data-i18n="nav.operations">Operations</a>
|
||||
<a href="/agents" class="nav-link" data-i18n="nav.agents">Agents</a>
|
||||
<a href="/api-keys" class="nav-link" data-i18n="nav.apikeys">API Keys</a>
|
||||
<a href="/secrets" class="nav-link" data-i18n="nav.secrets">Secrets</a>
|
||||
<a href="/logs" class="nav-link" data-i18n="nav.logs">Logs</a>
|
||||
<a href="/usage" class="nav-link" data-i18n="nav.usage">Usage</a>
|
||||
</div>
|
||||
|
||||
<div class="nav-right">
|
||||
<button class="nav-hamburger" data-i18n-title="nav.menu" data-i18n-aria-label="nav.menu" aria-label="Menu"><span></span><span></span><span></span></button>
|
||||
|
||||
|
||||
<button class="nav-icon-btn" data-i18n-title="nav.notifications" title="Notifications">
|
||||
<svg width="15" height="15"><use href="../../icons/general/bell.svg#icon"/></svg>
|
||||
</button>
|
||||
|
||||
<div class="nav-divider"></div>
|
||||
|
||||
<div class="user-menu">
|
||||
<div class="nav-avatar" data-testid="shell-avatar" data-i18n-title="nav.account" title="Account">AT</div>
|
||||
<div class="user-dropdown">
|
||||
<div class="user-dropdown-header">
|
||||
<div class="user-dropdown-name" data-testid="shell-user-name">Crank</div>
|
||||
<div class="user-dropdown-role" data-testid="shell-user-role">—</div>
|
||||
</div>
|
||||
<button class="user-dropdown-item" data-action="settings-link">
|
||||
<svg width="13" height="13"><use href="../../icons/general/settings.svg#icon"/></svg>
|
||||
<span data-i18n="nav.settings">Settings</span>
|
||||
</button>
|
||||
<div class="dropdown-divider"></div>
|
||||
<button class="user-dropdown-item danger" data-action="logout">
|
||||
<svg width="13" height="13"><use href="../../icons/general/logout.svg#icon"/></svg>
|
||||
<span data-i18n="nav.logout">Log out</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="mobile-nav" hidden>
|
||||
<a href="/" class="mobile-nav-link active" data-i18n="nav.operations">Operations</a>
|
||||
<a href="/agents" class="mobile-nav-link" data-i18n="nav.agents">Agents</a>
|
||||
<a href="/api-keys" class="mobile-nav-link" data-i18n="nav.apikeys">API Keys</a>
|
||||
<a href="/secrets" class="mobile-nav-link" data-i18n="nav.secrets">Secrets</a>
|
||||
<a href="/logs" class="mobile-nav-link" data-i18n="nav.logs">Logs</a>
|
||||
<a href="/usage" class="mobile-nav-link" data-i18n="nav.usage">Usage</a>
|
||||
</div>
|
||||
|
||||
<!-- ═══════════════════ WIZARD SHELL ═══════════════════ -->
|
||||
<div class="wizard-shell">
|
||||
|
||||
<!-- ══ TOP PROGRESS STRIP ══ -->
|
||||
<div class="progress-strip">
|
||||
<button class="back-to-catalog-btn" id="back-to-catalog" data-i18n-title="wizard.back_catalog" title="Back to catalog">
|
||||
<svg width="13" height="13" viewBox="0 0 16 16" fill="currentColor"><path d="M9.78 12.78a.75.75 0 01-1.06 0L4.47 8.53a.75.75 0 010-1.06l4.25-4.25a.75.75 0 011.06 1.06L6.06 8l3.72 3.72a.75.75 0 010 1.06z"/></svg>
|
||||
<span data-i18n="wizard.back_catalog">Operations</span>
|
||||
</button>
|
||||
<div class="progress-divider"></div>
|
||||
<span class="progress-label" data-i18n="wizard.progress.create">Create operation</span>
|
||||
<div class="progress-bar-wrap">
|
||||
<div class="progress-bar-fill"></div>
|
||||
</div>
|
||||
<span class="progress-pct">20%</span>
|
||||
<button class="progress-close" data-i18n-title="wizard.exit" title="Exit wizard" aria-label="Exit wizard">
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round">
|
||||
<line x1="1" y1="1" x2="11" y2="11"/><line x1="11" y1="1" x2="1" y2="11"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- ══ WIZARD BODY ══ -->
|
||||
<div class="wizard-body">
|
||||
|
||||
<!-- ── Step sidebar ── -->
|
||||
<div class="step-sidebar">
|
||||
<div class="step-sidebar-card">
|
||||
|
||||
<div class="step-sidebar-header">
|
||||
<div class="step-sidebar-logo-mark">M</div>
|
||||
<div class="step-sidebar-brand" data-wizard-brand="create">
|
||||
Create <span>operation</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="step-sidebar-body">
|
||||
<div class="step-sidebar-section-label" data-i18n="wizard.progress.section">Progress</div>
|
||||
|
||||
<div class="steps-list">
|
||||
|
||||
<div class="step-item active">
|
||||
<div class="step-indicator">1</div>
|
||||
<div class="step-content">
|
||||
<div class="step-number" data-step="1">Step 1</div>
|
||||
<div class="step-name" data-i18n="wizard.step_name.protocol">Protocol</div>
|
||||
<div class="step-status-text" data-i18n="wizard.status.in_progress">In progress</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="step-item pending">
|
||||
<div class="step-indicator">2</div>
|
||||
<div class="step-content">
|
||||
<div class="step-number" data-step="2">Step 2</div>
|
||||
<div class="step-name" data-i18n="wizard.step_name.upstream">Upstream target</div>
|
||||
<div class="step-status-text" data-i18n="wizard.status.not_started">Not started</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="step-item pending" id="sidebar-step-3">
|
||||
<div class="step-indicator">3</div>
|
||||
<div class="step-content">
|
||||
<div class="step-number" data-step="3">Step 3</div>
|
||||
<div class="step-name" id="sidebar-step-3-name">Request config</div>
|
||||
<div class="step-status-text" data-i18n="wizard.status.not_started">Not started</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="step-item pending">
|
||||
<div class="step-indicator">4</div>
|
||||
<div class="step-content">
|
||||
<div class="step-number" data-step="4">Step 4</div>
|
||||
<div class="step-name" data-i18n="wizard.step_name.tool">Tool config</div>
|
||||
<div class="step-status-text" data-i18n="wizard.status.not_started">Not started</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="step-item pending">
|
||||
<div class="step-indicator">5</div>
|
||||
<div class="step-content">
|
||||
<div class="step-number" data-step="5">Step 5</div>
|
||||
<div class="step-name" data-i18n="wizard.step_name.mapping">Mapping</div>
|
||||
<div class="step-status-text" data-i18n="wizard.status.not_started">Not started</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div><!-- /steps-list -->
|
||||
|
||||
<div class="sidebar-help" style="margin-top: 20px;">
|
||||
<div class="sidebar-help-title">
|
||||
<svg width="13" height="13" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="8" cy="8" r="7"/>
|
||||
<path d="M8 12v-4M8 5v-.5"/>
|
||||
</svg>
|
||||
<span data-i18n="wizard.help.title">Need help?</span>
|
||||
</div>
|
||||
<div class="sidebar-help-text">
|
||||
<span data-i18n="wizard.help.body">Unsure which protocol to choose? Read the</span>
|
||||
<a href="#" class="sidebar-help-link" data-i18n="wizard.help.link">protocol comparison guide →</a>
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- /step-sidebar-body -->
|
||||
</div><!-- /step-sidebar-card -->
|
||||
</div><!-- /step-sidebar -->
|
||||
|
||||
<!-- ── Main step panel ── -->
|
||||
<div class="wizard-main"><div id="step-panel-container"><!-- steps loaded dynamically --></div></div>
|
||||
</div><!-- /wizard-body -->
|
||||
|
||||
<!-- ══ BOTTOM ACTION BAR ══ -->
|
||||
<div class="action-bar" role="toolbar" aria-label="Wizard navigation">
|
||||
<div class="action-bar-inner">
|
||||
|
||||
<button class="btn btn-back" type="button" disabled>
|
||||
<svg width="13" height="13" viewBox="0 0 16 16" fill="currentColor">
|
||||
<path d="M9.78 12.78a.75.75 0 01-1.06 0L4.47 8.53a.75.75 0 010-1.06l4.25-4.25a.75.75 0 011.06 1.06L6.06 8l3.72 3.72a.75.75 0 010 1.06z"/>
|
||||
</svg>
|
||||
<span data-i18n="wizard.button.back">Back</span>
|
||||
</button>
|
||||
|
||||
<button class="btn-save-draft" type="button" data-i18n="wizard.button.save_draft">Save draft</button>
|
||||
|
||||
<div class="action-bar-spacer"></div>
|
||||
|
||||
<span class="step-counter" data-step-counter>Step <strong>1</strong> of 5</span>
|
||||
|
||||
<button id="btn-continue" class="btn btn-continue" type="button">
|
||||
<span data-i18n="wizard.button.continue">Continue</span>
|
||||
<svg width="13" height="13" viewBox="0 0 16 16" fill="currentColor">
|
||||
<path d="M6.22 3.22a.75.75 0 011.06 0l4.25 4.25a.75.75 0 010 1.06l-4.25 4.25a.75.75 0 01-1.06-1.06L9.94 8 6.22 4.28a.75.75 0 010-1.06z"/>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div><!-- /wizard-shell -->
|
||||
|
||||
<div class="modal-overlay" id="quick-secret-modal" data-testid="wizard-quick-secret-modal">
|
||||
<div class="modal">
|
||||
<div class="modal-header">
|
||||
<span class="modal-title" data-i18n="wizard.step2.quick_secret_title">Quick create secret</span>
|
||||
<button class="modal-close" id="quick-secret-close-btn" type="button">
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round">
|
||||
<line x1="1" y1="1" x2="11" y2="11"/><line x1="11" y1="1" x2="1" y2="11"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body" style="display:grid; gap:14px;">
|
||||
<div class="field-group">
|
||||
<label class="field-label" data-i18n="wizard.step2.quick_secret_name">Secret name</label>
|
||||
<input class="field-input" id="quick-secret-name" data-testid="wizard-quick-secret-name" type="text" autocomplete="off">
|
||||
</div>
|
||||
<div class="field-group">
|
||||
<label class="field-label" data-i18n="wizard.step2.quick_secret_kind">Secret kind</label>
|
||||
<select class="field-select" id="quick-secret-kind" data-testid="wizard-quick-secret-kind">
|
||||
<option value="token" data-i18n="secrets.kind.token">Token</option>
|
||||
<option value="header" data-i18n="secrets.kind.header">Header value</option>
|
||||
<option value="generic" data-i18n="secrets.kind.generic">Generic JSON</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="field-group">
|
||||
<label class="field-label" data-i18n="wizard.step2.quick_secret_value">Secret value</label>
|
||||
<input class="field-input" id="quick-secret-value" data-testid="wizard-quick-secret-value" type="text" autocomplete="off">
|
||||
<div class="field-hint" data-i18n="wizard.step2.quick_secret_hint">The plaintext is encrypted immediately and will not be returned again.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn-secondary" id="quick-secret-cancel-btn" type="button" data-i18n="btn.cancel">Cancel</button>
|
||||
<button class="btn-primary" id="quick-secret-submit-btn" data-testid="wizard-quick-secret-submit" type="button" data-i18n="wizard.step2.quick_secret_submit">Create secret</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="%CRANK_BUNDLE_WIZARD%"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,69 @@
|
||||
<!-- ════════════ STEP 1: Protocol ════════════ -->
|
||||
<div id="step-panel-1" class="step-pane">
|
||||
<div class="step-panel-header">
|
||||
<div class="step-panel-eyebrow">
|
||||
<div class="step-panel-eyebrow-line"></div>
|
||||
<span data-step-counter>Step 1 of 5</span>
|
||||
</div>
|
||||
<h1 class="step-panel-title" data-i18n="wizard.step1.title">Choose a protocol</h1>
|
||||
<p class="step-panel-subtitle" data-i18n="wizard.step1.subtitle">Select the transport protocol your upstream service uses. This determines how Crank will communicate with your API and which configuration options are available in subsequent steps.</p>
|
||||
</div>
|
||||
|
||||
<!-- Protocol selection cards -->
|
||||
<div class="protocol-grid" data-testid="wizard-protocol-grid">
|
||||
|
||||
<div class="protocol-card rest selected" data-protocol="rest" data-testid="wizard-protocol-rest" role="radio" aria-checked="true" tabindex="0">
|
||||
<div class="protocol-card-check">
|
||||
<svg viewBox="0 0 12 12"><polyline points="1.5,6 4.5,9 10.5,3"/></svg>
|
||||
</div>
|
||||
<div class="protocol-icon-wrap">
|
||||
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="#58a6ff" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||
<path d="M3.6 9h16.8M3.6 15h16.8"/>
|
||||
<path d="M12 3a15.3 15.3 0 010 18M12 3a15.3 15.3 0 000 18"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="protocol-card-name" data-i18n="wizard.step1.rest_name">REST / HTTP</div>
|
||||
<div class="protocol-card-tagline" data-i18n="wizard.step1.rest_tagline">Standard HTTP verbs over a RESTful endpoint. The most broadly supported option.</div>
|
||||
<div class="protocol-card-tags">
|
||||
<span class="protocol-tag">GET</span>
|
||||
<span class="protocol-tag">POST</span>
|
||||
<span class="protocol-tag">PUT</span>
|
||||
<span class="protocol-tag">PATCH</span>
|
||||
<span class="protocol-tag">DELETE</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div data-slot="wizard.protocol_cards.graphql"></div>
|
||||
<div data-slot="wizard.protocol_cards.grpc"></div>
|
||||
<div data-slot="wizard.protocol_cards.soap"></div>
|
||||
<div data-slot="wizard.protocol_cards.websocket"></div>
|
||||
|
||||
</div><!-- /protocol-grid -->
|
||||
|
||||
|
||||
<div class="info-callout" style="margin-bottom: 24px;">
|
||||
<svg class="info-callout-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polygon points="8,1 15,14 1,14" fill="none"/>
|
||||
<path d="M8 6v4M8 11.5v.5"/>
|
||||
</svg>
|
||||
<div class="info-callout-body">
|
||||
<div class="info-callout-title" data-i18n="wizard.step1.tip_title">You can change this later</div>
|
||||
<div class="info-callout-text">
|
||||
<span data-i18n="wizard.step1.tip_body">Switching protocol after configuring subsequent steps will clear schema and mapping data. Save your operation as a draft before experimenting. See protocol migration guide for details.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-callout" id="wizard-edition-protocol-note" hidden>
|
||||
<svg class="info-callout-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="8" cy="8" r="7"/>
|
||||
<path d="M8 11V8M8 5v-.5"/>
|
||||
</svg>
|
||||
<div class="info-callout-body">
|
||||
<div class="info-callout-title" data-i18n="wizard.step1.community_protocol_title">Community protocol scope</div>
|
||||
<div class="info-callout-text" id="wizard-edition-protocol-note-text"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div><!-- /step-pane-1 -->
|
||||
@@ -0,0 +1,224 @@
|
||||
<!-- ════════════ STEP 2: Upstream target ════════════ -->
|
||||
<div id="step-panel-2" class="step-pane">
|
||||
<div class="step-panel-header">
|
||||
<div class="step-panel-eyebrow">
|
||||
<div class="step-panel-eyebrow-line"></div>
|
||||
<span data-step-counter>Step 2 of 5</span>
|
||||
</div>
|
||||
<h1 class="step-panel-title" data-i18n="wizard.step2.title">Select upstream & endpoint</h1>
|
||||
<p class="step-panel-subtitle" data-i18n="wizard.step2.subtitle">Choose an existing upstream — a shared host with its auth config — or register a new one. Then define the specific endpoint path this operation will call.</p>
|
||||
</div>
|
||||
|
||||
<!-- ── Upstream selector ── -->
|
||||
<div class="config-card" style="margin-bottom: 20px; overflow: visible; position: relative; z-index: 10;">
|
||||
<div class="config-card-header">
|
||||
<div class="config-card-header-icon">
|
||||
<svg width="13" height="13" viewBox="0 0 16 16" fill="none" stroke="var(--text-secondary)" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="1" y="3" width="14" height="4" rx="1"/>
|
||||
<rect x="1" y="9" width="14" height="4" rx="1"/>
|
||||
<circle cx="4" cy="5" r="0.8" fill="var(--text-secondary)" stroke="none"/>
|
||||
<circle cx="4" cy="11" r="0.8" fill="var(--text-secondary)" stroke="none"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<div class="config-card-title" data-i18n="wizard.step2.upstream_title">Upstream</div>
|
||||
<div class="config-card-subtitle" data-i18n="wizard.step2.upstream_subtitle">Shared host, base URL, and authentication</div>
|
||||
</div>
|
||||
<span class="config-card-optional" style="color: var(--error, #f87171);" data-i18n="wizard.required">Required</span>
|
||||
</div>
|
||||
<div class="config-card-body" style="gap: 12px; padding: 16px;">
|
||||
|
||||
<!-- Searchable combobox -->
|
||||
<div class="upstream-combobox" id="upstream-combobox">
|
||||
<div class="upstream-combobox-trigger" id="upstream-combobox-trigger" onclick="toggleUpstreamDropdown(event)">
|
||||
<div class="upstream-combobox-value" id="upstream-combobox-value">
|
||||
<span class="upstream-combobox-placeholder" data-i18n="wizard.step2.upstream_placeholder">Select an upstream…</span>
|
||||
</div>
|
||||
<svg class="upstream-combobox-chevron" width="12" height="12" viewBox="0 0 16 16" fill="none" stroke="var(--text-muted)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M4 6l4 4 4-4"/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<!-- Dropdown panel -->
|
||||
<div class="upstream-dropdown" id="upstream-dropdown" hidden>
|
||||
<div class="upstream-search-wrap">
|
||||
<svg width="13" height="13" viewBox="0 0 16 16" fill="none" stroke="var(--text-muted)" stroke-width="1.8" stroke-linecap="round">
|
||||
<circle cx="7" cy="7" r="5"/><path d="M12 12l2.5 2.5"/>
|
||||
</svg>
|
||||
<input class="upstream-search-input" id="upstream-search" type="text" data-i18n-ph="wizard.step2.search_placeholder" placeholder="Search by name or URL…" oninput="filterUpstreams(this.value)" autocomplete="off" spellcheck="false">
|
||||
</div>
|
||||
<div class="upstream-dropdown-list" id="upstream-dropdown-list">
|
||||
<!-- populated by JS -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Selected upstream preview -->
|
||||
<div class="upstream-preview" id="upstream-preview" hidden>
|
||||
<div class="upstream-preview-name" id="upstream-preview-name"></div>
|
||||
<div class="upstream-preview-url" id="upstream-preview-url"></div>
|
||||
<span class="upstream-auth-badge" id="upstream-preview-badge"></span>
|
||||
<button class="upstream-preview-change" onclick="beginEditSelectedUpstream(event)" data-i18n="wizard.step2.change">Change</button>
|
||||
</div>
|
||||
|
||||
<!-- Register new upstream trigger row -->
|
||||
<div class="upstream-new-trigger" id="upstream-new-trigger" onclick="startNewUpstream()">
|
||||
<div class="upstream-new-trigger-radio" id="upstream-new-trigger-radio">
|
||||
<div class="upstream-new-trigger-dot"></div>
|
||||
</div>
|
||||
<svg width="12" height="12" viewBox="0 0 16 16" fill="none" stroke="var(--accent)" stroke-width="2.2" stroke-linecap="round">
|
||||
<path d="M8 3v10M3 8h10"/>
|
||||
</svg>
|
||||
<span data-i18n="wizard.step2.register_new">Register new upstream</span>
|
||||
</div>
|
||||
|
||||
<!-- Register new upstream form (kept as-is per design) -->
|
||||
<div class="upstream-new-form" id="upstream-new-form" hidden>
|
||||
<div class="upstream-new-form-inner">
|
||||
<div class="form-row" style="grid-template-columns: 1fr 2fr;">
|
||||
<div class="form-group">
|
||||
<label class="form-label"><span data-i18n="wizard.step2.name">Name</span> <span class="form-label-required" data-i18n="workspace_setup.required">required</span></label>
|
||||
<input class="form-input" id="new-upstream-name" type="text" placeholder="e.g. acme-api" autocomplete="off" spellcheck="false">
|
||||
<div class="form-hint" data-i18n="wizard.step2.unique_hint">Unique identifier for this upstream.</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label"><span data-i18n="wizard.step2.base_url">Base URL</span> <span class="form-label-required" data-i18n="workspace_setup.required">required</span></label>
|
||||
<input class="form-input input-mono" id="new-upstream-url" type="text" placeholder="https://api.example.com" autocomplete="off" spellcheck="false">
|
||||
<div class="form-hint" data-i18n="wizard.step2.base_url_hint">Root URL without a trailing slash. Auth is configured separately below.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" data-i18n="wizard.step2.auth_selector">Upstream auth</label>
|
||||
<select class="form-select" id="new-upstream-auth-mode" data-testid="wizard-auth-mode-select" onchange="updateUpstreamAuthUi()">
|
||||
<option value="none" data-i18n="wizard.step2.auth_mode.none">No auth</option>
|
||||
<option value="existing" data-i18n="wizard.step2.auth_mode.existing">Use existing auth profile</option>
|
||||
<option value="create" data-i18n="wizard.step2.auth_mode.create">Create auth profile now</option>
|
||||
</select>
|
||||
<div class="form-hint" data-i18n="wizard.step2.auth_selector_hint">Use secrets-backed auth profiles instead of embedding credential headers in the upstream definition.</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" id="upstream-auth-existing-group" hidden>
|
||||
<label class="form-label" data-i18n="wizard.step2.auth_profile">Auth profile</label>
|
||||
<select class="form-select" id="new-upstream-auth-profile" data-testid="wizard-auth-profile-select"></select>
|
||||
<div class="form-hint" id="new-upstream-auth-profile-hint" data-i18n="wizard.step2.auth_profile_hint">Selected profile will be resolved at runtime and attached through `execution_config.auth_profile_ref`.</div>
|
||||
</div>
|
||||
|
||||
<div class="config-card" id="upstream-auth-create-group" hidden style="margin-top: 6px;">
|
||||
<div class="config-card-header">
|
||||
<div>
|
||||
<div class="config-card-title" data-i18n="wizard.step2.create_profile_title">Create auth profile</div>
|
||||
<div class="config-card-subtitle" data-i18n="wizard.step2.create_profile_subtitle">Create a reusable profile backed by encrypted workspace secrets.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="config-card-body" style="gap: 14px;">
|
||||
<div class="form-group">
|
||||
<label class="form-label"><span data-i18n="wizard.step2.profile_name">Profile name</span> <span class="form-label-required" data-i18n="workspace_setup.required">required</span></label>
|
||||
<input class="form-input" id="new-auth-profile-name" type="text" autocomplete="off" spellcheck="false">
|
||||
</div>
|
||||
<div class="form-row" style="grid-template-columns: 1fr 1fr;">
|
||||
<div class="form-group">
|
||||
<label class="form-label"><span data-i18n="wizard.step2.profile_kind">Auth kind</span> <span class="form-label-required" data-i18n="workspace_setup.required">required</span></label>
|
||||
<select class="form-select" id="new-auth-profile-kind" data-testid="wizard-auth-profile-kind-select" onchange="updateAuthProfileCreateUi()">
|
||||
<option value="bearer" data-i18n="wizard.step2.auth_kind.bearer">Bearer token</option>
|
||||
<option value="basic" data-i18n="wizard.step2.auth_kind.basic">Basic auth</option>
|
||||
<option value="api_key_header" data-i18n="wizard.step2.auth_kind.api_key_header">API key header</option>
|
||||
<option value="api_key_query" data-i18n="wizard.step2.auth_kind.api_key_query">API key query</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group" id="auth-profile-header-name-group">
|
||||
<label class="form-label" data-i18n="wizard.step2.header_name">Header name</label>
|
||||
<input class="form-input input-mono" id="new-auth-profile-header-name" type="text" value="Authorization" autocomplete="off" spellcheck="false">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" id="auth-profile-query-param-group" hidden>
|
||||
<label class="form-label" data-i18n="wizard.step2.query_param">Query param</label>
|
||||
<input class="form-input input-mono" id="new-auth-profile-query-param" type="text" value="api_key" autocomplete="off" spellcheck="false">
|
||||
</div>
|
||||
<div class="form-row" id="auth-profile-basic-secret-row" hidden style="grid-template-columns: 1fr 1fr;">
|
||||
<div class="form-group">
|
||||
<label class="form-label" data-i18n="wizard.step2.username_secret">Username secret</label>
|
||||
<select class="form-select" id="new-auth-profile-username-secret" data-testid="wizard-auth-username-secret-select"></select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" data-i18n="wizard.step2.password_secret">Password secret</label>
|
||||
<select class="form-select" id="new-auth-profile-password-secret" data-testid="wizard-auth-password-secret-select"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" id="auth-profile-single-secret-group">
|
||||
<label class="form-label" data-i18n="wizard.step2.secret_value">Secret</label>
|
||||
<select class="form-select" id="new-auth-profile-secret-id" data-testid="wizard-auth-secret-select"></select>
|
||||
</div>
|
||||
<div style="display:flex; gap:8px; align-items:center; flex-wrap:wrap;">
|
||||
<button class="btn-ghost-sm" data-testid="wizard-open-quick-secret" onclick="openQuickSecretModal(event)" data-i18n="wizard.step2.quick_secret">Quick create secret</button>
|
||||
<a class="btn-ghost-sm" href="/secrets" target="_blank" rel="noopener" data-i18n="wizard.step2.manage_secrets">Open secrets page</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label" data-i18n="wizard.step2.static_headers">Static headers (optional)</label>
|
||||
<div class="code-block" style="border-radius: 6px;">
|
||||
<div class="code-toolbar">
|
||||
<div class="code-dots"><span></span><span></span><span></span></div>
|
||||
<span class="code-toolbar-label">json / static-headers</span>
|
||||
</div>
|
||||
<textarea class="form-textarea code-textarea" id="new-upstream-static-headers" rows="5">{
|
||||
}</textarea>
|
||||
</div>
|
||||
<div class="form-hint" data-i18n="wizard.step2.static_headers_hint">Optional non-secret headers sent on every request to this upstream. Use auth profiles for credentials.</div>
|
||||
</div>
|
||||
<div style="display:flex; gap:8px; margin-top:4px;">
|
||||
<button class="btn-primary-sm" onclick="saveNewUpstream(event)" data-i18n="wizard.step2.save_upstream">Save upstream</button>
|
||||
<button class="btn-ghost-sm" onclick="cancelNewUpstream(event)" data-i18n="btn.cancel">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ── Endpoint ── -->
|
||||
<div class="config-card" style="margin-bottom: 20px;">
|
||||
<div class="config-card-header">
|
||||
<div class="config-card-header-icon">
|
||||
<svg width="13" height="13" viewBox="0 0 16 16" fill="none" stroke="var(--text-secondary)" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M2 8h12M10 4l4 4-4 4"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<div class="config-card-title" data-i18n="wizard.step2.endpoint_title">Endpoint</div>
|
||||
<div class="config-card-subtitle" data-i18n="wizard.step2.endpoint_subtitle">Path and HTTP method for this specific operation</div>
|
||||
</div>
|
||||
<span class="config-card-optional" style="color: var(--error, #f87171);" data-i18n="wizard.required">Required</span>
|
||||
</div>
|
||||
<div class="config-card-body" style="gap: 16px;">
|
||||
<div class="form-group">
|
||||
<label class="form-label">
|
||||
<span data-i18n="wizard.step2.path_template">Path template</span>
|
||||
<span class="form-label-required" data-i18n="workspace_setup.required">required</span>
|
||||
</label>
|
||||
<input class="form-input input-mono" id="endpoint-path" type="text" value="/v1/leads" autocomplete="off" spellcheck="false">
|
||||
<div class="form-hint" data-i18n="wizard.step2.path_hint">Appended to the upstream base URL. Use {param} for path variables.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div><!-- /step-pane-2 -->
|
||||
|
||||
<!-- Template: upstream dropdown item -->
|
||||
<template id="tmpl-upstream-item">
|
||||
<div class="upstream-dropdown-item">
|
||||
<div class="upstream-dropdown-item-info">
|
||||
<div class="upstream-dropdown-item-name"></div>
|
||||
<div class="upstream-dropdown-item-url"></div>
|
||||
</div>
|
||||
<span class="upstream-auth-badge"></span>
|
||||
<svg class="upstream-dropdown-item-check" width="12" height="12" viewBox="0 0 16 16" fill="none" stroke="var(--accent)" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M3 8l4 4 6-7"/></svg>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- Template: upstream combobox trigger value -->
|
||||
<template id="tmpl-upstream-trigger-value">
|
||||
<div class="upstream-trigger-name"></div>
|
||||
<div class="upstream-trigger-url"></div>
|
||||
</template>
|
||||
@@ -0,0 +1,93 @@
|
||||
<!-- REST variant -->
|
||||
<div id="step-panel-3-rest" class="step-pane">
|
||||
<div class="step-panel-header">
|
||||
<div class="step-panel-eyebrow">
|
||||
<div class="step-panel-eyebrow-line"></div>
|
||||
<span data-step-counter>Step 3 of 5</span>
|
||||
</div>
|
||||
<h1 class="step-panel-title" data-i18n="wizard.step3.rest.title">HTTP method & format</h1>
|
||||
<p class="step-panel-subtitle" data-i18n="wizard.step3.rest.subtitle">Choose the HTTP verb this operation sends and configure content negotiation headers. Crank will serialize MCP tool arguments into the appropriate request body format.</p>
|
||||
</div>
|
||||
|
||||
<!-- HTTP method picker -->
|
||||
<div class="config-card" style="margin-bottom: 20px;">
|
||||
<div class="config-card-header">
|
||||
<div class="config-card-header-icon">
|
||||
<svg width="13" height="13" viewBox="0 0 16 16" fill="none" stroke="var(--text-secondary)" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M2 8h12M10 4l4 4-4 4"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<div class="config-card-title" data-i18n="wizard.step3.rest.method_title">HTTP method</div>
|
||||
<div class="config-card-subtitle" data-i18n="wizard.step3.rest.method_subtitle">Verb sent to the upstream on every tool invocation</div>
|
||||
</div>
|
||||
<span class="config-card-optional" style="color:var(--error,#f87171);" data-i18n="wizard.required">Required</span>
|
||||
</div>
|
||||
<div class="config-card-body">
|
||||
<div class="method-grid">
|
||||
<button class="method-card" data-method="GET" onclick="selectMethod(this)">
|
||||
<span class="method-name">GET</span>
|
||||
<span class="method-desc" data-i18n="wizard.step3.rest.read">Read</span>
|
||||
</button>
|
||||
<button class="method-card active" data-method="POST" onclick="selectMethod(this)">
|
||||
<span class="method-name">POST</span>
|
||||
<span class="method-desc" data-i18n="wizard.step3.rest.create">Create</span>
|
||||
</button>
|
||||
<button class="method-card" data-method="PUT" onclick="selectMethod(this)">
|
||||
<span class="method-name">PUT</span>
|
||||
<span class="method-desc" data-i18n="wizard.step3.rest.replace">Replace</span>
|
||||
</button>
|
||||
<button class="method-card" data-method="PATCH" onclick="selectMethod(this)">
|
||||
<span class="method-name">PATCH</span>
|
||||
<span class="method-desc" data-i18n="wizard.step3.rest.update">Update</span>
|
||||
</button>
|
||||
<button class="method-card" data-method="DELETE" onclick="selectMethod(this)">
|
||||
<span class="method-name">DELETE</span>
|
||||
<span class="method-desc" data-i18n="wizard.step3.rest.remove">Remove</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="method-callout" id="method-callout-rest" hidden></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Request format -->
|
||||
<div class="config-card" style="margin-bottom: 20px;">
|
||||
<div class="config-card-header">
|
||||
<div class="config-card-header-icon">
|
||||
<svg width="13" height="13" viewBox="0 0 16 16" fill="none" stroke="var(--text-secondary)" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="2" y="3" width="12" height="10" rx="1.5"/>
|
||||
<path d="M5 7h6M5 10h4"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<div class="config-card-title" data-i18n="wizard.step3.rest.format_title">Request format</div>
|
||||
<div class="config-card-subtitle" data-i18n="wizard.step3.rest.format_subtitle">Content negotiation and serialisation</div>
|
||||
</div>
|
||||
<span class="config-card-optional" data-i18n="wizard.optional">Optional</span>
|
||||
</div>
|
||||
<div class="config-card-body" style="gap: 16px;">
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label class="form-label" data-i18n="wizard.step3.rest.content_type">Content-Type</label>
|
||||
<select class="form-select">
|
||||
<option selected>application/json</option>
|
||||
<option>application/x-www-form-urlencoded</option>
|
||||
<option>multipart/form-data</option>
|
||||
<option>text/plain</option>
|
||||
</select>
|
||||
<div class="form-hint" data-i18n="wizard.step3.rest.content_type_hint">How the request body is encoded when sent to the upstream.</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" data-i18n="wizard.step3.rest.accept">Accept</label>
|
||||
<select class="form-select">
|
||||
<option selected>application/json</option>
|
||||
<option>text/plain</option>
|
||||
<option>*/*</option>
|
||||
</select>
|
||||
<div class="form-hint" data-i18n="wizard.step3.rest.accept_hint">Accepted response content types from the upstream server.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div><!-- /step-pane-3-rest -->
|
||||
@@ -0,0 +1,158 @@
|
||||
<div id="step-panel-4" class="step-pane">
|
||||
<div class="step-panel-header">
|
||||
<div class="step-panel-eyebrow">
|
||||
<div class="step-panel-eyebrow-line"></div>
|
||||
<span data-step-counter>Step 4 of 5</span>
|
||||
</div>
|
||||
<h1 class="step-panel-title" data-i18n="wizard.step4.title">Tool config</h1>
|
||||
<p class="step-panel-subtitle" data-i18n="wizard.step4.subtitle">Name your tool, write the LLM-facing description, and define the input/output schemas. The description is the primary signal the model uses when deciding whether to call this tool.</p>
|
||||
</div>
|
||||
|
||||
<div class="config-card" style="margin-bottom: 20px;">
|
||||
<div class="config-card-header">
|
||||
<div class="config-card-header-icon">
|
||||
<svg width="13" height="13" viewBox="0 0 16 16" fill="none" stroke="var(--text-secondary)" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M2 5h12M2 8h8M2 11h5"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<div class="config-card-title" data-i18n="wizard.step4.identity_title">Tool identity</div>
|
||||
<div class="config-card-subtitle" data-i18n="wizard.step4.identity_subtitle">Machine-readable name and LLM-facing description</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="config-card-body" style="gap: 20px;">
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label">
|
||||
<span data-i18n="wizard.step4.tool_name">Tool name</span>
|
||||
<span class="form-label-required" data-i18n="workspace_setup.required">required</span>
|
||||
</label>
|
||||
<input id="tool-name" class="form-input input-mono" type="text" value="create_crm_lead" autocomplete="off" spellcheck="false">
|
||||
<div class="form-hint" data-i18n="wizard.step4.tool_name_hint">Machine-readable identifier used in MCP tool calls. Only lowercase letters, numbers and underscores. Cannot be changed after publishing.</div>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label class="form-label">
|
||||
<span data-i18n="wizard.step4.display_name">Display name</span>
|
||||
<span class="form-label-required" data-i18n="workspace_setup.required">required</span>
|
||||
</label>
|
||||
<input id="tool-display-name" class="form-input" type="text" value="Create CRM Lead" autocomplete="off">
|
||||
<div class="form-hint" data-i18n="wizard.step4.display_name_hint">Human-readable name shown in the console and audit log.</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">
|
||||
<span data-i18n="wizard.step4.tool_title">Tool title</span>
|
||||
<span class="form-label-optional" data-i18n="workspace_setup.optional">(optional)</span>
|
||||
</label>
|
||||
<input id="tool-title" class="form-input" type="text" value="Create a new CRM lead record" autocomplete="off">
|
||||
<div class="form-hint" data-i18n="wizard.step4.tool_title_hint">Short imperative sentence shown in the MCP tool manifest.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label">
|
||||
<span data-i18n="wizard.step4.description">Description</span>
|
||||
<span class="form-label-required" data-i18n="workspace_setup.required">required</span>
|
||||
</label>
|
||||
<textarea id="tool-description" class="form-textarea" rows="5">Creates a new lead record in the CRM from the provided contact details. Use this tool when the user wants to add a new prospect or contact. Returns the new lead ID and creation timestamp on success.</textarea>
|
||||
<div class="form-hint" data-i18n="wizard.step4.description_hint">LLM-facing description. Be precise — this is the primary signal the model uses to decide whether to invoke this tool.</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-callout" style="margin-bottom: 24px;">
|
||||
<svg class="info-callout-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="8" cy="8" r="7"/>
|
||||
<path d="M8 11V8M8 5v-.5"/>
|
||||
</svg>
|
||||
<div class="info-callout-body">
|
||||
<div class="info-callout-title" data-i18n="wizard.step4.description_title">Writing effective descriptions</div>
|
||||
<div class="info-callout-text" data-i18n="wizard.step4.description_body">Start with a verb ("Creates", "Fetches", "Updates"). Mention key input requirements. Describe what a successful response looks like. Avoid vague phrasing like "handles" or "manages". See description best practices →</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="config-card" style="margin-bottom: 20px;">
|
||||
<div class="config-card-header">
|
||||
<div class="config-card-header-icon">
|
||||
<svg width="13" height="13" viewBox="0 0 16 16" fill="none" stroke="var(--text-secondary)" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M4 2h8a2 2 0 012 2v8a2 2 0 01-2 2H4a2 2 0 01-2-2V4a2 2 0 012-2z"/>
|
||||
<path d="M6 6l-2 2 2 2M10 6l2 2-2 2"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<div class="config-card-title" data-i18n="wizard.step4.input_schema_title">Input schema</div>
|
||||
<div class="config-card-subtitle" data-i18n="wizard.step4.input_schema_subtitle">Parameters the LLM passes when calling this tool</div>
|
||||
</div>
|
||||
<span class="config-card-optional" data-i18n="wizard.step4.schema_draft">JSON Schema draft-07</span>
|
||||
</div>
|
||||
<div class="config-card-body" style="gap: 0; padding: 0;">
|
||||
<div class="code-block" style="border-radius: 0; border: none;">
|
||||
<div class="code-toolbar">
|
||||
<div class="code-dots"><span></span><span></span><span></span></div>
|
||||
<span class="code-toolbar-label">json / input-schema</span>
|
||||
</div>
|
||||
<textarea id="tool-input-schema" class="form-textarea code-textarea" rows="16">{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"type": "object",
|
||||
"required": ["first_name", "last_name", "email"],
|
||||
"properties": {
|
||||
"first_name": { "type": "string", "description": "Contact first name" },
|
||||
"last_name": { "type": "string", "description": "Contact last name" },
|
||||
"email": { "type": "string", "format": "email", "description": "Primary contact email address" },
|
||||
"company": { "type": "string", "description": "Company or organisation name" },
|
||||
"phone": { "type": "string", "description": "Phone number in E.164 format" },
|
||||
"source": { "type": "string", "enum": ["web","api","import"], "default": "api" }
|
||||
},
|
||||
"additionalProperties": false
|
||||
}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="config-card" style="margin-bottom: 20px;">
|
||||
<div class="config-card-header">
|
||||
<div class="config-card-header-icon">
|
||||
<svg width="13" height="13" viewBox="0 0 16 16" fill="none" stroke="var(--text-secondary)" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M4 2h8a2 2 0 012 2v8a2 2 0 01-2 2H4a2 2 0 01-2-2V4a2 2 0 012-2z"/>
|
||||
<path d="M5 8h6M5 5h4M5 11h3"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<div class="config-card-title" data-i18n="wizard.step4.output_schema_title">Output schema</div>
|
||||
<div class="config-card-subtitle" data-i18n="wizard.step4.output_schema_subtitle">Shape of the data returned to the LLM after a successful call</div>
|
||||
</div>
|
||||
<span class="config-card-optional" data-i18n="wizard.step4.schema_draft">JSON Schema draft-07</span>
|
||||
</div>
|
||||
<div class="config-card-body" style="gap: 0; padding: 0;">
|
||||
<div class="code-block" style="border-radius: 0; border: none;">
|
||||
<div class="code-toolbar">
|
||||
<div class="code-dots"><span></span><span></span><span></span></div>
|
||||
<span class="code-toolbar-label">json / output-schema</span>
|
||||
</div>
|
||||
<textarea id="tool-output-schema" class="form-textarea code-textarea" rows="14">{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"lead_id": { "type": "string", "description": "Unique identifier of the created lead" },
|
||||
"status": { "type": "string", "description": "Initial lead status assigned by the CRM" },
|
||||
"created_at": { "type": "string", "format": "date-time" },
|
||||
"owner_id": { "type": "string", "description": "CRM user ID of the assigned owner" }
|
||||
}
|
||||
}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-callout" style="margin-bottom: 24px;">
|
||||
<svg class="info-callout-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="8" cy="8" r="7"/>
|
||||
<path d="M8 11V8M8 5v-.5"/>
|
||||
</svg>
|
||||
<div class="info-callout-body">
|
||||
<div class="info-callout-title" data-i18n="wizard.step4.protocol_agnostic_title">Schemas are protocol-agnostic</div>
|
||||
<div class="info-callout-text" data-i18n="wizard.step4.protocol_agnostic_body">These schemas describe the MCP contract, not the upstream wire format. Field mapping in Step 5 translates between the two. You can upload a sample JSON response to auto-generate the output schema.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- /step-pane-4 -->
|
||||
@@ -0,0 +1,265 @@
|
||||
<div id="step-panel-5" class="step-pane">
|
||||
<div class="step-panel-header">
|
||||
<div class="step-panel-eyebrow">
|
||||
<div class="step-panel-eyebrow-line"></div>
|
||||
<span data-step-counter>Step 5 of 5</span>
|
||||
</div>
|
||||
<h1 class="step-panel-title" data-i18n="wizard.step5.title">Mapping and execution</h1>
|
||||
<p class="step-panel-subtitle" data-i18n="wizard.step5.subtitle">Define how MCP tool arguments map to upstream request fields, and how the upstream response maps back to MCP output. Then set execution parameters — auth is configured per-upstream in step 2.</p>
|
||||
</div>
|
||||
|
||||
<div class="section-divider" style="margin-bottom: 16px;">
|
||||
<span class="section-divider-label" data-i18n="wizard.step5.input_request">Input → Request</span>
|
||||
<div class="section-divider-line"></div>
|
||||
</div>
|
||||
|
||||
<div class="config-card" style="margin-bottom: 20px;">
|
||||
<div class="config-card-body" style="gap: 0; padding: 0;">
|
||||
<div class="code-block" style="border-radius: 0; border: none;">
|
||||
<div class="code-toolbar">
|
||||
<div class="code-dots"><span></span><span></span><span></span></div>
|
||||
<span class="code-toolbar-label">yaml / input-mapping</span>
|
||||
</div>
|
||||
<textarea id="tool-input-mapping" class="form-textarea code-textarea" rows="12">first_name: "$.input.first_name"
|
||||
last_name: "$.input.last_name"
|
||||
email: "$.input.email"
|
||||
company: "$.input.company"
|
||||
phone: "$.input.phone"
|
||||
source: "$.input.source"</textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section-divider" style="margin-bottom: 16px;">
|
||||
<span class="section-divider-label" data-i18n="wizard.step5.output_response">Response → Output</span>
|
||||
<div class="section-divider-line"></div>
|
||||
</div>
|
||||
|
||||
<div class="config-card" style="margin-bottom: 20px;">
|
||||
<div class="config-card-body" style="gap: 0; padding: 0;">
|
||||
<div class="code-block" style="border-radius: 0; border: none;">
|
||||
<div class="code-toolbar">
|
||||
<div class="code-dots"><span></span><span></span><span></span></div>
|
||||
<span class="code-toolbar-label">yaml / output-mapping</span>
|
||||
</div>
|
||||
<textarea id="tool-output-mapping" class="form-textarea code-textarea" rows="10">lead_id: "$.response.data.id"
|
||||
status: "$.response.data.status"
|
||||
created_at: "$.response.data.created_at"
|
||||
owner_id: "$.response.data.owner.id"</textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section-divider" style="margin-bottom: 16px;">
|
||||
<span class="section-divider-label" data-i18n="wizard.step5.execution">Execution</span>
|
||||
<div class="section-divider-line"></div>
|
||||
</div>
|
||||
|
||||
<div class="config-card" style="margin-bottom: 20px;">
|
||||
<div class="config-card-header">
|
||||
<div class="config-card-header-icon">
|
||||
<svg width="13" height="13" viewBox="0 0 16 16" fill="none" stroke="var(--text-secondary)" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="8" cy="8" r="6"/>
|
||||
<path d="M8 5v3l2 2"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<div class="config-card-title" data-i18n="wizard.step5.exec_title">Execution config</div>
|
||||
<div class="config-card-subtitle" data-i18n="wizard.step5.exec_subtitle">Timeouts, retry policy and auth profile reference</div>
|
||||
</div>
|
||||
<span class="config-card-optional" data-i18n="wizard.optional">Optional</span>
|
||||
</div>
|
||||
<div class="config-card-body" style="gap: 0; padding: 0;">
|
||||
<div class="code-block" style="border-radius: 0; border: none;">
|
||||
<div class="code-toolbar">
|
||||
<div class="code-dots"><span></span><span></span><span></span></div>
|
||||
<span class="code-toolbar-label">yaml / exec-config</span>
|
||||
</div>
|
||||
<textarea id="tool-exec-config" class="form-textarea code-textarea" rows="10">timeout_ms: 10000
|
||||
retry:
|
||||
max_attempts: 3
|
||||
backoff_ms: 500
|
||||
auth:
|
||||
profile: acme_bearer
|
||||
tls:
|
||||
verify: true</textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-callout" id="wizard-security-level-note" hidden style="margin-bottom: 20px;">
|
||||
<svg class="info-callout-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="8" cy="8" r="7"/>
|
||||
<path d="M8 11V8M8 5v-.5"/>
|
||||
</svg>
|
||||
<div class="info-callout-body">
|
||||
<div class="info-callout-title" data-i18n="wizard.step5.security_level_title">Operation security in Community</div>
|
||||
<div class="info-callout-text"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section-divider" style="margin-bottom: 16px;">
|
||||
<span class="section-divider-label" data-i18n="wizard.step5.live_title">Live validation and publishing</span>
|
||||
<div class="section-divider-line"></div>
|
||||
</div>
|
||||
|
||||
<div id="wizard-live-status" class="info-callout" hidden style="margin-bottom: 20px;">
|
||||
<svg class="info-callout-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="8" cy="8" r="7"/>
|
||||
<path d="M8 11V8M8 5v-.5"/>
|
||||
</svg>
|
||||
<div class="info-callout-body">
|
||||
<div class="info-callout-title" id="wizard-live-status-title" data-i18n="wizard.step5.draft_actions">Draft actions</div>
|
||||
<div class="info-callout-text" id="wizard-live-status-text"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-callout info-callout-subtle" style="margin-bottom: 20px;">
|
||||
<svg class="info-callout-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="8" cy="8" r="7"/>
|
||||
<path d="M8 11V8M8 5v-.5"/>
|
||||
</svg>
|
||||
<div class="info-callout-body">
|
||||
<div class="info-callout-title" data-i18n="wizard.step5.live_save_title">Live actions save the draft first</div>
|
||||
<div class="info-callout-text" data-i18n="wizard.step5.live_save_body">Sample uploads, test runs, YAML actions and publish all persist the current draft before calling the backend.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="config-card" style="margin-bottom: 20px;">
|
||||
<div class="config-card-header">
|
||||
<div class="config-card-header-icon">
|
||||
<svg width="13" height="13" viewBox="0 0 16 16" fill="none" stroke="var(--text-secondary)" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="2" y="3" width="12" height="10" rx="1.5"/>
|
||||
<path d="M5 7h6M5 10h6"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<div class="config-card-title" data-i18n="wizard.step5.samples_title">Samples and draft generation</div>
|
||||
<div class="config-card-subtitle" data-i18n="wizard.step5.samples_subtitle">Persist input/output samples, then generate schemas and mappings from real payloads.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="config-card-body" style="gap: 16px;">
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label class="form-label" data-i18n="wizard.step5.input_sample">Input sample</label>
|
||||
<textarea id="wizard-input-sample" class="form-textarea code-textarea" rows="9" spellcheck="false">{
|
||||
"first_name": "Ada",
|
||||
"last_name": "Lovelace",
|
||||
"email": "ada@example.com"
|
||||
}</textarea>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" data-i18n="wizard.step5.output_sample">Output sample</label>
|
||||
<textarea id="wizard-output-sample" class="form-textarea code-textarea" rows="9" spellcheck="false">{
|
||||
"id": "lead_123",
|
||||
"status": "created"
|
||||
}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display:flex; gap: 8px; flex-wrap: wrap;">
|
||||
<button id="wizard-upload-input-sample" class="btn-primary-sm" type="button" data-i18n="wizard.step5.save_input_sample">Save input sample</button>
|
||||
<button id="wizard-upload-output-sample" class="btn-ghost-sm" type="button" data-i18n="wizard.step5.save_output_sample">Save output sample</button>
|
||||
<button id="wizard-generate-draft" class="btn-ghost-sm" type="button" data-i18n="wizard.step5.generate_draft">Generate draft from samples</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="config-card" style="margin-bottom: 20px;">
|
||||
<div class="config-card-header">
|
||||
<div class="config-card-header-icon">
|
||||
<svg width="13" height="13" viewBox="0 0 16 16" fill="none" stroke="var(--text-secondary)" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M2 8h12M10 4l4 4-4 4"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<div class="config-card-title" data-i18n="wizard.step5.test_title">Test run</div>
|
||||
<div class="config-card-subtitle" data-i18n="wizard.step5.test_subtitle">Run the current draft against the upstream before publishing it.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="config-card-body" style="gap: 16px;">
|
||||
<div class="form-group">
|
||||
<label class="form-label" data-i18n="wizard.step5.test_input_payload">Test input payload</label>
|
||||
<textarea id="wizard-test-input" class="form-textarea code-textarea" rows="8" spellcheck="false">{
|
||||
"first_name": "Ada",
|
||||
"last_name": "Lovelace",
|
||||
"email": "ada@example.com"
|
||||
}</textarea>
|
||||
</div>
|
||||
<div style="display:flex; gap: 8px; flex-wrap: wrap;">
|
||||
<button id="wizard-run-test" class="btn-primary-sm" type="button" data-i18n="wizard.step5.run_test">Run test</button>
|
||||
<button id="wizard-copy-test-response" class="btn-ghost-sm" type="button" data-i18n="wizard.step5.use_response_output">Use response as output sample</button>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label class="form-label" data-i18n="wizard.step5.request_preview">Request preview</label>
|
||||
<textarea id="wizard-test-request-preview" class="form-textarea code-textarea" rows="8" spellcheck="false" readonly></textarea>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" data-i18n="wizard.step5.response_preview">Response preview</label>
|
||||
<textarea id="wizard-test-response-preview" class="form-textarea code-textarea" rows="8" spellcheck="false" readonly></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" data-i18n="wizard.step5.errors">Errors</label>
|
||||
<textarea id="wizard-test-errors" class="form-textarea code-textarea" rows="4" spellcheck="false" readonly></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="config-card" style="margin-bottom: 20px;">
|
||||
<div class="config-card-header">
|
||||
<div class="config-card-header-icon">
|
||||
<svg width="13" height="13" viewBox="0 0 16 16" fill="none" stroke="var(--text-secondary)" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M8 2v12M3 7l5-5 5 5"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<div class="config-card-title" data-i18n="wizard.step5.yaml_title">YAML import and export</div>
|
||||
<div class="config-card-subtitle" data-i18n="wizard.step5.yaml_subtitle">Move operation drafts between environments without leaving the wizard.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="config-card-body" style="gap: 16px;">
|
||||
<div style="display:flex; gap: 8px; flex-wrap: wrap;">
|
||||
<button id="wizard-export-yaml" class="btn-ghost-sm" type="button" data-i18n="wizard.step5.export_yaml">Export YAML</button>
|
||||
<button id="wizard-import-yaml-file-trigger" class="btn-ghost-sm" type="button" data-i18n="wizard.step5.load_yaml_file">Load YAML file</button>
|
||||
<button id="wizard-import-yaml" class="btn-primary-sm" type="button" data-i18n="wizard.step5.import_yaml">Import YAML</button>
|
||||
<input id="wizard-import-yaml-file" type="file" accept=".yaml,.yml,text/yaml" style="display:none">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" data-i18n="wizard.step5.yaml_document">YAML document</label>
|
||||
<textarea id="wizard-import-yaml-text" class="form-textarea code-textarea" rows="10" spellcheck="false" data-i18n-ph="wizard.step5.yaml_placeholder" placeholder="Paste exported YAML here to create or upsert an operation."></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="config-card" style="margin-bottom: 20px;">
|
||||
<div class="config-card-header">
|
||||
<div class="config-card-header-icon">
|
||||
<svg width="13" height="13" viewBox="0 0 16 16" fill="none" stroke="var(--text-secondary)" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M3 8l3 3 7-7"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<div class="config-card-title" data-i18n="wizard.step5.publish_title">Publish</div>
|
||||
<div class="config-card-subtitle" data-i18n="wizard.step5.publish_subtitle">Save the current draft and expose it to published agents.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="config-card-body" style="gap: 12px;">
|
||||
<div class="form-hint" data-i18n="wizard.step5.publish_hint">Publishing uses the current draft version from this wizard session.</div>
|
||||
<div style="display:flex; gap: 8px; flex-wrap: wrap;">
|
||||
<button id="wizard-publish-operation" class="btn-primary-sm" type="button" data-i18n="wizard.step5.publish_action">Publish operation</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-callout" style="margin-bottom: 24px;">
|
||||
<svg class="info-callout-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="8" cy="8" r="7"/>
|
||||
<path d="M8 11V8M8 5v-.5"/>
|
||||
</svg>
|
||||
<div class="info-callout-body">
|
||||
<div class="info-callout-title" data-i18n="wizard.step5.editable_title">Operation stays editable in this wizard</div>
|
||||
<div class="info-callout-text" data-i18n="wizard.step5.editable_body">Saving creates or updates the current draft in place. Use the live actions above to upload samples, test the draft, export or import YAML, and publish when the upstream contract is ready.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- /step-pane-5 -->
|
||||
Reference in New Issue
Block a user