arcrun — AI workflow execution engine (clean history)
Self-hosted 開源:WASM 零件 + recipe + cypher-executor,跑在你自己的 Cloudflare。 此為重建的乾淨歷史起點(移除曾誤 commit 的 GCP SA 金鑰,舊歷史保留在 richblack/arcrun 與本地 backup 分支)。含: - acr init --self-hosted installer(建 KV/R2 + codeload 拉預編譯 wasm + wrangler deploy + seed recipe) - recipe push 把關(資料外流提醒 + 打通檢查) - 19 個正當零件預編譯 wasm(claude_api/km_writer/kbdb_upsert_block 排除:違反 DECISIONS §1) - CLI / cypher-executor / registry / 完整 SDD Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
// Top nav and sidebar
|
||||
|
||||
const TopNav = ({ onNav, current }) => {
|
||||
const [scrolled, setScrolled] = React.useState(false);
|
||||
React.useEffect(() => {
|
||||
const onScroll = () => setScrolled(window.scrollY > 8);
|
||||
window.addEventListener('scroll', onScroll);
|
||||
return () => window.removeEventListener('scroll', onScroll);
|
||||
}, []);
|
||||
return (
|
||||
<nav className={`topnav ${scrolled ? 'scrolled' : ''}`}>
|
||||
<div className="flex gap-12" style={{alignItems: 'center'}}>
|
||||
<Logo onClick={() => onNav('landing')} />
|
||||
<div className="nav-links" style={{marginLeft: 20}}>
|
||||
<a>Product</a>
|
||||
<a>Docs</a>
|
||||
<a>Pricing</a>
|
||||
<a>Changelog</a>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-8" style={{alignItems: 'center'}}>
|
||||
<button className="btn btn-ghost" onClick={() => onNav('auth')}>Log in</button>
|
||||
<button className="btn btn-primary" onClick={() => onNav('auth')}>
|
||||
Get started <Icon name="arrow_right" size={14} />
|
||||
</button>
|
||||
</div>
|
||||
</nav>
|
||||
);
|
||||
};
|
||||
|
||||
const Footer = ({ onNav }) => (
|
||||
<footer className="footer">
|
||||
<div className="flex gap-12" style={{alignItems: 'center'}}>
|
||||
<Logo size="sm" />
|
||||
<span>© 2026 Arcrun Labs</span>
|
||||
</div>
|
||||
<div className="footer-links">
|
||||
<a>Docs</a>
|
||||
<a>Pricing</a>
|
||||
<a>Changelog</a>
|
||||
<a>Status</a>
|
||||
<a>Privacy</a>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
|
||||
// App shell with sidebar for logged-in screens
|
||||
const Sidebar = ({ current, onNav }) => {
|
||||
const items = [
|
||||
{ id: 'dashboard', label: 'Dashboard', icon: 'home' },
|
||||
{ id: 'apps', label: 'Apps', icon: 'grid', count: 6 },
|
||||
{ id: 'workflows', label: 'Workflows', icon: 'workflow', count: 12 },
|
||||
{ id: 'keys', label: 'API Keys', icon: 'key' },
|
||||
{ id: 'docs', label: 'Docs', icon: 'book' },
|
||||
];
|
||||
const bottom = [
|
||||
{ id: 'settings', label: 'Settings', icon: 'settings' },
|
||||
];
|
||||
return (
|
||||
<aside className="sidebar">
|
||||
<div className="sidebar-head">
|
||||
<Logo size="md" onClick={() => onNav('landing')} />
|
||||
</div>
|
||||
<div className="sidebar-section">Workspace</div>
|
||||
{items.map(it => (
|
||||
<div key={it.id}
|
||||
className={`sidebar-item ${current === it.id ? 'active' : ''}`}
|
||||
onClick={() => onNav(it.id)}>
|
||||
<span className="sb-ico"><Icon name={it.icon} size={15} /></span>
|
||||
<span>{it.label}</span>
|
||||
{it.count != null && <span className="sb-count">{it.count}</span>}
|
||||
</div>
|
||||
))}
|
||||
<div style={{flex: 1}} />
|
||||
{bottom.map(it => (
|
||||
<div key={it.id} className="sidebar-item" onClick={() => onNav(it.id)}>
|
||||
<span className="sb-ico"><Icon name={it.icon} size={15} /></span>
|
||||
<span>{it.label}</span>
|
||||
</div>
|
||||
))}
|
||||
<div className="sidebar-foot">
|
||||
<div className="avatar-circ">MR</div>
|
||||
<div className="meta">
|
||||
<div className="name">Maya Rivera</div>
|
||||
<div className="email">maya@northwind.co</div>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
);
|
||||
};
|
||||
|
||||
Object.assign(window, { TopNav, Footer, Sidebar });
|
||||
@@ -0,0 +1,86 @@
|
||||
// Shared primitives: icons, logo, etc.
|
||||
|
||||
const Icon = ({ name, size = 16, stroke = 1.7 }) => {
|
||||
const paths = {
|
||||
arrow_right: <path d="M5 12h14M13 6l6 6-6 6" />,
|
||||
arrow_left: <path d="M19 12H5M11 6l-6 6 6 6" />,
|
||||
plus: <path d="M12 5v14M5 12h14" />,
|
||||
copy: <><rect x="9" y="9" width="13" height="13" rx="2" /><path d="M5 15V5a2 2 0 0 1 2-2h10" /></>,
|
||||
check: <path d="M20 6L9 17l-5-5" />,
|
||||
close: <path d="M18 6L6 18M6 6l12 12" />,
|
||||
eye: <><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z" /><circle cx="12" cy="12" r="3" /></>,
|
||||
search: <><circle cx="11" cy="11" r="7" /><path d="M21 21l-4.35-4.35" /></>,
|
||||
warn: <><path d="M10.3 3.86L1.82 18a2 2 0 001.72 3h16.92a2 2 0 001.72-3L13.7 3.86a2 2 0 00-3.4 0z" /><line x1="12" y1="9" x2="12" y2="13" /><circle cx="12" cy="17" r="0.5" fill="currentColor" /></>,
|
||||
home: <><path d="M3 10l9-7 9 7v10a2 2 0 01-2 2h-4a2 2 0 01-2-2v-5h-2v5a2 2 0 01-2 2H5a2 2 0 01-2-2V10z" /></>,
|
||||
grid: <><rect x="3" y="3" width="7" height="7" rx="1" /><rect x="14" y="3" width="7" height="7" rx="1" /><rect x="3" y="14" width="7" height="7" rx="1" /><rect x="14" y="14" width="7" height="7" rx="1" /></>,
|
||||
workflow: <><circle cx="5" cy="6" r="2" /><circle cx="19" cy="12" r="2" /><circle cx="5" cy="18" r="2" /><path d="M7 6h4a4 4 0 014 4v0m0 4a4 4 0 01-4 4H7" /></>,
|
||||
key: <><circle cx="7.5" cy="15.5" r="4.5" /><path d="M10.68 12.32L21 2M17 6l3 3M15 8l3 3" /></>,
|
||||
book: <><path d="M2 3h6a4 4 0 014 4v14a3 3 0 00-3-3H2zM22 3h-6a4 4 0 00-4 4v14a3 3 0 013-3h7z" /></>,
|
||||
settings: <><circle cx="12" cy="12" r="3" /><path d="M19.4 15a1.65 1.65 0 00.33 1.82l.06.06a2 2 0 01-2.83 2.83l-.06-.06a1.65 1.65 0 00-1.82-.33 1.65 1.65 0 00-1 1.51V21a2 2 0 01-4 0v-.09A1.65 1.65 0 009 19.4a1.65 1.65 0 00-1.82.33l-.06.06a2 2 0 01-2.83-2.83l.06-.06a1.65 1.65 0 00.33-1.82 1.65 1.65 0 00-1.51-1H3a2 2 0 010-4h.09A1.65 1.65 0 004.6 9a1.65 1.65 0 00-.33-1.82l-.06-.06a2 2 0 012.83-2.83l.06.06A1.65 1.65 0 009 4.6a1.65 1.65 0 001-1.51V3a2 2 0 014 0v.09A1.65 1.65 0 0015 4.6a1.65 1.65 0 001.82-.33l.06-.06a2 2 0 012.83 2.83l-.06.06A1.65 1.65 0 0019.4 9v0a1.65 1.65 0 001.51 1H21a2 2 0 010 4h-.09a1.65 1.65 0 00-1.51 1z" /></>,
|
||||
chevron_right: <path d="M9 6l6 6-6 6" />,
|
||||
chevron_down: <path d="M6 9l6 6 6-6" />,
|
||||
external: <><path d="M18 13v6a2 2 0 01-2 2H5a2 2 0 01-2-2V8a2 2 0 012-2h6" /><path d="M15 3h6v6M10 14L21 3" /></>,
|
||||
trash: <><polyline points="3 6 5 6 21 6" /><path d="M19 6l-1 14a2 2 0 01-2 2H8a2 2 0 01-2-2L5 6M10 11v6M14 11v6" /></>,
|
||||
spark: <path d="M12 3l2.5 6.5L21 12l-6.5 2.5L12 21l-2.5-6.5L3 12l6.5-2.5L12 3z" />,
|
||||
bolt: <path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z" />,
|
||||
github: <path d="M12 2C6.48 2 2 6.48 2 12c0 4.42 2.87 8.17 6.84 9.5.5.08.66-.22.66-.48v-1.7c-2.78.6-3.36-1.34-3.36-1.34-.46-1.15-1.12-1.46-1.12-1.46-.92-.62.07-.6.07-.6 1.01.07 1.55 1.04 1.55 1.04.9 1.54 2.36 1.1 2.94.84.09-.65.35-1.1.64-1.35-2.22-.25-4.55-1.11-4.55-4.94 0-1.09.39-1.98 1.03-2.68-.1-.25-.45-1.27.1-2.65 0 0 .84-.27 2.75 1.02A9.5 9.5 0 0112 6.8c.85 0 1.7.11 2.5.33 1.9-1.3 2.75-1.02 2.75-1.02.55 1.38.2 2.4.1 2.65.64.7 1.03 1.6 1.03 2.68 0 3.84-2.34 4.69-4.57 4.93.36.31.68.92.68 1.85V21c0 .27.16.57.67.48A10 10 0 0022 12c0-5.52-4.48-10-10-10z" fill="currentColor" stroke="none" />,
|
||||
google: <><path d="M21.35 11.1h-9.17v2.73h5.24c-.23 1.41-1.69 4.13-5.24 4.13-3.15 0-5.73-2.62-5.73-5.86 0-3.24 2.58-5.86 5.73-5.86 1.8 0 3 .77 3.69 1.43l2.5-2.4C16.95 3.74 14.8 2.8 12.18 2.8c-5.26 0-9.53 4.25-9.53 9.5s4.27 9.5 9.53 9.5c5.51 0 9.15-3.87 9.15-9.32 0-.63-.07-1.1-.15-1.38z" fill="currentColor" stroke="none" /></>,
|
||||
share: <><circle cx="18" cy="5" r="3" /><circle cx="6" cy="12" r="3" /><circle cx="18" cy="19" r="3" /><path d="M8.59 13.51l6.83 3.98M15.41 6.51l-6.82 3.98" /></>,
|
||||
download: <><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4" /><polyline points="7 10 12 15 17 10" /><line x1="12" y1="15" x2="12" y2="3" /></>,
|
||||
zoom_in: <><circle cx="11" cy="11" r="7" /><line x1="21" y1="21" x2="16.65" y2="16.65" /><line x1="11" y1="8" x2="11" y2="14" /><line x1="8" y1="11" x2="14" y2="11" /></>,
|
||||
zoom_out: <><circle cx="11" cy="11" r="7" /><line x1="21" y1="21" x2="16.65" y2="16.65" /><line x1="8" y1="11" x2="14" y2="11" /></>,
|
||||
maximize: <><path d="M8 3H5a2 2 0 00-2 2v3M21 8V5a2 2 0 00-2-2h-3M3 16v3a2 2 0 002 2h3M16 21h3a2 2 0 002-2v-3" /></>,
|
||||
slack: <><rect x="13" y="2" width="3" height="8" rx="1.5" /><rect x="2" y="13" width="8" height="3" rx="1.5" /><rect x="14" y="14" width="8" height="3" rx="1.5" /><rect x="8" y="8" width="3" height="8" rx="1.5" /></>,
|
||||
database: <><ellipse cx="12" cy="5" rx="9" ry="3" /><path d="M3 5v7c0 1.66 4.03 3 9 3s9-1.34 9-3V5M3 12v7c0 1.66 4.03 3 9 3s9-1.34 9-3v-7" /></>,
|
||||
mail: <><rect x="2" y="4" width="20" height="16" rx="2" /><path d="M2 6l10 7 10-7" /></>,
|
||||
filter: <path d="M3 4h18l-7 9v6l-4-2v-4L3 4z" />,
|
||||
star: <polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2" />,
|
||||
linear: <><rect x="3" y="3" width="18" height="18" rx="4" /><path d="M7 11l5 5M7 15l3 3M7 7l10 10M11 7l6 6M15 7l2 2" /></>,
|
||||
clock: <><circle cx="12" cy="12" r="9" /><polyline points="12 7 12 12 16 14" /></>,
|
||||
send: <><path d="M22 2L11 13M22 2l-7 20-4-9-9-4 20-7z" /></>,
|
||||
terminal: <><path d="M4 17l6-6-6-6M12 19h8" /></>,
|
||||
logout: <><path d="M9 21H5a2 2 0 01-2-2V5a2 2 0 012-2h4M16 17l5-5-5-5M21 12H9" /></>,
|
||||
};
|
||||
|
||||
return (
|
||||
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={stroke} strokeLinecap="round" strokeLinejoin="round" style={{display: 'block', flexShrink: 0}}>
|
||||
{paths[name]}
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
// Arcrun wordmark — custom "arc" glyph made of an arc stroke + ascending dot/node
|
||||
const Logo = ({ size = 'md', onClick }) => {
|
||||
const dims = size === 'sm' ? { w: 18, h: 18, f: 10 } : size === 'lg' ? { w: 28, h: 28, f: 14 } : { w: 22, h: 22, f: 12 };
|
||||
return (
|
||||
<div className="logo" onClick={onClick}>
|
||||
<span className="logo-mark" style={{width: dims.w, height: dims.h}}>
|
||||
<svg width={dims.w} height={dims.h} viewBox="0 0 24 24" fill="none">
|
||||
<path d="M5 17 Q 12 4, 19 17" stroke="white" strokeWidth="2.4" strokeLinecap="round" fill="none" opacity="0.95" />
|
||||
<circle cx="19" cy="17" r="2.2" fill="white" />
|
||||
</svg>
|
||||
</span>
|
||||
<span>Arcrun</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// App icon with gradient background
|
||||
const AppIcon = ({ tone = 'indigo', children, size = 38 }) => {
|
||||
const tones = {
|
||||
indigo: 'linear-gradient(135deg, #6366F1, #8B5CF6)',
|
||||
orange: 'linear-gradient(135deg, #F59E0B, #EF4444)',
|
||||
green: 'linear-gradient(135deg, #10B981, #22C55E)',
|
||||
pink: 'linear-gradient(135deg, #EC4899, #8B5CF6)',
|
||||
blue: 'linear-gradient(135deg, #3B82F6, #06B6D4)',
|
||||
slate: 'linear-gradient(135deg, #475569, #334155)',
|
||||
amber: 'linear-gradient(135deg, #F59E0B, #D97706)',
|
||||
};
|
||||
return (
|
||||
<div className="app-icon" style={{ background: tones[tone], width: size, height: size, color: 'white' }}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Object.assign(window, { Icon, Logo, AppIcon });
|
||||
Reference in New Issue
Block a user