mirror of
https://github.com/outbackdingo/optimclaw.git
synced 2026-08-27 08:00:17 +00:00
322 lines
14 KiB
HTML
322 lines
14 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
|
||
<title>IronClaw</title>
|
||
<link rel="icon" href="/favicon.ico" type="image/x-icon">
|
||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&family=IBM+Plex+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||
<link rel="stylesheet" href="/style.css">
|
||
<script
|
||
src="https://cdn.jsdelivr.net/npm/[email protected]/lib/marked.umd.min.js"
|
||
integrity="sha384-pN9zSKOnTZwXRtYZAu0PBPEgR2B7DOC1aeLxQ33oJ0oy5iN1we6gm57xldM2irDG"
|
||
crossorigin="anonymous"
|
||
></script>
|
||
</head>
|
||
<body>
|
||
<!-- Auth Screen -->
|
||
<div id="auth-screen">
|
||
<div class="auth-card-login">
|
||
<div class="auth-brand">
|
||
<h1>IronClaw</h1>
|
||
<p class="auth-tagline">Secure AI Assistant</p>
|
||
</div>
|
||
<div class="auth-form">
|
||
<label for="token-input">Gateway Token</label>
|
||
<input type="password" id="token-input" placeholder="Paste your auth token" autofocus>
|
||
<button onclick="authenticate()">Connect</button>
|
||
</div>
|
||
<div id="auth-error"></div>
|
||
<p class="auth-hint">Enter the GATEWAY_AUTH_TOKEN from your .env configuration.</p>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Restart Confirmation Modal -->
|
||
<div id="restart-confirm-modal" class="restart-modal" style="display: none;">
|
||
<div class="restart-modal-overlay" onclick="cancelRestart()"></div>
|
||
<div class="restart-modal-content">
|
||
<div class="restart-modal-header">
|
||
<h2>Restart IronClaw Instance</h2>
|
||
<button class="restart-modal-close" onclick="cancelRestart()" title="Close">×</button>
|
||
</div>
|
||
<div class="restart-modal-body">
|
||
<p class="restart-modal-description">
|
||
Are you sure you want to restart the IronClaw instance? This will gracefully restart the process.
|
||
</p>
|
||
<div class="restart-modal-warning">
|
||
<span class="restart-modal-warning-icon">⚠️</span>
|
||
<p>Any in-progress jobs may be interrupted. The restart will complete within a few seconds.</p>
|
||
</div>
|
||
</div>
|
||
<div class="restart-modal-footer">
|
||
<button class="restart-modal-btn cancel" onclick="cancelRestart()">Cancel</button>
|
||
<button class="restart-modal-btn confirm" onclick="confirmRestart()">Confirm Restart</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Restart Progress Modal -->
|
||
<div id="restart-loader" class="restart-loader" style="display: none;">
|
||
<div class="restart-loader-overlay"></div>
|
||
<div class="restart-loader-content">
|
||
<div class="restart-spinner"></div>
|
||
<div class="restart-loader-text">
|
||
<p class="restart-title">Restarting IronClaw</p>
|
||
<p class="restart-subtitle">Please wait while the process restarts...</p>
|
||
</div>
|
||
<div class="restart-progress-bar">
|
||
<div class="restart-progress-fill"></div>
|
||
</div>
|
||
<p class="restart-modal-info">
|
||
Check the Logs tab for details after the restart completes.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Main App (hidden until authenticated) -->
|
||
<div id="app">
|
||
<!-- Tab Bar -->
|
||
<div class="tab-bar">
|
||
<button class="active" data-tab="chat">Chat</button>
|
||
<button data-tab="memory">Memory</button>
|
||
<button data-tab="jobs">Jobs</button>
|
||
<button data-tab="routines">Routines</button>
|
||
<button data-tab="extensions">Extensions</button>
|
||
<button data-tab="skills">Skills</button>
|
||
<div class="spacer"></div>
|
||
<button class="status-logs-btn" data-tab="logs" title="Logs">Logs</button>
|
||
<div class="tee-shield" id="tee-shield" style="display:none" title="Running in a Trusted Execution Environment">
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>
|
||
</svg>
|
||
<span id="tee-shield-label">TEE Verified</span>
|
||
<div class="tee-popover" id="tee-popover"></div>
|
||
</div>
|
||
<div class="status" id="gateway-status-trigger">
|
||
<div class="dot" id="sse-dot"></div>
|
||
<span id="sse-status">Connected</span>
|
||
<div class="gateway-popover" id="gateway-popover"></div>
|
||
</div>
|
||
<button class="restart-btn" id="restart-btn" onclick="triggerRestart()" title="Gracefully restart the process">
|
||
<svg id="restart-icon" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<path d="M23 4v6h-6"></path>
|
||
<path d="M1 20v-6h6"></path>
|
||
<path d="M3.51 9a9 9 0 0114.85-3.36M20.49 15a9 9 0 01-14.85 3.36"></path>
|
||
</svg>
|
||
<span>Restart</span>
|
||
</button>
|
||
</div>
|
||
|
||
<!-- Chat Tab -->
|
||
<div class="tab-panel active" id="tab-chat">
|
||
<div class="thread-sidebar" id="thread-sidebar">
|
||
<div class="thread-sidebar-header">
|
||
<span>Threads</span>
|
||
<button class="thread-new-btn" onclick="createNewThread()" title="New thread (Ctrl/Cmd+N)">+</button>
|
||
<button class="thread-toggle-btn" id="thread-toggle-btn" onclick="toggleThreadSidebar()" title="Toggle sidebar">«</button>
|
||
</div>
|
||
<div class="assistant-item" id="assistant-thread" onclick="switchToAssistant()">
|
||
<span class="assistant-label">Assistant</span>
|
||
<span class="assistant-meta" id="assistant-meta"></span>
|
||
</div>
|
||
<div class="threads-section-header">
|
||
<span>Conversations</span>
|
||
</div>
|
||
<div class="thread-list" id="thread-list"></div>
|
||
</div>
|
||
<div class="chat-container">
|
||
<div class="chat-messages" id="chat-messages"></div>
|
||
<div id="slash-autocomplete" class="slash-autocomplete" style="display:none"></div>
|
||
<div class="image-preview-strip" id="image-preview-strip" style="display:none;"></div>
|
||
<div class="chat-input">
|
||
<input type="file" id="image-input" accept="image/*" multiple style="display:none">
|
||
<button id="attach-btn" class="attach-btn" title="Attach image" onclick="document.getElementById('image-input').click()">📎</button>
|
||
<textarea id="chat-input" placeholder="Message or / for commands..." rows="1"></textarea>
|
||
<button id="send-btn" onclick="sendMessage()">Send</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Memory Tab -->
|
||
<div class="tab-panel" id="tab-memory">
|
||
<div class="memory-container">
|
||
<div class="memory-sidebar">
|
||
<div class="search-box">
|
||
<input type="text" id="memory-search" placeholder="Search memory...">
|
||
</div>
|
||
<div class="memory-tree" id="memory-tree"></div>
|
||
</div>
|
||
<div class="memory-content">
|
||
<div class="memory-breadcrumb" id="memory-breadcrumb">
|
||
<span id="memory-breadcrumb-path">workspace /</span>
|
||
<button class="memory-edit-btn" id="memory-edit-btn" style="display:none" onclick="startMemoryEdit()">Edit</button>
|
||
</div>
|
||
<div class="memory-viewer" id="memory-viewer">
|
||
<div class="empty">Select a file to view its contents</div>
|
||
</div>
|
||
<div class="memory-editor" id="memory-editor" style="display:none">
|
||
<textarea id="memory-edit-textarea"></textarea>
|
||
<div class="memory-editor-actions">
|
||
<button class="btn-save" onclick="saveMemoryEdit()">Save</button>
|
||
<button class="btn-cancel-edit" onclick="cancelMemoryEdit()">Cancel</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Jobs Tab -->
|
||
<div class="tab-panel" id="tab-jobs">
|
||
<div class="jobs-container">
|
||
<div class="jobs-summary" id="jobs-summary"></div>
|
||
<table class="jobs-table" id="jobs-table">
|
||
<thead>
|
||
<tr>
|
||
<th>ID</th>
|
||
<th>Title</th>
|
||
<th>Source</th>
|
||
<th>Status</th>
|
||
<th>Created</th>
|
||
<th>Actions</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="jobs-tbody"></tbody>
|
||
</table>
|
||
<div class="empty-state" id="jobs-empty" style="display:none">No jobs found</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Logs Tab -->
|
||
<div class="tab-panel" id="tab-logs">
|
||
<div class="logs-container">
|
||
<div class="logs-toolbar">
|
||
<select id="logs-server-level" onchange="setServerLogLevel(this.value)" title="Server-side log level (changes what the server emits)">
|
||
<option value="error">Server: ERROR</option>
|
||
<option value="warn">Server: WARN</option>
|
||
<option value="info" selected>Server: INFO</option>
|
||
<option value="debug">Server: DEBUG</option>
|
||
</select>
|
||
<select id="logs-level-filter">
|
||
<option value="all">All Levels</option>
|
||
<option value="ERROR">Error</option>
|
||
<option value="WARN">Warn</option>
|
||
<option value="INFO">Info</option>
|
||
<option value="DEBUG">Debug</option>
|
||
</select>
|
||
<input type="text" id="logs-target-filter" placeholder="Filter by target...">
|
||
<label class="logs-checkbox"><input type="checkbox" id="logs-autoscroll" checked> Auto-scroll</label>
|
||
<button id="logs-pause-btn" onclick="toggleLogsPause()">Pause</button>
|
||
<button onclick="clearLogs()">Clear</button>
|
||
</div>
|
||
<div class="logs-output" id="logs-output"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Routines Tab -->
|
||
<div class="tab-panel" id="tab-routines">
|
||
<div class="routines-container">
|
||
<div class="routines-summary" id="routines-summary"></div>
|
||
<table class="routines-table" id="routines-table">
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Trigger</th>
|
||
<th>Action</th>
|
||
<th>Last Run</th>
|
||
<th>Next Run</th>
|
||
<th>Runs</th>
|
||
<th>Status</th>
|
||
<th>Actions</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="routines-tbody"></tbody>
|
||
</table>
|
||
<div class="empty-state" id="routines-empty" style="display:none">
|
||
No routines configured. Ask the assistant to create one.
|
||
</div>
|
||
<div class="routine-detail" id="routine-detail" style="display:none"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Extensions Tab -->
|
||
<div class="tab-panel" id="tab-extensions">
|
||
<div class="extensions-container">
|
||
<div class="extensions-section">
|
||
<h3>Installed Extensions</h3>
|
||
<div class="extensions-list" id="extensions-list">
|
||
<div class="empty-state">Loading extensions...</div>
|
||
</div>
|
||
</div>
|
||
<div class="extensions-section" id="available-wasm-section">
|
||
<h3>Available WASM Extensions</h3>
|
||
<div class="extensions-list" id="available-wasm-list">
|
||
<div class="empty-state">Loading...</div>
|
||
</div>
|
||
</div>
|
||
<div class="extensions-section">
|
||
<h3>Install WASM Extension</h3>
|
||
<div class="ext-install-form">
|
||
<input type="text" id="wasm-install-name" placeholder="Extension name">
|
||
<input type="text" id="wasm-install-url" placeholder="URL to .tar.gz bundle">
|
||
<button onclick="installWasmExtension()">Install</button>
|
||
</div>
|
||
</div>
|
||
<div class="extensions-section">
|
||
<h3>MCP Servers</h3>
|
||
<div class="extensions-list" id="mcp-servers-list">
|
||
<div class="empty-state">Loading...</div>
|
||
</div>
|
||
<h4>Add Custom MCP Server</h4>
|
||
<div class="ext-install-form">
|
||
<input type="text" id="mcp-install-name" placeholder="Server name">
|
||
<input type="text" id="mcp-install-url" placeholder="MCP server URL (https://...)">
|
||
<button onclick="addMcpServer()">Add</button>
|
||
</div>
|
||
</div>
|
||
<div class="extensions-section">
|
||
<h3>Registered Tools</h3>
|
||
<table class="tools-table" id="tools-table">
|
||
<thead><tr><th>Name</th><th>Description</th></tr></thead>
|
||
<tbody id="tools-tbody"></tbody>
|
||
</table>
|
||
<div class="empty-state" id="tools-empty" style="display:none">No tools registered</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Skills Tab -->
|
||
<div class="tab-panel" id="tab-skills">
|
||
<div class="extensions-container">
|
||
<div class="extensions-section">
|
||
<h3>Search ClawHub</h3>
|
||
<div class="skill-search-box">
|
||
<input type="text" id="skill-search-input" placeholder="Search for skills...">
|
||
<button onclick="searchClawHub()">Search</button>
|
||
</div>
|
||
<div class="extensions-list" id="skill-search-results"></div>
|
||
</div>
|
||
<div class="extensions-section">
|
||
<h3>Installed Skills</h3>
|
||
<div class="extensions-list" id="skills-list">
|
||
<div class="empty-state">Loading skills...</div>
|
||
</div>
|
||
</div>
|
||
<div class="extensions-section">
|
||
<h3>Install Skill by URL</h3>
|
||
<div class="ext-install-form">
|
||
<input type="text" id="skill-install-name" placeholder="Skill name or slug">
|
||
<input type="text" id="skill-install-url" placeholder="HTTPS URL to SKILL.md (optional)">
|
||
<button onclick="installSkillFromForm()">Install</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="toasts"></div>
|
||
<script src="/app.js"></script>
|
||
</body>
|
||
</html>
|