152 lines
6.4 KiB
HTML
152 lines
6.4 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}Dashboard - Hermes{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="grid-2">
|
|
<div>
|
|
<div class="card">
|
|
<h3>🔧 Worker Status</h3>
|
|
<div id="worker-status">
|
|
{% if worker_status.current_task %}
|
|
<p><strong>Current Task:</strong> #{{ worker_status.current_task.id }} - {{ worker_status.current_task.title[:60] }}</p>
|
|
<p><strong>Worker PID:</strong> {{ worker_status.current_task.worker_pid or 'N/A' }}</p>
|
|
{% if worker_status.current_task.started_at %}
|
|
<p><strong>Elapsed:</strong> <span class="elapsed-time" data-started="{{ worker_status.current_task.started_at }}"></span></p>
|
|
{% endif %}
|
|
{% else %}
|
|
<p class="text-muted">No task currently running</p>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<h3>📜 SOUL.md Excerpt</h3>
|
|
<pre>{{ worker_status.soul_excerpt[:500] }}</pre>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<div class="card">
|
|
<h3>⚙️ Running Processes</h3>
|
|
<div id="process-list">
|
|
{% if processes %}
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>PID</th>
|
|
<th>Elapsed</th>
|
|
<th>Command</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for proc in processes %}
|
|
<tr>
|
|
<td>{{ proc.pid }}</td>
|
|
<td><span class="elapsed-time" data-started="{{ proc.create_time }}"></span></td>
|
|
<td class="text-small">{{ proc.cmdline }}</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
{% else %}
|
|
<p class="text-muted">No opencode workers running</p>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<h2>📋 Kanban Board</h2>
|
|
<div class="kanban-board" id="kanban-board">
|
|
{% for status in ['ready', 'running', 'done', 'blocked', 'failed'] %}
|
|
<div class="kanban-column">
|
|
<h3>{{ status|upper }} ({{ board.get(status, [])|length }})</h3>
|
|
{% for task in board.get(status, []) %}
|
|
<a href="/tasks/{{ task.id }}" style="text-decoration: none;">
|
|
<div class="kanban-card">
|
|
<div class="kanban-card-title">#{{ task.id }} {{ task.title[:60] }}</div>
|
|
<div class="kanban-card-meta">
|
|
{% if task.status == 'running' and task.started_at %}
|
|
<span class="elapsed-time" data-started="{{ task.started_at }}"></span>
|
|
{% elif task.status == 'done' and task.completed_at %}
|
|
{{ task.completed_at|rel }}
|
|
{% endif %}
|
|
{% if task.assignee %}
|
|
<br>{{ task.assignee }}
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</a>
|
|
{% endfor %}
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
|
|
<h2 class="mt-20">📤 派活 Dispatch</h2>
|
|
<div class="card">
|
|
<form id="dispatch-form">
|
|
<div style="margin-bottom:10px;">
|
|
<label for="dispatch-template">Template:</label>
|
|
<select id="dispatch-template" style="width:100%;padding:6px;background:#1a1a1a;color:#e0e0e0;border:1px solid #3a3a3a;border-radius:3px;">
|
|
<option value="">-- choose a template (optional) --</option>
|
|
</select>
|
|
</div>
|
|
<div style="margin-bottom:10px;">
|
|
<label for="dispatch-title">Title (required):</label>
|
|
<input type="text" id="dispatch-title" name="title" required maxlength="200"
|
|
style="width:100%;padding:6px;background:#1a1a1a;color:#e0e0e0;border:1px solid #3a3a3a;border-radius:3px;"
|
|
placeholder="e.g. Fix CSRF in blog-app">
|
|
</div>
|
|
<div style="margin-bottom:10px;">
|
|
<label for="dispatch-body">Body (markdown):</label>
|
|
<textarea id="dispatch-body" name="body" rows="6"
|
|
style="width:100%;padding:6px;background:#1a1a1a;color:#e0e0e0;border:1px solid #3a3a3a;border-radius:3px;font-family:inherit;"
|
|
placeholder="## 背景 ... ## 任务 ... ## 验收 - [ ] ..."></textarea>
|
|
</div>
|
|
<div class="grid-2" style="margin-bottom:10px;">
|
|
<div>
|
|
<label for="dispatch-assignee">Assignee:</label>
|
|
<input type="text" id="dispatch-assignee" name="assignee" value="omo-pm" maxlength="64"
|
|
style="width:100%;padding:6px;background:#1a1a1a;color:#e0e0e0;border:1px solid #3a3a3a;border-radius:3px;">
|
|
</div>
|
|
<div>
|
|
<label for="dispatch-max-runtime">Max runtime (seconds, 60-7200):</label>
|
|
<input type="number" id="dispatch-max-runtime" name="max_runtime" value="1800" min="60" max="7200"
|
|
style="width:100%;padding:6px;background:#1a1a1a;color:#e0e0e0;border:1px solid #3a3a3a;border-radius:3px;">
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<button type="submit" class="btn btn-primary">🚀 Dispatch</button>
|
|
<span id="dispatch-result" class="text-small" style="margin-left:10px;"></span>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
<h2 class="mt-20">📦 Project Status</h2>
|
|
<div class="card">
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Name</th>
|
|
<th>Branch</th>
|
|
<th>Last Commit</th>
|
|
<th>Time</th>
|
|
<th>Dirty</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for proj in projects %}
|
|
<tr>
|
|
<td><a href="/projects/{{ proj.name }}">{{ proj.name }}</a></td>
|
|
<td>{{ proj.branch or 'N/A' }}</td>
|
|
<td class="text-small">{{ proj.commit_short }} {{ proj.commit_msg[:40] }}</td>
|
|
<td>{% if proj.commit_time %}{{ proj.commit_time|int|rel }}{% else %}N/A{% endif %}</td>
|
|
<td>{{ proj.dirty_count }}</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{% endblock %}
|