fix: convert antfarm from broken submodule to regular directory
Fixes Gitea 500 error caused by invalid submodule reference. Converted antfarm from pseudo-submodule (missing .gitmodules) to regular directory with all source files. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
48
antfarm/landing/__tests__/landing.test.js
Normal file
48
antfarm/landing/__tests__/landing.test.js
Normal file
@@ -0,0 +1,48 @@
|
||||
import { readFileSync, existsSync } from 'fs';
|
||||
import { resolve, dirname } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { describe, it } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const landingDir = resolve(__dirname, '..');
|
||||
|
||||
describe('Landing page', () => {
|
||||
it('index.html exists', () => {
|
||||
assert.ok(existsSync(resolve(landingDir, 'index.html')));
|
||||
});
|
||||
|
||||
it('style.css exists', () => {
|
||||
assert.ok(existsSync(resolve(landingDir, 'style.css')));
|
||||
});
|
||||
|
||||
it('index.html contains required sections', () => {
|
||||
const html = readFileSync(resolve(landingDir, 'index.html'), 'utf-8');
|
||||
assert.ok(html.includes('id="features"'), 'missing features section');
|
||||
assert.ok(html.includes('id="quickstart"'), 'missing quickstart section');
|
||||
assert.ok(html.includes('id="commands"'), 'missing commands section');
|
||||
assert.ok(html.includes('<title>'), 'missing title');
|
||||
assert.ok(html.includes('meta name="viewport"'), 'missing viewport meta');
|
||||
assert.ok(html.includes('meta name="description"'), 'missing description meta');
|
||||
});
|
||||
|
||||
it('index.html references style.css', () => {
|
||||
const html = readFileSync(resolve(landingDir, 'index.html'), 'utf-8');
|
||||
assert.ok(html.includes('style.css'));
|
||||
});
|
||||
|
||||
it('style.css contains essential rules', () => {
|
||||
const css = readFileSync(resolve(landingDir, 'style.css'), 'utf-8');
|
||||
assert.ok(css.includes('.hero'), 'missing hero styles');
|
||||
assert.ok(css.includes('.feature-grid'), 'missing feature grid');
|
||||
assert.ok(css.includes('@media'), 'missing responsive styles');
|
||||
});
|
||||
|
||||
it('all internal links have valid targets', () => {
|
||||
const html = readFileSync(resolve(landingDir, 'index.html'), 'utf-8');
|
||||
const anchors = [...html.matchAll(/href="#([^"]+)"/g)].map(m => m[1]);
|
||||
for (const id of anchors) {
|
||||
assert.ok(html.includes(`id="${id}"`), `missing target for #${id}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
BIN
antfarm/landing/dashboard-detail-screenshot.png
Normal file
BIN
antfarm/landing/dashboard-detail-screenshot.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.0 MiB |
278
antfarm/landing/dashboard-mockup-detail.html
Normal file
278
antfarm/landing/dashboard-mockup-detail.html
Normal file
@@ -0,0 +1,278 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Antfarm Dashboard</title>
|
||||
<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=Inter:wght@400;500;600;700&family=Geist+Mono&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
*{margin:0;padding:0;box-sizing:border-box}
|
||||
body{font-family:'Inter',-apple-system,BlinkMacSystemFont,sans-serif;background:#FAF8F5;color:#3A3226;min-height:100vh}
|
||||
header{background:#6B7F3B;border-bottom:2px solid #5a6b32;padding:12px 24px;display:flex;align-items:center;gap:16px;flex-wrap:wrap}
|
||||
header h1{font-family:'Inter',sans-serif;font-size:22px;font-weight:600;color:#fff;letter-spacing:0}
|
||||
header h1 span{color:#D4E8A0}
|
||||
select{background:#5a6b32;color:#fff;border:1px solid #4a5a28;border-radius:6px;padding:6px 12px;font-size:14px;cursor:pointer}
|
||||
select:focus{outline:none;border-color:#8ECFC0}
|
||||
.board{display:flex;gap:16px;padding:24px;overflow-x:auto;min-height:calc(100vh - 65px)}
|
||||
.column{min-width:220px;flex:1;background:#fff;border:none;border-radius:8px;display:flex;flex-direction:column;box-shadow:0 2px 8px rgba(58,50,38,.1)}
|
||||
.column-header{padding:12px 16px;border-bottom:1px solid #eee;font-size:13px;font-weight:600;text-transform:uppercase;letter-spacing:.5px;color:#6B7F3B;background:#f5f0e8;border-radius:8px 8px 0 0}
|
||||
.column-header .count{background:#6B7F3B;color:#fff;border-radius:10px;padding:1px 8px;font-size:11px;margin-left:8px}
|
||||
.cards{padding:8px;flex:1;display:flex;flex-direction:column;gap:8px;overflow-y:auto}
|
||||
.card{background:#FAF8F5;border:1px solid #D4C4A0;border-radius:6px;padding:12px;cursor:pointer;transition:border-color .15s,box-shadow .15s}
|
||||
.card:hover{border-color:#E8845C;box-shadow:0 2px 8px rgba(232,132,92,.15)}
|
||||
.card.done{border-left:3px solid #6B7F3B}
|
||||
.card.failed{border-left:3px solid #E8845C}
|
||||
.card-title{font-size:13px;font-weight:500;color:#3A3226;margin-bottom:6px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
||||
.card-meta{font-size:11px;color:#8b8072;display:flex;justify-content:space-between;align-items:center}
|
||||
.badge{font-size:10px;font-weight:600;padding:2px 6px;border-radius:4px;text-transform:uppercase}
|
||||
.badge-running{background:#8ECFC033;color:#3a9e8a}
|
||||
.badge-done{background:#6B7F3B22;color:#6B7F3B}
|
||||
.badge-failed{background:#E8845C22;color:#d4603a}
|
||||
.badge-pending{background:#D4C4A044;color:#8b8072}
|
||||
.empty{color:#8b8072;font-size:12px;text-align:center;padding:24px 8px}
|
||||
.refresh-note{color:rgba(255,255,255,.6);font-size:11px;margin-left:auto}
|
||||
|
||||
.overlay{position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(58,50,38,.5);z-index:100;display:flex;align-items:center;justify-content:center;opacity:1;pointer-events:auto}
|
||||
.panel{background:#fff;border:1px solid #D4C4A0;border-radius:12px;width:90%;max-width:640px;max-height:85vh;overflow-y:auto;padding:24px;position:relative;box-shadow:0 8px 32px rgba(58,50,38,.15)}
|
||||
.panel-close{position:absolute;top:12px;right:16px;background:none;border:none;color:#8b8072;font-size:20px;cursor:pointer;padding:4px 8px;border-radius:4px}
|
||||
.panel-close:hover{color:#3A3226;background:#f5f0e8}
|
||||
.panel h2{font-size:16px;font-weight:600;color:#3A3226;margin-bottom:4px;padding-right:40px}
|
||||
.panel-task{font-size:13px;color:#8b8072;margin-bottom:16px;line-height:1.5}
|
||||
.panel-meta{display:flex;gap:12px;margin-bottom:20px;font-size:12px;color:#8b8072;flex-wrap:wrap}
|
||||
.panel-meta span{display:flex;align-items:center;gap:4px}
|
||||
.steps-list{display:flex;flex-direction:column;gap:8px;margin-bottom:24px}
|
||||
.step-row{display:flex;align-items:center;gap:12px;padding:10px 12px;background:#FAF8F5;border:1px solid #D4C4A0;border-radius:6px}
|
||||
.step-icon{width:24px;height:24px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:12px;flex-shrink:0}
|
||||
.step-icon.done{background:#6B7F3B22;color:#6B7F3B}
|
||||
.step-icon.running{background:#8ECFC033;color:#3a9e8a}
|
||||
.step-icon.pending{background:#D4C4A044;color:#8b8072}
|
||||
.step-name{font-size:13px;font-weight:500;color:#3A3226;flex:1}
|
||||
.step-agent{font-size:11px;color:#8b8072;font-family:'Geist Mono',monospace}
|
||||
.step-status{font-size:11px;text-transform:uppercase;font-weight:600}
|
||||
|
||||
.stories-section{border-top:1px solid #D4C4A0;padding-top:20px}
|
||||
.stories-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:12px}
|
||||
.stories-header h3{font-size:15px;font-weight:600;color:#3A3226}
|
||||
.stories-header span{font-size:13px;font-weight:600;color:#6B7F3B}
|
||||
.progress-bar{background:#D4C4A044;border-radius:4px;height:8px;margin-bottom:16px;overflow:hidden}
|
||||
.progress-fill{background:#6B7F3B;height:100%;border-radius:4px}
|
||||
.story-row{display:flex;align-items:center;gap:8px;padding:10px 12px;background:#FAF8F5;border:1px solid #D4C4A0;border-radius:6px;margin-bottom:6px}
|
||||
.story-name{font-size:13px;font-weight:500;color:#3A3226;flex:1}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1><span>antfarm</span> dashboard</h1>
|
||||
<select><option>feature-dev</option></select>
|
||||
<span class="refresh-note">Auto-refresh: 30s</span>
|
||||
</header>
|
||||
<div class="board">
|
||||
|
||||
<div class="column">
|
||||
<div class="column-header">plan<span class="count">3</span></div>
|
||||
<div class="cards">
|
||||
<div class="card done"><div class="card-title">Add user authentication with OAuth</div><div class="card-meta"><span class="badge badge-done">done</span><span>Feb 9, 10:02 AM</span></div></div>
|
||||
<div class="card done"><div class="card-title">Refactor payment processing pipeline</div><div class="card-meta"><span class="badge badge-done">done</span><span>Feb 9, 9:45 AM</span></div></div>
|
||||
<div class="card done"><div class="card-title">Add webhook retry with exponential backoff</div><div class="card-meta"><span class="badge badge-done">done</span><span>Feb 9, 8:30 AM</span></div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="column">
|
||||
<div class="column-header">setup<span class="count">2</span></div>
|
||||
<div class="cards">
|
||||
<div class="card done"><div class="card-title">Add user authentication with OAuth</div><div class="card-meta"><span class="badge badge-done">done</span><span>Feb 9, 10:14 AM</span></div></div>
|
||||
<div class="card done"><div class="card-title">Refactor payment processing pipeline</div><div class="card-meta"><span class="badge badge-done">done</span><span>Feb 9, 9:58 AM</span></div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="column">
|
||||
<div class="column-header">implement<span class="count">3</span></div>
|
||||
<div class="cards">
|
||||
<div class="card"><div class="card-title">Add user authentication with OAuth</div><div class="card-meta"><span class="badge badge-running">running</span><span>Stories: 5/7</span></div></div>
|
||||
<div class="card"><div class="card-title">Refactor payment processing pipeline</div><div class="card-meta"><span class="badge badge-running">running</span><span>Stories: 2/5</span></div></div>
|
||||
<div class="card"><div class="card-title">Add webhook retry with exponential backoff</div><div class="card-meta"><span class="badge badge-pending">pending</span><span>Feb 9, 8:31 AM</span></div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="column">
|
||||
<div class="column-header">verify<span class="count">2</span></div>
|
||||
<div class="cards">
|
||||
<div class="card"><div class="card-title">Migrate database to connection pooling</div><div class="card-meta"><span class="badge badge-running">running</span><span>Feb 9, 9:22 AM</span></div></div>
|
||||
<div class="card"><div class="card-title">Add rate limiting to public API endpoints</div><div class="card-meta"><span class="badge badge-pending">pending</span><span>Feb 9, 9:10 AM</span></div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="column">
|
||||
<div class="column-header">test<span class="count">2</span></div>
|
||||
<div class="cards">
|
||||
<div class="card"><div class="card-title">Add real-time notifications via WebSockets</div><div class="card-meta"><span class="badge badge-running">running</span><span>Feb 9, 8:55 AM</span></div></div>
|
||||
<div class="card failed"><div class="card-title">Implement team invitation flow</div><div class="card-meta"><span class="badge badge-failed">failed</span><span>Retry 2/3</span></div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="column">
|
||||
<div class="column-header">PR<span class="count">2</span></div>
|
||||
<div class="cards">
|
||||
<div class="card"><div class="card-title">Add CSV export for billing reports</div><div class="card-meta"><span class="badge badge-running">running</span><span>Feb 9, 8:40 AM</span></div></div>
|
||||
<div class="card done"><div class="card-title">Add audit logging for admin actions</div><div class="card-meta"><span class="badge badge-done">done</span><span>Feb 9, 7:15 AM</span></div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="column">
|
||||
<div class="column-header">review<span class="count">3</span></div>
|
||||
<div class="cards">
|
||||
<div class="card"><div class="card-title">Add RBAC with role hierarchy</div><div class="card-meta"><span class="badge badge-running">running</span><span>Feb 9, 7:50 AM</span></div></div>
|
||||
<div class="card done"><div class="card-title">Implement SSO with SAML 2.0</div><div class="card-meta"><span class="badge badge-done">done</span><span>Feb 9, 6:30 AM</span></div></div>
|
||||
<div class="card done"><div class="card-title">Add dark mode support</div><div class="card-meta"><span class="badge badge-done">done</span><span>Feb 8, 11:45 PM</span></div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Detail panel overlay -->
|
||||
<div class="overlay">
|
||||
<div class="panel">
|
||||
<button class="panel-close">✕</button>
|
||||
<h2>feature-dev</h2>
|
||||
<div class="panel-task">Add user authentication with OAuth</div>
|
||||
<div class="panel-meta">
|
||||
<span><span class="badge badge-running">running</span></span>
|
||||
<span>Created: Feb 9, 9:58 AM</span>
|
||||
<span>Updated: Feb 9, 10:41 AM</span>
|
||||
</div>
|
||||
|
||||
<div class="steps-list">
|
||||
<div class="step-row">
|
||||
<div class="step-icon done">✓</div>
|
||||
<div class="step-name">plan</div>
|
||||
<div class="step-agent">planner</div>
|
||||
<div class="step-status"><span class="badge badge-done">done</span></div>
|
||||
</div>
|
||||
<div class="step-row">
|
||||
<div class="step-icon done">✓</div>
|
||||
<div class="step-name">setup</div>
|
||||
<div class="step-agent">setup</div>
|
||||
<div class="step-status"><span class="badge badge-done">done</span></div>
|
||||
</div>
|
||||
<div class="step-row">
|
||||
<div class="step-icon running">●</div>
|
||||
<div class="step-name">implement</div>
|
||||
<div class="step-agent">developer</div>
|
||||
<div class="step-status"><span class="badge badge-running">running</span></div>
|
||||
</div>
|
||||
<div class="step-row">
|
||||
<div class="step-icon pending">○</div>
|
||||
<div class="step-name">verify</div>
|
||||
<div class="step-agent">verifier</div>
|
||||
<div class="step-status"><span class="badge badge-pending">pending</span></div>
|
||||
</div>
|
||||
<div class="step-row">
|
||||
<div class="step-icon pending">○</div>
|
||||
<div class="step-name">test</div>
|
||||
<div class="step-agent">tester</div>
|
||||
<div class="step-status"><span class="badge badge-pending">pending</span></div>
|
||||
</div>
|
||||
<div class="step-row">
|
||||
<div class="step-icon pending">○</div>
|
||||
<div class="step-name">pr</div>
|
||||
<div class="step-agent">developer</div>
|
||||
<div class="step-status"><span class="badge badge-pending">pending</span></div>
|
||||
</div>
|
||||
<div class="step-row">
|
||||
<div class="step-icon pending">○</div>
|
||||
<div class="step-name">review</div>
|
||||
<div class="step-agent">reviewer</div>
|
||||
<div class="step-status"><span class="badge badge-pending">pending</span></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stories-section">
|
||||
<div class="stories-header">
|
||||
<h3>Stories</h3>
|
||||
<span>6 / 12 done</span>
|
||||
</div>
|
||||
<div class="progress-bar"><div class="progress-fill" style="width:50%"></div></div>
|
||||
|
||||
<div class="story-row">
|
||||
<div class="step-icon done">✓</div>
|
||||
<div class="story-name">S-1: Create OAuth provider configuration module</div>
|
||||
<div class="step-status"><span class="badge badge-done">done</span></div>
|
||||
</div>
|
||||
<div class="story-row">
|
||||
<div class="step-icon done">✓</div>
|
||||
<div class="story-name">S-2: Implement Google OAuth callback handler</div>
|
||||
<div class="step-status"><span class="badge badge-done">done</span></div>
|
||||
</div>
|
||||
<div class="story-row">
|
||||
<div class="step-icon done">✓</div>
|
||||
<div class="story-name">S-3: Implement GitHub OAuth callback handler</div>
|
||||
<div class="step-status"><span class="badge badge-done">done</span></div>
|
||||
</div>
|
||||
<div class="story-row" style="flex-direction:column;align-items:stretch;gap:0;padding:0;overflow:hidden">
|
||||
<div style="display:flex;align-items:center;gap:8px;padding:10px 12px;cursor:pointer">
|
||||
<div class="step-icon done">✓</div>
|
||||
<div class="story-name" style="flex:1">S-4: Add JWT token generation and validation</div>
|
||||
<div class="step-status"><span class="badge badge-done">done</span></div>
|
||||
<span style="color:#8b8072;font-size:10px;display:inline-block;transform:rotate(90deg)">▶</span>
|
||||
</div>
|
||||
<div style="padding:0 12px 12px 44px;font-size:12px;color:#5a5045;line-height:1.6">
|
||||
<div style="margin-bottom:8px">Generate signed JWT tokens on login, validate on every protected request. Support configurable expiry and issuer claims.</div>
|
||||
<div style="margin-bottom:8px">
|
||||
<div style="font-weight:600;font-size:11px;text-transform:uppercase;letter-spacing:.3px;color:#6B7F3B;margin-bottom:4px">Acceptance Criteria</div>
|
||||
<label style="display:flex;align-items:flex-start;gap:6px;margin-bottom:3px;cursor:default"><span style="color:#6B7F3B;flex-shrink:0">☑</span><span>JWT signed with RS256 using configurable secret</span></label>
|
||||
<label style="display:flex;align-items:flex-start;gap:6px;margin-bottom:3px;cursor:default"><span style="color:#6B7F3B;flex-shrink:0">☑</span><span>Token includes user ID, email, roles, exp, iat, iss claims</span></label>
|
||||
<label style="display:flex;align-items:flex-start;gap:6px;margin-bottom:3px;cursor:default"><span style="color:#6B7F3B;flex-shrink:0">☑</span><span>Validation middleware rejects expired and malformed tokens</span></label>
|
||||
<label style="display:flex;align-items:flex-start;gap:6px;margin-bottom:3px;cursor:default"><span style="color:#6B7F3B;flex-shrink:0">☑</span><span>Refresh token rotation implemented with reuse detection</span></label>
|
||||
<label style="display:flex;align-items:flex-start;gap:6px;margin-bottom:3px;cursor:default"><span style="color:#6B7F3B;flex-shrink:0">☑</span><span>Unit tests cover generation, validation, expiry, and invalid signatures</span></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="story-row">
|
||||
<div class="step-icon done">✓</div>
|
||||
<div class="story-name">S-5: Create session middleware with refresh tokens</div>
|
||||
<div class="step-status"><span class="badge badge-done">done</span></div>
|
||||
</div>
|
||||
<div class="story-row">
|
||||
<div class="step-icon done">✓</div>
|
||||
<div class="story-name">S-6: Build user profile merge for linked accounts</div>
|
||||
<div class="step-status"><span class="badge badge-done">done</span></div>
|
||||
</div>
|
||||
<div class="story-row">
|
||||
<div class="step-icon running">●</div>
|
||||
<div class="story-name">S-7: Add CSRF protection to auth endpoints</div>
|
||||
<div class="step-status"><span class="badge badge-running">running</span></div>
|
||||
</div>
|
||||
<div class="story-row">
|
||||
<div class="step-icon pending">○</div>
|
||||
<div class="story-name">S-8: Implement account lockout after failed attempts</div>
|
||||
<div class="step-status"><span class="badge badge-pending">pending</span></div>
|
||||
</div>
|
||||
<div class="story-row">
|
||||
<div class="step-icon pending">○</div>
|
||||
<div class="story-name">S-9: Add OAuth scope permission UI</div>
|
||||
<div class="step-status"><span class="badge badge-pending">pending</span></div>
|
||||
</div>
|
||||
<div class="story-row">
|
||||
<div class="step-icon pending">○</div>
|
||||
<div class="story-name">S-10: Create auth error handling and user feedback</div>
|
||||
<div class="step-status"><span class="badge badge-pending">pending</span></div>
|
||||
</div>
|
||||
<div class="story-row">
|
||||
<div class="step-icon pending">○</div>
|
||||
<div class="story-name">S-11: Add logout with token revocation</div>
|
||||
<div class="step-status"><span class="badge badge-pending">pending</span></div>
|
||||
</div>
|
||||
<div class="story-row">
|
||||
<div class="step-icon pending">○</div>
|
||||
<div class="story-name">S-12: Write auth integration tests</div>
|
||||
<div class="step-status"><span class="badge badge-pending">pending</span></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
165
antfarm/landing/dashboard-mockup.html
Normal file
165
antfarm/landing/dashboard-mockup.html
Normal file
@@ -0,0 +1,165 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Antfarm Dashboard</title>
|
||||
<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=Inter:wght@400;500;600;700&family=Geist+Mono&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
*{margin:0;padding:0;box-sizing:border-box}
|
||||
body{font-family:'Inter',-apple-system,BlinkMacSystemFont,sans-serif;background:#FAF8F5;color:#3A3226;min-height:100vh}
|
||||
header{background:#6B7F3B;border-bottom:2px solid #5a6b32;padding:12px 24px;display:flex;align-items:center;gap:16px;flex-wrap:wrap}
|
||||
header h1{font-family:'Inter',sans-serif;font-size:22px;font-weight:600;color:#fff;letter-spacing:0}
|
||||
header h1 span{color:#D4E8A0}
|
||||
select{background:#5a6b32;color:#fff;border:1px solid #4a5a28;border-radius:6px;padding:6px 12px;font-size:14px;cursor:pointer}
|
||||
select:focus{outline:none;border-color:#8ECFC0}
|
||||
.board{display:flex;gap:16px;padding:24px;overflow-x:auto;min-height:calc(100vh - 65px)}
|
||||
.column{min-width:220px;flex:1;background:#fff;border:none;border-radius:8px;display:flex;flex-direction:column;box-shadow:0 2px 8px rgba(58,50,38,.1)}
|
||||
.column-header{padding:12px 16px;border-bottom:1px solid #eee;font-size:13px;font-weight:600;text-transform:uppercase;letter-spacing:.5px;color:#6B7F3B;background:#f5f0e8;border-radius:8px 8px 0 0}
|
||||
.column-header .count{background:#6B7F3B;color:#fff;border-radius:10px;padding:1px 8px;font-size:11px;margin-left:8px}
|
||||
.cards{padding:8px;flex:1;display:flex;flex-direction:column;gap:8px;overflow-y:auto}
|
||||
.card{background:#FAF8F5;border:1px solid #D4C4A0;border-radius:6px;padding:12px;cursor:pointer;transition:border-color .15s,box-shadow .15s}
|
||||
.card:hover{border-color:#E8845C;box-shadow:0 2px 8px rgba(232,132,92,.15)}
|
||||
.card.done{border-left:3px solid #6B7F3B}
|
||||
.card.failed{border-left:3px solid #E8845C}
|
||||
.card-title{font-size:13px;font-weight:500;color:#3A3226;margin-bottom:6px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
||||
.card-meta{font-size:11px;color:#8b8072;display:flex;justify-content:space-between;align-items:center}
|
||||
.badge{font-size:10px;font-weight:600;padding:2px 6px;border-radius:4px;text-transform:uppercase}
|
||||
.badge-running{background:#8ECFC033;color:#3a9e8a}
|
||||
.badge-done{background:#6B7F3B22;color:#6B7F3B}
|
||||
.badge-failed{background:#E8845C22;color:#d4603a}
|
||||
.badge-pending{background:#D4C4A044;color:#8b8072}
|
||||
.empty{color:#8b8072;font-size:12px;text-align:center;padding:24px 8px}
|
||||
.refresh-note{color:rgba(255,255,255,.6);font-size:11px;margin-left:auto}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1><span>antfarm</span> dashboard</h1>
|
||||
<select><option>feature-dev</option></select>
|
||||
<span class="refresh-note">Auto-refresh: 30s</span>
|
||||
</header>
|
||||
<div class="board">
|
||||
|
||||
<!-- plan column -->
|
||||
<div class="column">
|
||||
<div class="column-header">plan<span class="count">3</span></div>
|
||||
<div class="cards">
|
||||
<div class="card done">
|
||||
<div class="card-title">Add user authentication with OAuth</div>
|
||||
<div class="card-meta"><span class="badge badge-done">done</span><span>Feb 9, 10:02 AM</span></div>
|
||||
</div>
|
||||
<div class="card done">
|
||||
<div class="card-title">Refactor payment processing pipeline</div>
|
||||
<div class="card-meta"><span class="badge badge-done">done</span><span>Feb 9, 9:45 AM</span></div>
|
||||
</div>
|
||||
<div class="card done">
|
||||
<div class="card-title">Add webhook retry with exponential backoff</div>
|
||||
<div class="card-meta"><span class="badge badge-done">done</span><span>Feb 9, 8:30 AM</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- setup column -->
|
||||
<div class="column">
|
||||
<div class="column-header">setup<span class="count">2</span></div>
|
||||
<div class="cards">
|
||||
<div class="card done">
|
||||
<div class="card-title">Add user authentication with OAuth</div>
|
||||
<div class="card-meta"><span class="badge badge-done">done</span><span>Feb 9, 10:14 AM</span></div>
|
||||
</div>
|
||||
<div class="card done">
|
||||
<div class="card-title">Refactor payment processing pipeline</div>
|
||||
<div class="card-meta"><span class="badge badge-done">done</span><span>Feb 9, 9:58 AM</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- implement column -->
|
||||
<div class="column">
|
||||
<div class="column-header">implement<span class="count">3</span></div>
|
||||
<div class="cards">
|
||||
<div class="card">
|
||||
<div class="card-title">Add user authentication with OAuth</div>
|
||||
<div class="card-meta"><span class="badge badge-running">running</span><span>Stories: 5/7</span></div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-title">Refactor payment processing pipeline</div>
|
||||
<div class="card-meta"><span class="badge badge-running">running</span><span>Stories: 2/5</span></div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-title">Add webhook retry with exponential backoff</div>
|
||||
<div class="card-meta"><span class="badge badge-pending">pending</span><span>Feb 9, 8:31 AM</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- verify column -->
|
||||
<div class="column">
|
||||
<div class="column-header">verify<span class="count">2</span></div>
|
||||
<div class="cards">
|
||||
<div class="card">
|
||||
<div class="card-title">Migrate database to connection pooling</div>
|
||||
<div class="card-meta"><span class="badge badge-running">running</span><span>Feb 9, 9:22 AM</span></div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-title">Add rate limiting to public API endpoints</div>
|
||||
<div class="card-meta"><span class="badge badge-pending">pending</span><span>Feb 9, 9:10 AM</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- test column -->
|
||||
<div class="column">
|
||||
<div class="column-header">test<span class="count">2</span></div>
|
||||
<div class="cards">
|
||||
<div class="card">
|
||||
<div class="card-title">Add real-time notifications via WebSockets</div>
|
||||
<div class="card-meta"><span class="badge badge-running">running</span><span>Feb 9, 8:55 AM</span></div>
|
||||
</div>
|
||||
<div class="card failed">
|
||||
<div class="card-title">Implement team invitation flow</div>
|
||||
<div class="card-meta"><span class="badge badge-failed">failed</span><span>Retry 2/3</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- PR column -->
|
||||
<div class="column">
|
||||
<div class="column-header">PR<span class="count">2</span></div>
|
||||
<div class="cards">
|
||||
<div class="card">
|
||||
<div class="card-title">Add CSV export for billing reports</div>
|
||||
<div class="card-meta"><span class="badge badge-running">running</span><span>Feb 9, 8:40 AM</span></div>
|
||||
</div>
|
||||
<div class="card done">
|
||||
<div class="card-title">Add audit logging for admin actions</div>
|
||||
<div class="card-meta"><span class="badge badge-done">done</span><span>Feb 9, 7:15 AM</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- review column -->
|
||||
<div class="column">
|
||||
<div class="column-header">review<span class="count">3</span></div>
|
||||
<div class="cards">
|
||||
<div class="card">
|
||||
<div class="card-title">Add RBAC with role hierarchy</div>
|
||||
<div class="card-meta"><span class="badge badge-running">running</span><span>Feb 9, 7:50 AM</span></div>
|
||||
</div>
|
||||
<div class="card done">
|
||||
<div class="card-title">Implement SSO with SAML 2.0</div>
|
||||
<div class="card-meta"><span class="badge badge-done">done</span><span>Feb 9, 6:30 AM</span></div>
|
||||
</div>
|
||||
<div class="card done">
|
||||
<div class="card-title">Add dark mode support</div>
|
||||
<div class="card-meta"><span class="badge badge-done">done</span><span>Feb 8, 11:45 PM</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
BIN
antfarm/landing/dashboard-screenshot.png
Normal file
BIN
antfarm/landing/dashboard-screenshot.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.5 MiB |
308
antfarm/landing/index.html
Normal file
308
antfarm/landing/index.html
Normal file
@@ -0,0 +1,308 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Antfarm — Build your agent team with one command</title>
|
||||
<meta name="description" content="Build your agent team in OpenClaw with one command. Deterministic multi-agent workflows defined in YAML. Zero infrastructure.">
|
||||
|
||||
<!-- Open Graph / Twitter Card -->
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:url" content="https://antfarm.cool">
|
||||
<meta property="og:title" content="Antfarm — Build your agent team with one command">
|
||||
<meta property="og:description" content="Multi-agent workflows for OpenClaw. Define a team of specialized AI agents in YAML. One install. Zero infrastructure.">
|
||||
<meta property="og:image" content="https://antfarm.cool/og-image.png">
|
||||
<meta property="og:image:width" content="1200">
|
||||
<meta property="og:image:height" content="630">
|
||||
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:title" content="Antfarm — Build your agent team with one command">
|
||||
<meta name="twitter:description" content="Multi-agent workflows for OpenClaw. Define a team of specialized AI agents in YAML. One install. Zero infrastructure.">
|
||||
<meta name="twitter:image" content="https://antfarm.cool/og-image.png">
|
||||
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="topbar">
|
||||
<div class="topbar-inner">
|
||||
<a href="#" class="topbar-brand">
|
||||
<span class="topbar-name">Antfarm</span>
|
||||
</a>
|
||||
<div class="topbar-links">
|
||||
<a href="#workflows">Workflows</a>
|
||||
<a href="#security">Security</a>
|
||||
<a href="#commands">Commands</a>
|
||||
<a href="https://github.com/snarktank/antfarm" class="topbar-gh">
|
||||
<svg height="18" width="18" viewBox="0 0 16 16" fill="currentColor"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"/></svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<main class="container">
|
||||
<!-- Hero -->
|
||||
<section class="hero">
|
||||
<div class="hero-row">
|
||||
<img src="logo.jpeg" alt="Antfarm" class="hero-logo">
|
||||
<h1>Build your agent team in <a href="https://docs.openclaw.ai">OpenClaw</a> with one command</h1>
|
||||
</div>
|
||||
<p class="hero-sub">You don't need to hire a dev team. You need to define one. Antfarm gives you a team of specialized AI agents — planner, developer, verifier, tester, reviewer — that work together in reliable, repeatable workflows. One install. Zero infrastructure.</p>
|
||||
<div class="hero-actions">
|
||||
<div class="install-cmd">
|
||||
<code><span class="cmd-prompt">$</span> install github.com/snarktank/antfarm</code>
|
||||
<button class="copy-btn" onclick="navigator.clipboard.writeText('install github.com/snarktank/antfarm').then(()=>{this.textContent='Copied!';setTimeout(()=>this.textContent='Copy',1500)})" title="Copy to clipboard">Copy</button>
|
||||
</div>
|
||||
<p class="install-hint">Tell your OpenClaw agent. That's it.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- The problem -->
|
||||
<section class="section problem-section">
|
||||
<h2 class="problem-text">Antfarm gives you a team of agents that specialize, verify each other, and run the same playbook every time.</h2>
|
||||
</section>
|
||||
|
||||
<!-- Bundled workflows -->
|
||||
<section id="workflows" class="section">
|
||||
<h2>What you get: Agent team workflows</h2>
|
||||
<div class="workflow-grid">
|
||||
<div class="wf-card">
|
||||
<div class="wf-header">
|
||||
<h3>feature-dev</h3>
|
||||
<span class="wf-badge">7 agents</span>
|
||||
</div>
|
||||
<p>Drop in a feature request. Get back a tested PR. The planner decomposes your task into stories. Each story gets implemented, verified, and tested in isolation. Failures retry automatically. Nothing ships without a code review.</p>
|
||||
<div class="wf-pipeline">
|
||||
<span>plan</span><span class="wf-arrow">→</span>
|
||||
<span>setup</span><span class="wf-arrow">→</span>
|
||||
<span>implement</span><span class="wf-arrow">→</span>
|
||||
<span>verify</span><span class="wf-arrow">→</span>
|
||||
<span>test</span><span class="wf-arrow">→</span>
|
||||
<span>PR</span><span class="wf-arrow">→</span>
|
||||
<span>review</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="wf-card">
|
||||
<div class="wf-header">
|
||||
<h3>security-audit</h3>
|
||||
<span class="wf-badge">7 agents</span>
|
||||
</div>
|
||||
<p>Point it at a repo. Get back a security fix PR with regression tests. Scans for vulnerabilities, ranks by severity, patches each one, re-audits after all fixes are applied.</p>
|
||||
<div class="wf-pipeline">
|
||||
<span>scan</span><span class="wf-arrow">→</span>
|
||||
<span>prioritize</span><span class="wf-arrow">→</span>
|
||||
<span>setup</span><span class="wf-arrow">→</span>
|
||||
<span>fix</span><span class="wf-arrow">→</span>
|
||||
<span>verify</span><span class="wf-arrow">→</span>
|
||||
<span>test</span><span class="wf-arrow">→</span>
|
||||
<span>PR</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="wf-card">
|
||||
<div class="wf-header">
|
||||
<h3>bug-fix</h3>
|
||||
<span class="wf-badge">6 agents</span>
|
||||
</div>
|
||||
<p>Paste a bug report. Get back a fix with a regression test. Triager reproduces it, investigator finds root cause, fixer patches, verifier confirms. Zero babysitting.</p>
|
||||
<div class="wf-pipeline">
|
||||
<span>triage</span><span class="wf-arrow">→</span>
|
||||
<span>investigate</span><span class="wf-arrow">→</span>
|
||||
<span>setup</span><span class="wf-arrow">→</span>
|
||||
<span>fix</span><span class="wf-arrow">→</span>
|
||||
<span>verify</span><span class="wf-arrow">→</span>
|
||||
<span>PR</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Why it works -->
|
||||
<section class="section">
|
||||
<h2>Why it works</h2>
|
||||
<div class="steps-grid">
|
||||
<div class="step-card">
|
||||
<h3>Deterministic workflows</h3>
|
||||
<p>Same workflow, same steps, same order. Not "hopefully the agent remembers to test."</p>
|
||||
</div>
|
||||
<div class="step-card">
|
||||
<h3>Agents verify each other</h3>
|
||||
<p>The developer doesn't mark their own homework. A separate verifier checks every story against acceptance criteria.</p>
|
||||
</div>
|
||||
<div class="step-card">
|
||||
<h3>Fresh context, every step</h3>
|
||||
<p>Each agent gets a clean session. No context window bloat. No hallucinated state from 50 messages ago.</p>
|
||||
</div>
|
||||
<div class="step-card">
|
||||
<h3>Retry and escalate</h3>
|
||||
<p>Failed steps retry automatically. If retries exhaust, it escalates to you. Nothing fails silently.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- How it works -->
|
||||
<section class="section">
|
||||
<h2>How it works</h2>
|
||||
<div class="steps-grid">
|
||||
<div class="step-card">
|
||||
<div class="step-num">1</div>
|
||||
<h3>Define</h3>
|
||||
<p>Agents and steps in YAML. Each agent gets a persona, workspace, and strict acceptance criteria. No ambiguity about who does what.</p>
|
||||
</div>
|
||||
<div class="step-card">
|
||||
<div class="step-num">2</div>
|
||||
<h3>Install</h3>
|
||||
<p>One command provisions everything: agent workspaces, cron polling, subagent permissions. No Docker, no queues, no external services.</p>
|
||||
</div>
|
||||
<div class="step-card">
|
||||
<div class="step-num">3</div>
|
||||
<h3>Run</h3>
|
||||
<p>Agents poll for work independently. Claim a step, do the work, pass context to the next agent. SQLite tracks state. Cron keeps it moving.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Minimal by design -->
|
||||
<section class="section minimal-section">
|
||||
<h2>Minimal by design</h2>
|
||||
<p class="section-desc">YAML + SQLite + cron. That's it. No Redis, no Kafka, no container orchestrator. Antfarm is a TypeScript CLI with zero external dependencies. It runs wherever OpenClaw runs.</p>
|
||||
</section>
|
||||
|
||||
<!-- Ralph loop -->
|
||||
<section class="section ralph-section">
|
||||
<div class="ralph-row">
|
||||
<img src="https://raw.githubusercontent.com/snarktank/ralph/main/ralph.webp" alt="Ralph" class="ralph-img">
|
||||
<div>
|
||||
<h3>Built on the Ralph loop</h3>
|
||||
<p>Each agent runs in a fresh session with clean context. Memory persists through git history and progress files — the same autonomous loop pattern from <a href="https://github.com/snarktank/ralph">Ralph</a>, scaled to multi-agent workflows.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Quick example -->
|
||||
<section class="section">
|
||||
<h2>Quick example</h2>
|
||||
<div class="code-block">
|
||||
<div class="code-header">
|
||||
<span class="code-tab active">Terminal</span>
|
||||
</div>
|
||||
<pre><code><span class="c-prompt">$</span> antfarm workflow install feature-dev
|
||||
<span class="c-ok">✓</span> Installed workflow: feature-dev
|
||||
|
||||
<span class="c-prompt">$</span> antfarm workflow run feature-dev <span class="c-str">"Add user authentication with OAuth"</span>
|
||||
<span class="c-dim">Run: a1fdf573</span>
|
||||
<span class="c-dim">Workflow: feature-dev</span>
|
||||
<span class="c-dim">Status: running</span>
|
||||
|
||||
<span class="c-prompt">$</span> antfarm workflow status <span class="c-str">"OAuth"</span>
|
||||
<span class="c-dim">Run: a1fdf573</span>
|
||||
<span class="c-dim">Workflow: feature-dev</span>
|
||||
<span class="c-dim">Steps:</span>
|
||||
<span class="c-ok">[done ]</span> plan (planner)
|
||||
<span class="c-ok">[done ]</span> setup (setup)
|
||||
<span class="c-run">[running]</span> implement (developer) <span class="c-dim">Stories: 3/7 done</span>
|
||||
<span class="c-pend">[pending]</span> verify (verifier)
|
||||
<span class="c-pend">[pending]</span> test (tester)
|
||||
<span class="c-pend">[pending]</span> pr (developer)
|
||||
<span class="c-pend">[pending]</span> review (reviewer)</code></pre>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Build your own -->
|
||||
<section class="section">
|
||||
<h2>Build your own</h2>
|
||||
<p class="section-desc">The bundled workflows are starting points. Define your own agents, steps, retry logic, and verification gates in plain YAML and Markdown. If you can write a prompt, you can build a workflow.</p>
|
||||
<div class="code-block">
|
||||
<div class="code-header">
|
||||
<span class="code-tab active">workflow.yml</span>
|
||||
</div>
|
||||
<pre><code><span class="c-key">id:</span> my-workflow
|
||||
<span class="c-key">name:</span> My Custom Workflow
|
||||
<span class="c-key">agents:</span>
|
||||
- <span class="c-key">id:</span> researcher
|
||||
<span class="c-key">name:</span> Researcher
|
||||
<span class="c-key">workspace:</span>
|
||||
<span class="c-key">files:</span>
|
||||
<span class="c-key">AGENTS.md:</span> agents/researcher/AGENTS.md
|
||||
|
||||
<span class="c-key">steps:</span>
|
||||
- <span class="c-key">id:</span> research
|
||||
<span class="c-key">agent:</span> researcher
|
||||
<span class="c-key">input:</span> <span class="c-str">|</span>
|
||||
<span class="c-str">Research {{task}} and report findings.</span>
|
||||
<span class="c-str">Reply with STATUS: done and FINDINGS: ...</span>
|
||||
<span class="c-key">expects:</span> <span class="c-str">"STATUS: done"</span></code></pre>
|
||||
</div>
|
||||
<p class="section-link">Full guide: <a href="https://github.com/snarktank/antfarm/blob/main/docs/creating-workflows.md">docs/creating-workflows.md</a></p>
|
||||
</section>
|
||||
|
||||
<!-- Security -->
|
||||
<section id="security" class="section">
|
||||
<h2>Security</h2>
|
||||
<p class="section-desc">You're installing agent teams that run code on your machine. We take that seriously.</p>
|
||||
<div class="security-grid">
|
||||
<div class="security-item">
|
||||
<h4>Curated repo only</h4>
|
||||
<p>Antfarm only installs workflows from the official <a href="https://github.com/snarktank/antfarm">snarktank/antfarm</a> repository. No arbitrary remote sources.</p>
|
||||
</div>
|
||||
<div class="security-item">
|
||||
<h4>Reviewed for prompt injection</h4>
|
||||
<p>Every workflow is reviewed for prompt injection attacks and malicious agent files before merging.</p>
|
||||
</div>
|
||||
<div class="security-item">
|
||||
<h4>Community contributions welcome</h4>
|
||||
<p>Want to add a workflow? Submit a PR. All submissions go through careful security review before they ship.</p>
|
||||
</div>
|
||||
<div class="security-item">
|
||||
<h4>Transparent by default</h4>
|
||||
<p>Every workflow is plain YAML and Markdown. You can read exactly what each agent will do before you install it.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Dashboard -->
|
||||
<section class="section">
|
||||
<h2>Dashboard</h2>
|
||||
<p class="section-desc">Monitor runs, track step progress, and view agent output in real time.</p>
|
||||
<div class="dashboard-frame">
|
||||
<img src="dashboard-screenshot.png" alt="Antfarm dashboard showing workflow runs and step status">
|
||||
</div>
|
||||
<div class="dashboard-frame" style="margin-top:16px">
|
||||
<img src="dashboard-detail-screenshot.png" alt="Antfarm dashboard showing run detail with stories">
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Commands -->
|
||||
<section id="commands" class="section">
|
||||
<h2>Commands</h2>
|
||||
<div class="cmd-grid">
|
||||
<div class="cmd-group">
|
||||
<h4>Lifecycle</h4>
|
||||
<div class="cmd-row"><code>antfarm install</code><span>Install all bundled workflows</span></div>
|
||||
<div class="cmd-row"><code>antfarm uninstall</code><span>Full teardown (agents, crons, DB)</span></div>
|
||||
</div>
|
||||
<div class="cmd-group">
|
||||
<h4>Workflows</h4>
|
||||
<div class="cmd-row"><code>antfarm workflow run <id> <task></code><span>Start a run</span></div>
|
||||
<div class="cmd-row"><code>antfarm workflow status <query></code><span>Check run status</span></div>
|
||||
<div class="cmd-row"><code>antfarm workflow runs</code><span>List all runs</span></div>
|
||||
<div class="cmd-row"><code>antfarm workflow resume <run-id></code><span>Resume a failed run</span></div>
|
||||
</div>
|
||||
<div class="cmd-group">
|
||||
<h4>Management</h4>
|
||||
<div class="cmd-row"><code>antfarm workflow list</code><span>List available workflows</span></div>
|
||||
<div class="cmd-row"><code>antfarm workflow install <id></code><span>Install a single workflow</span></div>
|
||||
<div class="cmd-row"><code>antfarm workflow uninstall <id></code><span>Remove a single workflow</span></div>
|
||||
<div class="cmd-row"><code>antfarm dashboard</code><span>Start the web dashboard</span></div>
|
||||
<div class="cmd-row"><code>antfarm logs</code><span>View recent log entries</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer class="footer">
|
||||
<div class="footer-inner">
|
||||
<p>Part of the <a href="https://docs.openclaw.ai">OpenClaw</a> ecosystem</p>
|
||||
<p>Built by <a href="https://ryancarson.com">Ryan Carson</a></p>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
BIN
antfarm/landing/logo.jpeg
Normal file
BIN
antfarm/landing/logo.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
BIN
antfarm/landing/og-image.png
Normal file
BIN
antfarm/landing/og-image.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 111 KiB |
22
antfarm/landing/progress.txt
Normal file
22
antfarm/landing/progress.txt
Normal file
@@ -0,0 +1,22 @@
|
||||
# Progress Log
|
||||
Run: redesign-landing-page
|
||||
Task: Redesign the Antfarm landing page to utilitarian repo-style
|
||||
Started: 2026-02-08 16:16 ET
|
||||
|
||||
## Codebase Patterns
|
||||
- Tests in `landing/__tests__/landing.test.js` check for: id="features", id="quickstart", id="commands", `.hero` and `.feature-grid` in CSS, `@media` in CSS, and that all `href="#x"` have matching `id="x"`
|
||||
- Tests use `node:test` + `node:assert/strict`, run with `node --test`
|
||||
- Landing page is pure HTML+CSS, no JS, no build step
|
||||
|
||||
---
|
||||
|
||||
## 2026-02-08 16:16 - US-001: Rewrite landing page HTML with utilitarian repo-style layout
|
||||
- Replaced marketing-style landing page with GitHub-repo-aesthetic utilitarian layout
|
||||
- 7 sections: header (logo + GitHub link), description, install (id="features"), example (id="quickstart"), dashboard placeholder, commands table (id="commands"), footer
|
||||
- Dark theme using GitHub-like color palette (#0d1117 bg, #58a6ff accent)
|
||||
- Zero emoji anywhere
|
||||
- Kept .hero class on header, .feature-grid in CSS for test compat
|
||||
- Responsive with @media breakpoint at 640px
|
||||
- Files changed: landing/index.html, landing/style.css
|
||||
- **Learnings:** Tests check CSS for class names (.hero, .feature-grid) even if not used in HTML, so keep them in the stylesheet
|
||||
---
|
||||
566
antfarm/landing/style.css
Normal file
566
antfarm/landing/style.css
Normal file
@@ -0,0 +1,566 @@
|
||||
/* === Reset === */
|
||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
|
||||
@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@400;500;600&family=IBM+Plex+Mono:wght@400;500&display=swap');
|
||||
|
||||
:root {
|
||||
--bg: #FAF8F5;
|
||||
--bg-card: #FFFFFF;
|
||||
--bg-code: #2D2A24;
|
||||
--bg-code-header: #3A3630;
|
||||
--text: #3A3226;
|
||||
--text-secondary: #6B6358;
|
||||
--text-muted: #9B9183;
|
||||
--accent: #6B7F3B;
|
||||
--accent-light: #7E9544;
|
||||
--coral: #E8845C;
|
||||
--border: #E2D9CC;
|
||||
--border-light: #EDE7DE;
|
||||
--radius: 8px;
|
||||
--radius-lg: 12px;
|
||||
--shadow-sm: 0 1px 2px rgba(58, 50, 38, 0.06);
|
||||
--shadow-md: 0 2px 8px rgba(58, 50, 38, 0.08);
|
||||
}
|
||||
|
||||
html { scroll-behavior: smooth; }
|
||||
|
||||
body {
|
||||
font-family: 'IBM Plex Sans', -apple-system, BlinkMacSystemFont, sans-serif;
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
line-height: 1.6;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
a { color: var(--accent); text-decoration: none; }
|
||||
a:hover { color: var(--accent-light); }
|
||||
|
||||
code {
|
||||
font-family: 'IBM Plex Mono', 'SFMono-Regular', Consolas, monospace;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
/* === Topbar === */
|
||||
.topbar {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
background: rgba(250, 248, 245, 0.92);
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
border-bottom: 1px solid var(--border-light);
|
||||
}
|
||||
|
||||
.topbar-inner {
|
||||
max-width: 1080px;
|
||||
margin: 0 auto;
|
||||
padding: 12px 32px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.topbar-brand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.topbar-logo {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.topbar-name {
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
color: var(--text);
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
|
||||
.topbar-links {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.topbar-links a {
|
||||
font-size: 0.85rem;
|
||||
color: var(--text-secondary);
|
||||
transition: color 0.15s;
|
||||
}
|
||||
|
||||
.topbar-links a:hover { color: var(--text); }
|
||||
|
||||
.topbar-gh {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: var(--text-muted) !important;
|
||||
}
|
||||
.topbar-gh:hover { color: var(--text) !important; }
|
||||
|
||||
/* === Container === */
|
||||
.container {
|
||||
max-width: 1080px;
|
||||
margin: 0 auto;
|
||||
padding: 0 32px;
|
||||
}
|
||||
|
||||
/* === Hero === */
|
||||
.hero {
|
||||
padding: 72px 0 56px;
|
||||
border-bottom: 1px solid var(--border-light);
|
||||
}
|
||||
|
||||
.hero-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.hero-sub {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.hero-logo {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
border-radius: var(--radius-lg);
|
||||
flex-shrink: 0;
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
.hero h1 {
|
||||
font-size: 2rem;
|
||||
font-weight: 600;
|
||||
line-height: 1.3;
|
||||
letter-spacing: -0.025em;
|
||||
max-width: 680px;
|
||||
}
|
||||
|
||||
.hero h1 a { text-decoration: underline; text-underline-offset: 3px; }
|
||||
|
||||
.hero-sub {
|
||||
font-size: 1.1rem;
|
||||
color: var(--text-secondary);
|
||||
max-width: 640px;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.hero-actions { margin-top: 32px; }
|
||||
|
||||
.install-cmd {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
background: var(--bg-code);
|
||||
border-radius: var(--radius);
|
||||
padding: 12px 20px;
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
.copy-btn {
|
||||
background: transparent;
|
||||
border: 1px solid rgba(232, 223, 208, 0.3);
|
||||
color: #E8DFD0;
|
||||
font-size: 0.75rem;
|
||||
padding: 4px 10px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.copy-btn:hover {
|
||||
background: rgba(232, 223, 208, 0.1);
|
||||
border-color: rgba(232, 223, 208, 0.5);
|
||||
}
|
||||
|
||||
.install-cmd code {
|
||||
color: #E8DFD0;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.cmd-prompt { color: var(--coral); margin-right: 8px; }
|
||||
|
||||
.install-hint {
|
||||
margin-top: 10px;
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
/* === Sections === */
|
||||
.section {
|
||||
padding: 56px 0;
|
||||
border-bottom: 1px solid var(--border-light);
|
||||
}
|
||||
|
||||
h2.problem-text {
|
||||
font-size: 1.3rem;
|
||||
font-weight: 600;
|
||||
color: var(--text);
|
||||
line-height: 1.5;
|
||||
letter-spacing: -0.015em;
|
||||
max-width: 680px;
|
||||
}
|
||||
|
||||
.ralph-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 24px;
|
||||
}
|
||||
.ralph-img {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border-radius: 12px;
|
||||
flex-shrink: 0;
|
||||
object-fit: cover;
|
||||
}
|
||||
.ralph-row h3 { margin: 0 0 8px; }
|
||||
.ralph-row p { margin: 0; color: var(--text-muted); font-size: 0.9rem; }
|
||||
.ralph-row a { color: var(--coral); }
|
||||
|
||||
.section h2 {
|
||||
font-size: 1.3rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: -0.015em;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.section-desc {
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.95rem;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.section-link {
|
||||
margin-top: 16px;
|
||||
font-size: 0.85rem;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.section-link a { text-decoration: underline; text-underline-offset: 2px; }
|
||||
|
||||
/* === Steps grid === */
|
||||
.steps-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 16px;
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
.step-card {
|
||||
background: var(--bg-card);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: 24px;
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.step-num {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 50%;
|
||||
background: var(--accent);
|
||||
color: white;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.step-card h3 {
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.step-card p {
|
||||
font-size: 0.875rem;
|
||||
color: var(--text-secondary);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.step-card code {
|
||||
background: var(--bg);
|
||||
padding: 1px 5px;
|
||||
border-radius: 3px;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
/* === Code blocks === */
|
||||
.code-block {
|
||||
border: 1px solid #44403A;
|
||||
border-radius: var(--radius-lg);
|
||||
overflow: hidden;
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
.code-header {
|
||||
background: var(--bg-code-header);
|
||||
padding: 8px 16px;
|
||||
border-bottom: 1px solid #44403A;
|
||||
}
|
||||
|
||||
.code-tab {
|
||||
font-size: 0.75rem;
|
||||
color: #A89F93;
|
||||
font-family: 'IBM Plex Mono', monospace;
|
||||
}
|
||||
|
||||
.code-tab.active { color: #D4CABC; }
|
||||
|
||||
.code-block pre {
|
||||
background: var(--bg-code);
|
||||
margin: 0;
|
||||
padding: 20px 24px;
|
||||
overflow-x: auto;
|
||||
line-height: 1.75;
|
||||
}
|
||||
|
||||
.code-block code {
|
||||
color: #E8DFD0;
|
||||
font-size: 0.825rem;
|
||||
background: none;
|
||||
}
|
||||
|
||||
.c-prompt { color: var(--coral); }
|
||||
.c-dim { color: #8B8278; }
|
||||
.c-ok { color: #8CAA50; }
|
||||
.c-run { color: #D4A843; }
|
||||
.c-pend { color: #7B7368; }
|
||||
.c-str { color: #C9A96E; }
|
||||
.c-key { color: #8CAA50; }
|
||||
.c-comment { color: #6B6358; }
|
||||
|
||||
/* === Workflow grid === */
|
||||
.workflow-grid {
|
||||
display: grid;
|
||||
gap: 16px;
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
.wf-card {
|
||||
background: var(--bg-card);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: 24px;
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.wf-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.wf-header h3 {
|
||||
font-family: 'IBM Plex Mono', monospace;
|
||||
font-size: 0.95rem;
|
||||
font-weight: 600;
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.wf-badge {
|
||||
font-size: 0.7rem;
|
||||
color: var(--text-muted);
|
||||
background: var(--bg);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 999px;
|
||||
padding: 2px 10px;
|
||||
}
|
||||
|
||||
.wf-card > p {
|
||||
font-size: 0.875rem;
|
||||
color: var(--text-secondary);
|
||||
line-height: 1.65;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.wf-pipeline {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px;
|
||||
font-family: 'IBM Plex Mono', monospace;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.wf-pipeline span {
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.wf-arrow {
|
||||
color: var(--text-muted);
|
||||
margin: 0 2px;
|
||||
}
|
||||
|
||||
/* === Security === */
|
||||
.security-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 16px;
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
.security-item {
|
||||
background: var(--bg-card);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: 24px;
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.security-item h4 {
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.security-item p {
|
||||
font-size: 0.85rem;
|
||||
color: var(--text-secondary);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.security-grid { grid-template-columns: 1fr; }
|
||||
}
|
||||
|
||||
/* === Dashboard === */
|
||||
.dashboard-frame {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-lg);
|
||||
overflow: hidden;
|
||||
box-shadow: var(--shadow-md);
|
||||
background: var(--bg-card);
|
||||
}
|
||||
|
||||
.dashboard-frame img {
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* === Commands === */
|
||||
.cmd-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
gap: 24px;
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
.cmd-group h4 {
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.06em;
|
||||
color: var(--text-muted);
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.cmd-row {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 12px;
|
||||
padding: 6px 0;
|
||||
border-bottom: 1px solid var(--border-light);
|
||||
}
|
||||
|
||||
.cmd-row:last-child { border-bottom: none; }
|
||||
|
||||
.cmd-row code {
|
||||
color: var(--coral);
|
||||
white-space: nowrap;
|
||||
background: none;
|
||||
font-size: 0.8rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.cmd-row span {
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
/* === Architecture === */
|
||||
.arch-block {
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
.arch-block pre {
|
||||
background: var(--bg-code);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: 24px 28px;
|
||||
overflow-x: auto;
|
||||
line-height: 1.6;
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
.arch-block code {
|
||||
color: #E8DFD0;
|
||||
font-size: 0.8rem;
|
||||
background: none;
|
||||
}
|
||||
|
||||
/* === Footer === */
|
||||
.footer {
|
||||
padding: 32px 0;
|
||||
}
|
||||
|
||||
.footer-inner {
|
||||
max-width: 1080px;
|
||||
margin: 0 auto;
|
||||
padding: 0 32px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.footer p {
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.footer a { color: var(--text-secondary); }
|
||||
.footer a:hover { color: var(--text); }
|
||||
|
||||
/* === Responsive === */
|
||||
@media (max-width: 768px) {
|
||||
.container { padding: 0 20px; }
|
||||
.topbar-inner { padding: 12px 20px; }
|
||||
.topbar-links a:not(.topbar-gh) { display: none; }
|
||||
|
||||
.hero { padding: 48px 0 40px; }
|
||||
.hero-row { flex-direction: row; gap: 24px; align-items: flex-start; }
|
||||
.hero-logo { width: 100px; height: 100px; }
|
||||
.hero h1 { font-size: 1.5rem; }
|
||||
.hero-sub { font-size: 1rem; }
|
||||
|
||||
.steps-grid { grid-template-columns: 1fr; }
|
||||
|
||||
.section { padding: 40px 0; }
|
||||
|
||||
.cmd-grid { grid-template-columns: 1fr; }
|
||||
.cmd-row { flex-direction: column; gap: 2px; }
|
||||
.cmd-row code { white-space: normal; }
|
||||
|
||||
.wf-pipeline { font-size: 0.7rem; }
|
||||
|
||||
.footer-inner {
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
padding: 0 20px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.hero-row { gap: 16px; }
|
||||
.hero-logo { width: 80px; height: 80px; }
|
||||
.hero h1 { font-size: 1.3rem; }
|
||||
.install-cmd { padding: 10px 14px; }
|
||||
.install-cmd code { font-size: 0.8rem; }
|
||||
.code-block pre { padding: 14px 16px; }
|
||||
.arch-block pre { padding: 16px 18px; }
|
||||
}
|
||||
Reference in New Issue
Block a user